public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: caiyuqing_hz@163.com
To: devel@edk2.groups.io, sunilvl@ventanamicro.com
Cc: caiyuqing379 <202235273@mail.sdu.edu.cn>,
	USER0FISH <libing1202@outlook.com>
Subject: [edk2-devel] [PATCH v2 2/8] Sophgo/SG2042Pkg: Add PlatformUpdateMmuDxe module.
Date: Thu, 31 Aug 2023 21:44:18 +0800	[thread overview]
Message-ID: <d4bbdb23c113c1ef75dc91668399f546e6a10d17.1693483202.git.202235273@mail.sdu.edu.cn> (raw)
In-Reply-To: <cover.1693483202.git.202235273@mail.sdu.edu.cn>

From: caiyuqing379 <202235273@mail.sdu.edu.cn>

SG2042 (Xuantie C920) MMU can be enabled in SV39 mode. C920 has five
customizable page properties that control whether the page is Strong
order(SO), Cacheable(C), Bufferable(B), Shareable(SH), Trustable(Sec).
This driver modifies the page table attributes to avoid exceptions
based on the memory attributes of the C920.

Introduces a PCD variable PcdForceNoMMU to disable MMU configuration.
Currently, enabling MMU results in a timeout for reading data blocks
from the SD card.

Signed-off-by: caiyuqing379 <202235273@mail.sdu.edu.cn>
Co-authored-by: USER0FISH <libing1202@outlook.com>
Cc: dahogn <dahogn@hotmail.com>
Cc: meng-cz <mengcz1126@gmail.com>
Cc: yli147 <yong.li@intel.com>
Cc: ChaiEvan <evan.chai@intel.com>
Cc: Sunil V L <sunilvl@ventanamicro.com>
---
 .../PlatformUpdateMmuDxe.inf                  |  34 +
 .../PlatformUpdateMmuDxe.c                    | 591 ++++++++++++++++++
 2 files changed, 625 insertions(+)
 create mode 100644 Platform/Sophgo/SG2042Pkg/Universal/Dxe/PlatformUpdateMmuDxe/PlatformUpdateMmuDxe.inf
 create mode 100644 Platform/Sophgo/SG2042Pkg/Universal/Dxe/PlatformUpdateMmuDxe/PlatformUpdateMmuDxe.c

diff --git a/Platform/Sophgo/SG2042Pkg/Universal/Dxe/PlatformUpdateMmuDxe/PlatformUpdateMmuDxe.inf b/Platform/Sophgo/SG2042Pkg/Universal/Dxe/PlatformUpdateMmuDxe/PlatformUpdateMmuDxe.inf
new file mode 100644
index 000000000000..b65db77c2bd6
--- /dev/null
+++ b/Platform/Sophgo/SG2042Pkg/Universal/Dxe/PlatformUpdateMmuDxe/PlatformUpdateMmuDxe.inf
@@ -0,0 +1,34 @@
+## @file
+#  This driver modifies the page table attribute based on the memory attribute of the C920.
+#
+#  Copyright (c) 2023, Academy of Intelligent Innovation, Shandong Universiy, China.P.R. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION         = 0x0001001b
+  BASE_NAME           = PlatformUpdateMmuDxe
+  FILE_GUID           = 9d1dd27f-6d7f-427b-aec4-b62f6279c2f1
+  MODULE_TYPE         = UEFI_DRIVER
+  VERSION_STRING      = 1.0
+  ENTRY_POINT         = PlatformUpdateMmu
+
+[Sources]
+  PlatformUpdateMmuDxe.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  UefiCpuPkg/UefiCpuPkg.dec
+  Platform/Sophgo/SG2042Pkg/SG2042Pkg.dec
+
+[LibraryClasses]
+  BaseLib
+  UefiLib
+  UefiDriverEntryPoint
+  RiscVMmuLib
+  DxeServicesTableLib
+
+[FixedPcd]
+  gSophgoSG2042PlatformPkgTokenSpaceGuid.PcdForceNoMMU            ## CONSUMES
diff --git a/Platform/Sophgo/SG2042Pkg/Universal/Dxe/PlatformUpdateMmuDxe/PlatformUpdateMmuDxe.c b/Platform/Sophgo/SG2042Pkg/Universal/Dxe/PlatformUpdateMmuDxe/PlatformUpdateMmuDxe.c
new file mode 100644
index 000000000000..3b2020bbc023
--- /dev/null
+++ b/Platform/Sophgo/SG2042Pkg/Universal/Dxe/PlatformUpdateMmuDxe/PlatformUpdateMmuDxe.c
@@ -0,0 +1,591 @@
+/** @file
+  This driver modifies the page table attribute based on the memory attribute of the C920.
+  C920 has five customizable page properties that control whether the page is Strong order,
+  Cacheable, Bufferable, Shareable,Trustable.
+
+  Copyright (c) 2011-2020, ARM Limited. All rights reserved.
+  Copyright (c) 2016, Linaro Limited. All rights reserved.
+  Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
+  Copyright (c) 2023, Ventana Micro Systems Inc. All Rights Reserved.<BR>
+  Copyright (c) 2023, Academy of Intelligent Innovation, Shandong Universiy, China.P.R. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiDxe.h>
+#include <Uefi.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/BaseRiscVMmuLib.h>
+#include <Library/CacheMaintenanceLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PcdLib.h>
+#include <Register/RiscV64/RiscVEncoding.h>
+
+#define RISCV_PG_V           BIT0
+#define RISCV_PG_R           BIT1
+#define RISCV_PG_W           BIT2
+#define RISCV_PG_X           BIT3
+#define RISCV_PG_G           BIT5
+#define RISCV_PG_A           BIT6
+#define RISCV_PG_D           BIT7
+#define RISCV_PG_SH          BIT60
+#define RISCV_PG_B           BIT61
+#define RISCV_PG_C           BIT62
+#define RISCV_PG_SO          BIT63
+#define PTE_ATTRIBUTES_MASK  0xE
+
+#define PTE_PPN_MASK          0x3FFFFFFFFFFC00ULL
+#define PTE_PPN_SHIFT         10
+#define RISCV_MMU_PAGE_SHIFT  12
+
+STATIC UINTN  mMaxRootTableLevel;
+STATIC UINTN  mBitPerLevel;
+STATIC UINTN  mTableEntryCount;
+
+/**
+  Determine if the MMU enabled or not.
+
+  @retval TRUE  The MMU already enabled.
+  @retval FALSE The MMU not enabled.
+
+**/
+STATIC
+VOID
+RiscVMmuDisabled (
+  VOID
+  )
+{
+  RiscVSetSupervisorAddressTranslationRegister (SATP_MODE_OFF << SATP64_MODE_SHIFT);
+}
+
+/**
+  Determine if the MMU enabled or not.
+
+  @retval TRUE  The MMU already enabled.
+  @retval FALSE The MMU not enabled.
+
+**/
+STATIC
+BOOLEAN
+RiscVMmuEnabled (
+  VOID
+  )
+{
+  return ((RiscVGetSupervisorAddressTranslationRegister () &
+           SATP64_MODE) != (SATP_MODE_OFF << SATP64_MODE_SHIFT));
+}
+
+/**
+  Retrieve the root translate table.
+
+  @return The root translate table.
+
+**/
+STATIC
+UINTN
+RiscVGetRootTranslateTable (
+  VOID
+  )
+{
+  return (RiscVGetSupervisorAddressTranslationRegister () & SATP64_PPN) <<
+         RISCV_MMU_PAGE_SHIFT;
+}
+
+/**
+  Determine if an entry is valid pte.
+
+  @param    Entry   The entry value.
+
+  @retval   TRUE    The entry is a valid pte.
+  @retval   FALSE   The entry is not a valid pte.
+
+**/
+STATIC
+BOOLEAN
+IsValidPte (
+  IN  UINTN  Entry
+  )
+{
+  if (((Entry & RISCV_PG_V) == 0) ||
+      (((Entry & (RISCV_PG_R | RISCV_PG_W)) == RISCV_PG_W)))
+  {
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+/**
+  Set an entry to be a valid pte.
+
+  @param  Entry   The entry value.
+
+  @return         The entry value.
+
+**/
+STATIC
+UINTN
+SetValidPte (
+  IN  UINTN  Entry
+  )
+{
+  /* Set Valid and Global mapping bits */
+  return Entry | RISCV_PG_G | RISCV_PG_V;
+}
+
+/**
+  Determine if an entry is a block pte.
+
+  @param    Entry   The entry value.
+
+  @retval   TRUE    The entry is a block pte.
+  @retval   FALSE   The entry is not a block pte.
+
+**/
+STATIC
+BOOLEAN
+IsBlockEntry (
+  IN  UINTN  Entry
+  )
+{
+  return IsValidPte (Entry) &&
+         (Entry & (RISCV_PG_X | RISCV_PG_R));
+}
+
+/**
+  Determine if an entry is a table pte.
+
+  @param    Entry   The entry value.
+
+  @retval   TRUE    The entry is a table pte.
+  @retval   FALSE   The entry is not a table pte.
+
+**/
+STATIC
+BOOLEAN
+IsTableEntry (
+  IN  UINTN  Entry
+  )
+{
+  return IsValidPte (Entry) &&
+         !IsBlockEntry (Entry);
+}
+
+/**
+  Set an entry to be a table pte.
+
+  @param  Entry   The entry value.
+
+  @return         The entry value.
+
+**/
+STATIC
+UINTN
+SetTableEntry (
+  IN  UINTN  Entry
+  )
+{
+  Entry  = SetValidPte (Entry);
+  Entry &= ~(RISCV_PG_X | RISCV_PG_W | RISCV_PG_R);
+
+  return Entry;
+}
+
+/**
+  Replace an existing entry with new value.
+
+  @param  Entry               The entry pointer.
+  @param  Value               The new entry value.
+  @param  RegionStart         The start of region that new value affects.
+  @param  IsLiveBlockMapping  TRUE if this is live update, FALSE otherwise.
+
+**/
+STATIC
+VOID
+ReplaceTableEntry (
+  IN  UINTN    *Entry,
+  IN  UINTN    Value,
+  IN  UINTN    RegionStart,
+  IN  BOOLEAN  IsLiveBlockMapping
+  )
+{
+  *Entry = Value;
+
+  if (IsLiveBlockMapping && RiscVMmuEnabled ()) {
+    RiscVLocalTlbFlush (RegionStart);
+  }
+}
+
+/**
+  Get an ppn value from an entry.
+
+  @param  Entry   The entry value.
+
+  @return         The ppn value.
+
+**/
+STATIC
+UINTN
+GetPpnfromPte (
+  IN UINTN  Entry
+  )
+{
+  return ((Entry & PTE_PPN_MASK) >> PTE_PPN_SHIFT);
+}
+
+/**
+  Set an ppn value to a entry.
+
+  @param  Entry   The entry value.
+  @param  Address The address.
+
+  @return The new entry value.
+
+**/
+STATIC
+UINTN
+SetPpnToPte (
+  UINTN  Entry,
+  UINTN  Address
+  )
+{
+  UINTN  Ppn;
+
+  Ppn = ((Address >> RISCV_MMU_PAGE_SHIFT) << PTE_PPN_SHIFT);
+  ASSERT (~(Ppn & ~PTE_PPN_MASK));
+  Entry &= ~PTE_PPN_MASK;
+  return Entry | Ppn;
+}
+
+/**
+  Free resources of translation table recursively.
+
+  @param  TranslationTable  The pointer of table.
+  @param  Level             The current level.
+
+**/
+STATIC
+VOID
+FreePageTablesRecursive (
+  IN  UINTN  *TranslationTable,
+  IN  UINTN  Level
+  )
+{
+  UINTN  Index;
+
+  if (Level < mMaxRootTableLevel - 1) {
+    for (Index = 0; Index < mTableEntryCount; Index++) {
+      if (IsTableEntry (TranslationTable[Index])) {
+        FreePageTablesRecursive (
+          (UINTN *)(GetPpnfromPte ((TranslationTable[Index])) <<
+                    RISCV_MMU_PAGE_SHIFT),
+          Level + 1
+          );
+      }
+    }
+  }
+
+  FreePages (TranslationTable, 1);
+}
+
+/**
+  Update region mapping recursively.
+
+  @param  RegionStart           The start address of the region.
+  @param  RegionEnd             The end address of the region.
+  @param  AttributeSetMask      The attribute mask to be set.
+  @param  AttributeClearMask    The attribute mask to be clear.
+  @param  PageTable             The pointer of current page table.
+  @param  Level                 The current level.
+  @param  TableIsLive           TRUE if this is live update, FALSE otherwise.
+
+  @retval EFI_OUT_OF_RESOURCES  Not enough resource.
+  @retval EFI_SUCCESS           The operation succesfully.
+
+**/
+STATIC
+EFI_STATUS
+UpdateRegionMappingRecursive (
+  IN  UINTN    RegionStart,
+  IN  UINTN    RegionEnd,
+  IN  UINTN    AttributeSetMask,
+  IN  UINTN    AttributeClearMask,
+  IN  UINTN    *PageTable,
+  IN  UINTN    Level,
+  IN  BOOLEAN  TableIsLive
+  )
+{
+  EFI_STATUS  Status;
+  UINTN       BlockShift;
+  UINTN       BlockMask;
+  UINTN       BlockEnd;
+  UINTN       *Entry;
+  UINTN       EntryValue;
+  UINTN       *TranslationTable;
+  BOOLEAN     NextTableIsLive;
+
+  ASSERT (Level < mMaxRootTableLevel);
+  ASSERT (((RegionStart | RegionEnd) & EFI_PAGE_MASK) == 0);
+
+  BlockShift = (mMaxRootTableLevel - Level - 1) * mBitPerLevel + RISCV_MMU_PAGE_SHIFT;
+  BlockMask  = MAX_ADDRESS >> (64 - BlockShift);
+
+  DEBUG (
+    (
+     DEBUG_VERBOSE,
+     "%a(%d): %llx - %llx set %lx clr %lx\n",
+     __func__,
+     Level,
+     RegionStart,
+     RegionEnd,
+     AttributeSetMask,
+     AttributeClearMask
+    )
+    );
+
+  for ( ; RegionStart < RegionEnd; RegionStart = BlockEnd) {
+    BlockEnd = MIN (RegionEnd, (RegionStart | BlockMask) + 1);
+    Entry    = &PageTable[(RegionStart >> BlockShift) & (mTableEntryCount - 1)];
+
+    //
+    // If RegionStart or BlockEnd is not aligned to the block size at this
+    // level, we will have to create a table mapping in order to map less
+    // than a block, and recurse to create the block or page entries at
+    // the next level. No block mappings are allowed at all at level 0,
+    // so in that case, we have to recurse unconditionally.
+    //
+    if ((Level == 0) ||
+        (((RegionStart | BlockEnd) & BlockMask) != 0) || IsTableEntry (*Entry))
+    {
+      ASSERT (Level < mMaxRootTableLevel - 1);
+      if (!IsTableEntry (*Entry)) {
+        //
+        // No table entry exists yet, so we need to allocate a page table
+        // for the next level.
+        //
+        TranslationTable = AllocatePages (1);
+        if (TranslationTable == NULL) {
+          return EFI_OUT_OF_RESOURCES;
+        }
+
+        ZeroMem (TranslationTable, EFI_PAGE_SIZE);
+
+        if (IsBlockEntry (*Entry)) {
+          //
+          // We are splitting an existing block entry, so we have to populate
+          // the new table with the attributes of the block entry it replaces.
+          //
+          Status = UpdateRegionMappingRecursive (
+                     RegionStart & ~BlockMask,
+                     (RegionStart | BlockMask) + 1,
+                     *Entry & PTE_ATTRIBUTES_MASK,
+                     PTE_ATTRIBUTES_MASK,
+                     TranslationTable,
+                     Level + 1,
+                     FALSE
+                     );
+          if (EFI_ERROR (Status)) {
+            //
+            // The range we passed to UpdateRegionMappingRecursive () is block
+            // aligned, so it is guaranteed that no further pages were allocated
+            // by it, and so we only have to free the page we allocated here.
+            //
+            FreePages (TranslationTable, 1);
+            return Status;
+          }
+        }
+
+        NextTableIsLive = FALSE;
+      } else {
+        TranslationTable = (UINTN *)(GetPpnfromPte (*Entry) << RISCV_MMU_PAGE_SHIFT);
+        NextTableIsLive  = TableIsLive;
+      }
+
+      //
+      // Recurse to the next level
+      //
+      Status = UpdateRegionMappingRecursive (
+                 RegionStart,
+                 BlockEnd,
+                 AttributeSetMask,
+                 AttributeClearMask,
+                 TranslationTable,
+                 Level + 1,
+                 NextTableIsLive
+                 );
+      if (EFI_ERROR (Status)) {
+        if (!IsTableEntry (*Entry)) {
+          //
+          // We are creating a new table entry, so on failure, we can free all
+          // allocations we made recursively, given that the whole subhierarchy
+          // has not been wired into the live page tables yet. (This is not
+          // possible for existing table entries, since we cannot revert the
+          // modifications we made to the subhierarchy it represents.)
+          //
+          FreePageTablesRecursive (TranslationTable, Level + 1);
+        }
+
+        return Status;
+      }
+
+      if (!IsTableEntry (*Entry)) {
+        EntryValue = SetPpnToPte (0, (UINTN)TranslationTable);
+        EntryValue = SetTableEntry (EntryValue);
+        ReplaceTableEntry (
+          Entry,
+          EntryValue,
+          RegionStart,
+          TableIsLive
+          );
+      }
+    } else {
+      EntryValue = (*Entry & ~AttributeClearMask) | AttributeSetMask;
+      //
+      // We don't have page fault exception handler when a virtual page is accessed and
+      // the A bit is clear, or is written and the D bit is clear.
+      // So just set A for read and D for write permission.
+      //
+      if ((AttributeSetMask & RISCV_PG_R) != 0) {
+        EntryValue |= RISCV_PG_A;
+      }
+
+      if ((AttributeSetMask & RISCV_PG_W) != 0) {
+        EntryValue |= RISCV_PG_D;
+      }
+
+      EntryValue = SetPpnToPte (EntryValue, RegionStart);
+      EntryValue = SetValidPte (EntryValue);
+      ReplaceTableEntry (Entry, EntryValue, RegionStart, TableIsLive);
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Update region mapping at root table.
+
+  @param  RegionStart           The start address of the region.
+  @param  RegionLength          The length of the region.
+  @param  AttributeSetMask      The attribute mask to be set.
+  @param  AttributeClearMask    The attribute mask to be clear.
+  @param  RootTable             The pointer of root table.
+  @param  TableIsLive           TRUE if this is live update, FALSE otherwise.
+
+  @retval EFI_INVALID_PARAMETER The RegionStart or RegionLength was not valid.
+  @retval EFI_OUT_OF_RESOURCES  Not enough resource.
+  @retval EFI_SUCCESS           The operation succesfully.
+
+**/
+STATIC
+EFI_STATUS
+UpdateRegionMapping (
+  IN  UINTN    RegionStart,
+  IN  UINTN    RegionLength,
+  IN  UINTN    AttributeSetMask,
+  IN  UINTN    AttributeClearMask,
+  IN  UINTN    *RootTable,
+  IN  BOOLEAN  TableIsLive
+  )
+{
+  if (((RegionStart | RegionLength) & EFI_PAGE_MASK) != 0) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  return UpdateRegionMappingRecursive (
+           RegionStart,
+           RegionStart + RegionLength,
+           AttributeSetMask,
+           AttributeClearMask,
+           RootTable,
+           0,
+           TableIsLive
+           );
+}
+
+/**
+  The API to configure and enable RISC-V MMU with the highest mode supported.
+
+  @retval EFI_OUT_OF_RESOURCES    Not enough resource.
+  @retval EFI_SUCCESS             The operation succesfully.
+
+**/
+EFI_STATUS
+EFIAPI
+PlatformUpdateMmu (
+  IN EFI_HANDLE         ImageHandle,
+  IN EFI_SYSTEM_TABLE   *SystemTable
+  )
+{
+  EFI_GCD_MEMORY_SPACE_DESCRIPTOR  *MemoryMap;
+  UINTN                            NumberOfDescriptors;
+  UINTN                            Index;
+  EFI_STATUS                       Status;
+
+  Status = EFI_SUCCESS;
+
+  if(RiscVMmuEnabled () && PcdGetBool (PcdForceNoMMU)){
+    RiscVMmuDisabled ();
+    DEBUG ((
+       DEBUG_INFO,
+       "%a: SG2042 MMU disabled\n",
+       __func__
+      ));
+
+    FreePageTablesRecursive ((UINTN *)RiscVGetRootTranslateTable (), 0);
+    return Status;
+  }
+
+  if (RiscVMmuEnabled ()) {
+    mMaxRootTableLevel = 3;
+    mBitPerLevel       = 9;
+    mTableEntryCount   = 512;
+
+    NumberOfDescriptors = 0;
+    MemoryMap           = NULL;
+    Status              = gDS->GetMemorySpaceMap (
+                                 &NumberOfDescriptors,
+                                 &MemoryMap
+                                 );
+    ASSERT_EFI_ERROR (Status);
+
+    for (Index = 0; Index < NumberOfDescriptors; Index++) {
+      if (MemoryMap[Index].GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) {
+        // Update Read/Write/Strong Order attribute for memory mapped IO
+        UpdateRegionMapping (
+          MemoryMap[Index].BaseAddress,
+          MemoryMap[Index].Length,
+          RISCV_PG_R | RISCV_PG_W | RISCV_PG_SO,
+          PTE_ATTRIBUTES_MASK,
+          (UINTN *)RiscVGetRootTranslateTable (),
+          TRUE
+          );
+      } else if (MemoryMap[Index].GcdMemoryType == EfiGcdMemoryTypeSystemMemory) {
+        // Update Read/Write/Execute/Sharable/Cacheable attribute for system memory
+        UpdateRegionMapping (
+          MemoryMap[Index].BaseAddress,
+          MemoryMap[Index].Length,
+          RISCV_PG_R | RISCV_PG_W | RISCV_PG_X | RISCV_PG_SH | RISCV_PG_C,
+          PTE_ATTRIBUTES_MASK,
+          (UINTN *)RiscVGetRootTranslateTable (),
+          TRUE
+          );
+      }
+    }
+
+    DEBUG ((
+      DEBUG_INFO,
+      "%a: SG2042 Update memory attribute\n",
+      __func__
+      ));
+
+    FreePool ((VOID *)MemoryMap);
+  }
+
+  return Status;
+}
-- 
2.34.1



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#108213): https://edk2.groups.io/g/devel/message/108213
Mute This Topic: https://groups.io/mt/101084025/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



  parent reply	other threads:[~2023-08-31 22:28 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-08-31 13:44 [edk2-devel] [PATCH v2 0/8] EDK2 on RISC-V Sophgo SG2042 platform caiyuqing_hz
2023-08-31 13:44 ` [edk2-devel] [PATCH v2 1/8] Sophgo/SG2042Pkg: Add SmbiosPlatformDxe module caiyuqing_hz
2023-08-31 13:44 ` caiyuqing_hz [this message]
2023-08-31 13:44 ` [edk2-devel] [PATCH v2 3/8] Sophgo/SG2042Pkg: Add Sophgo SDHCI driver caiyuqing_hz
2023-08-31 13:44 ` [edk2-devel] [PATCH v2 4/8] Sophgo/SG2042Pkg: Add base MMC driver caiyuqing_hz
2023-08-31 13:44 ` [edk2-devel] [PATCH v2 5/8] Sophgo/SG2042Pkg: Add SEC module caiyuqing_hz
2023-08-31 13:44 ` [edk2-devel] [PATCH v2 6/8] SG2042Pkg/SG2042_EVB_Board: Add Sophgo SG2042 platform caiyuqing_hz
2023-08-31 13:44 ` [edk2-devel] [PATCH v2 7/8] Sophgo/SG2042Pkg: Add SG2042Pkg caiyuqing_hz
2023-08-31 13:44 ` [edk2-devel] [PATCH v2 8/8] Sophgo/SG2042Pkg: Add platform readme and document caiyuqing_hz
2023-09-04 15:16 ` [edk2-devel] [PATCH v2 0/8] EDK2 on RISC-V Sophgo SG2042 platform Leif Lindholm
2023-09-07 10:33   ` caiyuqing_hz

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=d4bbdb23c113c1ef75dc91668399f546e6a10d17.1693483202.git.202235273@mail.sdu.edu.cn \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox