public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
* [edk2-platforms][PATCH V3 00/16] Platform: Add Loongson support.
@ 2022-09-29  7:07 xianglai
  2022-09-29  7:07 ` [edk2-platforms][PATCH V3 01/16] Platform/Loongson: Add Serial Port library xianglai
                   ` (15 more replies)
  0 siblings, 16 replies; 21+ messages in thread
From: xianglai @ 2022-09-29  7:07 UTC (permalink / raw)
  To: devel; +Cc: quic_llindhol, michael.d.kinney, kraxel, ardb, maobibo

REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4054

The uploaded code generates firmware to support Linux launching on the LoongArch platform under qemu,
So it will run in a virtual machine.

LoongArch is the general processor architecture of Loongson.
You can get the latest LoongArch documents or LoongArch tools at https://github.com/loongson/.

You can also view the code through the Loongson community.
The edk2 code in Loongson community:
https://github.com/loongson/edk2/tree/LoongArch
The edk2-platform code in Loonson community:
https://github.com/loongson/edk2-platforms
The qemu code in Loongson community:
https://gitlab.com/qemu-project/qemu.git
The LoongArch Documentation in Loongson community:
https://github.com/loongson/LoongArch-Documentation/tree/main/docs
The all patches at:
https://github.com/loongson/edk2-platforms/tree/devel-LoongArch-patch

v2 changes:
 - Remove the inline assembly from StableTimerLib.
 - troubleshoot TAB strings, convert TAB characters to spaces.
 - remove smm related code, loongarch has no smm mode.

v3 changes:
 - delete ExtractHandler related code.
 - Boot UEFI with low 256M memory.
 - Modify common interrupt handling.

xianglai li (16):
  Platform/Loongson: Add Serial Port library
  Platform/Loongson: Support SEC And Add Readme.md
  Platform/Loongson: Add PeiServicesTablePointerLib.
  Platform/Loongson: Add QemuFwCfgLib.
  Platform/Loongson: Add MmuLib.
  Platform/Loongson: Add StableTimerLib.
  Platform/Loongson: Support PEI phase.
  Platform/Loongson: Add CPU DXE driver.
  Platform/Loongson: Add PciCpuIoDxe driver.
  Platform/Loongson:  Add timer Dxe driver.
  Platform/Loongson: Add RealTime Clock lib.
  Platform/Loongson: Add Platform Boot Manager Lib.
  Platform/Loongson: Add Reset System Lib.
  Platform/Loongson: Support Dxe
  Platform/Loongson: Add QemuFlashFvbServicesRuntimeDxe driver.
  Platform/Loongson: Support for saving variables to flash.

 Maintainers.txt                               |    4 +
 .../LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.c  |  382 ++++++
 .../LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.h  |  151 +++
 .../Drivers/CpuDxe/CpuDxe.inf                 |   56 +
 .../Drivers/CpuDxe/LoongArch64/Exception.c    |  338 +++++
 .../Drivers/CpuDxe/LoongArch64/Fpu.S          |   67 +
 .../Drivers/CpuDxe/LoongArch64/LoongArch.S    |  292 +++++
 .../Drivers/PciCpuIo2Dxe/PciCpuIo2Dxe.c       |  548 ++++++++
 .../Drivers/PciCpuIo2Dxe/PciCpuIo2Dxe.h       |  219 ++++
 .../Drivers/PciCpuIo2Dxe/PciCpuIo2Dxe.inf     |   40 +
 .../QemuFlashFvbServicesRuntimeDxe/FvbInfo.c  |  115 ++
 .../FvbServicesRuntimeDxe.inf                 |   73 ++
 .../FwBlockService.c                          | 1158 +++++++++++++++++
 .../FwBlockService.h                          |  178 +++
 .../FwBlockServiceDxe.c                       |  152 +++
 .../QemuFlash.c                               |  251 ++++
 .../QemuFlash.h                               |   86 ++
 .../QemuFlashDxe.c                            |   21 +
 .../Drivers/StableTimerDxe/Timer.c            |  405 ++++++
 .../Drivers/StableTimerDxe/Timer.h            |  165 +++
 .../Drivers/StableTimerDxe/TimerDxe.inf       |   40 +
 .../Include/IndustryStandard/QemuFwCfg.h      |   95 ++
 .../LoongArchQemuPkg/Include/Library/Cpu.h    |  387 ++++++
 .../LoongArchQemuPkg/Include/Library/MmuLib.h |   85 ++
 .../Include/Library/QemuFwCfgLib.h            |  193 +++
 .../Include/Library/StableTimer.h             |   43 +
 .../Include/LoongArchAsmMacro.h               |   23 +
 .../Include/LoongArchQemuPlatform.h           |   97 ++
 .../LsRealTimeClockLib/LsRealTimeClock.h      |   41 +
 .../LsRealTimeClockLib/LsRealTimeClockLib.c   |  343 +++++
 .../LsRealTimeClockLib/LsRealTimeClockLib.inf |   41 +
 .../LoongArchQemuPkg/Library/MmuLib/Mmu.S     |   35 +
 .../Library/MmuLib/MmuBaseLib.inf             |   35 +
 .../Library/MmuLib/MmuBaseLibPei.inf          |   42 +
 .../Library/MmuLib/MmuLibCore.c               |  908 +++++++++++++
 .../Library/MmuLib/MmuLibCore.h               |   39 +
 .../Library/MmuLib/MmuLibCorePei.c            |  236 ++++
 .../LoongArchQemuPkg/Library/MmuLib/mmu.h     |  104 ++
 .../LoongArchQemuPkg/Library/MmuLib/page.h    |  267 ++++
 .../LoongArchQemuPkg/Library/MmuLib/pte.h     |   57 +
 .../PeiServicesTablePointer.c                 |   78 ++
 .../PeiServicesTablePointerLib.inf            |   32 +
 .../PlatformBootManagerLib/PlatformBm.c       |  760 +++++++++++
 .../PlatformBootManagerLib/PlatformBm.h       |  112 ++
 .../PlatformBootManagerLib.inf                |   78 ++
 .../PlatformBootManagerLib/QemuKernel.c       |   81 ++
 .../QemuFwCfgLib/QemuFwCfgLibInternal.h       |   64 +
 .../Library/QemuFwCfgLib/QemuFwCfgPei.c       |  119 ++
 .../Library/QemuFwCfgLib/QemuFwCfgPeiLib.c    |  477 +++++++
 .../Library/QemuFwCfgLib/QemuFwCfgPeiLib.inf  |   44 +
 .../BaseResetSystemAcpiGed.c                  |  155 +++
 .../BaseResetSystemAcpiGedLib.inf             |   42 +
 .../DxeResetSystemAcpiGed.c                   |  270 ++++
 .../DxeResetSystemAcpiGedLib.inf              |   47 +
 .../ResetSystemAcpiLib/ResetSystemAcpiGed.c   |  128 ++
 .../ResetSystemAcpiLib/ResetSystemAcpiGed.h   |   20 +
 .../Library/SerialPortLib/SerialPortLib.c     |  612 +++++++++
 .../Library/SerialPortLib/SerialPortLib.inf   |   36 +
 .../Library/StableTimerLib/Count.S            |   35 +
 .../Library/StableTimerLib/TimerLib.c         |  242 ++++
 .../Library/StableTimerLib/TimerLib.inf       |   28 +
 .../Loongson/LoongArchQemuPkg/Loongson.dec    |   76 ++
 .../Loongson/LoongArchQemuPkg/Loongson.dsc    |  606 +++++++++
 .../Loongson/LoongArchQemuPkg/Loongson.fdf    |  361 +++++
 .../LoongArchQemuPkg/Loongson.fdf.inc         |   74 ++
 .../LoongArchQemuPkg/PlatformPei/Fv.c         |   61 +
 .../LoongArchQemuPkg/PlatformPei/MemDetect.c  |  108 ++
 .../LoongArchQemuPkg/PlatformPei/Platform.c   |  264 ++++
 .../LoongArchQemuPkg/PlatformPei/Platform.h   |   87 ++
 .../PlatformPei/PlatformPei.inf               |   71 +
 Platform/Loongson/LoongArchQemuPkg/Readme.md  |   59 +
 .../LoongArchQemuPkg/Sec/LoongArch64/Start.S  |   76 ++
 .../Loongson/LoongArchQemuPkg/Sec/SecMain.c   |  510 ++++++++
 .../Loongson/LoongArchQemuPkg/Sec/SecMain.inf |   47 +
 .../LoongArchQemuPkg/VarStore.fdf.inc         |   64 +
 Readme.md                                     |    9 +
 76 files changed, 13745 insertions(+)
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.inf
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Exception.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Fpu.S
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/LoongArch.S
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/PciCpuIo2Dxe/PciCpuIo2Dxe.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/PciCpuIo2Dxe/PciCpuIo2Dxe.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/PciCpuIo2Dxe/PciCpuIo2Dxe.inf
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbInfo.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockServiceDxe.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/StableTimerDxe/Timer.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/StableTimerDxe/Timer.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/StableTimerDxe/TimerDxe.inf
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Include/IndustryStandard/QemuFwCfg.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Include/Library/Cpu.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Include/Library/MmuLib.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Include/Library/QemuFwCfgLib.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Include/Library/StableTimer.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Include/LoongArchAsmMacro.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Include/LoongArchQemuPlatform.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/LsRealTimeClockLib/LsRealTimeClock.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/LsRealTimeClockLib/LsRealTimeClockLib.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/LsRealTimeClockLib/LsRealTimeClockLib.inf
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/Mmu.S
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLib.inf
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLibPei.inf
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCorePei.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/mmu.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/page.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/pte.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/PeiServicesTablePointerLib/PeiServicesTablePointer.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/PeiServicesTablePointerLib/PeiServicesTablePointerLib.inf
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/PlatformBm.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/PlatformBm.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/QemuKernel.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/QemuFwCfgLib/QemuFwCfgLibInternal.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/QemuFwCfgLib/QemuFwCfgPei.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/QemuFwCfgLib/QemuFwCfgPeiLib.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/QemuFwCfgLib/QemuFwCfgPeiLib.inf
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/ResetSystemAcpiLib/BaseResetSystemAcpiGed.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/ResetSystemAcpiLib/BaseResetSystemAcpiGedLib.inf
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/ResetSystemAcpiLib/DxeResetSystemAcpiGed.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/ResetSystemAcpiLib/DxeResetSystemAcpiGedLib.inf
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/ResetSystemAcpiLib/ResetSystemAcpiGed.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/ResetSystemAcpiLib/ResetSystemAcpiGed.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/SerialPortLib/SerialPortLib.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/SerialPortLib/SerialPortLib.inf
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/StableTimerLib/Count.S
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/StableTimerLib/TimerLib.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/StableTimerLib/TimerLib.inf
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Loongson.dec
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Loongson.dsc
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Loongson.fdf
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Loongson.fdf.inc
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/PlatformPei/Fv.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/PlatformPei/MemDetect.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/PlatformPei/Platform.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/PlatformPei/Platform.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/PlatformPei/PlatformPei.inf
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Readme.md
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Sec/LoongArch64/Start.S
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Sec/SecMain.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Sec/SecMain.inf
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/VarStore.fdf.inc

-- 
2.31.1


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [edk2-platforms][PATCH V3 01/16] Platform/Loongson: Add Serial Port library
  2022-09-29  7:07 [edk2-platforms][PATCH V3 00/16] Platform: Add Loongson support xianglai
@ 2022-09-29  7:07 ` xianglai
  2022-09-29  7:07 ` [edk2-platforms][PATCH V3 02/16] Platform/Loongson: Support SEC And Add Readme.md xianglai
                   ` (14 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: xianglai @ 2022-09-29  7:07 UTC (permalink / raw)
  To: devel; +Cc: quic_llindhol, michael.d.kinney, kraxel, ardb, maobibo

Serial Port library for LoongarchQemuPkg

REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4054

Signed-off-by: xianglai li <lixianglai@loongson.cn>
---
 .../LoongArchQemuPkg/Include/Library/Cpu.h    | 387 +++++++++++
 .../Include/LoongArchQemuPlatform.h           |  97 +++
 .../Library/SerialPortLib/SerialPortLib.c     | 612 ++++++++++++++++++
 .../Library/SerialPortLib/SerialPortLib.inf   |  36 ++
 4 files changed, 1132 insertions(+)
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Include/Library/Cpu.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Include/LoongArchQemuPlatform.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/SerialPortLib/SerialPortLib.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/SerialPortLib/SerialPortLib.inf

diff --git a/Platform/Loongson/LoongArchQemuPkg/Include/Library/Cpu.h b/Platform/Loongson/LoongArchQemuPkg/Include/Library/Cpu.h
new file mode 100644
index 0000000000..8c3c21bd96
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Include/Library/Cpu.h
@@ -0,0 +1,387 @@
+/** @file
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Glossary:
+    - EXC     - Exception
+    - INT     - Interrupt
+    - FPU     - Floating Point Unit
+    - CSR     - CPU Status Register
+    - READQ   - Read Quad Word
+**/
+#ifndef LOONGARCH_CPU_H_
+#define LOONGARCH_CPU_H_
+
+/* Exception types decoded by machdep exception decoder */
+#define EXC_INT                     0       /* HW interrupt */
+#define EXC_TLBL                    1       /* TLB miss on a load */
+#define EXC_TLBS                    2       /* TLB miss on a store */
+#define EXC_TLBI                    3       /* TLB miss on a ifetch */
+#define EXC_TLBM                    4       /* TLB modified fault */
+#define EXC_TLBRI                   5       /* TLB Read-Inhibit exception */
+#define EXC_TLBXI                   6       /* TLB Execution-Inhibit exception */
+#define EXC_TLBPE                   7       /* TLB Privilege Error */
+#define EXC_ADE                     8       /* Address Error */
+#define EXC_ALE                     9       /* Unalign Access */
+#define EXC_OOB                     10      /* Out of bounds */
+#define EXC_SYS                     11      /* System call */
+#define EXC_BP                      12      /* Breakpoint */
+#define EXC_INE                     13      /* Inst. Not Exist */
+#define EXC_IPE                     14      /* Inst. Privileged Error */
+#define EXC_FPDIS                   15      /* FPU Disabled */
+#define EXC_LSXDIS                  16      /* LSX Disabled */
+#define EXC_LASXDIS                 17      /* LASX Disabled */
+#define EXC_FPE                     18      /* Floating Point Exception */
+#define EXC_WATCH                   19      /* Watch address reference */
+#define EXC_BAD                     255     /* Undecodeable */
+
+#define COPY_SIGCODE    // copy sigcode above user stack in exec
+#define ZERO                        $r0 /* wired zero */
+#define RA                          $r1 /* return address */
+#define GP                          $r2 /* global pointer - caller saved for PIC */
+#define SP                          $r3 /* stack pointer */
+#define V0                          $r4 /* return value - caller saved */
+#define V1                          $r5
+#define A0                          $r4 /* argument registers */
+#define A1                          $r5
+#define A2                          $r6
+#define A3                          $r7
+#define A4                          $r8 /* arg reg 64 bit; caller saved in 32 bit */
+#define A5                          $r9
+#define A6                          $r10
+#define A7                          $r11
+#define T0                          $r12 /* caller saved */
+#define T1                          $r13
+#define T2                          $r14
+#define T3                          $r15
+#define T4                          $r16 /* callee saved */
+#define T5                          $r17
+#define T6                          $r18
+#define T7                          $r19
+#define T8                          $r20 /* caller saved */
+#define TP                          $r21 /* TLS */
+#define FP                          $r22 /* frame pointer */
+#define S0                          $r23 /* callee saved */
+#define S1                          $r24
+#define S2                          $r25
+#define S3                          $r26
+#define S4                          $r27
+#define S5                          $r28
+#define S6                          $r29
+#define S7                          $r30
+#define S8                          $r31 /* callee saved */
+
+#define FCSR0                       $r0
+
+//
+// Location of the saved registers relative to ZERO.
+// Usage is p->p_regs[XX].
+//
+#define RA_NUM                      1
+#define GP_NUM                      2
+#define SP_NUM                      3
+#define A0_NUM                      4
+#define A1_NUM                      5
+#define A2_NUM                      6
+#define A3_NUM                      7
+#define A4_NUM                      8
+#define A5_NUM                      9
+#define A6_NUM                      10
+#define A7_NUM                      11
+#define T0_NUM                      12
+#define T1_NUM                      13
+#define T2_NUM                      14
+#define T3_NUM                      15
+#define T4_NUM                      16
+#define T5_NUM                      17
+#define T6_NUM                      18
+#define T7_NUM                      19
+#define T8_NUM                      20
+#define TP_NUM                      21
+#define FP_NUM                      22
+#define S0_NUM                      23
+#define S1_NUM                      24
+#define S2_NUM                      25
+#define S3_NUM                      26
+#define S4_NUM                      27
+#define S5_NUM                      28
+#define S6_NUM                      29
+#define S7_NUM                      30
+#define S8_NUM                      31
+
+#define FP0_NUM                     0
+#define FP1_NUM                     1
+#define FP2_NUM                     2
+#define FP3_NUM                     3
+#define FP4_NUM                     4
+#define FP5_NUM                     5
+#define FP6_NUM                     6
+#define FP7_NUM                     7
+#define FP8_NUM                     8
+#define FP9_NUM                     9
+#define FP10_NUM                    10
+#define FP11_NUM                    11
+#define FP12_NUM                    12
+#define FP13_NUM                    13
+#define FP14_NUM                    14
+#define FP15_NUM                    15
+#define FP16_NUM                    16
+#define FP17_NUM                    17
+#define FP18_NUM                    18
+#define FP19_NUM                    19
+#define FP20_NUM                    20
+#define FP21_NUM                    21
+#define FP22_NUM                    22
+#define FP23_NUM                    23
+#define FP24_NUM                    24
+#define FP25_NUM                    25
+#define FP26_NUM                    26
+#define FP27_NUM                    27
+#define FP28_NUM                    28
+#define FP29_NUM                    29
+#define FP30_NUM                    30
+#define FP31_NUM                    31
+#define FCSR_NUM                    32
+#define FCC_NUM                     33
+
+#ifdef __ASSEMBLY__
+#define _ULCAST_
+#define _U64CAST_
+#else
+#define _ULCAST_ (unsigned long)
+#define _U64CAST_ (u64)
+#endif
+
+#define LOONGARCH_CSR_CRMD          0
+#define LOONGARCH_CSR_PRMD          1
+#define LOONGARCH_CSR_EUEN          2
+#define CSR_EUEN_LBTEN_SHIFT        3
+#define CSR_EUEN_LBTEN              (_ULCAST_(0x1) << CSR_EUEN_LBTEN_SHIFT)
+#define CSR_EUEN_LASXEN_SHIFT       2
+#define CSR_EUEN_LASXEN             (_ULCAST_(0x1) << CSR_EUEN_LASXEN_SHIFT)
+#define CSR_EUEN_LSXEN_SHIFT        1
+#define CSR_EUEN_LSXEN              (_ULCAST_(0x1) << CSR_EUEN_LSXEN_SHIFT)
+#define CSR_EUEN_FPEN_SHIFT         0
+#define CSR_EUEN_FPEN               (_ULCAST_(0x1) << CSR_EUEN_FPEN_SHIFT)
+#define LOONGARCH_CSR_ECFG          4
+
+/* Exception status */
+#define LOONGARCH_CSR_ESTAT         5
+#define CSR_ESTAT_ESUBCODE_SHIFT    22
+#define CSR_ESTAT_ESUBCODE_WIDTH    9
+#define CSR_ESTAT_ESUBCODE          (_ULCAST_(0x1ff) << CSR_ESTAT_ESUBCODE_SHIFT)
+#define CSR_ESTAT_EXC_SHIFT         16
+#define CSR_ESTAT_EXC_WIDTH         6
+#define CSR_ESTAT_EXC               (_ULCAST_(0x3f) << CSR_ESTAT_EXC_SHIFT)
+#define CSR_ESTAT_IS_SHIFT          0
+#define CSR_ESTAT_IS_WIDTH          15
+#define CSR_ESTAT_IS                (_ULCAST_(0x7fff) << CSR_ESTAT_IS_SHIFT)
+
+#define LOONGARCH_CSR_EPC           6
+#define LOONGARCH_CSR_BADV          7
+#define LOONGARCH_CSR_BADINST       8
+#define LOONGARCH_CSR_BADI          8
+#define LOONGARCH_CSR_EBASE         0xc     /* Exception entry base address */
+#define LOONGARCH_CSR_CPUNUM        0x20    /* CPU core number */
+
+/* register number save in stack on exception */
+#define FP_BASE_NUM                 34
+#define BASE_NUM                    32
+#define CSR_NUM                     10
+#define FP_BASE_INDEX               (CSR_NUM + BASE_NUM)
+#define BOOTCORE_ID                 0
+
+#define LOONGSON_IOCSR_IPI_STATUS   0x1000
+#define LOONGSON_IOCSR_IPI_EN       0x1004
+#define LOONGSON_IOCSR_IPI_SET      0x1008
+#define LOONGSON_IOCSR_IPI_CLEAR    0x100c
+#define LOONGSON_CSR_MAIL_BUF0      0x1020
+#define LOONGSON_CSR_MAIL_BUF1      0x1028
+#define LOONGSON_CSR_MAIL_BUF2      0x1030
+#define LOONGSON_CSR_MAIL_BUF3      0x1038
+
+/* Bit Domains for CFG registers */
+#define LOONGARCH_CPUCFG4           0x4
+#define LOONGARCH_CPUCFG5           0x5
+
+/* Kscratch registers */
+#define LOONGARCH_CSR_KS0           0x30
+#define LOONGARCH_CSR_KS1           0x31
+
+/* Stable timer registers */
+#define LOONGARCH_CSR_TMCFG         0x41
+#define LOONGARCH_CSR_TMCFG_EN      (1ULL << 0)
+#define LOONGARCH_CSR_TMCFG_PERIOD  (1ULL << 1)
+#define LOONGARCH_CSR_TMCFG_TIMEVAL (0x3fffffffffffULL << 2)
+#define LOONGARCH_CSR_TVAL           0x42    /* Timer value */
+#define LOONGARCH_CSR_CNTC           0x43    /* Timer offset */
+#define LOONGARCH_CSR_TINTCLR        0x44    /* Timer interrupt clear */
+
+/* TLB refill exception base address */
+#define LOONGARCH_CSR_TLBREBASE      0x88
+#define LOONGARCH_CSR_TLBRSAVE       0x8b    /* KScratch for TLB refill exception */
+#define LOONGARCH_CSR_PGD            0x1b    /* Page table base */
+
+/* Bits 8 and 9 of FPU Status Register specify the rounding mode */
+#define FPU_CSR_RM                   0x300
+#define FPU_CSR_RN                   0x000   /* nearest */
+#define FPU_CSR_RZ                   0x100   /* towards zero */
+#define FPU_CSR_RU                   0x200   /* towards +Infinity */
+#define FPU_CSR_RD                   0x300   /* towards -Infinity */
+
+/*
+  Reads data from the specified CSR register.
+
+  @param[OUT]  val   The value is read from the CSR specified register.
+  @param[IN]  reg    Specifies the register number of the CSR to read the data.
+
+  @retval  VOID
+ */
+#ifdef __GNUC__
+#define LOONGARCH_CSR_READQ(val, reg)                   \
+do {                                                    \
+  UINT64 __res;                                         \
+  /* csrrd rd, csr_num */                               \
+  __asm__ __volatile__(                                 \
+          "csrrd  %0, %1 \n\t"                          \
+          :"=r"(__res)                                  \
+          :"i"(reg)                                     \
+          :                                             \
+          );                                            \
+  (val) = __res;                                        \
+} while(0)
+#endif
+
+/*
+  Write data to the specified CSR register.
+
+  @param[OUT]  val   The value is write to the CSR specified register.
+  @param[IN]  reg    Specifies the register number of the CSR to write the data.
+
+  @retval  VOID
+ */
+#ifdef __GNUC__
+#define LOONGARCH_CSR_WRITEQ(val, reg)                 \
+do {                                                   \
+  UINT64 __val = val;                                  \
+  /* csrwr rd, csr_num */                              \
+  __asm__ __volatile__(                                \
+          "csrwr  %0, %1 \n\t"                         \
+          : "+r"(__val)                                \
+          : "i"(reg), "r"(__val)                       \
+          : "memory"                                   \
+          );                                           \
+} while(0)
+#endif
+
+/*
+  Exchange specified bit data with the specified CSR registers
+
+  @param[IN]  val   The value Exchanged with the CSR specified register.
+  @param[IN]  mask   Specifies the mask for swapping bits
+  @param[IN]  reg    Specifies the register number of the CSR to Exchange the data.
+
+  @retval  VOID
+ */
+#ifdef __GNUC__
+#define LOONGARCH_CSR_XCHGQ(val, mask, reg)          \
+do {                                                 \
+  UINT64 __val = val;                                \
+  UINT64 __mask = mask;                              \
+  /* csrwr rd, csr_num */                            \
+  __asm__ __volatile__(                              \
+          "csrxchg  %0, %1, %2 \n\t"                 \
+          : "+r"(__val)                              \
+          : "r"(__mask), "i"(reg), "r"(__val)        \
+          : "memory"                                 \
+          );                                         \
+} while(0)
+#endif
+
+/*
+  Search for tlb
+
+  @param  VOID
+
+  @retval  VOID
+ */
+#ifdef __GNUC__
+#define LOONGARCH_TLB_SRCH()                         \
+do {                                                 \
+  /* tlbsrch */                                      \
+  __asm__ __volatile__(                              \
+          "tlbsrch"                                  \
+          );                                         \
+} while(0)
+#endif
+
+/*
+  Writes data to the specified tlb table entry.
+
+  @param  VOID
+
+  @retval  VOID
+ */
+#ifdef __GNUC__
+#define LOONGARCH_TLB_WR()                         \
+do {                                               \
+  /* tlbwr */                                      \
+  __asm__ __volatile__(                            \
+          "tlbwr"                                  \
+          );                                       \
+} while(0)
+#endif
+/*
+  Reads data from the specified CPUCFG register.
+
+  @param[OUT]  val   The value is read from the CPUCFG specified register.
+  @param[IN]  reg    Specifies the register number of the CPUCFG to read the data.
+
+  @retval  VOID
+ */
+#ifdef __GNUC__
+#define LOONGARCH_GET_CPUCFG(val, reg)               \
+do {                                                 \
+  UINT64 __res;                                      \
+  /* cpucfg rd, rj */                                \
+  __asm__ __volatile__(                              \
+          "cpucfg  %0, %1 \n\t"                      \
+          :"=r"(__res)                               \
+          :"r"(reg)                                  \
+          :                                          \
+          );                                         \
+  val = (UINT32)__res;                               \
+} while(0)
+#endif
+
+/*
+  Enables  floating-point unit
+
+  @param  VOID
+
+  @retval  VOID
+ */
+#define LOONGARCH_ENABLR_FPU()                       \
+do {                                                 \
+  LOONGARCH_CSR_XCHGQ(CSR_EUEN_FPEN,                 \
+                      CSR_EUEN_FPEN,                 \
+                      LOONGARCH_CSR_EUEN);           \
+} while (0)
+
+/*
+  Disable  floating-point unit
+
+  @param  VOID
+
+  @retval  VOID
+ */
+#define LOONGARCH_DISABLE_FPU()                      \
+do {                                                 \
+  LOONGARCH_CSR_XCHGQ(0,                             \
+                      CSR_EUEN_FPEN,                 \
+                      LOONGARCH_CSR_EUEN);           \
+} while (0)
+
+#endif
diff --git a/Platform/Loongson/LoongArchQemuPkg/Include/LoongArchQemuPlatform.h b/Platform/Loongson/LoongArchQemuPkg/Include/LoongArchQemuPlatform.h
new file mode 100644
index 0000000000..ed672844b4
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Include/LoongArchQemuPlatform.h
@@ -0,0 +1,97 @@
+/** @file
+   LoongArch Qemu Platform macro definition.
+
+   Copyright (c) 2021, Loongson Limited. All rights reserved.
+
+   SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ **/
+
+#ifndef LOONGARCH_QEMU_PLATFORM_H_
+#define LOONGARCH_QEMU_PLATFORM_H_
+
+
+/* Acpi pm device */
+#define LS7A_PCH_REG_BASE             0x10000000UL
+#define LS7A_ACPI_REG_BASE            (LS7A_PCH_REG_BASE  + 0x000D0000)
+#define LS7A_PM_CNT_BLK               (0x14) /* 2 bytes */
+#define LS7A_GPE0_RESET_REG           (0x30) /* 4 bytes */
+
+#define ACPI_BITMASK_SLEEP_TYPE       0x1C00
+#define ACPI_BITMASK_SLEEP_ENABLE     0x2000
+
+
+//---------------------------------------------
+// UART Register Offsets
+//---------------------------------------------
+#define BAUD_LOW_OFFSET               0x00
+#define BAUD_HIGH_OFFSET              0x01
+#define IER_OFFSET                    0x01
+#define LCR_SHADOW_OFFSET             0x01
+#define FCR_SHADOW_OFFSET             0x02
+#define IR_CONTROL_OFFSET             0x02
+#define FCR_OFFSET                    0x02
+#define EIR_OFFSET                    0x02
+#define BSR_OFFSET                    0x03
+#define LCR_OFFSET                    0x03
+#define MCR_OFFSET                    0x04
+#define LSR_OFFSET                    0x05
+#define MSR_OFFSET                    0x06
+
+/* character format control register */
+#define CFCR_DLAB                     0x80  /* divisor latch */
+#define CFCR_SBREAK                   0x40  /* send break */
+#define CFCR_PZERO                    0x30  /* zero parity */
+#define CFCR_PONE                     0x20  /* one parity */
+#define CFCR_PEVEN                    0x10  /* even parity */
+#define CFCR_PODD                     0x00  /* odd parity */
+#define CFCR_PENAB                    0x08  /* parity enable */
+#define CFCR_STOPB                    0x04  /* 2 stop bits */
+#define CFCR_8BITS                    0x03  /* 8 data bits */
+#define CFCR_7BITS                    0x02  /* 7 data bits */
+#define CFCR_6BITS                    0x01  /* 6 data bits */
+#define CFCR_5BITS                    0x00  /* 5 data bits */
+/* modem control register */
+#define MCR_LOOPBACK                  0x10  /* loopback */
+#define MCR_IENABLE                   0x08  /* output 2 = int enable */
+#define MCR_DRS                       0x04  /* output 1 = xxx */
+#define MCR_RTS                       0x02  /* enable RTS */
+#define MCR_DTR                       0x01  /* enable DTR */
+
+/* line status register */
+#define LSR_RCV_FIFO                  0x80  /* error in receive fifo */
+#define LSR_TSRE                      0x40  /* transmitter empty */
+#define LSR_TXRDY                     0x20  /* transmitter ready */
+#define LSR_BI                        0x10  /* break detected */
+#define LSR_FE                        0x08  /* framing error */
+#define LSR_PE                        0x04  /* parity error */
+#define LSR_OE                        0x02  /* overrun error */
+#define LSR_RXRDY                     0x01  /* receiver ready */
+#define LSR_RCV_MASK                  0x1f
+
+/* 16550 UART register offsets and bitfields */
+#define R_UART_RXBUF                  0
+#define R_UART_TXBUF                  0
+#define R_UART_BAUD_LOW               0
+#define R_UART_BAUD_HIGH              1
+#define R_UART_FCR                    2
+#define B_UART_FCR_FIFOE              BIT0
+#define B_UART_FCR_FIFO64             BIT5
+#define R_UART_LCR                    3
+#define B_UART_LCR_DLAB               BIT7
+#define R_UART_MCR                    4
+#define B_UART_MCR_DTRC               BIT0
+#define B_UART_MCR_RTS                BIT1
+#define R_UART_LSR                    5
+#define B_UART_LSR_RXRDY              BIT0
+#define B_UART_LSR_TXRDY              BIT5
+#define B_UART_LSR_TEMT               BIT6
+#define R_UART_MSR                    6
+#define B_UART_MSR_CTS                BIT4
+#define B_UART_MSR_DSR                BIT5
+#define B_UART_MSR_RI                 BIT6
+#define B_UART_MSR_DCD                BIT7
+#define UART_BASE_ADDRESS             (0x1fe001e0)
+#define UART_BPS                      (115200)
+#define UART_WAIT_TIMOUT              (1000000)
+#endif
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/SerialPortLib/SerialPortLib.c b/Platform/Loongson/LoongArchQemuPkg/Library/SerialPortLib/SerialPortLib.c
new file mode 100644
index 0000000000..eb1ce17d69
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/SerialPortLib/SerialPortLib.c
@@ -0,0 +1,612 @@
+/** @file
+  UART Serial Port library functions
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Glossary:
+    - Bps - Bit Per Second
+    - CTL - Control
+    - Config - Configure
+**/
+
+#include <Base.h>
+#include <Library/Cpu.h>
+#include <Library/IoLib.h>
+#include <Library/SerialPortLib.h>
+#include <LoongArchQemuPlatform.h>
+
+UINTN   gUartBase = UART_BASE_ADDRESS;
+UINTN   gBps      = UART_BPS;
+
+/**
+  Initialize the serial device hardware.
+
+  If no initialization is required, then return RETURN_SUCCESS.
+  If the serial device was successfuly initialized, then return RETURN_SUCCESS.
+  If the serial device could not be initialized, then return RETURN_DEVICE_ERROR.
+
+  @retval RETURN_SUCCESS        The serial device was initialized.
+  @retval RETURN_DEVICE_ERROR   The serail device could not be initialized.
+
+**/
+RETURN_STATUS
+EFIAPI
+SerialPortInitialize (
+  VOID
+  )
+{
+  UINTN  TimeOut;
+  //
+  // wait for Tx fifo to completely drain */
+  //
+  TimeOut = UART_WAIT_TIMOUT;
+  while (!(MmioRead8 ((UINTN) gUartBase + LSR_OFFSET) & LSR_TSRE)) {
+    if (--TimeOut == 0) {
+      break;
+    }
+  }
+  //
+  // Set communications format
+  //
+  MmioWrite8 ((UINTN) (gUartBase + LCR_OFFSET), CFCR_DLAB);
+
+  //
+  // Configure baud rate
+  //
+
+
+  MmioWrite8 ((UINTN) (gUartBase + LCR_OFFSET), CFCR_8BITS);
+  MmioWrite8 ((UINTN) (gUartBase + MCR_OFFSET), MCR_IENABLE | MCR_DTR | MCR_RTS);
+  //
+  // if enable interrupt the kernel of lemote will error in STR mode during wake up phase.
+  //
+  //MmioWrite8 ((UINTN) (gUartBase + IER_OFFSET), CFCR_8BITS);
+
+  return RETURN_SUCCESS;
+}
+
+/**
+  Write data from buffer to serial device.
+
+  Writes NumberOfBytes data bytes from Buffer to the serial device.
+  The number of bytes actually written to the serial device is returned.
+  If the return value is less than NumberOfBytes, then the write operation failed.
+
+  If Buffer is NULL, then ASSERT ().
+
+  If NumberOfBytes is zero, then return 0.
+
+  @param  Buffer           Pointer to the data buffer to be written.
+  @param  NumberOfBytes    Number of bytes to written to the serial device.
+
+  @retval 0                NumberOfBytes is 0.
+  @retval >0               The number of bytes written to the serial device.
+                           If this value is less than NumberOfBytes, then the read operation failed.
+
+**/
+UINTN
+EFIAPI
+UartCtlWrite (
+  IN UINT8     *Buffer,
+  IN UINTN     NumberOfBytes,
+  IN UINTN     CtlAddr
+)
+{
+  UINTN  Result;
+  UINT8  Data;
+
+  if (Buffer == NULL) {
+    return 0;
+  }
+
+  Result = NumberOfBytes;
+
+  while (NumberOfBytes--) {
+    //
+    // Wait for the serail port to be ready.
+    //
+    do {
+      Data = MmioRead8 (CtlAddr + LSR_OFFSET);
+    } while ((Data & LSR_TXRDY) == 0);
+    MmioWrite8 (CtlAddr, *Buffer++);
+  }
+
+  return Result;
+}
+/**
+  Writes data to serial port.
+
+  @param  Buffer           Pointer to the data buffer to store the data writed to serial port.
+  @param  NumberOfBytes    Number of bytes to write to the serial port.
+
+  @retval 0                NumberOfBytes is 0.
+  @retval >0               The number of bytes write the serial port.
+                           If this value is less than NumberOfBytes, then the write operation failed.
+
+**/
+UINTN
+EFIAPI
+SerialPortWrite (
+  IN UINT8     *Buffer,
+  IN UINTN     NumberOfBytes
+)
+{
+  return UartCtlWrite (Buffer, NumberOfBytes, gUartBase);
+}
+/**
+  Reads data from a serial device into a buffer.
+
+  @param  Buffer           Pointer to the data buffer to store the data read from the serial device.
+  @param  NumberOfBytes    Number of bytes to read from the serial device.
+
+  @retval 0                NumberOfBytes is 0.
+  @retval >0               The number of bytes read from the serial device.
+                           If this value is less than NumberOfBytes, then the read operation failed.
+
+**/
+UINTN
+EFIAPI
+UartCtlRead (
+  OUT UINT8     *Buffer,
+  IN  UINTN     NumberOfBytes,
+  IN  UINTN     CtlAddr
+)
+{
+  UINTN  Result;
+  UINT8  Data;
+
+  if (NULL == Buffer) {
+    return 0;
+  }
+
+  Result = NumberOfBytes;
+
+  while (NumberOfBytes--) {
+    //
+    // Wait for the serail port to be ready.
+    //
+    do {
+      Data = MmioRead8 (CtlAddr + LSR_OFFSET);
+    } while ((Data & LSR_RXRDY) == 0);
+
+    *Buffer++ = MmioRead8 (CtlAddr);
+  }
+
+  return Result;
+}
+/**
+  Read data from serial port.
+
+  @param  Buffer           Pointer to the data buffer to store the data read from serial port.
+  @param  NumberOfBytes    Number of bytes to read from the serial port.
+
+  @retval 0                NumberOfBytes is 0.
+  @retval >0               The number of bytes read from the serial port.
+                           If this value is less than NumberOfBytes, then the read operation failed.
+
+**/
+UINTN
+EFIAPI
+SerialPortRead (
+  OUT UINT8     *Buffer,
+  IN  UINTN     NumberOfBytes
+)
+{
+  return UartCtlRead (Buffer, NumberOfBytes, gUartBase);
+}
+/**
+  Polls a serial device to see if there is any data waiting to be read.
+
+  Polls aserial device to see if there is any data waiting to be read.
+  If there is data waiting to be read from the serial device, then TRUE is returned.
+  If there is no data waiting to be read from the serial device, then FALSE is returned.
+
+  @retval TRUE             Data is waiting to be read from the serial device.
+  @retval FALSE            There is no data waiting to be read from the serial device.
+
+**/
+BOOLEAN
+EFIAPI
+SerialPortPoll (
+  VOID
+  )
+{
+  UINT8  Data;
+
+  //
+  // Read the serial port status.
+  //
+  Data = MmioRead8 ((UINTN) gUartBase + LSR_OFFSET);
+
+  return (BOOLEAN) ((Data & LSR_RXRDY) != 0);
+}
+/**
+  To get serial register base address.
+
+  @param  VOID
+
+  @return  serial register base address.
+**/
+UINTN
+GetSerialRegisterBase (
+  VOID
+  )
+{
+  return gUartBase;
+}
+/**
+  Read an 8-bit register.
+  @param  Base    The base address register of UART device.
+  @param  Offset  The offset of the register to read.
+
+  @return The value read from the 16550 register.
+
+**/
+UINT8
+SerialPortReadRegister (
+  UINTN  Base,
+  UINTN  Offset
+  )
+{
+    return MmioRead8 (Base + Offset);
+}
+
+/**
+  Write an 8-bit register.
+  @param  Base    The base address register of UART device.
+  @param  Offset  The offset of the register to write.
+  @param  Value   The value to write to the register specified by Offset.
+
+  @return The value written to the 16550 register.
+
+**/
+UINT8
+SerialPortWriteRegister (
+  UINTN  Base,
+  UINTN  Offset,
+  UINT8  Value
+  )
+{
+    return MmioWrite8 (Base + Offset, Value);
+}
+
+/**
+  Sets the control bits on a serial device.
+
+  @param Control                Sets the bits of Control that are settable.
+
+  @retval RETURN_SUCCESS        The new control bits were set on the serial device.
+  @retval RETURN_UNSUPPORTED    The serial device does not support this operation.
+  @retval RETURN_DEVICE_ERROR   The serial device is not functioning correctly.
+
+**/
+RETURN_STATUS
+EFIAPI
+SerialPortSetControl (
+  IN UINT32 Control
+  )
+{
+  UINTN SerialRegisterBase;
+  UINT8 Mcr;
+
+  //
+  // First determine the parameter is invalid.
+  //
+  if ((Control & (~(EFI_SERIAL_REQUEST_TO_SEND | EFI_SERIAL_DATA_TERMINAL_READY |
+                    EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE))) != 0)
+  {
+    return RETURN_UNSUPPORTED;
+  }
+
+  SerialRegisterBase = GetSerialRegisterBase ();
+  if (SerialRegisterBase ==0) {
+    return RETURN_UNSUPPORTED;
+  }
+
+  //
+  // Read the Modem Control Register.
+  //
+  Mcr = SerialPortReadRegister (SerialRegisterBase, R_UART_MCR);
+  Mcr &= (~(B_UART_MCR_DTRC | B_UART_MCR_RTS));
+
+  if ((Control & EFI_SERIAL_DATA_TERMINAL_READY) == EFI_SERIAL_DATA_TERMINAL_READY) {
+    Mcr |= B_UART_MCR_DTRC;
+  }
+
+  if ((Control & EFI_SERIAL_REQUEST_TO_SEND) == EFI_SERIAL_REQUEST_TO_SEND) {
+    Mcr |= B_UART_MCR_RTS;
+  }
+
+  //
+  // Write the Modem Control Register.
+  //
+  SerialPortWriteRegister (SerialRegisterBase, R_UART_MCR, Mcr);
+
+  return RETURN_SUCCESS;
+}
+
+/**
+  Retrieve the status of the control bits on a serial device.
+
+  @param Control                A pointer to return the current control signals from the serial device.
+
+  @retval RETURN_SUCCESS        The control bits were read from the serial device.
+  @retval RETURN_UNSUPPORTED    The serial device does not support this operation.
+  @retval RETURN_DEVICE_ERROR   The serial device is not functioning correctly.
+
+**/
+RETURN_STATUS
+EFIAPI
+SerialPortGetControl (
+  OUT UINT32 *Control
+  )
+{
+  UINTN SerialRegisterBase;
+  UINT8 Msr;
+  UINT8 Mcr;
+  UINT8 Lsr;
+
+  SerialRegisterBase = GetSerialRegisterBase ();
+  if (SerialRegisterBase ==0) {
+    return RETURN_UNSUPPORTED;
+  }
+
+  *Control = 0;
+
+  //
+  // Read the Modem Status Register.
+  //
+  Msr = SerialPortReadRegister (SerialRegisterBase, R_UART_MSR);
+
+  if ((Msr & B_UART_MSR_CTS) == B_UART_MSR_CTS) {
+    *Control |= EFI_SERIAL_CLEAR_TO_SEND;
+  }
+
+  if ((Msr & B_UART_MSR_DSR) == B_UART_MSR_DSR) {
+    *Control |= EFI_SERIAL_DATA_SET_READY;
+  }
+
+  if ((Msr & B_UART_MSR_RI) == B_UART_MSR_RI) {
+    *Control |= EFI_SERIAL_RING_INDICATE;
+  }
+
+  if ((Msr & B_UART_MSR_DCD) == B_UART_MSR_DCD) {
+    *Control |= EFI_SERIAL_CARRIER_DETECT;
+  }
+
+  //
+  // Read the Modem Control Register.
+  //
+  Mcr = SerialPortReadRegister (SerialRegisterBase, R_UART_MCR);
+
+  if ((Mcr & B_UART_MCR_DTRC) == B_UART_MCR_DTRC) {
+    *Control |= EFI_SERIAL_DATA_TERMINAL_READY;
+  }
+
+  if ((Mcr & B_UART_MCR_RTS) == B_UART_MCR_RTS) {
+    *Control |= EFI_SERIAL_REQUEST_TO_SEND;
+  }
+
+  if (PcdGetBool (PcdSerialUseHardwareFlowControl)) {
+    *Control |= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE;
+  }
+
+  //
+  // Read the Line Status Register.
+  //
+  Lsr = SerialPortReadRegister (SerialRegisterBase, R_UART_LSR);
+
+  if ((Lsr & (B_UART_LSR_TEMT | B_UART_LSR_TXRDY)) == (B_UART_LSR_TEMT | B_UART_LSR_TXRDY)) {
+    *Control |= EFI_SERIAL_OUTPUT_BUFFER_EMPTY;
+  }
+
+  if ((Lsr & B_UART_LSR_RXRDY) == 0) {
+    *Control |= EFI_SERIAL_INPUT_BUFFER_EMPTY;
+  }
+
+  return RETURN_SUCCESS;
+}
+
+/**
+  Sets the baud rate, receive FIFO depth, transmit/receice time out, parity,
+  data bits, and stop bits on a serial device.
+
+  @param BaudRate           The requested baud rate. A BaudRate value of 0 will use the
+                            device's default interface speed.
+                            On output, the value actually set.
+  @param ReveiveFifoDepth   The requested depth of the FIFO on the receive side of the
+                            serial interface. A ReceiveFifoDepth value of 0 will use
+                            the device's default FIFO depth.
+                            On output, the value actually set.
+  @param Timeout            The requested time out for a single character in microseconds.
+                            This timeout applies to both the transmit and receive side of the
+                            interface. A Timeout value of 0 will use the device's default time
+                            out value.
+                            On output, the value actually set.
+  @param Parity             The type of parity to use on this serial device. A Parity value of
+                            DefaultParity will use the device's default parity value.
+                            On output, the value actually set.
+  @param DataBits           The number of data bits to use on the serial device. A DataBits
+                            vaule of 0 will use the device's default data bit setting.
+                            On output, the value actually set.
+  @param StopBits           The number of stop bits to use on this serial device. A StopBits
+                            value of DefaultStopBits will use the device's default number of
+                            stop bits.
+                            On output, the value actually set.
+
+  @retval RETURN_SUCCESS            The new attributes were set on the serial device.
+  @retval RETURN_UNSUPPORTED        The serial device does not support this operation.
+  @retval RETURN_INVALID_PARAMETER  One or more of the attributes has an unsupported value.
+  @retval RETURN_DEVICE_ERROR       The serial device is not functioning correctly.
+
+**/
+RETURN_STATUS
+EFIAPI
+UartCtlConfig (
+  IN OUT UINT64             *BaudRate,
+  IN OUT UINT32             *ReceiveFifoDepth,
+  IN OUT UINT32             *Timeout,
+  IN OUT EFI_PARITY_TYPE    *Parity,
+  IN OUT UINT8              *DataBits,
+  IN OUT EFI_STOP_BITS_TYPE *StopBits,
+  IN UINTN                   CtlAddr
+  )
+{
+  UINTN     SerialRegisterBase;
+  UINT8     Lcr;
+  UINT8     LcrData;
+  UINT8     LcrParity;
+  UINT8     LcrStop;
+
+
+  SerialRegisterBase = CtlAddr;
+  if (SerialRegisterBase ==0) {
+    return RETURN_UNSUPPORTED;
+  }
+
+  //
+  // Check for default settings and fill in actual values.
+  //
+  if (*BaudRate == 0) {
+    *BaudRate = PcdGet32 (PcdSerialBaudRate);
+  }
+
+  if (*DataBits == 0) {
+    LcrData = (UINT8) (PcdGet8 (PcdSerialLineControl) & 0x3);
+    *DataBits = LcrData + 5;
+  } else {
+    if ((*DataBits < 5)
+      || (*DataBits > 8))
+    {
+      return RETURN_INVALID_PARAMETER;
+    }
+    //
+    // Map 5..8 to 0..3
+    //
+    LcrData = (UINT8) (*DataBits - (UINT8) 5);
+  }
+
+  if (*Parity == DefaultParity) {
+    LcrParity = (UINT8) ((PcdGet8 (PcdSerialLineControl) >> 3) & 0x7);
+    switch (LcrParity) {
+      case 0:
+        *Parity = NoParity;
+        break;
+
+      case 3:
+        *Parity = EvenParity;
+        break;
+
+      case 1:
+        *Parity = OddParity;
+        break;
+
+      case 7:
+        *Parity = SpaceParity;
+        break;
+
+      case 5:
+        *Parity = MarkParity;
+        break;
+
+      default:
+        break;
+    }
+  } else {
+    switch (*Parity) {
+      case NoParity:
+        LcrParity = 0;
+        break;
+
+      case EvenParity:
+        LcrParity = 3;
+        break;
+
+      case OddParity:
+        LcrParity = 1;
+        break;
+
+      case SpaceParity:
+        LcrParity = 7;
+        break;
+
+      case MarkParity:
+        LcrParity = 5;
+        break;
+
+      default:
+        return RETURN_INVALID_PARAMETER;
+    }
+  }
+
+  if (*StopBits == DefaultStopBits) {
+    LcrStop = (UINT8) ((PcdGet8 (PcdSerialLineControl) >> 2) & 0x1);
+    switch (LcrStop) {
+      case 0:
+        *StopBits = OneStopBit;
+        break;
+
+      case 1:
+        if (*DataBits == 5) {
+          *StopBits = OneFiveStopBits;
+        } else {
+          *StopBits = TwoStopBits;
+        }
+        break;
+
+      default:
+        break;
+    }
+  } else {
+    switch (*StopBits) {
+      case OneStopBit:
+        LcrStop = 0;
+        break;
+
+      case OneFiveStopBits:
+      case TwoStopBits:
+        LcrStop = 1;
+        break;
+
+      default:
+        return RETURN_INVALID_PARAMETER;
+    }
+  }
+  SerialPortWriteRegister (SerialRegisterBase, R_UART_LCR, B_UART_LCR_DLAB);
+
+  //
+  // Clear DLAB and configure Data Bits, Parity, and Stop Bits.
+  // Strip reserved bits from line control value
+  //
+  Lcr = (UINT8) ((LcrParity << 3) | (LcrStop << 2) | LcrData);
+  SerialPortWriteRegister (SerialRegisterBase, R_UART_LCR, (UINT8) (Lcr & 0x3F));
+
+  return RETURN_SUCCESS;
+}
+/**
+  Set the serial port Attributes.
+
+  @param  VOID
+
+  @return  serial register base address.
+**/
+RETURN_STATUS
+EFIAPI
+SerialPortSetAttributes (
+  IN OUT UINT64             *BaudRate,
+  IN OUT UINT32             *ReceiveFifoDepth,
+  IN OUT UINT32             *Timeout,
+  IN OUT EFI_PARITY_TYPE    *Parity,
+  IN OUT UINT8              *DataBits,
+  IN OUT EFI_STOP_BITS_TYPE *StopBits
+  )
+{
+  UINTN     SerialRegisterBase;
+
+  SerialRegisterBase = GetSerialRegisterBase ();
+
+  return  UartCtlConfig (&gBps, ReceiveFifoDepth, Timeout, Parity, DataBits, StopBits,
+            SerialRegisterBase);
+}
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/SerialPortLib/SerialPortLib.inf b/Platform/Loongson/LoongArchQemuPkg/Library/SerialPortLib/SerialPortLib.inf
new file mode 100644
index 0000000000..6c4674151b
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/SerialPortLib/SerialPortLib.inf
@@ -0,0 +1,36 @@
+## @file
+#  UART Serial Port library functions
+#
+#  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = PcAtSerialPortLib
+  FILE_GUID                      = f4fb883d-8138-4f29-bb0c-c574e9312c74
+  MODULE_TYPE                    = BASE
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = SerialPortLib
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  Platform/Loongson/LoongArchQemuPkg/Loongson.dec
+
+[LibraryClasses]
+  BaseLib
+  IoLib
+  PcdLib
+
+[Sources]
+  SerialPortLib.c
+
+[Pcd]
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSerialUseHardwareFlowControl  ## CONSUMES
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSerialBaudRate                ## CONSUMES
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSerialLineControl             ## CONSUMES
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSerialClockRate               ## CONSUMES
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [edk2-platforms][PATCH V3 02/16] Platform/Loongson: Support SEC And Add Readme.md
  2022-09-29  7:07 [edk2-platforms][PATCH V3 00/16] Platform: Add Loongson support xianglai
  2022-09-29  7:07 ` [edk2-platforms][PATCH V3 01/16] Platform/Loongson: Add Serial Port library xianglai
@ 2022-09-29  7:07 ` xianglai
  2022-09-29  7:07 ` [edk2-platforms][PATCH V3 03/16] Platform/Loongson: Add PeiServicesTablePointerLib xianglai
                   ` (13 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: xianglai @ 2022-09-29  7:07 UTC (permalink / raw)
  To: devel; +Cc: quic_llindhol, michael.d.kinney, kraxel, ardb, maobibo

Add SEC Code And Readme.md for LoongArchQemu

REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4054

Signed-off-by: xianglai li  <lixianglai@loongson.cn>
Signed-off-by: xianglai li <lixianglai@loongson.cn>
---
 Maintainers.txt                               |   4 +
 .../Include/LoongArchAsmMacro.h               |  23 +
 .../Loongson/LoongArchQemuPkg/Loongson.dec    |  36 ++
 .../Loongson/LoongArchQemuPkg/Loongson.dsc    | 131 +++++
 .../Loongson/LoongArchQemuPkg/Loongson.fdf    |  53 ++
 .../LoongArchQemuPkg/Loongson.fdf.inc         |  21 +
 Platform/Loongson/LoongArchQemuPkg/Readme.md  |  59 ++
 .../LoongArchQemuPkg/Sec/LoongArch64/Start.S  |  76 +++
 .../Loongson/LoongArchQemuPkg/Sec/SecMain.c   | 510 ++++++++++++++++++
 .../Loongson/LoongArchQemuPkg/Sec/SecMain.inf |  47 ++
 Readme.md                                     |   9 +
 11 files changed, 969 insertions(+)
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Include/LoongArchAsmMacro.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Loongson.dec
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Loongson.dsc
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Loongson.fdf
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Loongson.fdf.inc
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Readme.md
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Sec/LoongArch64/Start.S
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Sec/SecMain.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Sec/SecMain.inf

diff --git a/Maintainers.txt b/Maintainers.txt
index 700ca0af60..8921d6b4e1 100644
--- a/Maintainers.txt
+++ b/Maintainers.txt
@@ -329,6 +329,10 @@ M: Bob Feng <bob.c.feng@intel.com>
 M: Liming Gao <gaoliming@byosoft.com.cn>
 R: Yuwei Chen <yuwei.chen@intel.com>
 
+Loongson platforms
+F: Platform/Loongson/
+M: xianglai li <lixianglai@loongson.cn>
+
 Marvell platforms and silicon
 F: Platform/Marvell/
 F: Platform/SolidRun/Armada80x0McBin/
diff --git a/Platform/Loongson/LoongArchQemuPkg/Include/LoongArchAsmMacro.h b/Platform/Loongson/LoongArchQemuPkg/Include/LoongArchAsmMacro.h
new file mode 100644
index 0000000000..366d4308e8
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Include/LoongArchAsmMacro.h
@@ -0,0 +1,23 @@
+/** @file
+   LoongArch ASM macro definition.
+
+   Copyright (c) 2021, Loongson Limited. All rights reserved.
+
+   SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ **/
+
+#ifndef LOONGARCH_ASM_MACRO_H_
+#define LOONGARCH_ASM_MACRO_H_
+
+#include <Base.h>
+
+#define _ASM_FUNC(Name, Section)    \
+  .global   Name                  ; \
+  .section  #Section, "ax"        ; \
+  .type     Name, %function       ; \
+  Name:
+
+#define ASM_FUNC(Name)            _ASM_FUNC(ASM_PFX(Name), .text. ## Name)
+
+#endif // __LOONGARCH_ASM_MACRO_H__
diff --git a/Platform/Loongson/LoongArchQemuPkg/Loongson.dec b/Platform/Loongson/LoongArchQemuPkg/Loongson.dec
new file mode 100644
index 0000000000..8cfa63ee8a
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Loongson.dec
@@ -0,0 +1,36 @@
+## @file
+#
+#  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  DEC_SPECIFICATION              = 0x00010005
+  PACKAGE_NAME                   = LoongArchQemuPkg
+  PACKAGE_GUID                   = b51d765a-41da-45fc-a537-de3ee785c0f6
+  PACKAGE_VERSION                = 0.1
+
+################################################################################
+#
+# Include Section - list of Include Paths that are provided by this package.
+#                   Comments are used for Keywords and Module Types.
+#
+# Supported Module Types:
+#  BASE SEC PEI_CORE PEIM DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER DXE_SAL_DRIVER UEFI_DRIVER UEFI_APPLICATION
+#
+################################################################################
+[Includes.common]
+  Include                        # Root include for the package
+
+[Guids]
+  gLoongArchQemuPkgTokenSpaceGuid  = { 0x0e0383ce, 0x0151, 0x4d01, { 0x80, 0x0e, 0x3f, 0xef, 0x8b, 0x27, 0x6d, 0x52 } }
+
+[PcdsFixedAtBuild, PcdsDynamic]
+  gLoongArchQemuPkgTokenSpaceGuid.PcdFlashPeiFvBase|0x0|UINT64|0x00000003
+  gLoongArchQemuPkgTokenSpaceGuid.PcdFlashPeiFvSize|0x0|UINT32|0x00000004
+  gLoongArchQemuPkgTokenSpaceGuid.PcdSecPeiTempRamBase|0|UINT64|0x0000001c
+  gLoongArchQemuPkgTokenSpaceGuid.PcdSecPeiTempRamSize|0|UINT32|0x0000001d
+  gLoongArchQemuPkgTokenSpaceGuid.PcdFlashSecFvBase|0x0|UINT64|0x00000028
+  gLoongArchQemuPkgTokenSpaceGuid.PcdFlashSecFvSize|0x0|UINT32|0x00000029
diff --git a/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc b/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc
new file mode 100644
index 0000000000..52ab11de9a
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc
@@ -0,0 +1,131 @@
+## @file
+#
+#  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+################################################################################
+#
+# Defines Section - statements that will be processed to create a Makefile.
+#
+###############################################################################
+[Defines]
+  PLATFORM_NAME                  = LoongArchQemu
+  PLATFORMPKG_NAME               = LoongArchQemu
+  PLATFORM_GUID                  = 7926ea52-b0dc-4ee8-ac63-341eebd84ed4
+  PLATFORM_VERSION               = 0.1
+  DSC_SPECIFICATION              = 0x00010005
+  OUTPUT_DIRECTORY               = Build/$(PLATFORM_NAME)
+  SUPPORTED_ARCHITECTURES        = LOONGARCH64
+  BUILD_TARGETS                  = DEBUG|RELEASE
+  SKUID_IDENTIFIER               = DEFAULT
+  FLASH_DEFINITION               = Platform/Loongson/LoongArchQemuPkg/Loongson.fdf
+  TTY_TERMINAL                   = FALSE
+
+############################################################################
+#
+# Defines for default states.  These can be changed on the command line.
+# -D FLAG=VALUE
+############################################################################
+[BuildOptions]
+  GCC:RELEASE_*_*_CC_FLAGS       = -DSPEEDUP
+
+  #
+  # Disable deprecated APIs.
+  #
+  GCC:*_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES
+
+
+[BuildOptions.LOONGARCH64.EDKII.SEC]
+  *_*_*_CC_FLAGS                 =
+
+#[BuildOptions.common.EDKII.DXE_CORE,BuildOptions.common.EDKII.DXE_DRIVER,BuildOptions.common.EDKII.UEFI_DRIVER,BuildOptions.common.EDKII.UEFI_APPLICATION]
+#  GCC:*_*_*_DLINK_FLAGS = -z common-page-size=0x1000
+
+[BuildOptions.common.EDKII.DXE_RUNTIME_DRIVER]
+  GCC:*_*_LOONGARCH64_DLINK_FLAGS = -z common-page-size=0x10000
+
+################################################################################
+#
+# Library Class section - list of all Library Classes needed by this Platform.
+#
+################################################################################
+
+!include MdePkg/MdeLibs.dsc.inc
+
+[LibraryClasses.common]
+  PcdLib                           | MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
+  PrintLib                         | MdePkg/Library/BasePrintLib/BasePrintLib.inf
+  BaseMemoryLib                    | MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
+
+
+  BaseLib                          | MdePkg/Library/BaseLib/BaseLib.inf
+  PeCoffLib                        | MdePkg/Library/BasePeCoffLib/BasePeCoffLib.inf
+  PeCoffGetEntryPointLib           | MdePkg/Library/BasePeCoffGetEntryPointLib/BasePeCoffGetEntryPointLib.inf
+  IoLib                            | MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf
+  SerialPortLib                    | Platform/Loongson/LoongArchQemuPkg/Library/SerialPortLib/SerialPortLib.inf
+  DebugPrintErrorLevelLib          | MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf
+  PeCoffExtraActionLib             | MdePkg/Library/BasePeCoffExtraActionLibNull/BasePeCoffExtraActionLibNull.inf
+  DebugAgentLib                    | MdeModulePkg/Library/DebugAgentLibNull/DebugAgentLibNull.inf
+
+  DebugLib                         | MdePkg/Library/BaseDebugLibSerialPort/BaseDebugLibSerialPort.inf
+
+
+
+
+################################################################################
+#
+# Pcd Section - list of all EDK II PCD Entries defined by this Platform.
+#
+################################################################################
+[PcdsFixedAtBuild]
+## BaseLib ##
+  gEfiMdePkgTokenSpaceGuid.PcdMaximumUnicodeStringLength               | 1000000
+  gEfiMdePkgTokenSpaceGuid.PcdMaximumAsciiStringLength                 | 1000000
+  gEfiMdePkgTokenSpaceGuid.PcdMaximumLinkedListLength                  | 1000000
+
+  gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel                     | 0x8000004F
+  # DEBUG_INIT      0x00000001  // Initialization
+  # DEBUG_WARN      0x00000002  // Warnings
+  # DEBUG_LOAD      0x00000004  // Load events
+  # DEBUG_FS        0x00000008  // EFI File system
+  # DEBUG_POOL      0x00000010  // Alloc & Free (pool)
+  # DEBUG_PAGE      0x00000020  // Alloc & Free (page)
+  # DEBUG_INFO      0x00000040  // Informational debug messages
+  # DEBUG_DISPATCH  0x00000080  // PEI/DXE/SMM Dispatchers
+  # DEBUG_VARIABLE  0x00000100  // Variable
+  # DEBUG_BM        0x00000400  // Boot Manager
+  # DEBUG_BLKIO     0x00001000  // BlkIo Driver
+  # DEBUG_NET       0x00004000  // Network Io Driver
+  # DEBUG_UNDI      0x00010000  // UNDI Driver
+  # DEBUG_LOADFILE  0x00020000  // LoadFile
+  # DEBUG_EVENT     0x00080000  // Event messages
+  # DEBUG_GCD       0x00100000  // Global Coherency Database changes
+  # DEBUG_CACHE     0x00200000  // Memory range cachability changes
+  # DEBUG_VERBOSE   0x00400000  // Detailed debug messages that may
+  # DEBUG_ERROR     0x80000000  // Error
+
+!if $(TARGET) == RELEASE
+  gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask                        | 0x21
+!else
+  gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask                        | 0x2f
+!endif
+  # DEBUG_ASSERT_ENABLED       0x01
+  # DEBUG_PRINT_ENABLED        0x02
+  # DEBUG_CODE_ENABLED         0x04
+  # CLEAR_MEMORY_ENABLED       0x08
+  # ASSERT_BREAKPOINT_ENABLED  0x10
+  # ASSERT_DEADLOOP_ENABLED    0x20
+
+  gLoongArchQemuPkgTokenSpaceGuid.PcdSecPeiTempRamBase                 | 0x10000
+  gLoongArchQemuPkgTokenSpaceGuid.PcdSecPeiTempRamSize                 | 0x10000
+
+
+[Components]
+
+  #
+  # SEC Phase modules
+  #
+  Platform/Loongson/LoongArchQemuPkg/Sec/SecMain.inf
diff --git a/Platform/Loongson/LoongArchQemuPkg/Loongson.fdf b/Platform/Loongson/LoongArchQemuPkg/Loongson.fdf
new file mode 100644
index 0000000000..128b3843db
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Loongson.fdf
@@ -0,0 +1,53 @@
+## @file
+#
+#  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+#####################################################################################################
+[Defines]
+!include Loongson.fdf.inc
+
+#####################################################################################################
+[FD.QEMU_EFI]
+BaseAddress   = $(FD_BASE_ADDRESS)
+Size          = $(FD_SIZE)
+ErasePolarity = 1
+BlockSize     = $(BLOCK_SIZE)
+NumBlocks     = $(FD_BLOCKS)
+
+$(SECFV_OFFSET)|$(SECFV_SIZE)
+gLoongArchQemuPkgTokenSpaceGuid.PcdFlashSecFvBase|gLoongArchQemuPkgTokenSpaceGuid.PcdFlashSecFvSize
+FV = SECFV
+
+#####################################################################################################
+[FV.SECFV]
+FvNameGuid         = 587d4265-5e71-41da-9c35-4258551f1e22
+BlockSize          = $(BLOCK_SIZE)
+FvAlignment        = 16
+ERASE_POLARITY     = 1
+MEMORY_MAPPED      = TRUE
+STICKY_WRITE       = TRUE
+LOCK_CAP           = TRUE
+LOCK_STATUS        = TRUE
+WRITE_DISABLED_CAP = TRUE
+WRITE_ENABLED_CAP  = TRUE
+WRITE_STATUS       = TRUE
+WRITE_LOCK_CAP     = TRUE
+WRITE_LOCK_STATUS  = TRUE
+READ_DISABLED_CAP  = TRUE
+READ_ENABLED_CAP   = TRUE
+READ_STATUS        = TRUE
+READ_LOCK_CAP      = TRUE
+READ_LOCK_STATUS   = TRUE
+
+INF  Platform/Loongson/LoongArchQemuPkg/Sec/SecMain.inf
+
+#####################################################################################################
+[Rule.Common.SEC]
+  FILE SEC = $(NAMED_GUID) {
+    TE       TE Align = Auto   $(INF_OUTPUT)/$(MODULE_NAME).efi
+    UI       STRING ="$(MODULE_NAME)" Optional
+  }
diff --git a/Platform/Loongson/LoongArchQemuPkg/Loongson.fdf.inc b/Platform/Loongson/LoongArchQemuPkg/Loongson.fdf.inc
new file mode 100644
index 0000000000..a1a2d537e3
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Loongson.fdf.inc
@@ -0,0 +1,21 @@
+## @file
+#
+#  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+DEFINE BLOCK_SIZE                 = 0x1000
+
+############################################################################
+# fd total
+DEFINE FD_BASE_ADDRESS            = 0x1c000000
+DEFINE FD_BLOCKS                  = 0x400
+DEFINE FD_SIZE                    = 0x400000
+
+############################################################################
+#flash code layout
+#Set Sec base address and size in flash
+DEFINE SECFV_OFFSET               = 0x00000000
+DEFINE SECFV_SIZE                 = 0x00010000
diff --git a/Platform/Loongson/LoongArchQemuPkg/Readme.md b/Platform/Loongson/LoongArchQemuPkg/Readme.md
new file mode 100644
index 0000000000..b0d172f829
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Readme.md
@@ -0,0 +1,59 @@
+# Introduction
+
+  This document provides the guideline to build UEFI firmware for Qemu of LoongArch.
+
+  LoongArch is the general processor architecture of Loongson.
+
+  We can get the latest LoongArch documents or LoongArch tools at https://github.com/loongson/.
+
+# How to build (X86 Linux Environment)
+
+  1. Install LoongArch cross-tools on X86 machines.
+    Download cross-tools from https://github.com/loongson/ ,Then config cross-tools env.
+    For Example:
+
+    $ wget https://github.com/loongson/build-tools/releases/latest/download/loongarch64-clfs-20211202-cross-tools.tar.xz
+    $ tar -vxf loongarch64-clfs-20211202-cross-tools.tar.xz  -C /opt
+    $ export PATH=/opt/cross-tools/bin:$PATH
+
+  2. Follow edk2-platforms/Readme.md to obtaining source code,And config build env.
+    For Example:
+
+    $ export WORKSPACE=/work/git/tianocore
+    $ mkdir -p $WORKSPACE
+    $ cd $WORKSPACE
+    $ git clone https://github.com/tianocore/edk2.git
+    $ git submodule update --init
+    $ git clone https://github.com/tianocore/edk2-platforms.git
+    $ git submodule update --init
+    $ git clone https://github.com/tianocore/edk2-non-osi.git
+    $ export PACKAGES_PATH=$PWD/edk2:$PWD/edk2-platforms:$PWD/edk2-non-osi
+
+  3. Config  cross compiler prefix.
+    For Example:
+
+    $ export GCC5_LOONGARCH64_PREFIX=loongarch64-unknown-linux-gnu-
+
+  4.Set up the build environment And  build BaseTool.
+    For Example:
+
+    $. edk2/edksetup.sh
+    $make -C edk2/BaseTools
+
+  5.Build  platform.
+    For Exmaple:
+
+    $build --buildtarget=DEBUG --tagname=GCC5 --arch=LOONGARCH64  --platform=Platform/Loongson/LoongArchQemuPkg/Loongson.dsc
+
+  After a successful build, the resulting images can be found in `Build/{Platform Name}/{TARGET}_{TOOL_CHAIN_TAG}/FV/QEMU_EFI.fd`.
+
+  A compile script is provided here:
+
+    #!/bin/bash
+    export WORKSPACE=/work/git/tianocore
+    export PACKAGES_PATH=$WORKSPACE/edk2:$WORKSPACE/edk2-platforms
+    export GCC5_LOONGARCH64_PREFIX=loongarch64-unknown-linux-gnu-
+    . edk2/edksetup.sh
+    make -C edk2/BaseTools
+    build --buildtarget=DEBUG --tagname=GCC5 --arch=LOONGARCH64  --platform=Platform/Loongson/LoongArchQemuPkg/Loongson.dsc
+
diff --git a/Platform/Loongson/LoongArchQemuPkg/Sec/LoongArch64/Start.S b/Platform/Loongson/LoongArchQemuPkg/Sec/LoongArch64/Start.S
new file mode 100644
index 0000000000..48c044fe28
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Sec/LoongArch64/Start.S
@@ -0,0 +1,76 @@
+#------------------------------------------------------------------------------
+#
+# Start for LoongArch
+#
+# Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#  @par Glossary:
+#    - CSR - CPU Status Register
+#    - EBASE - Exception Base Address
+#------------------------------------------------------------------------------
+#ifndef __ASSEMBLY__
+#define  __ASSEMBLY__
+#endif
+
+#include <Library/Cpu.h>
+#include "LoongArchAsmMacro.h"
+
+    .text
+    .globl  _ModuleEntryPoint
+_ModuleEntryPoint:
+
+    /* configure reset ebase */
+    li.d    T0, 0x1c000000
+    csrwr   T0, LOONGARCH_CSR_EBASE
+
+    /*disable interrupt*/
+    li.d    T0, (1 << 2)
+    csrxchg ZERO, T0, LOONGARCH_CSR_CRMD
+
+    /* read physical cpu number id */
+    csrrd   T0, LOONGARCH_CSR_CPUNUM
+    andi    T0, T0, 0x3ff
+    li.d    A0, BOOTCORE_ID  //0
+    bne     T0, A0, slave_main
+
+call_centry:
+    /*call C function make sure parameter true*/
+    li.d    A1, FixedPcdGet64(PcdSecPeiTempRamBase) + FixedPcdGet32(PcdSecPeiTempRamSize)  # stack base
+    li.d    A0, FixedPcdGet64(PcdFlashPeiFvBase) # PEI Fv base
+    move    SP, A1
+    addi.d  SP, SP, -0x8
+    bl      SecCoreStartupWithStack
+
+slave_main:
+    # clear mailbox
+    li.d      T1, LOONGSON_CSR_MAIL_BUF0
+    iocsrwr.d ZERO, T1
+
+    # enable IPI interrupt
+    li.d      T0, (1 << 12)
+    csrxchg   T0, T0, LOONGARCH_CSR_ECFG
+
+1:
+    # wait for wakeup
+    idle 0
+    nop
+    iocsrrd.w T0, T1
+    beqz      T0, 1b
+
+    # read and clear ipi interrupt
+    li.d      T1, LOONGSON_IOCSR_IPI_STATUS
+    iocsrrd.w T0, T1
+    li.d      T1, LOONGSON_IOCSR_IPI_CLEAR
+    iocsrwr.w T0, T1
+
+    # disable IPI interrupt
+    li.d      T0, (1 << 12)
+    csrxchg   ZERO, T0, LOONGARCH_CSR_ECFG
+
+    # read mail buf and jump to specified entry
+    li.d      T1, LOONGSON_CSR_MAIL_BUF0
+    iocsrrd.d T0, T1
+    or        RA, T0, ZERO
+    jirl      ZERO, RA, 0x0
diff --git a/Platform/Loongson/LoongArchQemuPkg/Sec/SecMain.c b/Platform/Loongson/LoongArchQemuPkg/Sec/SecMain.c
new file mode 100644
index 0000000000..73d3a2c1a3
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Sec/SecMain.c
@@ -0,0 +1,510 @@
+/** @file
+  Main SEC phase code.  Transitions to PEI.
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiPei.h>
+
+#include <Library/PeimEntryPoint.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/PeiServicesLib.h>
+#include <Library/PcdLib.h>
+#include <Library/DebugAgentLib.h>
+#include <Library/IoLib.h>
+#include <Library/PeCoffLib.h>
+#include <Library/PeCoffGetEntryPointLib.h>
+#include <Library/PeCoffExtraActionLib.h>
+#include <Library/ExtractGuidedSectionLib.h>
+
+#include <Ppi/TemporaryRamSupport.h>
+
+/**
+  temporary memory to permanent memory and do stack switching.
+
+  @param[in]  PeiServices     Pointer to the PEI Services Table.
+  @param[in]  TemporaryMemoryBase    Temporary Memory Base address.
+  @param[in]  PermanentMemoryBase   Permanent Memory Base address.
+  @param[in]  CopySize   The size of memory that needs to be migrated.
+
+  @retval   EFI_SUCCESS  Migration successful.
+**/
+EFI_STATUS
+EFIAPI
+TemporaryRamMigration (
+  IN CONST EFI_PEI_SERVICES   **PeiServices,
+  IN EFI_PHYSICAL_ADDRESS     TemporaryMemoryBase,
+  IN EFI_PHYSICAL_ADDRESS     PermanentMemoryBase,
+  IN UINTN                    CopySize
+  );
+
+EFI_PEI_TEMPORARY_RAM_SUPPORT_PPI mTemporaryRamSupportPpi = {
+  TemporaryRamMigration
+};
+
+EFI_PEI_PPI_DESCRIPTOR mPrivateDispatchTable[] = {
+  {
+    (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
+    &gEfiTemporaryRamSupportPpiGuid,
+    &mTemporaryRamSupportPpi
+  },
+};
+
+/**
+  Locates a section within a series of sections
+  with the specified section type.
+
+  The Instance parameter indicates which instance of the section
+  type to return. (0 is first instance, 1 is second...)
+
+  @param[in]   Sections        The sections to search
+  @param[in]   SizeOfSections  Total size of all sections
+  @param[in]   SectionType     The section type to locate
+  @param[in]   Instance        The section instance number
+  @param[out]  FoundSection    The FFS section if found
+
+  @retval EFI_SUCCESS           The file and section was found
+  @retval EFI_NOT_FOUND         The file and section was not found
+  @retval EFI_VOLUME_CORRUPTED  The firmware volume was corrupted
+
+**/
+EFI_STATUS
+FindFfsSectionInstance (
+  IN  VOID                             *Sections,
+  IN  UINTN                            SizeOfSections,
+  IN  EFI_SECTION_TYPE                 SectionType,
+  IN  UINTN                            Instance,
+  OUT EFI_COMMON_SECTION_HEADER        **FoundSection
+  )
+{
+  EFI_PHYSICAL_ADDRESS        CurrentAddress;
+  UINT32                      Size;
+  EFI_PHYSICAL_ADDRESS        EndOfSections;
+  EFI_COMMON_SECTION_HEADER   *Section;
+  EFI_PHYSICAL_ADDRESS        EndOfSection;
+
+  //
+  // Loop through the FFS file sections within the PEI Core FFS file
+  //
+  EndOfSection = (EFI_PHYSICAL_ADDRESS) (UINTN) Sections;
+  EndOfSections = EndOfSection + SizeOfSections;
+  for (;;) {
+    if (EndOfSection == EndOfSections) {
+      break;
+    }
+    CurrentAddress = (EndOfSection + 3) & ~(3ULL);
+    if (CurrentAddress >= EndOfSections) {
+      return EFI_VOLUME_CORRUPTED;
+    }
+
+    Section = (EFI_COMMON_SECTION_HEADER*) (UINTN) CurrentAddress;
+
+    Size = SECTION_SIZE (Section);
+    if (Size < sizeof (*Section)) {
+      return EFI_VOLUME_CORRUPTED;
+    }
+
+    EndOfSection = CurrentAddress + Size;
+    if (EndOfSection > EndOfSections) {
+      return EFI_VOLUME_CORRUPTED;
+    }
+
+    //
+    // Look for the requested section type
+    //
+    if (Section->Type == SectionType) {
+      if (Instance == 0) {
+        *FoundSection = Section;
+        return EFI_SUCCESS;
+      } else {
+        Instance--;
+      }
+    }
+  }
+
+  return EFI_NOT_FOUND;
+}
+
+/**
+  Locates a section within a series of sections
+  with the specified section type.
+
+  @param[in]   Sections        The sections to search
+  @param[in]   SizeOfSections  Total size of all sections
+  @param[in]   SectionType     The section type to locate
+  @param[out]  FoundSection    The FFS section if found
+
+  @retval EFI_SUCCESS           The file and section was found
+  @retval EFI_NOT_FOUND         The file and section was not found
+  @retval EFI_VOLUME_CORRUPTED  The firmware volume was corrupted
+
+**/
+EFI_STATUS
+FindFfsSectionInSections (
+  IN  VOID                             *Sections,
+  IN  UINTN                            SizeOfSections,
+  IN  EFI_SECTION_TYPE                 SectionType,
+  OUT EFI_COMMON_SECTION_HEADER        **FoundSection
+  )
+{
+  return FindFfsSectionInstance (
+           Sections,
+           SizeOfSections,
+           SectionType,
+           0,
+           FoundSection
+           );
+}
+
+/**
+  Locates a FFS file with the specified file type and a section
+  within that file with the specified section type.
+
+  @param[in]   Fv            The firmware volume to search
+  @param[in]   FileType      The file type to locate
+  @param[in]   SectionType   The section type to locate
+  @param[out]  FoundSection  The FFS section if found
+
+  @retval EFI_SUCCESS           The file and section was found
+  @retval EFI_NOT_FOUND         The file and section was not found
+  @retval EFI_VOLUME_CORRUPTED  The firmware volume was corrupted
+
+**/
+EFI_STATUS
+FindFfsFileAndSection (
+  IN  EFI_FIRMWARE_VOLUME_HEADER       *Fv,
+  IN  EFI_FV_FILETYPE                  FileType,
+  IN  EFI_SECTION_TYPE                 SectionType,
+  OUT EFI_COMMON_SECTION_HEADER        **FoundSection
+  )
+{
+  EFI_STATUS                  Status;
+  EFI_PHYSICAL_ADDRESS        CurrentAddress;
+  EFI_PHYSICAL_ADDRESS        EndOfFirmwareVolume;
+  EFI_FFS_FILE_HEADER         *File;
+  UINT32                      Size;
+  EFI_PHYSICAL_ADDRESS        EndOfFile;
+
+  if (Fv->Signature != EFI_FVH_SIGNATURE) {
+    DEBUG ((DEBUG_ERROR, "FV at %p does not have FV header signature\n", Fv));
+    return EFI_VOLUME_CORRUPTED;
+  }
+
+  CurrentAddress = (EFI_PHYSICAL_ADDRESS) (UINTN) Fv;
+  EndOfFirmwareVolume = CurrentAddress + Fv->FvLength;
+
+  //
+  // Loop through the FFS files in the Boot Firmware Volume
+  //
+  for (EndOfFile = CurrentAddress + Fv->HeaderLength; ; ) {
+
+    CurrentAddress = (EndOfFile + 7) & ~(7ULL);
+    if (CurrentAddress > EndOfFirmwareVolume) {
+      return EFI_VOLUME_CORRUPTED;
+    }
+
+    File = (EFI_FFS_FILE_HEADER*) (UINTN) CurrentAddress;
+    Size = *(UINT32*) File->Size & 0xffffff;
+    if (Size < (sizeof (*File) + sizeof (EFI_COMMON_SECTION_HEADER))) {
+      return EFI_VOLUME_CORRUPTED;
+    }
+
+    EndOfFile = CurrentAddress + Size;
+    if (EndOfFile > EndOfFirmwareVolume) {
+      return EFI_VOLUME_CORRUPTED;
+    }
+
+    //
+    // Look for the request file type
+    //
+    if (File->Type != FileType) {
+      continue;
+    }
+
+    Status = FindFfsSectionInSections (
+               (VOID*) (File + 1),
+               (UINTN) EndOfFile - (UINTN) (File + 1),
+               SectionType,
+               FoundSection
+               );
+    if (!EFI_ERROR (Status)
+      || (Status == EFI_VOLUME_CORRUPTED))
+    {
+      return Status;
+    }
+  }
+}
+
+/**
+  Locates the PEI Core entry point address
+
+  @param[in]  Fv                 The firmware volume to search
+  @param[out] PeiCoreEntryPoint  The entry point of the PEI Core image
+
+  @retval EFI_SUCCESS           The file and section was found
+  @retval EFI_NOT_FOUND         The file and section was not found
+  @retval EFI_VOLUME_CORRUPTED  The firmware volume was corrupted
+
+**/
+EFI_STATUS
+FindPeiCoreImageBaseInFv (
+  IN  EFI_FIRMWARE_VOLUME_HEADER       *Fv,
+  OUT  EFI_PHYSICAL_ADDRESS             *PeiCoreImageBase
+  )
+{
+  EFI_STATUS                  Status;
+  EFI_COMMON_SECTION_HEADER   *Section;
+
+  Status = FindFfsFileAndSection (
+             Fv,
+             EFI_FV_FILETYPE_PEI_CORE,
+             EFI_SECTION_PE32,
+             &Section
+             );
+  if (EFI_ERROR (Status)) {
+    Status = FindFfsFileAndSection (
+               Fv,
+               EFI_FV_FILETYPE_PEI_CORE,
+               EFI_SECTION_TE,
+               &Section
+               );
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "Unable to find PEI Core image\n"));
+      return Status;
+    }
+  }
+
+  *PeiCoreImageBase = (EFI_PHYSICAL_ADDRESS)(UINTN)(Section + 1);
+  return EFI_SUCCESS;
+}
+
+/**
+  Find and return Pei Core entry point.
+
+  It also find SEC and PEI Core file debug information. It will report them if
+  remote debug is enabled.
+
+**/
+VOID
+FindAndReportEntryPoints (
+  IN  EFI_FIRMWARE_VOLUME_HEADER       **BootFirmwareVolumePtr,
+  OUT EFI_PEI_CORE_ENTRY_POINT         *PeiCoreEntryPoint
+  )
+{
+  EFI_STATUS                       Status;
+  EFI_PHYSICAL_ADDRESS             PeiCoreImageBase = 0;
+  PE_COFF_LOADER_IMAGE_CONTEXT     ImageContext;
+
+  Status = FindPeiCoreImageBaseInFv (*BootFirmwareVolumePtr, &PeiCoreImageBase);
+  ASSERT (Status == EFI_SUCCESS);
+
+  ZeroMem ((VOID *) &ImageContext, sizeof (PE_COFF_LOADER_IMAGE_CONTEXT));
+
+  //
+  // Report PEI Core debug information when remote debug is enabled
+  //
+  ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)PeiCoreImageBase;
+  ImageContext.PdbPointer = PeCoffLoaderGetPdbPointer ((VOID*) (UINTN) ImageContext.ImageAddress);
+  PeCoffLoaderRelocateImageExtraAction (&ImageContext);
+
+  //
+  // Find PEI Core entry point
+  //
+  Status = PeCoffLoaderGetEntryPoint ((VOID *) (UINTN) PeiCoreImageBase, (VOID**) PeiCoreEntryPoint);
+  if (EFI_ERROR (Status)) {
+    *PeiCoreEntryPoint = 0;
+  }
+
+  return;
+}
+
+/**
+  Find the peicore entry point and jump to the entry point to execute.
+
+  @param[in] Context    The first input parameter of InitializeDebugAgent().
+
+**/
+VOID
+EFIAPI
+SecStartupPhase2 (
+  IN VOID                     *Context
+  )
+{
+  EFI_SEC_PEI_HAND_OFF        *SecCoreData;
+  EFI_FIRMWARE_VOLUME_HEADER  *BootFv;
+  EFI_PEI_CORE_ENTRY_POINT    PeiCoreEntryPoint;
+
+  SecCoreData = (EFI_SEC_PEI_HAND_OFF *) Context;
+
+  //
+  // Find PEI Core entry point. It will report SEC and Pei Core debug information if remote debug
+  // is enabled.
+  //
+  BootFv = (EFI_FIRMWARE_VOLUME_HEADER *)SecCoreData->BootFirmwareVolumeBase;
+  FindAndReportEntryPoints (&BootFv, &PeiCoreEntryPoint);
+  SecCoreData->BootFirmwareVolumeBase = BootFv;
+  SecCoreData->BootFirmwareVolumeSize = (UINTN) BootFv->FvLength;
+
+  DEBUG ((DEBUG_INFO, "Find Pei EntryPoint=%p\n", PeiCoreEntryPoint));
+
+  //
+  // Transfer the control to the PEI core
+  //
+  DEBUG ((DEBUG_INFO, "SecStartupPhase2 %p\n", PeiCoreEntryPoint));
+
+  (*PeiCoreEntryPoint) (SecCoreData, (EFI_PEI_PPI_DESCRIPTOR *)&mPrivateDispatchTable);
+
+  //
+  // If we get here then the PEI Core returned, which is not recoverable.
+  //
+  ASSERT (FALSE);
+  CpuDeadLoop ();
+}
+/**
+  Entry point to the C language phase of SEC. initialize some temporary memory and set up the stack,
+  the control is transferred to this function.
+
+  @param[in]  BootFv   The pointer to the PEI FV in memory.
+  @param[in]  TopOfCurrentStack  Top of Current Stack.
+**/
+VOID
+EFIAPI
+SecCoreStartupWithStack (
+  IN EFI_FIRMWARE_VOLUME_HEADER       *BootFv,
+  IN VOID                             *TopOfCurrentStack
+  )
+{
+  EFI_SEC_PEI_HAND_OFF             SecCoreData;
+  EFI_FIRMWARE_VOLUME_HEADER       *BootPeiFv = (EFI_FIRMWARE_VOLUME_HEADER*) BootFv;
+
+  DEBUG ((DEBUG_INFO, "Entering C environment\n"));
+
+  ProcessLibraryConstructorList (NULL, NULL);
+
+  DEBUG ((DEBUG_INFO,
+    "SecCoreStartupWithStack (0x%lx, 0x%lx)\n",
+    (UINTN)BootFv,
+    (UINTN)TopOfCurrentStack
+    ));
+  DEBUG ((DEBUG_INFO,
+    "(0x%lx, 0x%lx)\n",
+    (UINTN) (PcdGet64 (PcdSecPeiTempRamBase)),
+    (UINTN) (PcdGet32 (PcdSecPeiTempRamSize))
+    ));
+
+  // |-------------|       <-- TopOfCurrentStack
+  // |   Stack     | 32k
+  // |-------------|
+  // |    Heap     | 32k
+  // |-------------|       <-- SecCoreData.TemporaryRamBase
+  //
+
+  ASSERT ((UINTN) (PcdGet64 (PcdSecPeiTempRamBase) +
+                   PcdGet32 (PcdSecPeiTempRamSize)) ==
+          (UINTN) TopOfCurrentStack);
+
+  //
+  // Initialize SEC hand-off state
+  //
+  SecCoreData.DataSize = sizeof (EFI_SEC_PEI_HAND_OFF);
+
+  SecCoreData.TemporaryRamSize       = (UINTN) PcdGet32 (PcdSecPeiTempRamSize);
+  SecCoreData.TemporaryRamBase       = (VOID *) PcdGet64 (PcdSecPeiTempRamBase);
+
+  SecCoreData.PeiTemporaryRamBase    = SecCoreData.TemporaryRamBase;
+  SecCoreData.PeiTemporaryRamSize    = SecCoreData.TemporaryRamSize >> 1;
+
+  SecCoreData.StackBase              = (UINT8 *)SecCoreData.TemporaryRamBase + SecCoreData.PeiTemporaryRamSize;
+  SecCoreData.StackSize              = SecCoreData.TemporaryRamSize >> 1;
+
+  SecCoreData.BootFirmwareVolumeBase = BootPeiFv;
+  SecCoreData.BootFirmwareVolumeSize = (UINTN) BootPeiFv->FvLength;
+
+  DEBUG ((DEBUG_INFO,
+    "&SecCoreData.BootFirmwareVolumeBase=%lx SecCoreData.BootFirmwareVolumeBase=%lx\n",
+    (UINT64)&(SecCoreData.BootFirmwareVolumeBase),
+    (UINT64) (SecCoreData.BootFirmwareVolumeBase)));
+  DEBUG ((DEBUG_INFO,
+    "&SecCoreData.BootFirmwareVolumeSize=%lx SecCoreData.BootFirmwareVolumeSize=%lx\n",
+    (UINT64)&(SecCoreData.BootFirmwareVolumeSize),
+    (UINT64) (SecCoreData.BootFirmwareVolumeSize)));
+
+  //
+  // Initialize Debug Agent to support source level debug in SEC/PEI phases before memory ready.
+  //
+  InitializeDebugAgent (DEBUG_AGENT_INIT_PREMEM_SEC, NULL, NULL);
+  SecStartupPhase2 (&SecCoreData);
+}
+
+/**
+  temporary memory to permanent memory and do stack switching.
+
+  @param[in]  PeiServices     Pointer to the PEI Services Table.
+  @param[in]  TemporaryMemoryBase    Temporary Memory Base address.
+  @param[in]  PermanentMemoryBase   Permanent Memory Base address.
+  @param[in]  CopySize   The size of memory that needs to be migrated.
+
+  @retval   EFI_SUCCESS  Migration successful.
+**/
+EFI_STATUS
+EFIAPI
+TemporaryRamMigration (
+  IN CONST EFI_PEI_SERVICES   **PeiServices,
+  IN EFI_PHYSICAL_ADDRESS     TemporaryMemoryBase,
+  IN EFI_PHYSICAL_ADDRESS     PermanentMemoryBase,
+  IN UINTN                    CopySize
+  )
+{
+  VOID                             *OldHeap;
+  VOID                             *NewHeap;
+  VOID                             *OldStack;
+  VOID                             *NewStack;
+  BASE_LIBRARY_JUMP_BUFFER         JumpBuffer;
+
+  DEBUG ((DEBUG_INFO,
+    "TemporaryRamMigration (0x%Lx, 0x%Lx, 0x%Lx)\n",
+    TemporaryMemoryBase,
+    PermanentMemoryBase,
+    (UINT64)CopySize
+    ));
+
+  OldHeap = (VOID*) (UINTN)TemporaryMemoryBase;
+  NewHeap = (VOID*) ((UINTN)PermanentMemoryBase + (CopySize >> 1));
+
+  OldStack = (VOID*) ((UINTN)TemporaryMemoryBase + (CopySize >> 1));
+  NewStack = (VOID*) (UINTN)PermanentMemoryBase;
+
+#if 0
+  DebugAgentContext.HeapMigrateOffset = (UINTN)NewHeap - (UINTN)OldHeap;
+  DebugAgentContext.StackMigrateOffset = (UINTN)NewStack - (UINTN)OldStack;
+
+  OldStatus = SaveAndSetDebugTimerInterrupt (FALSE);
+  InitializeDebugAgent (DEBUG_AGENT_INIT_POSTMEM_SEC, (VOID *) &DebugAgentContext, NULL);
+#endif
+
+  //
+  // Migrate Heap
+  //
+  CopyMem (NewHeap, OldHeap, CopySize >> 1);
+
+  //
+  // Migrate Stack
+  //
+  CopyMem (NewStack, OldStack, CopySize >> 1);
+
+  // Use SetJump ()/LongJump () to switch to a new stack.
+  //
+  if (SetJump (&JumpBuffer) == 0) {
+    JumpBuffer.SP = JumpBuffer.SP - (UINTN)OldStack + (UINTN)NewStack ;
+    LongJump (&JumpBuffer, (UINTN)-1);
+  }
+
+  //SaveAndSetDebugTimerInterrupt (OldStatus);
+
+  return EFI_SUCCESS;
+}
diff --git a/Platform/Loongson/LoongArchQemuPkg/Sec/SecMain.inf b/Platform/Loongson/LoongArchQemuPkg/Sec/SecMain.inf
new file mode 100644
index 0000000000..eddd151a31
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Sec/SecMain.inf
@@ -0,0 +1,47 @@
+## @file
+#  SEC Driver
+#
+#  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = SecMain
+  FILE_GUID                      = 57d02d4f-5a5d-4bfa-b7d6-ba0a4d2c72ce
+  MODULE_TYPE                    = SEC
+  VERSION_STRING                 = 1.0
+
+[Sources.LOONGARCH64]
+  LoongArch64/Start.S
+  SecMain.c
+
+[Packages]
+  Platform/Loongson/LoongArchQemuPkg/Loongson.dec
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+  BaseLib
+  DebugLib
+  BaseMemoryLib
+  PcdLib
+  DebugAgentLib
+  IoLib
+  PeCoffLib
+  PeCoffGetEntryPointLib
+  PeCoffExtraActionLib
+
+[Ppis]
+  gEfiTemporaryRamSupportPpiGuid                # PPI ALWAYS_PRODUCED
+
+[FixedPcd]
+  gLoongArchQemuPkgTokenSpaceGuid.PcdSecPeiTempRamBase
+  gLoongArchQemuPkgTokenSpaceGuid.PcdSecPeiTempRamSize
+
+  gLoongArchQemuPkgTokenSpaceGuid.PcdFlashSecFvBase
+  gLoongArchQemuPkgTokenSpaceGuid.PcdFlashSecFvSize
+  gLoongArchQemuPkgTokenSpaceGuid.PcdFlashPeiFvBase
+  gLoongArchQemuPkgTokenSpaceGuid.PcdFlashPeiFvSize
diff --git a/Readme.md b/Readme.md
index 62876b4b7d..5ae58bca20 100644
--- a/Readme.md
+++ b/Readme.md
@@ -57,6 +57,7 @@ IA32                | i?86-linux-gnu-* _or_ x86_64-linux-gnu-
 IPF                 | ia64-linux-gnu
 X64                 | x86_64-linux-gnu-
 RISCV64             | riscv64-unknown-elf-
+LOONGARCH64         | loongarch64-unknown-linux-gnu-
 
 \* i386, i486, i586 or i686
 
@@ -71,6 +72,11 @@ RISC-V open source community provides GCC toolchains for
 [riscv64-unknown-elf](https://github.com/riscv/riscv-gnu-toolchain)
 compiled to run on x86 Linux.
 
+### GCC for LoongArch
+Loonson open source community provides GCC toolchains for
+[loongarch64-unknown-elf](https://github.com/loongson/build-tools)
+compiled to run on x86 Linux
+
 ### clang
 Clang does not require separate cross compilers, but it does need a
 target-specific binutils. These are included with any prepackaged GCC toolchain
@@ -257,6 +263,9 @@ For more information, see the
 ##### Minnowboard Max/Turbot based on Intel Valleyview2 SoC
 * [Minnowboard Max](Platform/Intel/Vlv2TbltDevicePkg)
 
+## Loongson
+* [LoongArchQemu](Platform/Loongson/LoongArchQemuPkg)
+
 ## Marvell
 * [Armada 70x0](Platform/Marvell/Armada70x0Db)
 * [Armada 80x0](Platform/Marvell/Armada80x0Db)
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [edk2-platforms][PATCH V3 03/16] Platform/Loongson: Add PeiServicesTablePointerLib.
  2022-09-29  7:07 [edk2-platforms][PATCH V3 00/16] Platform: Add Loongson support xianglai
  2022-09-29  7:07 ` [edk2-platforms][PATCH V3 01/16] Platform/Loongson: Add Serial Port library xianglai
  2022-09-29  7:07 ` [edk2-platforms][PATCH V3 02/16] Platform/Loongson: Support SEC And Add Readme.md xianglai
@ 2022-09-29  7:07 ` xianglai
  2022-09-29  7:07 ` [edk2-platforms][PATCH V3 04/16] Platform/Loongson: Add QemuFwCfgLib xianglai
                   ` (12 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: xianglai @ 2022-09-29  7:07 UTC (permalink / raw)
  To: devel; +Cc: quic_llindhol, michael.d.kinney, kraxel, ardb, maobibo

Use a register to save PeiServicesTable pointer,
This lib Provides PeiServicesTable pointer saving
and retrieval services.

REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4054

Signed-off-by: xianglai li <lixianglai@loongson.cn>
---
 .../PeiServicesTablePointer.c                 | 78 +++++++++++++++++++
 .../PeiServicesTablePointerLib.inf            | 32 ++++++++
 2 files changed, 110 insertions(+)
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/PeiServicesTablePointerLib/PeiServicesTablePointer.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/PeiServicesTablePointerLib/PeiServicesTablePointerLib.inf

diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/PeiServicesTablePointerLib/PeiServicesTablePointer.c b/Platform/Loongson/LoongArchQemuPkg/Library/PeiServicesTablePointerLib/PeiServicesTablePointer.c
new file mode 100644
index 0000000000..068960d4ce
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/PeiServicesTablePointerLib/PeiServicesTablePointer.c
@@ -0,0 +1,78 @@
+/** @file
+  PEI Services Table Pointer Library.
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiPei.h>
+#include <Library/PeiServicesTablePointerLib.h>
+#include <Library/DebugLib.h>
+#include "Library/Cpu.h"
+
+/**
+  Caches a pointer PEI Services Table.
+
+  Caches the pointer to the PEI Services Table specified by PeiServicesTablePointer
+  in a platform specific manner.
+
+  If PeiServicesTablePointer is NULL, then ASSERT ().
+
+  @param    PeiServicesTablePointer   The address of PeiServices pointer.
+**/
+VOID
+EFIAPI
+SetPeiServicesTablePointer (
+  IN CONST EFI_PEI_SERVICES ** PeiServicesTablePointer
+  )
+{
+  LOONGARCH_CSR_WRITEQ ((UINTN)PeiServicesTablePointer, LOONGARCH_CSR_KS0);
+}
+
+/**
+  Retrieves the cached value of the PEI Services Table pointer.
+
+  Returns the cached value of the PEI Services Table pointer in a CPU specific manner
+  as specified in the CPU binding section of the Platform Initialization Pre-EFI
+  Initialization Core Interface Specification.
+
+  If the cached PEI Services Table pointer is NULL, then ASSERT ().
+
+  @return  The pointer to PeiServices.
+**/
+CONST EFI_PEI_SERVICES **
+EFIAPI
+GetPeiServicesTablePointer (
+  VOID
+  )
+{
+  UINTN  val;
+
+  LOONGARCH_CSR_READQ (val, LOONGARCH_CSR_KS0);
+  return (CONST EFI_PEI_SERVICES **)val;
+}
+
+/**
+Perform CPU specific actions required to migrate the PEI Services Table
+pointer from temporary RAM to permanent RAM.
+
+For IA32 CPUs, the PEI Services Table pointer is stored in the 4 bytes
+immediately preceding the Interrupt Descriptor Table (IDT) in memory.
+For X64 CPUs, the PEI Services Table pointer is stored in the 8 bytes
+immediately preceding the Interrupt Descriptor Table (IDT) in memory.
+For Itanium and ARM CPUs, a the PEI Services Table Pointer is stored in
+a dedicated CPU register.  This means that there is no memory storage
+associated with storing the PEI Services Table pointer, so no additional
+migration actions are required for Itanium or ARM CPUs.
+
+**/
+VOID
+EFIAPI
+MigratePeiServicesTablePointer (
+VOID
+)
+{
+  return;
+}
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/PeiServicesTablePointerLib/PeiServicesTablePointerLib.inf b/Platform/Loongson/LoongArchQemuPkg/Library/PeiServicesTablePointerLib/PeiServicesTablePointerLib.inf
new file mode 100644
index 0000000000..6fe76d1351
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/PeiServicesTablePointerLib/PeiServicesTablePointerLib.inf
@@ -0,0 +1,32 @@
+## @file
+#  PEI Services Table Pointer Library.
+#
+#  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = PeiServicesTablePointerLib
+  FILE_GUID                      = C3C9C4ED-EB8A-4548-BE1B-ABB0B6F35B1E
+  MODULE_TYPE                    = PEIM
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = PeiServicesTablePointerLib|PEIM PEI_CORE SEC
+
+#
+#  VALID_ARCHITECTURES           = LOONGARCH64
+#
+
+[Sources]
+  PeiServicesTablePointer.c
+
+[Packages]
+  Platform/Loongson/LoongArchQemuPkg/Loongson.dec
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  DebugLib
+
+[Pcd]
+
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [edk2-platforms][PATCH V3 04/16] Platform/Loongson: Add QemuFwCfgLib.
  2022-09-29  7:07 [edk2-platforms][PATCH V3 00/16] Platform: Add Loongson support xianglai
                   ` (2 preceding siblings ...)
  2022-09-29  7:07 ` [edk2-platforms][PATCH V3 03/16] Platform/Loongson: Add PeiServicesTablePointerLib xianglai
@ 2022-09-29  7:07 ` xianglai
  2022-09-29  7:07 ` [edk2-platforms][PATCH V3 05/16] Platform/Loongson: Add MmuLib xianglai
                   ` (11 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: xianglai @ 2022-09-29  7:07 UTC (permalink / raw)
  To: devel; +Cc: quic_llindhol, michael.d.kinney, kraxel, ardb, maobibo

QemuFwCfgLib for PEI phase.
This library obtains the QemuFWCfg base address by
directly parsing the fdt, and reads and writes the data
in the QemuFWCfg by operating on the QemuFWCfg base address.

REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4054

Signed-off-by: xianglai li <lixianglai@loongson.cn>
---
 .../Include/IndustryStandard/QemuFwCfg.h      |  95 ++++
 .../Include/Library/QemuFwCfgLib.h            | 193 +++++++
 .../QemuFwCfgLib/QemuFwCfgLibInternal.h       |  64 +++
 .../Library/QemuFwCfgLib/QemuFwCfgPei.c       | 119 +++++
 .../Library/QemuFwCfgLib/QemuFwCfgPeiLib.c    | 477 ++++++++++++++++++
 .../Library/QemuFwCfgLib/QemuFwCfgPeiLib.inf  |  44 ++
 6 files changed, 992 insertions(+)
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Include/IndustryStandard/QemuFwCfg.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Include/Library/QemuFwCfgLib.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/QemuFwCfgLib/QemuFwCfgLibInternal.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/QemuFwCfgLib/QemuFwCfgPei.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/QemuFwCfgLib/QemuFwCfgPeiLib.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/QemuFwCfgLib/QemuFwCfgPeiLib.inf

diff --git a/Platform/Loongson/LoongArchQemuPkg/Include/IndustryStandard/QemuFwCfg.h b/Platform/Loongson/LoongArchQemuPkg/Include/IndustryStandard/QemuFwCfg.h
new file mode 100644
index 0000000000..a028ca1124
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Include/IndustryStandard/QemuFwCfg.h
@@ -0,0 +1,95 @@
+/** @file
+  Macro and type definitions corresponding to the QEMU fw_cfg interface.
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Glossary:
+    - FW     - FireWare
+    - CFG    - Configure
+    - FNAME    - File Name
+    - CTL    - Contorl
+**/
+
+#ifndef QEMU_FW_CFG_H_
+#define QEMU_FW_CFG_H_
+
+#include <Base.h>
+
+//
+// The size, in bytes, of names of firmware configuration files, including at
+// least one terminating NUL byte.
+//
+#define QEMU_FW_CFG_FNAME_SIZE      56
+
+//
+// If the following bit is set in the UINT32 fw_cfg revision / feature bitmap
+// -- read from key 0x0001 with the basic IO Port or MMIO method --, then the
+// DMA interface is available.
+//
+#define FW_CFG_F_DMA BIT1
+
+//
+// Macros for the FW_CFG_DMA_ACCESS.Control bitmap (in native encoding).
+//
+#define FW_CFG_DMA_CTL_ERROR         BIT0
+#define FW_CFG_DMA_CTL_READ          BIT1
+#define FW_CFG_DMA_CTL_SKIP          BIT2
+#define FW_CFG_DMA_CTL_SELECT        BIT3
+#define FW_CFG_DMA_CTL_WRITE         BIT4
+
+//
+// The fw_cfg registers can be found at these IO Ports, on the IO-mapped
+// platforms (Ia32 and X64).
+//
+#define FW_CFG_IO_SELECTOR          0x510
+#define FW_CFG_IO_DATA              0x511
+#define FW_CFG_IO_DMA_ADDRESS       0x514
+
+//
+// Numerically defined keys.
+//
+typedef enum {
+  QemuFwCfgItemSignature            = 0x0000,
+  QemuFwCfgItemInterfaceVersion     = 0x0001,
+  QemuFwCfgItemSystemUuid           = 0x0002,
+  QemuFwCfgItemRamSize              = 0x0003,
+  QemuFwCfgItemGraphicsEnabled      = 0x0004,
+  QemuFwCfgItemSmpCpuCount          = 0x0005,
+  QemuFwCfgItemMachineId            = 0x0006,
+  QemuFwCfgItemKernelAddress        = 0x0007,
+  QemuFwCfgItemKernelSize           = 0x0008,
+  QemuFwCfgItemKernelCommandLine    = 0x0009,
+  QemuFwCfgItemInitrdAddress        = 0x000a,
+  QemuFwCfgItemInitrdSize           = 0x000b,
+  QemuFwCfgItemBootDevice           = 0x000c,
+  QemuFwCfgItemNumaData             = 0x000d,
+  QemuFwCfgItemBootMenu             = 0x000e,
+  QemuFwCfgItemMaximumCpuCount      = 0x000f,
+  QemuFwCfgItemKernelEntry          = 0x0010,
+  QemuFwCfgItemKernelData           = 0x0011,
+  QemuFwCfgItemInitrdData           = 0x0012,
+  QemuFwCfgItemCommandLineAddress   = 0x0013,
+  QemuFwCfgItemCommandLineSize      = 0x0014,
+  QemuFwCfgItemCommandLineData      = 0x0015,
+  QemuFwCfgItemKernelSetupAddress   = 0x0016,
+  QemuFwCfgItemKernelSetupSize      = 0x0017,
+  QemuFwCfgItemKernelSetupData      = 0x0018,
+  QemuFwCfgItemFileDir              = 0x0019,
+
+} FIRMWARE_CONFIG_ITEM;
+
+//
+// Communication structure for the DMA access method. All fields are encoded in
+// big endian.
+//
+#pragma pack (1)
+typedef struct {
+  UINT32 Control;
+  UINT32 Length;
+  UINT64 Address;
+} FW_CFG_DMA_ACCESS;
+#pragma pack ()
+
+#endif
diff --git a/Platform/Loongson/LoongArchQemuPkg/Include/Library/QemuFwCfgLib.h b/Platform/Loongson/LoongArchQemuPkg/Include/Library/QemuFwCfgLib.h
new file mode 100644
index 0000000000..6b42cf3073
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Include/Library/QemuFwCfgLib.h
@@ -0,0 +1,193 @@
+/** @file
+  QEMU/KVM Firmware Configuration access
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Glossary:
+    - FW or Fw    - Firmware
+    - Cfg         - Configure
+**/
+
+#ifndef QEMU_FW_CFG_LIB_
+#define QEMU_FW_CFG_LIB_
+
+#include <IndustryStandard/QemuFwCfg.h>
+
+typedef enum {
+  EfiAcpiAddressRangeMemory   = 1,
+  EfiAcpiAddressRangeReserved = 2,
+  EfiAcpiAddressRangeACPI     = 3,
+  EfiAcpiAddressRangeNVS      = 4
+} EFI_ACPI_MEMORY_TYPE;
+
+typedef struct {
+  UINT64  BaseAddr;
+  UINT64  Length;
+  UINT32  Type;
+  UINT32  Reserved;
+} LOONGARCH_MEMMAP_ENTRY;
+
+/**
+  Returns a boolean indicating if the firmware configuration interface
+  is available or not.
+
+  This function may change fw_cfg state.
+
+  @retval    TRUE   The interface is available
+  @retval    FALSE  The interface is not available
+
+**/
+BOOLEAN
+EFIAPI
+QemuFwCfgIsAvailable (
+  VOID
+  );
+
+
+/**
+  Selects a firmware configuration item for reading.
+
+  Following this call, any data read from this item will start from
+  the beginning of the configuration item's data.
+
+  @param[in] QemuFwCfgItem - Firmware Configuration item to read
+
+**/
+VOID
+EFIAPI
+QemuFwCfgSelectItem (
+  IN FIRMWARE_CONFIG_ITEM   QemuFwCfgItem
+  );
+
+
+/**
+  Reads firmware configuration bytes into a buffer
+
+  If called multiple times, then the data read will
+  continue at the offset of the firmware configuration
+  item where the previous read ended.
+
+  @param[in] Size - Size in bytes to read
+  @param[in] Buffer - Buffer to store data into
+
+**/
+VOID
+EFIAPI
+QemuFwCfgReadBytes (
+  IN UINTN                  Size,
+  IN VOID                   *Buffer  OPTIONAL
+  );
+
+
+/**
+  Writes firmware configuration bytes from a buffer
+
+  If called multiple times, then the data written will
+  continue at the offset of the firmware configuration
+  item where the previous write ended.
+
+  @param[in] Size - Size in bytes to write
+  @param[in] Buffer - Buffer to read data from
+
+**/
+VOID
+EFIAPI
+QemuFwCfgWriteBytes (
+  IN UINTN                  Size,
+  IN VOID                   *Buffer
+  );
+
+
+/**
+  Skip bytes in the firmware configuration item.
+
+  Increase the offset of the firmware configuration item without transferring
+  bytes between the item and a caller-provided buffer. Subsequent read, write
+  or skip operations will commence at the increased offset.
+
+  @param[in] Size  Number of bytes to skip.
+**/
+VOID
+EFIAPI
+QemuFwCfgSkipBytes (
+  IN UINTN                  Size
+  );
+
+
+/**
+  Reads a UINT8 firmware configuration value
+
+  @retval    Value of Firmware Configuration item read
+
+**/
+UINT8
+EFIAPI
+QemuFwCfgRead8 (
+  VOID
+  );
+
+
+/**
+  Reads a UINT16 firmware configuration value
+
+  @retval    Value of Firmware Configuration item read
+
+**/
+UINT16
+EFIAPI
+QemuFwCfgRead16 (
+  VOID
+  );
+
+
+/**
+  Reads a UINT32 firmware configuration value
+
+  @retval    Value of Firmware Configuration item read
+
+**/
+UINT32
+EFIAPI
+QemuFwCfgRead32 (
+  VOID
+  );
+
+
+/**
+  Reads a UINT64 firmware configuration value
+
+  @retval    Value of Firmware Configuration item read
+
+**/
+UINT64
+EFIAPI
+QemuFwCfgRead64 (
+  VOID
+  );
+
+
+/**
+  Find the configuration item corresponding to the firmware configuration file.
+
+  @param[in]  Name - Name of file to look up.
+  @param[out] Item - Configuration item corresponding to the file, to be passed
+                     to QemuFwCfgSelectItem ().
+  @param[out] Size - Number of bytes in the file.
+
+  @retval    RETURN_SUCCESS       If file is found.
+             RETURN_NOT_FOUND     If file is not found.
+             RETURN_UNSUPPORTED   If firmware configuration is unavailable.
+
+**/
+RETURN_STATUS
+EFIAPI
+QemuFwCfgFindFile (
+  IN   CONST CHAR8           *Name,
+  OUT  FIRMWARE_CONFIG_ITEM  *Item,
+  OUT  UINTN                 *Size
+  );
+
+#endif
+
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/QemuFwCfgLib/QemuFwCfgLibInternal.h b/Platform/Loongson/LoongArchQemuPkg/Library/QemuFwCfgLib/QemuFwCfgLibInternal.h
new file mode 100644
index 0000000000..33eeb927dd
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/QemuFwCfgLib/QemuFwCfgLibInternal.h
@@ -0,0 +1,64 @@
+/** @file
+  fw_cfg library implementation.
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Glossary:
+    - FwCfg   - firmWare  Configure
+**/
+
+#ifndef QEMU_FW_CFG_LIB_INTERNAL_H_
+#define QEMU_FW_CFG_LIB_INTERNAL_H_
+
+/**
+  Returns a boolean indicating if the firmware configuration interface is
+  available for library-internal purposes.
+
+  This function never changes fw_cfg state.
+
+  @retval    TRUE   The interface is available internally.
+  @retval    FALSE  The interface is not available internally.
+**/
+BOOLEAN
+InternalQemuFwCfgIsAvailable (
+  VOID
+  );
+
+
+/**
+  Returns a boolean indicating whether QEMU provides the DMA-like access method
+  for fw_cfg.
+
+  @retval    TRUE   The DMA-like access method is available.
+  @retval    FALSE  The DMA-like access method is unavailable.
+**/
+BOOLEAN
+InternalQemuFwCfgDmaIsAvailable (
+  VOID
+  );
+
+/**
+  Transfer an array of bytes, or skip a number of bytes, using the DMA
+  interface.
+
+  @param[in]     Size     Size in bytes to transfer or skip.
+
+  @param[in,out] Buffer   Buffer to read data into or write data from. Ignored,
+                          and may be NULL, if Size is zero, or Control is
+                          FW_CFG_DMA_CTL_SKIP.
+
+  @param[in]     Control  One of the following:
+                          FW_CFG_DMA_CTL_WRITE - write to fw_cfg from Buffer.
+                          FW_CFG_DMA_CTL_READ  - read from fw_cfg into Buffer.
+                          FW_CFG_DMA_CTL_SKIP  - skip bytes in fw_cfg.
+**/
+VOID
+InternalQemuFwCfgDmaBytes (
+  IN     UINT32   Size,
+  IN OUT VOID     *Buffer OPTIONAL,
+  IN     UINT32   Control
+  );
+
+#endif
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/QemuFwCfgLib/QemuFwCfgPei.c b/Platform/Loongson/LoongArchQemuPkg/Library/QemuFwCfgLib/QemuFwCfgPei.c
new file mode 100644
index 0000000000..91995c90b5
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/QemuFwCfgLib/QemuFwCfgPei.c
@@ -0,0 +1,119 @@
+/** @file
+  fw_cfg library implementation.
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Glossary:
+    - FwCfg   - firmWare  Configure
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/QemuFwCfgLib.h>
+
+#include "QemuFwCfgLibInternal.h"
+
+/**
+  Returns a boolean indicating if the firmware configuration interface
+  is available or not.
+
+  This function may change fw_cfg state.
+
+  @retval    TRUE   The interface is available
+  @retval    FALSE  The interface is not available
+
+**/
+BOOLEAN
+EFIAPI
+QemuFwCfgIsAvailable (
+  VOID
+  )
+{
+  UINT32 Signature;
+  UINT32 Revision;
+
+  QemuFwCfgSelectItem (QemuFwCfgItemSignature);
+  Signature = QemuFwCfgRead32 ();
+  DEBUG ((DEBUG_INFO, "FW CFG Signature: 0x%x\n", Signature));
+  QemuFwCfgSelectItem (QemuFwCfgItemInterfaceVersion);
+  Revision = QemuFwCfgRead32 ();
+  DEBUG ((DEBUG_INFO, "FW CFG Revision: 0x%x\n", Revision));
+  if ((Signature != SIGNATURE_32 ('Q', 'E', 'M', 'U'))
+    || (Revision < 1))
+  {
+    DEBUG ((DEBUG_INFO, "QemuFwCfg interface not supported.\n"));
+    return FALSE;
+  }
+
+  DEBUG ((DEBUG_INFO, "QemuFwCfg interface is supported.\n"));
+  return TRUE;
+}
+
+
+/**
+  Returns a boolean indicating if the firmware configuration interface is
+  available for library-internal purposes.
+
+  This function never changes fw_cfg state.
+
+  @retval    TRUE   The interface is available internally.
+  @retval    FALSE  The interface is not available internally.
+**/
+BOOLEAN
+InternalQemuFwCfgIsAvailable (
+  VOID
+  )
+{
+  //
+  // We always return TRUE, because the consumer of this library ought to have
+  // called QemuFwCfgIsAvailable before making other calls which would hit this
+  // path.
+  //
+  return TRUE;
+}
+
+/**
+  Returns a boolean indicating whether QEMU provides the DMA-like access method
+  for fw_cfg.
+
+  @retval    TRUE   The DMA-like access method is available.
+  @retval    FALSE  The DMA-like access method is unavailable.
+**/
+BOOLEAN
+InternalQemuFwCfgDmaIsAvailable (
+  VOID
+  )
+{
+  return FALSE;
+}
+
+/**
+  Transfer an array of bytes, or skip a number of bytes, using the DMA
+  interface.
+
+  @param[in]     Size     Size in bytes to transfer or skip.
+
+  @param[in, out] Buffer   Buffer to read data into or write data from. Ignored,
+                          and may be NULL, if Size is zero, or Control is
+                          FW_CFG_DMA_CTL_SKIP.
+
+  @param[in]     Control  One of the following:
+                          FW_CFG_DMA_CTL_WRITE - write to fw_cfg from Buffer.
+                          FW_CFG_DMA_CTL_READ  - read from fw_cfg into Buffer.
+                          FW_CFG_DMA_CTL_SKIP  - skip bytes in fw_cfg.
+**/
+VOID
+InternalQemuFwCfgDmaBytes (
+  IN     UINT32   Size,
+  IN OUT VOID     *Buffer OPTIONAL,
+  IN     UINT32   Control
+  )
+{
+  //
+  // We should never reach here
+  //
+  ASSERT (FALSE);
+  CpuDeadLoop ();
+}
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/QemuFwCfgLib/QemuFwCfgPeiLib.c b/Platform/Loongson/LoongArchQemuPkg/Library/QemuFwCfgLib/QemuFwCfgPeiLib.c
new file mode 100644
index 0000000000..2bbaa9ed35
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/QemuFwCfgLib/QemuFwCfgPeiLib.c
@@ -0,0 +1,477 @@
+/** @file
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Glossary:
+    - FwCfg   - firmWare  Configure
+    - CTL   - Control
+**/
+
+#include "Uefi.h"
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/IoLib.h>
+#include <Library/QemuFwCfgLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/PcdLib.h>
+#include <libfdt.h>
+#include "QemuFwCfgLibInternal.h"
+
+STATIC UINTN mFwCfgSelectorAddress;
+STATIC UINTN mFwCfgDataAddress;
+/**
+  To get firmware configure selector address.
+
+  @param VOID
+
+  @retval  firmware configure selector address
+**/
+UINTN
+EFIAPI
+QemuGetFwCfgSelectorAddress (
+  VOID
+  )
+{
+  UINTN FwCfgSelectorAddress = mFwCfgSelectorAddress;
+  if (FwCfgSelectorAddress == 0) {
+    FwCfgSelectorAddress = (UINTN)PcdGet64 (PcdFwCfgSelectorAddress);
+  }
+  return FwCfgSelectorAddress;
+}
+/**
+  To get firmware configure Data address.
+
+  @param VOID
+
+  @retval  firmware configure data address
+**/
+UINTN
+EFIAPI
+QemuGetFwCfgDataAddress (
+  VOID
+  )
+{
+  UINTN FwCfgDataAddress = mFwCfgDataAddress;
+  if (FwCfgDataAddress == 0) {
+    FwCfgDataAddress = (UINTN)PcdGet64 (PcdFwCfgDataAddress);
+  }
+  return FwCfgDataAddress;
+}
+/**
+  Selects a firmware configuration item for reading.
+
+  Following this call, any data read from this item will start from
+  the beginning of the configuration item's data.
+
+  @param[in] QemuFwCfgItem - Firmware Configuration item to read
+
+**/
+VOID
+EFIAPI
+QemuFwCfgSelectItem (
+  IN FIRMWARE_CONFIG_ITEM   QemuFwCfgItem
+  )
+{
+  UINTN FwCfgSelectorAddress;
+  FwCfgSelectorAddress = QemuGetFwCfgSelectorAddress ();
+  MmioWrite16 (FwCfgSelectorAddress, SwapBytes16((UINT16) (UINTN)QemuFwCfgItem));
+}
+
+/**
+  Slow READ_BYTES_FUNCTION.
+
+  @param[in]  The size of the data to be read.
+  @param[in]  Buffer    The buffer that stores the readout data.
+**/
+VOID
+EFIAPI
+MmioReadBytes (
+  IN UINTN Size,
+  IN VOID  *Buffer OPTIONAL
+  )
+{
+  UINTN Left;
+  UINT8 *Ptr;
+  UINT8 *End;
+  UINTN FwCfgDataAddress;
+  Left = Size & 7;
+
+  Size -= Left;
+  Ptr = Buffer;
+  End = Ptr + Size;
+  FwCfgDataAddress = QemuGetFwCfgDataAddress ();
+  while (Ptr < End) {
+    *(UINT64 *)Ptr = MmioRead64 (FwCfgDataAddress);
+    Ptr += 8;
+  }
+  if (Left & 4) {
+    *(UINT32 *)Ptr = MmioRead32 (FwCfgDataAddress);
+    Ptr += 4;
+  }
+  if (Left & 2) {
+    *(UINT16 *)Ptr = MmioRead16 (FwCfgDataAddress);
+    Ptr += 2;
+  }
+  if (Left & 1) {
+    *Ptr = MmioRead8 (FwCfgDataAddress);
+  }
+}
+
+/**
+  Slow WRITE_BYTES_FUNCTION.
+
+  @param[in]  The size of the data to be write.
+  @param[in]  Buffer    The buffer that stores the writein data.
+**/
+VOID
+EFIAPI
+MmioWriteBytes (
+  IN UINTN Size,
+  IN VOID  *Buffer OPTIONAL
+  )
+{
+  UINTN Idx;
+  UINTN FwCfgDataAddress;
+  FwCfgDataAddress = QemuGetFwCfgDataAddress ();
+  for (Idx = 0; Idx < Size; ++Idx) {
+    MmioWrite8 (FwCfgDataAddress, ((UINT8 *)Buffer)[Idx]);
+  }
+}
+
+/**
+  Reads firmware configuration bytes into a buffer
+
+  @param[in] Size - Size in bytes to read
+  @param[in] Buffer - Buffer to store data into  (OPTIONAL if Size is 0)
+
+**/
+VOID
+EFIAPI
+InternalQemuFwCfgReadBytes (
+  IN UINTN                  Size,
+  IN VOID                   *Buffer  OPTIONAL
+  )
+{
+  if ((InternalQemuFwCfgDmaIsAvailable ())
+    && (Size <= MAX_UINT32))
+  {
+    InternalQemuFwCfgDmaBytes ((UINT32)Size, Buffer, FW_CFG_DMA_CTL_READ);
+    return;
+  }
+  MmioReadBytes (Size, Buffer);
+}
+
+
+/**
+  Reads firmware configuration bytes into a buffer
+
+  If called multiple times, then the data read will
+  continue at the offset of the firmware configuration
+  item where the previous read ended.
+
+  @param[in] Size - Size in bytes to read
+  @param[in] Buffer - Buffer to store data into
+**/
+VOID
+EFIAPI
+QemuFwCfgReadBytes (
+  IN UINTN                  Size,
+  IN VOID                   *Buffer
+  )
+{
+  if (InternalQemuFwCfgIsAvailable ()) {
+    InternalQemuFwCfgReadBytes (Size, Buffer);
+  } else {
+    ZeroMem (Buffer, Size);
+  }
+}
+
+/**
+  Write firmware configuration bytes from a buffer
+
+  If called multiple times, then the data written will
+  continue at the offset of the firmware configuration
+  item where the previous write ended.
+
+  @param[in] Size - Size in bytes to write
+  @param[in] Buffer - Buffer to read data from
+**/
+VOID
+EFIAPI
+QemuFwCfgWriteBytes (
+  IN UINTN                  Size,
+  IN VOID                   *Buffer
+  )
+{
+  if (InternalQemuFwCfgIsAvailable ()) {
+    if ((InternalQemuFwCfgDmaIsAvailable ())
+      && (Size <= MAX_UINT32))
+    {
+      InternalQemuFwCfgDmaBytes ((UINT32)Size, Buffer, FW_CFG_DMA_CTL_WRITE);
+      return;
+    }
+    MmioWriteBytes (Size, Buffer);
+  }
+}
+
+
+/**
+  Skip bytes in the firmware configuration item.
+
+  Increase the offset of the firmware configuration item without transferring
+  bytes between the item and a caller-provided buffer. Subsequent read, write
+  or skip operations will commence at the increased offset.
+
+  @param[in] Size  Number of bytes to skip.
+**/
+VOID
+EFIAPI
+QemuFwCfgSkipBytes (
+  IN UINTN                  Size
+  )
+{
+  UINTN ChunkSize;
+  UINT8 SkipBuffer[256];
+
+  if (!InternalQemuFwCfgIsAvailable ()) {
+    return;
+  }
+
+  if ((InternalQemuFwCfgDmaIsAvailable ())
+    && (Size <= MAX_UINT32))
+  {
+    InternalQemuFwCfgDmaBytes ((UINT32)Size, NULL, FW_CFG_DMA_CTL_SKIP);
+    return;
+  }
+
+  //
+  // Emulate the skip by reading data in chunks, and throwing it away. The
+  // implementation below is suitable even for phases where RAM or dynamic
+  // allocation is not available or appropriate. It also doesn't affect the
+  // static data footprint for client modules. Large skips are not expected,
+  // therefore this fallback is not performance critical. The size of
+  // SkipBuffer is thought not to exert a large pressure on the stack in any
+  // phase.
+  //
+  while (Size > 0) {
+    ChunkSize = MIN (Size, sizeof SkipBuffer);
+    MmioReadBytes (ChunkSize, SkipBuffer);
+    Size -= ChunkSize;
+  }
+}
+
+
+/**
+  Reads a UINT8 firmware configuration value
+
+  @return    Value of Firmware Configuration item read
+
+**/
+UINT8
+EFIAPI
+QemuFwCfgRead8 (
+  VOID
+  )
+{
+  UINT8 Result;
+
+  QemuFwCfgReadBytes (sizeof (Result), &Result);
+
+  return Result;
+}
+
+
+/**
+  Reads a UINT16 firmware configuration value
+
+  @return    Value of Firmware Configuration item read
+
+**/
+UINT16
+EFIAPI
+QemuFwCfgRead16 (
+  VOID
+  )
+{
+  UINT16 Result;
+
+  QemuFwCfgReadBytes (sizeof (Result), &Result);
+
+  return Result;
+}
+
+
+/**
+  Reads a UINT32 firmware configuration value
+
+  @return    Value of Firmware Configuration item read
+
+**/
+UINT32
+EFIAPI
+QemuFwCfgRead32 (
+  VOID
+  )
+{
+  UINT32 Result;
+
+  QemuFwCfgReadBytes (sizeof (Result), &Result);
+
+  return Result;
+}
+
+
+/**
+  Reads a UINT64 firmware configuration value
+
+  @return    Value of Firmware Configuration item read
+
+**/
+UINT64
+EFIAPI
+QemuFwCfgRead64 (
+  VOID
+  )
+{
+  UINT64 Result;
+
+  QemuFwCfgReadBytes (sizeof (Result), &Result);
+
+  return Result;
+}
+
+
+/**
+  Find the configuration item corresponding to the firmware configuration file.
+
+  @param[in]  Name - Name of file to look up.
+  @param[out] Item - Configuration item corresponding to the file, to be passed
+                     to QemuFwCfgSelectItem ().
+  @param[out] Size - Number of bytes in the file.
+
+  @return    RETURN_SUCCESS       If file is found.
+             RETURN_NOT_FOUND     If file is not found.
+             RETURN_UNSUPPORTED   If firmware configuration is unavailable.
+
+**/
+RETURN_STATUS
+EFIAPI
+QemuFwCfgFindFile (
+  IN   CONST CHAR8           *Name,
+  OUT  FIRMWARE_CONFIG_ITEM  *Item,
+  OUT  UINTN                 *Size
+  )
+{
+  UINT32 Count;
+  UINT32 Idx;
+
+  if (!InternalQemuFwCfgIsAvailable ()) {
+    return RETURN_UNSUPPORTED;
+  }
+
+  QemuFwCfgSelectItem (QemuFwCfgItemFileDir);
+  Count = SwapBytes32 (QemuFwCfgRead32 ());
+
+  for (Idx = 0; Idx < Count; ++Idx) {
+    UINT32 FileSize;
+    UINT16 FileSelect;
+    CHAR8  FileName[QEMU_FW_CFG_FNAME_SIZE];
+
+    FileSize     = QemuFwCfgRead32 ();
+    FileSelect   = QemuFwCfgRead16 ();
+    QemuFwCfgRead16 (); // skip the field called "reserved"
+    InternalQemuFwCfgReadBytes (sizeof (FileName), FileName);
+
+    if (AsciiStrCmp (Name, FileName) == 0) {
+      *Item = SwapBytes16 (FileSelect);
+      *Size = SwapBytes32 (FileSize);
+      return RETURN_SUCCESS;
+    }
+  }
+
+  return RETURN_NOT_FOUND;
+}
+
+/**
+  firmware config initialize.
+
+  @param  VOID
+
+  @return    RETURN_SUCCESS  Initialization succeeded.
+**/
+RETURN_STATUS
+EFIAPI
+QemuFwCfgInitialize (
+  VOID
+  )
+{
+  VOID              *DeviceTreeBase;
+  INT32             Node;
+  INT32             Prev;
+  CONST CHAR8       *Type;
+  INT32             Len;
+  CONST UINT64      *RegProp;
+  UINT64            FwCfgSelectorAddress;
+  UINT64            FwCfgDataAddress;
+  UINT64            FwCfgDataSize;
+  RETURN_STATUS     PcdStatus;
+
+  DeviceTreeBase = (VOID *) (UINTN)PcdGet64 (PcdDeviceTreeBase);
+  ASSERT (DeviceTreeBase != NULL);
+  //
+  // Make sure we have a valid device tree blob
+  //
+  ASSERT (fdt_check_header (DeviceTreeBase) == 0);
+
+  for (Prev = 0;; Prev = Node) {
+    Node = fdt_next_node (DeviceTreeBase, Prev, NULL);
+    if (Node < 0) {
+      break;
+    }
+
+    //
+    // Check for memory node
+    //
+    Type = fdt_getprop (DeviceTreeBase, Node, "compatible", &Len);
+    if ((Type)
+      && (AsciiStrnCmp (Type, "qemu,fw-cfg-mmio", Len) == 0))
+    {
+      //
+      // Get the 'reg' property of this node. For now, we will assume
+      // two 8 byte quantities for base and size, respectively.
+      //
+      RegProp = fdt_getprop (DeviceTreeBase, Node, "reg", &Len);
+      if ((RegProp != 0)
+        && (Len == (2 * sizeof (UINT64))))
+      {
+        FwCfgDataAddress      = SwapBytes64 (RegProp[0]);
+        FwCfgDataSize         = 8;
+        FwCfgSelectorAddress  = FwCfgDataAddress + FwCfgDataSize;
+
+        mFwCfgSelectorAddress = FwCfgSelectorAddress;
+        mFwCfgDataAddress = FwCfgDataAddress;
+
+        PcdStatus = PcdSet64S (
+          PcdFwCfgSelectorAddress,
+          FwCfgSelectorAddress
+          );
+        ASSERT_RETURN_ERROR (PcdStatus);
+        PcdStatus = PcdSet64S (
+          PcdFwCfgDataAddress,
+          FwCfgDataAddress
+          );
+        ASSERT_RETURN_ERROR (PcdStatus);
+        break;
+      } else {
+        DEBUG ((DEBUG_ERROR, "%a: Failed to parse FDT QemuCfg node\n",
+          __FUNCTION__));
+        break;
+      }
+    }
+  }
+  return RETURN_SUCCESS;
+}
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/QemuFwCfgLib/QemuFwCfgPeiLib.inf b/Platform/Loongson/LoongArchQemuPkg/Library/QemuFwCfgLib/QemuFwCfgPeiLib.inf
new file mode 100644
index 0000000000..e6df7d9120
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/QemuFwCfgLib/QemuFwCfgPeiLib.inf
@@ -0,0 +1,44 @@
+## @file
+#  initialized fw_cfg library.
+#
+#  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = QemuFwCfgSecLib
+  FILE_GUID                      = cdf9a9d5-7422-4dcb-b41d-607151ad320b
+  MODULE_TYPE                    = BASE
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = QemuFwCfgLib|PEIM
+
+  CONSTRUCTOR                    = QemuFwCfgInitialize
+
+
+[Sources]
+  QemuFwCfgLibInternal.h
+  QemuFwCfgPeiLib.c
+  QemuFwCfgPei.c
+
+[Packages]
+  EmbeddedPkg/EmbeddedPkg.dec
+  MdePkg/MdePkg.dec
+  OvmfPkg/OvmfPkg.dec
+  Platform/Loongson/LoongArchQemuPkg/Loongson.dec
+
+[LibraryClasses]
+  BaseLib
+  BaseMemoryLib
+  DebugLib
+  IoLib
+  MemoryAllocationLib
+  FdtLib
+  PcdLib
+
+[Pcd]
+  gLoongArchQemuPkgTokenSpaceGuid.PcdDeviceTreeBase
+  gLoongArchQemuPkgTokenSpaceGuid.PcdFwCfgSelectorAddress
+  gLoongArchQemuPkgTokenSpaceGuid.PcdFwCfgDataAddress
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [edk2-platforms][PATCH V3 05/16] Platform/Loongson: Add MmuLib.
  2022-09-29  7:07 [edk2-platforms][PATCH V3 00/16] Platform: Add Loongson support xianglai
                   ` (3 preceding siblings ...)
  2022-09-29  7:07 ` [edk2-platforms][PATCH V3 04/16] Platform/Loongson: Add QemuFwCfgLib xianglai
@ 2022-09-29  7:07 ` xianglai
  2022-09-29  7:07 ` [edk2-platforms][PATCH V3 06/16] Platform/Loongson: Add StableTimerLib xianglai
                   ` (10 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: xianglai @ 2022-09-29  7:07 UTC (permalink / raw)
  To: devel; +Cc: quic_llindhol, michael.d.kinney, kraxel, ardb, maobibo

Read the memory map information through the QemuFwCfg interface,
then build the page table through the memory map information,
and finally enable Mmu.

REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4054

Signed-off-by: xianglai li <lixianglai@loongson.cn>
---
 .../LoongArchQemuPkg/Include/Library/MmuLib.h |  85 ++
 .../LoongArchQemuPkg/Library/MmuLib/Mmu.S     |  35 +
 .../Library/MmuLib/MmuBaseLib.inf             |  35 +
 .../Library/MmuLib/MmuBaseLibPei.inf          |  42 +
 .../Library/MmuLib/MmuLibCore.c               | 908 ++++++++++++++++++
 .../Library/MmuLib/MmuLibCore.h               |  39 +
 .../Library/MmuLib/MmuLibCorePei.c            | 236 +++++
 .../LoongArchQemuPkg/Library/MmuLib/mmu.h     | 104 ++
 .../LoongArchQemuPkg/Library/MmuLib/page.h    | 267 +++++
 .../LoongArchQemuPkg/Library/MmuLib/pte.h     |  57 ++
 10 files changed, 1808 insertions(+)
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Include/Library/MmuLib.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/Mmu.S
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLib.inf
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLibPei.inf
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCorePei.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/mmu.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/page.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/pte.h

diff --git a/Platform/Loongson/LoongArchQemuPkg/Include/Library/MmuLib.h b/Platform/Loongson/LoongArchQemuPkg/Include/Library/MmuLib.h
new file mode 100644
index 0000000000..6c501eca07
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Include/Library/MmuLib.h
@@ -0,0 +1,85 @@
+/** @file
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Glossary:
+    - EXC     - execute
+**/
+#ifndef MMU_LIB_H_
+#define MMU_LIB_H_
+/**
+  write operation is performed Count times from the first element of Buffer.
+Convert EFI Attributes to Loongarch Attributes.
+  @param[in]  EfiAttributes     Efi Attributes.
+
+  @retval  LoongArch Attributes.
+**/
+UINTN
+EfiAttributeToLoongArchAttribute (
+  IN UINTN  EfiAttributes
+  );
+
+/**
+  Finds the length and memory properties of the memory region corresponding to the specified base address.
+
+  @param[in]  BaseAddress    To find the base address of the memory region.
+  @param[in]  EndAddress     To find the end address of the memory region.
+  @param[out]  RegionLength    The length of the memory region found.
+  @param[out]  RegionAttributes    Properties of the memory region found.
+
+  @retval  EFI_SUCCESS    The corresponding memory area was successfully found
+           EFI_NOT_FOUND    No memory area found
+**/
+EFI_STATUS
+GetLoongArchMemoryRegion (
+  IN     UINTN  BaseAddress,
+  IN     UINTN  EndAddress,
+  OUT    UINTN  *RegionLength,
+  OUT    UINTN  *RegionAttributes
+  );
+
+/**
+  Sets the Attributes  of the specified memory region
+
+  @param[in]  BaseAddress  The base address of the memory region to set the Attributes.
+  @param[in]  Length       The length of the memory region to set the Attributes.
+  @param[in]  Attributes   The Attributes to be set.
+
+  @retval  EFI_SUCCESS    The Attributes was set successfully
+
+**/
+EFI_STATUS
+LoongArchSetMemoryAttributes (
+  IN EFI_PHYSICAL_ADDRESS  BaseAddress,
+  IN UINTN                 Length,
+  IN UINTN                 Attributes
+  );
+
+/**
+  Sets the non-executable Attributes for the specified memory region
+
+  @param[in]  BaseAddress  The base address of the memory region to set the Attributes.
+  @param[in]  Length       The length of the memory region to set the Attributes.
+
+  @retval  EFI_SUCCESS    The Attributes was set successfully
+**/
+EFI_STATUS
+LoongArchSetMemoryRegionNoExec (
+  IN  EFI_PHYSICAL_ADDRESS  BaseAddress,
+  IN  UINTN                Length
+  );
+/**
+  Create a page table and initialize the MMU.
+
+  @param[] VOID
+
+  @retval  VOID
+**/
+VOID
+EFIAPI
+ConfigureMmu (
+  VOID
+  );
+#endif
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/Mmu.S b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/Mmu.S
new file mode 100644
index 0000000000..a697b54e65
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/Mmu.S
@@ -0,0 +1,35 @@
+#------------------------------------------------------------------------------
+#
+# LoongArch for LoongArch
+#
+# Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#-----------------------------------------------------------------------------
+
+#ifndef _KERNEL
+#define _KERNEL
+#endif
+
+#include "Library/Cpu.h"
+#include "LoongArchAsmMacro.h"
+
+#  Query the page table.
+#
+#  @param  VOID
+#
+#  @retval  VOID
+ASM_FUNC(HandleTlbRefill)
+  csrwr T0, LOONGARCH_CSR_TLBRSAVE
+  csrrd T0, LOONGARCH_CSR_PGD
+  lddir T0, T0, 3   #Put pud BaseAddress into T0
+  lddir T0, T0, 2   #Put pmd BaseAddress into T0
+  lddir T0, T0, 1   #Put pte BaseAddress into T0
+  ldpte T0, 0
+  ldpte T0, 1
+  tlbfill
+  csrrd T0, LOONGARCH_CSR_TLBRSAVE
+  ertn
+.globl  HandleTlbRefillEnd
+HandleTlbRefillEnd:
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLib.inf b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLib.inf
new file mode 100644
index 0000000000..d8cfe6776e
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLib.inf
@@ -0,0 +1,35 @@
+## @file
+#
+#  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = MmuBaseLib
+  FILE_GUID                      = da8f0232-fb14-42f0-922c-63104d2c70be
+  MODULE_TYPE                    = BASE
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = MmuLib
+
+  CONSTRUCTOR                    = MmuInitialize
+[Sources.common]
+  MmuLibCore.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  Platform/Loongson/LoongArchQemuPkg/Loongson.dec
+
+[PCD]
+  gLoongArchQemuPkgTokenSpaceGuid.PcdSwapPageDir
+  gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPgd
+  gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPud
+  gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPmd
+  gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPte
+
+[LibraryClasses]
+  MemoryAllocationLib
+  PcdLib
+  DebugLib
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLibPei.inf b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLibPei.inf
new file mode 100644
index 0000000000..f6a3cab45f
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLibPei.inf
@@ -0,0 +1,42 @@
+## @file
+#
+#  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = MmuPeiLib
+  FILE_GUID                      = da8f0232-fb14-42f0-922c-63104d2c70bd
+  MODULE_TYPE                    = BASE
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = MmuLib | SEC PEIM
+
+[Sources.common]
+  MmuLibCorePei.c
+  Mmu.S
+  MmuLibCore.h
+  MmuLibCore.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  Platform/Loongson/LoongArchQemuPkg/Loongson.dec
+  OvmfPkg/OvmfPkg.dec
+
+[PCD]
+  gLoongArchQemuPkgTokenSpaceGuid.PcdSwapPageDir
+  gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPgd
+  gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPud
+  gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPmd
+  gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPte
+  gLoongArchQemuPkgTokenSpaceGuid.PcdFlashSecFvSize
+  gLoongArchQemuPkgTokenSpaceGuid.PcdFlashSecFvBase
+  gLoongArchQemuPkgTokenSpaceGuid.PcdRamSize
+
+[LibraryClasses]
+  MemoryAllocationLib
+  PcdLib
+  DebugLib
+  QemuFwCfgLib
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.c b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.c
new file mode 100644
index 0000000000..d737759ad2
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.c
@@ -0,0 +1,908 @@
+/** @file
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Glossary:
+    - Pgd or Pgd or PGD    - Page Global Directory
+    - Pud or Pud or PUD    - Page Upper Directory
+    - Pmd or Pmd or PMD    - Page Middle Directory
+    - Pte or pte or PTE    - Page Table Entry
+    - Val or VAL or val    - Value
+    - Dir    - Directory
+**/
+#include <Uefi.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include "Library/Cpu.h"
+#include "pte.h"
+#include "page.h"
+#include "mmu.h"
+
+BOOLEAN  mMmuInited = FALSE;
+/**
+  Check to see if mmu successfully initializes.
+
+  @param  VOID.
+
+  @retval  TRUE  Initialization has been completed.
+           FALSE Initialization did not complete.
+**/
+BOOLEAN
+MmuIsInit (VOID) {
+  if ((mMmuInited == TRUE) ||
+      (PcdGet64 (PcdSwapPageDir) != 0)) {
+    return TRUE;
+  }
+  return FALSE;
+}
+
+/**
+  Iterates through the page directory to initialize it.
+
+  @param  Dst  A pointer to the directory of the page to initialize.
+  @param  Num  The number of page directories to initialize.
+  @param  Src  A pointer to the data used to initialize the page directory.
+
+  @retval VOID.
+**/
+VOID
+PageDirInit (
+  IN VOID *Dst,
+  IN UINTN Num,
+  IN VOID *Src
+  )
+{
+  UINTN *Ptr;
+  UINTN *End;
+  UINTN Entry;
+
+  Entry = (UINTN)Src;
+  Ptr = (UINTN *)Dst;
+  End = Ptr + Num;
+
+  for ( ;Ptr < End; Ptr++) {
+    *Ptr = Entry;
+  }
+
+  return ;
+}
+/**
+  Gets the virtual address corresponding to the page global directory table entry.
+
+  @param  Address  the virtual address for the table entry.
+
+  @retval PGD A pointer to get the table item.
+**/
+PGD *
+PgdOffset (
+  IN UINTN Address
+  )
+{
+  return ((PGD *)PcdGet64 (PcdSwapPageDir)) + PGD_INDEX (Address);
+}
+/**
+  Gets the virtual address corresponding to the page upper directory table entry.
+
+  @param  Pgd  A pointer to a page global directory table entry.
+  @param  Address  the virtual address for the table entry.
+
+  @retval PUD A pointer to get the table item.
+**/
+PUD *
+PudOffset (
+  IN PGD *Pgd,
+  IN UINTN Address
+  )
+{
+  UINTN PgdVal = (UINTN)PGD_VAL (*Pgd);
+  return (PUD *)PgdVal + PUD_INDEX (Address);
+}
+/**
+  Gets the virtual address corresponding to the page middle directory table entry.
+
+  @param  Pud  A pointer to a page upper directory table entry.
+  @param  Address  the virtual address for the table entry.
+
+  @retval PMD A pointer to get the table item.
+**/
+PMD *
+PmdOffset (
+  IN PUD *Pud,
+  IN UINTN Address
+  )
+{
+  UINTN PudVal = PUD_VAL (*Pud);
+  return (PMD *)PudVal + PMD_INDEX (Address);
+}
+/**
+  Gets the virtual address corresponding to the page table entry.
+
+  @param  Pmd  A pointer to a page middle directory table entry.
+  @param  Address  the virtual address for the table entry.
+
+  @retval PTE A pointer to get the table item.
+**/
+PTE *
+PteOffset (
+  IN PMD *Pmd,
+  IN UINTN Address
+  )
+{
+  UINTN PmdVal = (UINTN)PMD_VAL (*Pmd);
+  return (PTE *)PmdVal + PTE_INDEX (Address);
+}
+
+/**
+  Sets the value of the page table entry.
+
+  @param  Pte  A pointer to a page table entry.
+  @param  PteVal  The value of the page table entry to set.
+
+  @retval VOID
+**/
+VOID
+SetPte (
+  IN PTE *Pte,
+  IN PTE PteVal
+  )
+{
+  *Pte = PteVal;
+}
+/**
+  Sets the value of the page global directory.
+
+  @param  Pgd  A pointer to a page global directory.
+  @param  Pud  The value of the page global directory to set.
+
+  @retval VOID
+**/
+VOID
+SetPgd (
+  IN PGD *Pgd,
+  IN PUD *Pud
+  )
+{
+  *Pgd = (PGD) {((UINTN)Pud)};
+}
+
+/**
+  Sets the value of the page upper directory.
+
+  @param  Pud  A pointer to a page upper directory.
+  @param  Pmd  The value of the page upper directory to set.
+
+  @retval VOID
+**/
+VOID
+SetPud (
+  IN PUD *Pud,
+  IN PMD *Pmd
+  )
+{
+  *Pud = (PUD) {((UINTN)Pmd)};
+}
+
+/**
+  Sets the value of the page middle directory.
+
+  @param  Pmd  A pointer to a page middle directory.
+  @param  Pte  The value of the page middle directory to set.
+
+  @retval VOID
+**/
+VOID
+SetPmd (
+  IN PMD *Pmd,
+  IN PTE *Pte
+  )
+{
+  *Pmd = (PMD) {((UINTN)Pte)};
+}
+/**
+  Free up memory space occupied by page tables.
+
+  @param  Pte  A pointer to the page table.
+
+  @retval VOID
+**/
+VOID
+PteFree (
+  IN PTE *Pte
+  )
+{
+  FreePages ((VOID *)Pte, 1);
+}
+/**
+  Free up memory space occupied by page middle directory.
+
+  @param  Pmd  A pointer to the page middle directory.
+
+  @retval VOID
+**/
+VOID
+PmdFree (
+  IN PMD *Pmd
+  )
+{
+  FreePages ((VOID *)Pmd, 1);
+}
+/**
+  Free up memory space occupied by page upper directory.
+
+  @param  Pud  A pointer to the page upper directory.
+
+  @retval VOID
+**/
+VOID
+PudFree (
+  IN PUD *Pud
+  )
+{
+  FreePages ((VOID *)Pud, 1);
+}
+/**
+  Requests the memory space required for the page upper directory,
+  initializes it, and places it in the specified page global directory
+
+  @param  Pgd  A pointer to the page global directory.
+
+  @retval  EFI_SUCCESS  Memory request successful.
+  @retval  EFI_OUT_OF_RESOURCES  Resource exhaustion cannot be requested to memory.
+**/
+INTN
+PudAlloc (
+  IN PGD *Pgd
+  )
+{
+  PUD *Pud = (PUD *) AllocatePages (1);
+  if (!Pud) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  PageDirInit ((VOID *)Pud, ENTRYS_PER_PUD, (VOID *)PcdGet64 (PcdInvalidPmd));
+
+  if (pgd_none (*Pgd)) {
+    SetPgd (Pgd, Pud);
+  } else { /* Another has populated it */
+    PudFree (Pud);
+  }
+
+  return EFI_SUCCESS;
+}
+/**
+  Requests the memory space required for the page middle directory,
+  initializes it, and places it in the specified page upper directory
+
+  @param  Pud  A pointer to the page upper directory.
+
+  @retval  EFI_SUCCESS  Memory request successful.
+  @retval  EFI_OUT_OF_RESOURCES  Resource exhaustion cannot be requested to memory.
+**/
+EFI_STATUS
+PmdAlloc (
+  IN PUD *Pud
+  )
+{
+  PMD *Pmd;
+
+  Pmd = (PMD *) AllocatePages (1);
+  if (!Pmd) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  PageDirInit ((VOID *)Pmd, ENTRYS_PER_PMD, (VOID *)PcdGet64 (PcdInvalidPte));
+
+  if (pud_none (*Pud)) {
+    SetPud (Pud, Pmd);
+  } else {/* Another has populated it */
+    PmdFree (Pmd);
+  }
+
+  return EFI_SUCCESS;
+}
+/**
+  Requests the memory space required for the page table,
+  initializes it, and places it in the specified page middle directory
+
+  @param  Pmd  A pointer to the page middle directory.
+
+  @retval  EFI_SUCCESS  Memory request successful.
+  @retval  EFI_OUT_OF_RESOURCES  Resource exhaustion cannot be requested to memory.
+**/
+INTN
+PteAlloc (
+  IN PMD *Pmd
+  )
+{
+  PTE *Pte;
+
+  Pte = (PTE *) AllocatePages (1);
+  if (!Pte) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  Pte = ZeroMem (Pte, EFI_PAGE_SIZE);
+
+  if (pmd_none (*Pmd)) {
+    SetPmd (Pmd, Pte);
+  } else { /* Another has populated it */
+    PteFree (Pte);
+  }
+
+  return EFI_SUCCESS;
+}
+/**
+  Requests the memory space required for the page upper directory,
+  initializes it, and places it in the specified page global directory,
+  and get the page upper directory entry corresponding to the virtual address
+
+  @param  Pgd  A pointer to the page global directory.
+
+  @retval   Gets the page upper directory entry
+**/
+PUD *
+PudAllocGet (
+  IN PGD *Pgd,
+  IN UINTN Address
+  )
+{
+  return ((pgd_none (*(Pgd)) && PudAlloc (Pgd)) ?
+           NULL : PudOffset (Pgd, Address));
+}
+/**
+  Requests the memory space required for the page middle directory,
+  initializes it, and places it in the specified page upper directory,
+  and get the page middle directory entry corresponding to the virtual address
+
+  @param  Pud  A pointer to the page upper directory.
+
+  @retval   Gets the page middle directory entry
+**/
+PMD *
+PmdAllocGet (
+  IN PUD *Pud,
+  IN UINTN Address
+  )
+{
+  PMD * ret =  (pud_none (*Pud) && PmdAlloc (Pud))?
+            NULL: PmdOffset (Pud, Address);
+  DEBUG ((DEBUG_VERBOSE, "%a %d PudVal %p PmdOffset %p PMD_INDEX %p .\n", __func__, __LINE__,
+    Pud->PudVal, PmdOffset (Pud, Address), PMD_INDEX (Address) ));
+
+  return ret;
+}
+/**
+  Requests the memory space required for the page table,
+  initializes it, and places it in the specified page middle directory,
+  and get the page table entry corresponding to the virtual address
+
+  @param  Pmd  A pointer to the page upper directory.
+
+  @retval   Gets the page table entry
+**/
+PTE *
+PteAllocGet (
+  IN PMD *Pmd,
+  IN UINTN Address
+  )
+{
+  return (pmd_none (*Pmd) && PteAlloc (Pmd))?
+    NULL: PteOffset (Pmd, Address);
+}
+ /**
+  Convert page middle directory table entry  to tlb entry.
+
+  @param  PmdVal   page middle directory table entry value.
+
+  @retval  tlb entry value.
+ **/
+UINTN
+PmdToTlbEntry (
+  UINTN PmdVal
+  )
+{
+  UINTN Value;
+
+  Value = PmdVal ^ PAGE_HUGE;
+  Value |= ((Value & PAGE_HGLOBAL) >>
+           (PAGE_HGLOBAL_SHIFT - PAGE_GLOBAL_SHIFT));
+
+  return Value;
+}
+
+ /**
+  Update huge tlb.
+
+  @param  address  The address corresponding to tlb.
+  @param  Pte   A pointer to the page table entry.
+
+  @retval  VOID.
+ **/
+VOID
+UpdateHugeTlb (
+  IN UINTN address,
+  PTE *Pte)
+{
+  INTN Idx;
+  UINTN TlbEntry;
+  address &= (PAGE_MASK << 1);
+  LOONGARCH_CSR_WRITEQ (address, LOONGARCH_CSR_TLBEHI);
+  LOONGARCH_TLB_SRCH();
+  LOONGARCH_CSR_READQ (Idx, LOONGARCH_CSR_TLBIDX);
+
+  if (Idx < 0) {
+    return ;
+  }
+  WRITE_CSR_PAGE_SIZE (HUGE_PAGE_SIZE);
+  TlbEntry = PmdToTlbEntry(PTE_VAL (*Pte));
+  LOONGARCH_CSR_WRITEQ(TlbEntry, LOONGARCH_CSR_TLBELO0);
+  LOONGARCH_CSR_WRITEQ(TlbEntry + (HUGE_PAGE_SIZE >> 1), LOONGARCH_CSR_TLBELO1);
+  LOONGARCH_TLB_WR ();
+
+  WRITE_CSR_PAGE_SIZE (DEFAULT_PAGE_SIZE);
+
+  return ;
+}
+ /**
+  Update tlb.
+
+  @param  address  The address corresponding to tlb.
+  @param  Pte   A pointer to the page table entry.
+
+  @retval  VOID.
+ **/
+VOID
+UpdateTlb (
+  IN UINTN address,
+  PTE *Pte)
+{
+  INTN Idx;
+  if (IS_HUGE_PAGE (Pte->PteVal)) {
+    return UpdateHugeTlb(address, Pte);
+  }
+
+  address &= (PAGE_MASK << 1);
+  LOONGARCH_CSR_WRITEQ (address, LOONGARCH_CSR_TLBEHI);
+  LOONGARCH_TLB_SRCH();
+  LOONGARCH_CSR_READQ (Idx, LOONGARCH_CSR_TLBIDX);
+
+  if (Idx < 0) {
+    return ;
+  }
+
+  if ((UINTN)Pte & sizeof(PTE)) {
+    Pte--;
+  }
+
+  WRITE_CSR_PAGE_SIZE (DEFAULT_PAGE_SIZE);
+  LOONGARCH_CSR_WRITEQ(PTE_VAL (*Pte), LOONGARCH_CSR_TLBELO0);
+  Pte++;
+  LOONGARCH_CSR_WRITEQ(PTE_VAL (*Pte), LOONGARCH_CSR_TLBELO1);
+  LOONGARCH_TLB_WR ();
+
+  return ;
+}
+
+/**
+  Gets the physical address of the page table entry corresponding to the specified virtual address.
+
+  @param  Address  the corresponding virtual address of the page table entry.
+
+  @retval     A pointer to the page table entry.
+  @retval  NULL
+**/
+PTE *
+GetPteAddress (
+  IN UINTN Address
+  )
+{
+  PGD *Pgd;
+  PUD *Pud;
+  PMD *Pmd;
+
+  Pgd = PgdOffset (Address);
+
+  if (pgd_none (*Pgd)) {
+    return NULL;
+  }
+
+  Pud = PudOffset (Pgd, Address);
+
+  if (pud_none (*Pud)) {
+    return NULL;
+  }
+
+  Pmd = PmdOffset (Pud, Address);
+  if (pmd_none (*Pmd)) {
+    return NULL;
+  }
+
+  if (IS_HUGE_PAGE (Pmd->PmdVal)) {
+    return ((PTE *)Pmd);
+  }
+
+  return PteOffset (Pmd, Address);
+}
+/**
+  Establishes a page table entry based on the specified memory region.
+
+  @param  Pmd  A pointer to the page middle directory.
+  @param  Address  The memory space start address.
+  @param  End  The end address of the memory space.
+  @param  Attributes  Memory space Attributes.
+
+  @retval     EFI_SUCCESS   The page table entry was created successfully.
+  @retval     EFI_OUT_OF_RESOURCES  Page table entry establishment failed due to resource exhaustion.
+**/
+EFI_STATUS
+MemoryMapPteRange (
+  IN PMD *Pmd,
+  IN UINTN Address,
+  IN UINTN End,
+  IN UINTN Attributes
+  )
+{
+  PTE *Pte;
+  PTE PteVal;
+  BOOLEAN UpDate;
+
+  Pte = PteAllocGet (Pmd, Address);
+  if (!Pte) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  do {
+    UpDate = FALSE;
+    PteVal = MAKE_PTE (Address, Attributes);
+    DEBUG ((DEBUG_VERBOSE,
+      "%a %d Address %p  PGD_INDEX %p PUD_INDEX   %p PMD_INDEX  %p PTE_INDEX  %p MAKE_PTE  %p\n",
+      __func__, __LINE__,  Address, PGD_INDEX (Address), PUD_INDEX (Address), PMD_INDEX (Address),
+      PTE_INDEX (Address), PteVal));
+
+    if ((!pte_none (*Pte)) &&
+        (PTE_VAL(*Pte) != PTE_VAL(PteVal)))
+    {
+      UpDate = TRUE;
+    }
+
+    SetPte (Pte, PteVal);
+    if (UpDate) {
+      UpdateTlb (Address, Pte);
+    }
+  } while (Pte++, Address += EFI_PAGE_SIZE, Address != End);
+
+  return EFI_SUCCESS;
+}
+/**
+  Establishes a page middle directory based on the specified memory region.
+
+  @param  Pud  A pointer to the page upper directory.
+  @param  Address  The memory space start address.
+  @param  End  The end address of the memory space.
+  @param  Attributes  Memory space Attributes.
+
+  @retval     EFI_SUCCESS   The page middle directory was created successfully.
+  @retval     EFI_OUT_OF_RESOURCES  Page middle directory establishment failed due to resource exhaustion.
+**/
+EFI_STATUS
+MemoryMapPmdRange (
+  IN PUD *Pud,
+  IN UINTN Address,
+  IN UINTN End,
+  IN UINTN Attributes
+  )
+{
+  PMD *Pmd;
+  PTE *Pte;
+  UINTN Next;
+  UINTN AddressStart_HugePage;
+  UINTN AddressEnd_HugePage;
+
+  Pmd = PmdAllocGet (Pud, Address);
+  if (!Pmd) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  do {
+    Next = PMD_ADDRESS_END (Address, End);
+    if (((Address & (~PMD_MASK)) == 0) &&
+        ((Next &  (~PMD_MASK)) == 0) &&
+        (pmd_none (*Pmd)))
+    {
+      DEBUG ((DEBUG_VERBOSE,
+        "%a %d Address %p  PGD_INDEX %p PUD_INDEX   %p PMD_INDEX  %p MAKE_HUGE_PTE  %p\n",
+        __func__, __LINE__,  Address, PGD_INDEX (Address), PUD_INDEX (Address), PMD_INDEX (Address),
+        MAKE_HUGE_PTE (Address, Attributes)));
+
+      SetPmd (Pmd, (PTE *)MAKE_HUGE_PTE (Address, Attributes));
+    } else {
+       if ((pmd_none (*Pmd)) ||
+          ((!pmd_none (*Pmd)) &&
+           (!IS_HUGE_PAGE (Pmd->PmdVal))))
+       {
+         if (MemoryMapPteRange (Pmd, Address, Next, Attributes)) {
+           return EFI_OUT_OF_RESOURCES;
+         }
+       } else {
+         SetPmd (Pmd, (PTE *)PcdGet64 (PcdInvalidPte));
+         AddressStart_HugePage = Address & PMD_MASK;
+         AddressEnd_HugePage = AddressStart_HugePage + HUGE_PAGE_SIZE;
+         if (MemoryMapPteRange (Pmd, AddressStart_HugePage, AddressEnd_HugePage, Attributes)) {
+           return EFI_OUT_OF_RESOURCES;
+         }
+         Pte = GetPteAddress (AddressStart_HugePage);
+         if (Pte == NULL) {
+           continue ;
+         }
+         UpdateTlb (AddressStart_HugePage, Pte);
+         if (AddressEnd_HugePage > End) {
+           Next = End;
+         }
+       }
+    }
+  } while (Pmd++, Address = Next, Address != End);
+
+  return 0;
+}
+/**
+  Establishes a page upper directory based on the specified memory region.
+
+  @param  Pgd  A pointer to the page global directory.
+  @param  Address  The memory space start address.
+  @param  End  The end address of the memory space.
+  @param  Attributes  Memory space Attributes.
+
+  @retval     EFI_SUCCESS   The page upper directory was created successfully.
+  @retval     EFI_OUT_OF_RESOURCES  Page upper directory establishment failed due to resource exhaustion.
+**/
+EFI_STATUS
+MemoryMapPudRange (
+  IN PGD *Pgd,
+  IN UINTN Address,
+  IN UINTN End,
+  IN UINTN Attributes
+  )
+{
+  PUD *Pud;
+  UINTN Next;
+
+  Pud = PudAllocGet (Pgd, Address);
+  if (!Pud) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  do {
+    Next = PUD_ADDRESS_END (Address, End);
+    if (MemoryMapPmdRange (Pud, Address, Next, Attributes)) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+  } while (Pud++, Address = Next, Address != End);
+  return EFI_SUCCESS;
+}
+/**
+  Establishes a page global directory based on the specified memory region.
+
+  @param  Start  The memory space start address.
+  @param  End  The end address of the memory space.
+  @param  Attributes  Memory space Attributes.
+
+  @retval     EFI_SUCCESS   The page global directory was created successfully.
+  @retval     EFI_OUT_OF_RESOURCES  Page global directory establishment failed due to resource exhaustion.
+**/
+EFI_STATUS
+MemoryMapPageRange (
+  IN UINTN Start,
+  IN UINTN End,
+  IN UINTN Attributes
+  )
+{
+  PGD *Pgd;
+  UINTN Next;
+  UINTN Address = Start;
+  EFI_STATUS Err;
+
+  Pgd = PgdOffset (Address);
+  do {
+    Next = PGD_ADDRESS_END (Address, End);
+    Err = MemoryMapPudRange (Pgd, Address, Next, Attributes);
+    if (Err) {
+      return Err;
+    }
+  } while (Pgd++, Address = Next, Address != End);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Page tables are established from memory-mapped tables.
+
+  @param  MemoryRegion   A pointer to a memory-mapped table entry.
+
+  @retval     EFI_SUCCESS   The page table was created successfully.
+  @retval     EFI_OUT_OF_RESOURCES  Page table  establishment failed due to resource exhaustion.
+**/
+EFI_STATUS
+FillTranslationTable (
+  IN  MEMORY_REGION_DESCRIPTOR  *MemoryRegion
+  )
+{
+  return MemoryMapPageRange (MemoryRegion->VirtualBase,
+           (MemoryRegion->Length + MemoryRegion->VirtualBase),
+           MemoryRegion->Attributes);
+}
+
+/**
+  write operation is performed Count times from the first element of Buffer.
+Convert EFI Attributes to Loongarch Attributes.
+  @param[in]  EfiAttributes     Efi Attributes.
+
+  @retval  LoongArch Attributes.
+**/
+UINTN
+EfiAttributeToLoongArchAttribute (
+  IN UINTN  EfiAttributes
+  )
+{
+  UINTN  LoongArchAttributes = PAGE_VALID | PAGE_DIRTY | CACHE_CC | PAGE_USER;
+  switch (EfiAttributes & EFI_MEMORY_CACHETYPE_MASK) {
+    case EFI_MEMORY_UC:
+      LoongArchAttributes |= CACHE_SUC;
+      break;
+    case EFI_MEMORY_WC:
+    case EFI_MEMORY_WT:
+    case EFI_MEMORY_WB:
+      LoongArchAttributes |= CACHE_CC;
+      break;
+    default :
+       LoongArchAttributes |= CACHE_CC;
+      break;
+  }
+
+  // Write protection attributes
+  if ((EfiAttributes & EFI_MEMORY_RO) != 0) {
+    LoongArchAttributes &= ~PAGE_DIRTY;
+  }
+
+  //eXecute protection attribute
+  if ((EfiAttributes & EFI_MEMORY_XP) != 0) {
+    LoongArchAttributes |= PAGE_NO_EXEC;
+  }
+
+  return LoongArchAttributes;
+}
+
+/**
+  Finds the length and memory properties of the memory region corresponding to the specified base address.
+
+  @param[in]  BaseAddress    To find the base address of the memory region.
+  @param[in]  EndAddress     To find the end address of the memory region.
+  @param[out]  RegionLength    The length of the memory region found.
+  @param[out]  RegionAttributes    Properties of the memory region found.
+
+  @retval  EFI_SUCCESS    The corresponding memory area was successfully found
+           EFI_NOT_FOUND    No memory area found
+**/
+EFI_STATUS
+GetLoongArchMemoryRegion (
+  IN     UINTN  BaseAddress,
+  IN     UINTN  EndAddress,
+  OUT    UINTN  *RegionLength,
+  OUT    UINTN  *RegionAttributes
+  )
+{
+  PTE *Pte;
+  UINTN Attributes;
+  UINTN AttributesTmp;
+  UINTN MaxAddress;
+  MaxAddress     = LShiftU64 (1ULL, MAX_VA_BITS) - 1;
+  Pte = GetPteAddress (BaseAddress);
+
+  if (!MmuIsInit ()) {
+    return EFI_SUCCESS;
+  }
+  if (Pte == NULL) {
+    return EFI_NOT_FOUND;
+  }
+  Attributes = GET_PAGE_ATTRIBUTES (*Pte);
+  if (IS_HUGE_PAGE (Pte->PteVal)) {
+    *RegionAttributes = Attributes & (~(PAGE_HUGE));
+    *RegionLength += HUGE_PAGE_SIZE;
+  } else {
+    *RegionLength += EFI_PAGE_SIZE;
+    *RegionAttributes = Attributes;
+  }
+
+  while (BaseAddress <= MaxAddress) {
+    Pte = GetPteAddress (BaseAddress);
+    if (Pte == NULL) {
+      return EFI_SUCCESS;
+    }
+    AttributesTmp = GET_PAGE_ATTRIBUTES (*Pte);
+    if (IS_HUGE_PAGE (Pte->PteVal)) {
+      if (AttributesTmp == Attributes) {
+         *RegionLength += HUGE_PAGE_SIZE;
+      }
+      BaseAddress += HUGE_PAGE_SIZE;
+    } else {
+      if (AttributesTmp == Attributes) {
+        *RegionLength += EFI_PAGE_SIZE;
+      }
+      BaseAddress += EFI_PAGE_SIZE;
+    }
+
+    if (BaseAddress > EndAddress) {
+      break;
+    }
+  }
+  return EFI_SUCCESS;
+}
+
+/**
+  Sets the Attributes  of the specified memory region
+
+  @param[in]  BaseAddress  The base address of the memory region to set the Attributes.
+  @param[in]  Length       The length of the memory region to set the Attributes.
+  @param[in]  Attributes   The Attributes to be set.
+
+  @retval  EFI_SUCCESS    The Attributes was set successfully
+
+**/
+EFI_STATUS
+LoongArchSetMemoryAttributes (
+  IN EFI_PHYSICAL_ADDRESS  BaseAddress,
+  IN UINTN                 Length,
+  IN UINTN                 Attributes
+  )
+{
+
+  if (!MmuIsInit ()) {
+    return EFI_SUCCESS;
+  }
+  Attributes = EfiAttributeToLoongArchAttribute (Attributes);
+  DEBUG ((DEBUG_VERBOSE, "%a %d %p %p %p.\n", __func__, __LINE__, BaseAddress , Length, Attributes));
+  MemoryMapPageRange (BaseAddress, BaseAddress + Length, Attributes);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Sets the non-executable Attributes for the specified memory region
+
+  @param[in]  BaseAddress  The base address of the memory region to set the Attributes.
+  @param[in]  Length       The length of the memory region to set the Attributes.
+
+  @retval  EFI_SUCCESS    The Attributes was set successfully
+**/
+EFI_STATUS
+LoongArchSetMemoryRegionNoExec (
+  IN  EFI_PHYSICAL_ADDRESS  BaseAddress,
+  IN  UINTN                Length
+  )
+{
+  if (MmuIsInit ()) {
+    Length = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (Length));
+    LoongArchSetMemoryAttributes (BaseAddress, Length, EFI_MEMORY_XP);
+  }
+  return EFI_SUCCESS;
+}
+
+/**
+  Check to see if mmu successfully initializes and saves the result.
+
+  @param  VOID.
+
+  @retval  EFI_SUCCESS    Initialization succeeded.
+**/
+EFI_STATUS
+MmuInitialize (VOID)
+{
+   if (PcdGet64 (PcdSwapPageDir) != 0) {
+     mMmuInited = TRUE;
+   }
+
+  return EFI_SUCCESS;
+}
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.h b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.h
new file mode 100644
index 0000000000..e9f294b356
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.h
@@ -0,0 +1,39 @@
+/** @file
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Glossary:
+    - Dir    - Directory
+**/
+#ifndef  MMU_LIB_CORE_H_
+#define  MMU_LIB_CORE_H_
+/**
+  Iterates through the page directory to initialize it.
+
+  @param  Dst  A pointer to the directory of the page to initialize.
+  @param  Num  The number of page directories to initialize.
+  @param  Src  A pointer to the data used to initialize the page directory.
+
+  @retval VOID.
+**/
+VOID
+PageDirInit (
+  IN VOID *dest,
+  IN UINTN Count,
+  IN VOID *src
+  );
+/**
+  Page tables are established from memory-mapped tables.
+
+  @param  MemoryRegion   A pointer to a memory-mapped table entry.
+
+  @retval     EFI_SUCCESS   The page table was created successfully.
+  @retval     EFI_OUT_OF_RESOURCES  Page table  establishment failed due to resource exhaustion.
+**/
+EFI_STATUS
+FillTranslationTable (
+  IN  MEMORY_REGION_DESCRIPTOR  *MemoryRegion
+  );
+#endif
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCorePei.c b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCorePei.c
new file mode 100644
index 0000000000..899f40fd94
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCorePei.c
@@ -0,0 +1,236 @@
+/** @file
+  Platform PEI driver
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Glossary:
+    - FwCfg    - Firmeware Config
+    - Tlb      - Translation Lookaside Buffer
+**/
+#include <Uefi.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include "Library/Cpu.h"
+#include "pte.h"
+#include "page.h"
+#include "mmu.h"
+#include <Library/QemuFwCfgLib.h>
+#include "MmuLibCore.h"
+
+/**
+  Return the Virtual Memory Map of your platform
+
+  This Virtual Memory Map is used by MemoryInitPei Module to initialize the MMU
+  on your platform.
+
+  @param[out]   VirtualMemoryMap    Array of MEMORY_REGION_DESCRIPTOR
+                                    describing a Physical-to-Virtual Memory
+                                    mapping. This array must be ended by a
+                                    zero-filled entry. The allocated memory
+                                    will not be freed.
+
+**/
+VOID
+GetMemoryMapFromFwCfg (
+  OUT MEMORY_REGION_DESCRIPTOR  **VirtualMemoryMap
+  )
+{
+
+  EFI_STATUS           Status;
+  FIRMWARE_CONFIG_ITEM FwCfgItem;
+  UINTN                FwCfgSize;
+  LOONGARCH_MEMMAP_ENTRY  MemoryMapEntry;
+  LOONGARCH_MEMMAP_ENTRY  *StartEntry;
+  LOONGARCH_MEMMAP_ENTRY  *pEntry;
+  UINTN                Processed;
+  MEMORY_REGION_DESCRIPTOR  *VirtualMemoryTable;
+  UINTN  Index = 0;
+  ASSERT (VirtualMemoryMap != NULL);
+
+  VirtualMemoryTable = AllocatePool (
+                         sizeof (MEMORY_REGION_DESCRIPTOR) *
+                         MAX_VIRTUAL_MEMORY_MAP_DESCRIPTORS
+                         );
+  VirtualMemoryTable[Index].PhysicalBase = 0x10000000;
+  VirtualMemoryTable[Index].VirtualBase  = VirtualMemoryTable[Index].PhysicalBase;
+  VirtualMemoryTable[Index].Length       = 0x80000000;
+  VirtualMemoryTable[Index].Attributes   = PAGE_VALID | PAGE_USER |  CACHE_CC | PAGE_DIRTY;
+  ++Index;
+
+  Status = QemuFwCfgFindFile ("etc/memmap", &FwCfgItem, &FwCfgSize);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a %d read etc/memmap error Status %d \n", __func__, __LINE__, Status));
+    ZeroMem (&VirtualMemoryTable[Index], sizeof (MEMORY_REGION_DESCRIPTOR));
+    *VirtualMemoryMap = VirtualMemoryTable;
+    return ;
+  }
+  if (FwCfgSize % sizeof MemoryMapEntry != 0) {
+    DEBUG ((DEBUG_ERROR, "no MemoryMapEntry FwCfgSize:%d\n", FwCfgSize));
+  }
+
+  QemuFwCfgSelectItem (FwCfgItem);
+  StartEntry = AllocatePages  (EFI_SIZE_TO_PAGES (FwCfgSize));
+  QemuFwCfgReadBytes (FwCfgSize, StartEntry);
+  for (Processed = 0; Processed < (FwCfgSize / sizeof MemoryMapEntry); Processed++) {
+    pEntry = StartEntry + Processed;
+    if (pEntry->Length == 0) {
+      continue;
+    }
+
+    DEBUG ((DEBUG_INFO, "MemmapEntry Base %p length %p  type %d\n", pEntry->BaseAddr, pEntry->Length, pEntry->Type));
+    VirtualMemoryTable[Index].PhysicalBase = pEntry->BaseAddr;
+    VirtualMemoryTable[Index].VirtualBase  = VirtualMemoryTable[Index].PhysicalBase;
+    VirtualMemoryTable[Index].Length       = pEntry->Length;
+    VirtualMemoryTable[Index].Attributes   = PAGE_VALID | PAGE_USER |  CACHE_CC | PAGE_DIRTY;
+    ++Index;
+  }
+
+  FreePages (StartEntry, EFI_SIZE_TO_PAGES (FwCfgSize));
+  // End of Table
+  ZeroMem (&VirtualMemoryTable[Index], sizeof (MEMORY_REGION_DESCRIPTOR));
+  *VirtualMemoryMap = VirtualMemoryTable;
+  return ;
+}
+
+/**
+  Create a page table and initialize the MMU.
+
+  @param[] VOID
+
+  @retval  VOID
+**/
+EFIAPI
+VOID
+ConfigureMmu (VOID)
+{
+  PGD *SwapperPageDir = NULL;
+  PGD *InvalidPgd = NULL;
+  PUD *InvalidPudTable = NULL;
+  PMD *InvalidPmdTable = NULL;
+  PTE *InvalidPteTable = NULL;
+  MEMORY_REGION_DESCRIPTOR  *MemoryTable = NULL;
+  RETURN_STATUS PcdStatus;
+  UINTN PgdShift = PGD_SHIFT;
+  UINTN PgdWide = PGD_WIDE;
+  UINTN PudShift = PUD_SHIFT;
+  UINTN PudWide = PUD_WIDE;
+  UINTN PmdShift = PMD_SHIFT;
+  UINTN PmdWide = PMD_WIDE;
+  UINTN PteShift = PTE_SHIFT;
+  UINTN PteWide = PTE_WIDE;
+  UINTN PageEnable = 1 << 4;
+  VOID *TlbReEntry;
+  UINTN PageSize;
+
+  SwapperPageDir = AllocatePages (EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE));
+  InvalidPgd = AllocatePages (EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE));
+  InvalidPudTable = AllocatePages (EFI_SIZE_TO_PAGES (PUD_TABLE_SIZE));
+  InvalidPmdTable = AllocatePages (EFI_SIZE_TO_PAGES (PMD_TABLE_SIZE));
+  InvalidPteTable = AllocatePages (EFI_SIZE_TO_PAGES (PTE_TABLE_SIZE));
+  ZeroMem (InvalidPteTable, PTE_TABLE_SIZE);
+
+  if ((!InvalidPgd) ||
+      (!InvalidPudTable) ||
+      (!InvalidPmdTable) ||
+      (!InvalidPteTable))
+  {
+    goto FreeTranslationTable;
+  }
+
+  /*pgd init*/
+  PageDirInit (SwapperPageDir , ENTRYS_PER_PGD, InvalidPudTable);
+  /*pgd init*/
+  PageDirInit (InvalidPgd, ENTRYS_PER_PGD, InvalidPudTable);
+  /*pud init*/
+  PageDirInit (InvalidPudTable, ENTRYS_PER_PUD, InvalidPmdTable);
+  /*pmd init*/
+  PageDirInit (InvalidPmdTable, ENTRYS_PER_PMD, InvalidPteTable);
+  GetMemoryMapFromFwCfg (&MemoryTable);
+
+  PcdStatus |= PcdSet64S (PcdSwapPageDir, (UINTN)SwapperPageDir);
+  PcdStatus |= PcdSet64S (PcdInvalidPgd, (UINTN)InvalidPgd);
+  PcdStatus |= PcdSet64S (PcdInvalidPud, (UINTN)InvalidPudTable);
+  PcdStatus |= PcdSet64S (PcdInvalidPmd, (UINTN)InvalidPmdTable);
+  PcdStatus |= PcdSet64S (PcdInvalidPte, (UINTN)InvalidPteTable);
+  ASSERT_RETURN_ERROR (PcdStatus);
+
+  while (MemoryTable->Length != 0) {
+    DEBUG ((DEBUG_VERBOSE, "%a %d VirtualBase %p VirtualEnd %p Attributes %p .\n", __func__, __LINE__,
+      MemoryTable->VirtualBase,
+      (MemoryTable->Length + MemoryTable->VirtualBase),
+      MemoryTable->Attributes));
+
+    PcdStatus = FillTranslationTable (MemoryTable);
+    if (EFI_ERROR (PcdStatus)) {
+      goto FreeTranslationTable;
+    }
+    MemoryTable++;
+  }
+
+  /*set page size*/
+  WRITE_CSR_PAGE_SIZE (DEFAULT_PAGE_SIZE);
+  WRITE_CSR_STLB_PAGE_SIZE (DEFAULT_PAGE_SIZE);
+  WRITE_CSR_TLBREFILL_PAGE_SIZE (DEFAULT_PAGE_SIZE);
+  READ_CSR_PAGE_SIZE (PageSize);
+  if (PageSize != DEFAULT_PAGE_SIZE) {
+    goto FreeTranslationTable;
+  }
+
+  TlbReEntry = AllocatePages (1);
+  if (TlbReEntry == NULL) {
+    goto FreeTranslationTable;
+  }
+  CopyMem ((char *)TlbReEntry, HandleTlbRefill, (HandleTlbRefillEnd - HandleTlbRefill));
+  SET_REFILL_TLBBASE ((UINTN)HandleTlbRefill);
+
+  DEBUG ((DEBUG_VERBOSE,
+    "%a  %d PteShift %d PteWide %d PmdShift %d PmdWide %d PudShift %d PudWide %d PgdShift %d PgdWide %d.\n",
+    __func__, __LINE__,
+    PteShift, PteWide, PmdShift, PmdWide,PudShift, PudWide, PgdShift, PgdWide));
+
+  LOONGARCH_CSR_WRITEQ (PteShift | PteWide << 5 | PmdShift << 10 | PmdWide << 15 | PudShift << 20 | PudWide << 25,
+    LOONGARCH_CSR_PWCTL0);
+  LOONGARCH_CSR_WRITEQ (PgdShift | PgdWide << 6, LOONGARCH_CSR_PWCTL1);
+  LOONGARCH_CSR_WRITEQ ((UINTN)SwapperPageDir, LOONGARCH_CSR_PGDL);
+  LOONGARCH_CSR_WRITEQ ((UINTN)InvalidPgd, LOONGARCH_CSR_PGDH);
+
+  DEBUG ((DEBUG_INFO, "%a %d Enable Mmu Start PageBassAddress %p.\n", __func__, __LINE__, SwapperPageDir));
+  LOONGARCH_CSR_XCHGQ ( PageEnable, 1 << 4, LOONGARCH_CSR_CRMD);
+  DEBUG ((DEBUG_INFO, "%a %d Enable Mmu End.\n", __func__, __LINE__));
+
+  return ;
+
+FreeTranslationTable:
+  if (SwapperPageDir) {
+    FreePages (SwapperPageDir, EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE));
+  }
+
+  if (InvalidPgd) {
+    FreePages (InvalidPgd, EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE));
+  }
+
+  if (InvalidPudTable) {
+    FreePages (InvalidPudTable, EFI_SIZE_TO_PAGES (PUD_TABLE_SIZE));
+  }
+
+  if (InvalidPmdTable) {
+    FreePages (InvalidPmdTable, EFI_SIZE_TO_PAGES (PMD_TABLE_SIZE));
+  }
+
+  if (InvalidPteTable) {
+    FreePages (InvalidPteTable, EFI_SIZE_TO_PAGES (PTE_TABLE_SIZE));
+  }
+
+  PcdSet64S (PcdSwapPageDir, (UINTN)0);
+  PcdSet64S (PcdInvalidPgd, (UINTN)0);
+  PcdSet64S (PcdInvalidPud, (UINTN)0);
+  PcdSet64S (PcdInvalidPmd, (UINTN)0);
+  PcdSet64S (PcdInvalidPte, (UINTN)0);
+
+  return ;
+}
+
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/mmu.h b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/mmu.h
new file mode 100644
index 0000000000..50b785bb6e
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/mmu.h
@@ -0,0 +1,104 @@
+/** @file
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Glossary:
+    - Tlb or TLB     - Translation Lookaside Buffer
+    - CSR            - Cpu State Register
+    - PGDL           - Page Global Directory Low
+    - PGDH           - Page Global Directory High
+    - TLBIDX         - TLB Index
+    - TLBREHI        - TLB Refill Entry High
+    - PWCTL          - Page Walk Control
+    - STLB           - Singular Page Size TLB
+    - PS             - Page Size
+**/
+#ifndef MMU_H_
+#define MMU_H_
+/*page size 4k*/
+#define DEFAULT_PAGE_SIZE         0x0c
+#define LOONGARCH_CSR_PGDL        0x19 /* Page table base address when VA[47] = 0 */
+#define LOONGARCH_CSR_PGDH        0x1a /* Page table base address when VA[47] = 1 */
+#define LOONGARCH_CSR_TLBIDX      0x10 /* TLB Index, EHINV, PageSize, NP */
+#define LOONGARCH_CSR_TLBEHI      0x11 /* TLB EntryHi */
+#define LOONGARCH_CSR_TLBELO0     0x12 /* TLB EntryLo0 */
+#define LOONGARCH_CSR_TLBELO1     0x13 /* TLB EntryLo1 */
+#define LOONGARCH_CSR_TLBREHI     0x8e /* TLB refill entryhi */
+#define LOONGARCH_CSR_PWCTL0      0x1c /* PWCtl0 */
+#define LOONGARCH_CSR_PWCTL1      0x1d /* PWCtl1 */
+#define LOONGARCH_CSR_STLBPGSIZE  0x1e
+#define CSR_TLBIDX_SIZE_MASK      0x3f000000
+#define CSR_TLBIDX_PS_SHIFT       24
+#define CSR_TLBIDX_SIZE           CSR_TLBIDX_PS_SHIFT
+
+/*
+  Set Cpu Status Register STLB Page Size.
+
+  @param val  Page Size.
+
+  @retval  VOID
+ */
+#define WRITE_CSR_STLB_PAGE_SIZE(val)  LOONGARCH_CSR_WRITEQ((val), LOONGARCH_CSR_STLBPGSIZE)
+/*
+  Set Cpu Status Register Page Size.
+
+  @param size  Page Size.
+
+  @retval  VOID
+ */
+#define WRITE_CSR_PAGE_SIZE(size)  LOONGARCH_CSR_XCHGQ((size) << CSR_TLBIDX_SIZE, CSR_TLBIDX_SIZE_MASK, LOONGARCH_CSR_TLBIDX)
+/*
+  Set Cpu Status Register TLBREFILL Page Size.
+
+  @param size  Page Size.
+
+  @retval  VOID
+ */
+#define WRITE_CSR_TLBREFILL_PAGE_SIZE(size)  LOONGARCH_CSR_XCHGQ((size) << CSR_TLBREHI_PS_SHIFT, CSR_TLBREHI_PS, LOONGARCH_CSR_TLBREHI)
+/*
+  Set Cpu Status Register TLBREFILL Base Address.
+
+  @param BaseAddress the code base address of TLB refills .
+
+  @retval  VOID
+ */
+#define SET_REFILL_TLBBASE(BaseAddress) LOONGARCH_CSR_WRITEQ((BaseAddress), LOONGARCH_CSR_TLBREBASE);
+/*
+  Get Cpu Status Register Page Size.
+
+  @param  val  Gets the page size.
+
+  @retval  VOID
+ */
+#define READ_CSR_PAGE_SIZE(val)                               \
+{                                                             \
+  LOONGARCH_CSR_READQ ((val), LOONGARCH_CSR_TLBIDX);          \
+  (val) = ((val) & CSR_TLBIDX_SIZE_MASK) >> CSR_TLBIDX_SIZE;  \
+}
+
+
+#define  CSR_TLBREHI_PS_SHIFT    0
+#define  CSR_TLBREHI_PS      ((UINTN)(0x3f) << CSR_TLBREHI_PS_SHIFT)
+
+#define EFI_MEMORY_CACHETYPE_MASK     (EFI_MEMORY_UC  | \
+                                       EFI_MEMORY_WC  | \
+                                       EFI_MEMORY_WT  | \
+                                       EFI_MEMORY_WB  | \
+                                       EFI_MEMORY_UCE   \
+                                       )
+
+
+typedef struct {
+  EFI_PHYSICAL_ADDRESS PhysicalBase;
+  EFI_VIRTUAL_ADDRESS  VirtualBase;
+  UINTN                Length;
+  UINTN                Attributes;
+} MEMORY_REGION_DESCRIPTOR;
+
+// The total number of descriptors, including the final "end-of-table" descriptor.
+#define MAX_VIRTUAL_MEMORY_MAP_DESCRIPTORS (128)
+
+extern CHAR8 HandleTlbRefill[], HandleTlbRefillEnd[];
+#endif
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/page.h b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/page.h
new file mode 100644
index 0000000000..3b3b0b72c3
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/page.h
@@ -0,0 +1,267 @@
+/** @file
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Glossary:
+    - Pgd or Pgd or PGD    - Page Global Directory
+    - Pud or Pud or PUD    - Page Upper Directory
+    - Pmd or Pmd or PMD    - Page Middle Directory
+    - Pte or pte or PTE    - Page Table Entry
+    - Val or VAL or val    - Value
+    - Dir    - Directory
+**/
+#ifndef PAGE_H_
+#define PAGE_H_
+
+#define MAX_VA_BITS                         47
+#define PGD_WIDE                            (8)
+#define PUD_WIDE                            (9)
+#define PMD_WIDE                            (9)
+#define PTE_WIDE                            (9)
+
+#define ENTRYS_PER_PGD                      (1 << PGD_WIDE)
+#define ENTRYS_PER_PUD                      (1 << PUD_WIDE)
+#define ENTRYS_PER_PMD                      (1 << PMD_WIDE)
+#define ENTRYS_PER_PTE                      (1 << PTE_WIDE)
+
+#define PGD_SHIFT                           (PUD_SHIFT + PUD_WIDE)
+#define PUD_SHIFT                           (PMD_SHIFT + PMD_WIDE)
+#define PMD_SHIFT                           (EFI_PAGE_SHIFT + PTE_WIDE)
+#define PTE_SHIFT                           (EFI_PAGE_SHIFT)
+
+#define PGD_SIZE                            (1UL << PGD_SHIFT)
+#define PUD_SIZE                            (1UL << PUD_SHIFT)
+#define PMD_SIZE                            (1UL << PMD_SHIFT)
+
+#define PGD_MASK                            (~(PGD_SIZE-1))
+#define PUD_MASK                            (~(PUD_SIZE-1))
+#define PMD_MASK                            (~(PMD_SIZE-1))
+#define PAGE_MASK                           (~(EFI_PAGE_SIZE - 1))
+#define PFN_MASK                            (~(((UINTN)(1) << (EFI_PAGE_SHIFT)) - 1) & \
+                                             (((UINTN)(1) << (PAGE_PFN_END_SHIFT)) - 1))
+
+typedef struct { UINTN PgdVal; } PGD;
+typedef struct { UINTN PudVal; } PUD;
+typedef struct { UINTN PmdVal; } PMD;
+typedef struct { UINTN PteVal; } PTE;
+/**
+  Gets the value of the page global directory table entry.
+
+  @param  x    Page global directory struct variables.
+
+  @retval   the value of the page global directory table entry.
+ **/
+#define PGD_VAL(x)                          ((x).PgdVal)
+/**
+  Gets the value of the page upper directory table entry.
+
+  @param  x    Page upper directory struct variables.
+
+  @retval  the value of the page upper directory table entry.
+ **/
+#define PUD_VAL(x)                          ((x).PudVal)
+/**
+  Gets the value of the page middle directory table entry.
+
+  @param  x    Page middle directory struct variables.
+
+  @retval  the value of the page middle directory table entry.
+ **/
+#define PMD_VAL(x)                          ((x).PmdVal)
+/**
+  Gets the value of the page table entry.
+
+  @param  x    Page table entry struct variables.
+
+  @retval  the value of the page table entry.
+ **/
+#define PTE_VAL(x)                          ((x).PteVal)
+
+
+#define PGD_TABLE_SIZE                      (ENTRYS_PER_PGD * sizeof(PGD))
+#define PUD_TABLE_SIZE                      (ENTRYS_PER_PUD * sizeof(PUD))
+#define PMD_TABLE_SIZE                      (ENTRYS_PER_PMD * sizeof(PMD))
+#define PTE_TABLE_SIZE                      (ENTRYS_PER_PTE * sizeof(PTE))
+/**
+  Gets the physical address of the record in the page table entry.
+
+  @param  x    Page table entry struct variables.
+
+  @retval  the value of the physical address.
+ **/
+#define GET_PAGE_ATTRIBUTES(x)              (UINTN) {(PTE_VAL(x) & ~PFN_MASK)}
+/**
+  Gets the virtual address of the next block of the specified virtual address
+  that is aligned with the size of the global page directory mapping.
+
+  @param  Address  Specifies the virtual address.
+  @param  End    The end address of the memory region.
+
+  @retval   the specified virtual address  of the next block.
+ **/
+#define PGD_ADDRESS_END(Address, End)            \
+({  UINTN Boundary = ((Address) + PGD_SIZE) & PGD_MASK;  \
+  (Boundary - 1 < (End) - 1)? Boundary: (End);    \
+})
+/**
+  Gets the virtual address of the next block of the specified virtual address
+  that is aligned with the size of the page upper directory mapping.
+
+  @param  Address  Specifies the virtual address.
+  @param  End    The end address of the memory region.
+
+  @retval   the specified virtual address  of the next block.
+ **/
+#define PUD_ADDRESS_END(Address, End)            \
+({  UINTN Boundary = ((Address) + PUD_SIZE) & PUD_MASK;  \
+  (Boundary - 1 < (End) - 1)? Boundary: (End);    \
+})
+/**
+  Gets the virtual address of the next block of the specified virtual address
+  that is aligned with the size of the page middle directory mapping.
+
+  @param  Address  Specifies the virtual address.
+  @param  End    The end address of the memory region.
+
+  @retval   the specified virtual address  of the next block.
+ **/
+#define PMD_ADDRESS_END(Address, End)            \
+({  UINTN Boundary = ((Address) + PMD_SIZE) & PMD_MASK;  \
+  (Boundary - 1 < (End) - 1)? Boundary: (End);    \
+})
+/**
+  Get Specifies the virtual address corresponding to the index of the page global directory table entry.
+
+  @param  Address  Specifies the virtual address.
+
+  @retval   the index of the page global directory table entry.
+ **/
+#define PGD_INDEX(Address)                  (((Address) >> PGD_SHIFT) & (ENTRYS_PER_PGD-1))
+/**
+  Get Specifies the virtual address corresponding to the index of the page upper directory table entry.
+
+  @param  Address  Specifies the virtual address.
+  @param  End    The end address of the memory region.
+
+  @retval   the index of the page upper directory table entry.
+ **/
+#define PUD_INDEX(Address)                  (((Address) >> PUD_SHIFT) & (ENTRYS_PER_PUD - 1))
+/**
+  Get Specifies the virtual address corresponding to the index of the page middle directory table entry.
+
+  @param  Address  Specifies the virtual address.
+
+  @retval   the index of the page middle directory table entry.
+ **/
+#define PMD_INDEX(Address)                  (((Address) >> PMD_SHIFT) & (ENTRYS_PER_PMD - 1))
+/**
+  Get Specifies the virtual address corresponding to the index of the page table entry.
+
+  @param  Address  Specifies the virtual address.
+
+  @retval   the index of the page table entry.
+ **/
+#define PTE_INDEX(Address)                  (((Address) >> EFI_PAGE_SHIFT) & (ENTRYS_PER_PTE - 1))
+
+/**
+  Calculates the value of the page table entry based on the specified virtual address and properties.
+
+  @param  Address  Specifies the virtual address.
+  @param  Attributes  Specifies the Attributes.
+
+  @retval    the value of the page table entry.
+ **/
+#define MAKE_PTE(Address, Attributes)       (PTE){((((Address) >> EFI_PAGE_SHIFT) << 12) | (Attributes))}
+/**
+  Calculates the value of the Huge page table entry based on the specified virtual address and properties.
+
+  @param  Address  Specifies the virtual address.
+  @param  Attributes  Specifies the Attributes.
+
+  @retval    the value of the HUGE page table entry.
+ **/
+#define MAKE_HUGE_PTE(Address, Attributes)  (((((Address) >> PMD_SHIFT) << PMD_SHIFT) | ((Attributes) | PAGE_HUGE)))
+ /**
+  Check whether the large page table entry is.
+
+  @param  Val The value of the page table entry.
+
+  @retval    1   Is huge page table entry.
+  @retval    0   Isn't huge page table entry.
+ **/
+#define IS_HUGE_PAGE(Val)                   (((Val) & PAGE_HUGE) == PAGE_HUGE)
+#define HUGE_PAGE_SIZE                      (PMD_SIZE)
+
+ /**
+  Check that the global page directory table entry is empty.
+
+  @param  pgd   the global page directory struct variables.
+
+  @retval    1   Is huge page table entry.
+  @retval    0   Isn't huge page table entry.
+ **/
+STATIC
+inline
+UINTN
+pgd_none (
+  IN PGD pgd
+  )
+{
+  return (PGD_VAL(pgd) == (UINTN)PcdGet64(PcdInvalidPud));
+}
+
+ /**
+  Check that the page upper directory table entry is empty.
+
+  @param  pud   Page upper directory struct variables.
+
+  @retval    1   Is huge page table entry.
+  @retval    0   Isn't huge page table entry.
+ **/
+STATIC
+inline
+UINTN
+pud_none (
+  IN PUD pud
+  )
+{
+  return (PUD_VAL(pud) == (UINTN)PcdGet64 (PcdInvalidPmd));
+}
+
+ /**
+  Check that the page middle directory table entry is empty.
+
+  @param  pmd   Page middle directory struct variables.
+
+  @retval    1   Is huge page table entry.
+  @retval    0   Isn't huge page table entry.
+ **/
+STATIC
+inline
+UINTN
+pmd_none (
+  IN PMD pmd
+  )
+{
+  return (PMD_VAL(pmd) == (UINTN)PcdGet64(PcdInvalidPte));
+}
+ /**
+  Check that the page  table entry is empty.
+
+  @param  pmd   Page table entry struct variables.
+
+  @retval    1   Is huge page table entry.
+  @retval    0   Isn't huge page table entry.
+ **/
+STATIC
+inline
+UINTN
+pte_none (
+  IN PTE pte
+  )
+{
+  return (!(PTE_VAL(pte) & (~PAGE_GLOBAL)));
+}
+#endif
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/pte.h b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/pte.h
new file mode 100644
index 0000000000..d6ac74f9c9
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/pte.h
@@ -0,0 +1,57 @@
+/** @file
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Glossary:
+    - Tlb or TLB     - Translation Lookaside Buffer
+    - HGLOBAL     - Huge Global
+    - PFN       - Page Frame number
+    - EXEC       - Execute
+    - PLV       - Privilege Level
+    - RPLV       - Restricted Privilege Level
+    - SUC       - Strong-ordered UnCached
+    - CC       - Coherent Cached
+    - WUC       - Weak-ordered UnCached
+**/
+#ifndef PTE_H_
+#define PTE_H_
+/*Page table property definitions */
+#define  PAGE_VALID_SHIFT    0
+#define  PAGE_DIRTY_SHIFT    1
+#define  PAGE_PLV_SHIFT      2  /* 2~3, two bits */
+#define  CACHE_SHIFT         4  /* 4~5, two bits */
+#define  PAGE_GLOBAL_SHIFT   6
+#define  PAGE_HUGE_SHIFT     6  /* HUGE is a PMD bit */
+
+#define  PAGE_HGLOBAL_SHIFT  12 /* HGlobal is a PMD bit */
+#define  PAGE_PFN_SHIFT      12
+#define  PAGE_PFN_END_SHIFT  48
+#define  PAGE_NO_READ_SHIFT  61
+#define  PAGE_NO_EXEC_SHIFT  62
+#define  PAGE_RPLV_SHIFT     63
+
+/* Used by TLB hardware (placed in EntryLo*) */
+#define PAGE_VALID           ((UINTN)(1) << PAGE_VALID_SHIFT)
+#define PAGE_DIRTY           ((UINTN)(1) << PAGE_DIRTY_SHIFT)
+#define PAGE_PLV             ((UINTN)(3) << PAGE_PLV_SHIFT)
+#define PAGE_GLOBAL          ((UINTN)(1) << PAGE_GLOBAL_SHIFT)
+#define PAGE_HUGE            ((UINTN)(1) << PAGE_HUGE_SHIFT)
+#define PAGE_HGLOBAL         ((UINTN)(1) << PAGE_HGLOBAL_SHIFT)
+#define PAGE_NO_READ         ((UINTN)(1) << PAGE_NO_READ_SHIFT)
+#define PAGE_NO_EXEC         ((UINTN)(1) << PAGE_NO_EXEC_SHIFT)
+#define PAGE_RPLV            ((UINTN)(1) << PAGE_RPLV_SHIFT)
+#define CACHE_MASK           ((UINTN)(3) << CACHE_SHIFT)
+#define PFN_SHIFT            (EFI_PAGE_SHIFT - 12 + PAGE_PFN_SHIFT)
+
+#define PLV_KERNEL           0
+#define PLV_USER             3
+
+#define PAGE_USER            (PLV_USER << PAGE_PLV_SHIFT)
+#define PAGE_KERNEL          (PLV_KERN << PAGE_PLV_SHIFT)
+
+#define CACHE_SUC            (0 << CACHE_SHIFT) /* Strong-ordered UnCached */
+#define CACHE_CC             (1 << CACHE_SHIFT) /* Coherent Cached */
+#define CACHE_WUC            (2 << CACHE_SHIFT) /* Weak-ordered UnCached */
+#endif
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [edk2-platforms][PATCH V3 06/16] Platform/Loongson: Add StableTimerLib.
  2022-09-29  7:07 [edk2-platforms][PATCH V3 00/16] Platform: Add Loongson support xianglai
                   ` (4 preceding siblings ...)
  2022-09-29  7:07 ` [edk2-platforms][PATCH V3 05/16] Platform/Loongson: Add MmuLib xianglai
@ 2022-09-29  7:07 ` xianglai
  2022-09-29  7:07 ` [edk2-platforms][PATCH V3 07/16] Platform/Loongson: Support PEI phase xianglai
                   ` (9 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: xianglai @ 2022-09-29  7:07 UTC (permalink / raw)
  To: devel; +Cc: quic_llindhol, michael.d.kinney, kraxel, ardb, maobibo

This library provides a delay interface and a timing interface.

REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4054

Signed-off-by: xianglai li <lixianglai@loongson.cn>
---
 .../Include/Library/StableTimer.h             |  43 ++++
 .../Library/StableTimerLib/Count.S            |  35 +++
 .../Library/StableTimerLib/TimerLib.c         | 242 ++++++++++++++++++
 .../Library/StableTimerLib/TimerLib.inf       |  28 ++
 4 files changed, 348 insertions(+)
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Include/Library/StableTimer.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/StableTimerLib/Count.S
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/StableTimerLib/TimerLib.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/StableTimerLib/TimerLib.inf

diff --git a/Platform/Loongson/LoongArchQemuPkg/Include/Library/StableTimer.h b/Platform/Loongson/LoongArchQemuPkg/Include/Library/StableTimer.h
new file mode 100644
index 0000000000..09a2d07404
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Include/Library/StableTimer.h
@@ -0,0 +1,43 @@
+/** @file
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Glossary:
+    - Csr    - Cpu Status Register
+    - Calc   - Calculation
+    - Freq   - frequency
+**/
+
+#ifndef STABLE_TIMER_H_
+#define STABLE_TIMER_H_
+#include "Library/Cpu.h"
+
+/**
+  Gets the timer count value.
+
+  @param[] VOID
+
+  @retval  timer count value.
+**/
+extern
+UINTN
+EFIAPI
+LoongArchReadTime (
+  VOID
+  );
+
+/**
+  Calculate the timer frequency.
+
+  @param[] VOID
+
+  @retval  Timer frequency.
+**/
+UINT32
+EFIAPI
+CalcConstFreq (
+  VOID
+  );
+#endif
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/StableTimerLib/Count.S b/Platform/Loongson/LoongArchQemuPkg/Library/StableTimerLib/Count.S
new file mode 100644
index 0000000000..f22635712c
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/StableTimerLib/Count.S
@@ -0,0 +1,35 @@
+#------------------------------------------------------------------------------
+#
+# Count for LoongArch
+#
+# Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#------------------------------------------------------------------------------
+
+#ifndef __ASSEMBLY__
+#define  __ASSEMBLY__
+#endif
+
+#include "Library/Cpu.h"
+#include "LoongArchAsmMacro.h"
+#
+# Set cpu interrupts
+# @param A0 The interrupt number
+#
+ASM_FUNC(CpuSetIP)
+    csrrd   T0, LOONGARCH_CSR_ECFG
+    or      T0, T0, A0
+    csrwr   T0, LOONGARCH_CSR_ECFG
+    jirl    ZERO, RA,0
+
+#
+#Gets the timer count value.
+#
+#@param[] VOID
+#
+#@retval  timer count value.
+ASM_FUNC(LoongArchReadTime)
+    rdtime.d   A0, ZERO
+    jirl    ZERO, RA,0
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/StableTimerLib/TimerLib.c b/Platform/Loongson/LoongArchQemuPkg/Library/StableTimerLib/TimerLib.c
new file mode 100644
index 0000000000..660a5d8b0b
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/StableTimerLib/TimerLib.c
@@ -0,0 +1,242 @@
+/** @file
+  Generic LoongArch implementation of TimerLib.h
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Glossary:
+    - Freq - Frequency
+    - Csr  - Cpu Status Register
+    - calc - calculate
+**/
+
+#include <Base.h>
+#include <Library/TimerLib.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include "Library/StableTimer.h"
+#include "Library/Cpu.h"
+
+UINT32 StableTimerFreq = 0;
+
+/**
+  Calculate the timer frequency.
+
+  @param[] VOID
+
+  @retval  Timer frequency.
+**/
+UINT32
+EFIAPI
+CalcConstFreq (
+  VOID
+  )
+{
+  UINT32 Result;
+  UINT32 BaseFreq;
+  UINT32 ClockMultiplier;
+  UINT32 ClockDivide;
+
+  LOONGARCH_GET_CPUCFG (BaseFreq, LOONGARCH_CPUCFG4);
+  LOONGARCH_GET_CPUCFG (Result, LOONGARCH_CPUCFG5);
+  ClockMultiplier = Result & 0xffff;
+  ClockDivide = (Result >> 16) & 0xffff;
+
+  if ((!BaseFreq)
+    || (!ClockMultiplier)
+    || (!ClockDivide))
+  {
+    return 0;
+  }
+  else {
+    return (BaseFreq * ClockMultiplier / ClockDivide);
+  }
+}
+/**
+  Get the timer frequency.
+
+  @param[] VOID
+
+  @retval  Timer frequency.
+**/
+UINT32
+EFIAPI
+GetFreq (
+  VOID
+  )
+{
+  if (StableTimerFreq) {
+  } else {
+    StableTimerFreq = CalcConstFreq ();
+  }
+
+  return StableTimerFreq;
+}
+
+/**
+  Stalls the CPU for at least the given number of microseconds.
+
+  Stalls the CPU for the number of microseconds specified by MicroSeconds.
+
+  @param  MicroSeconds  The minimum number of microseconds to delay.
+
+  @return MicroSeconds
+
+**/
+UINTN
+EFIAPI
+MicroSecondDelay (
+  IN  UINTN MicroSeconds
+  )
+{
+
+  UINTN Count;
+  UINTN Ticks;
+  UINTN Start;
+  UINTN End;
+
+  Count = GetFreq ();
+  Count = (Count * MicroSeconds) / 1000000;
+  Start = LoongArchReadTime ();
+  End = Start + Count;
+
+  do {
+    Ticks = LoongArchReadTime ();
+  } while (Ticks < End);
+
+  return MicroSeconds;
+}
+
+/**
+  Stalls the CPU for at least the given number of nanoseconds.
+
+  Stalls the CPU for the number of nanoseconds specified by NanoSeconds.
+
+  @param  NanoSeconds The minimum number of nanoseconds to delay.
+
+  @return NanoSeconds
+
+**/
+UINTN
+EFIAPI
+NanoSecondDelay (
+  IN      UINTN                     NanoSeconds
+  )
+{
+  UINT32  MicroSeconds;
+
+  if (NanoSeconds % 1000 == 0) {
+    MicroSeconds = NanoSeconds/1000;
+  }else {
+    MicroSeconds = NanoSeconds/1000 + 1;
+  }
+  MicroSecondDelay (MicroSeconds);
+
+  return NanoSeconds;
+}
+
+/**
+  Retrieves the current value of a 64-bit free running performance counter.
+
+  Retrieves the current value of a 64-bit free running performance counter. The
+  counter can either count up by 1 or count down by 1. If the physical
+  performance counter counts by a larger increment, then the counter values
+  must be translated. The properties of the counter can be retrieved from
+  GetPerformanceCounterProperties ().
+
+  @return The current value of the free running performance counter.
+
+**/
+UINT64
+EFIAPI
+GetPerformanceCounter (
+  VOID
+  )
+{
+  return LoongArchReadTime ();
+}
+/**
+  Retrieves the 64-bit frequency in Hz and the range of performance counter
+  values.
+
+  If StartValue is not NULL, then the value that the performance counter starts
+  with immediately after is it rolls over is returned in StartValue. If
+  EndValue is not NULL, then the value that the performance counter end with
+  immediately before it rolls over is returned in EndValue. The 64-bit
+  frequency of the performance counter in Hz is always returned. If StartValue
+  is less than EndValue, then the performance counter counts up. If StartValue
+  is greater than EndValue, then the performance counter counts down. For
+  example, a 64-bit free running counter that counts up would have a StartValue
+  of 0 and an EndValue of 0xFFFFFFFFFFFFFFFF. A 24-bit free running counter
+  that counts down would have a StartValue of 0xFFFFFF and an EndValue of 0.
+
+  @param  StartValue  The value the performance counter starts with when it
+                      rolls over.
+  @param  EndValue    The value that the performance counter ends with before
+                      it rolls over.
+
+  @return The frequency in Hz.
+
+**/
+UINT64
+EFIAPI
+GetPerformanceCounterProperties (
+  OUT      UINT64                    *StartValue,  OPTIONAL
+  OUT      UINT64                    *EndValue     OPTIONAL
+  )
+{
+  if (StartValue != NULL) {
+    *StartValue = BIT2;
+  }
+
+  if (EndValue != NULL) {
+    *EndValue = BIT48 - 1;
+  }
+
+  return GetFreq ();
+}
+
+/**
+  Converts elapsed ticks of performance counter to time in nanoseconds.
+
+  This function converts the elapsed ticks of running performance counter to
+  time value in unit of nanoseconds.
+
+  @param  Ticks     The number of elapsed ticks of running performance counter.
+
+  @return The elapsed time in nanoseconds.
+
+**/
+UINT64
+EFIAPI
+GetTimeInNanoSecond (
+  IN      UINT64                     Ticks
+  )
+{
+  UINT64  Frequency;
+  UINT64  NanoSeconds;
+  UINT64  Remainder;
+  INTN    Shift;
+
+  Frequency = GetPerformanceCounterProperties (NULL, NULL);
+
+  //
+  //          Ticks
+  // Time = --------- x 1,000,000,000
+  //        Frequency
+  //
+  NanoSeconds = MultU64x32 (DivU64x64Remainder (Ticks, Frequency, &Remainder), 1000000000u);
+
+  //
+  // Ensure (Remainder * 1,000,000,000) will not overflow 64-bit.
+  // Since 2^29 < 1,000,000,000 = 0x3B9ACA00 < 2^30, Remainder should < 2^(64-30) = 2^34,
+  // i.e. highest bit set in Remainder should <= 33.
+  //
+  Shift = MAX (0, HighBitSet64 (Remainder) - 33);
+  Remainder = RShiftU64 (Remainder, (UINTN) Shift);
+  Frequency = RShiftU64 (Frequency, (UINTN) Shift);
+  NanoSeconds += DivU64x64Remainder (MultU64x32 (Remainder, 1000000000u), Frequency, NULL);
+
+  return NanoSeconds;
+}
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/StableTimerLib/TimerLib.inf b/Platform/Loongson/LoongArchQemuPkg/Library/StableTimerLib/TimerLib.inf
new file mode 100644
index 0000000000..fef0fac08c
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/StableTimerLib/TimerLib.inf
@@ -0,0 +1,28 @@
+## @file
+#  Generic LoongArch implementation of TimerLib.h
+#
+#  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                       = 0x00010005
+  BASE_NAME                         = TimerLib
+  FILE_GUID                         = 740389C7-CC44-4A2F-88DC-89D97D312E7C
+  MODULE_TYPE                       = BASE
+  VERSION_STRING                    = 1.0
+  LIBRARY_CLASS                     = TimerLib
+
+[Sources.common]
+  TimerLib.c
+  Count.S
+
+[Packages]
+  Platform/Loongson/LoongArchQemuPkg/Loongson.dec
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  DebugLib
+  IoLib
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [edk2-platforms][PATCH V3 07/16] Platform/Loongson: Support PEI phase.
  2022-09-29  7:07 [edk2-platforms][PATCH V3 00/16] Platform: Add Loongson support xianglai
                   ` (5 preceding siblings ...)
  2022-09-29  7:07 ` [edk2-platforms][PATCH V3 06/16] Platform/Loongson: Add StableTimerLib xianglai
@ 2022-09-29  7:07 ` xianglai
  2022-09-29  7:07 ` [edk2-platforms][PATCH V3 08/16] Platform/Loongson: Add CPU DXE driver xianglai
                   ` (8 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: xianglai @ 2022-09-29  7:07 UTC (permalink / raw)
  To: devel; +Cc: quic_llindhol, michael.d.kinney, kraxel, ardb, maobibo

Platform PEI module for LoongArch platform initialization.

REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4054

Signed-off-by: xianglai li <lixianglai@loongson.cn>
---
 .../Loongson/LoongArchQemuPkg/Loongson.dec    |  22 ++
 .../Loongson/LoongArchQemuPkg/Loongson.dsc    |  65 ++++-
 .../Loongson/LoongArchQemuPkg/Loongson.fdf    |  51 ++++
 .../LoongArchQemuPkg/PlatformPei/Fv.c         |  61 ++++
 .../LoongArchQemuPkg/PlatformPei/MemDetect.c  | 108 +++++++
 .../LoongArchQemuPkg/PlatformPei/Platform.c   | 264 ++++++++++++++++++
 .../LoongArchQemuPkg/PlatformPei/Platform.h   |  87 ++++++
 .../PlatformPei/PlatformPei.inf               |  71 +++++
 8 files changed, 728 insertions(+), 1 deletion(-)
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/PlatformPei/Fv.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/PlatformPei/MemDetect.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/PlatformPei/Platform.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/PlatformPei/Platform.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/PlatformPei/PlatformPei.inf

diff --git a/Platform/Loongson/LoongArchQemuPkg/Loongson.dec b/Platform/Loongson/LoongArchQemuPkg/Loongson.dec
index 8cfa63ee8a..6a423f3ed6 100644
--- a/Platform/Loongson/LoongArchQemuPkg/Loongson.dec
+++ b/Platform/Loongson/LoongArchQemuPkg/Loongson.dec
@@ -30,7 +30,29 @@
 [PcdsFixedAtBuild, PcdsDynamic]
   gLoongArchQemuPkgTokenSpaceGuid.PcdFlashPeiFvBase|0x0|UINT64|0x00000003
   gLoongArchQemuPkgTokenSpaceGuid.PcdFlashPeiFvSize|0x0|UINT32|0x00000004
+  gLoongArchQemuPkgTokenSpaceGuid.PcdFlashDxeFvBase|0x0|UINT64|0x00000008
+  gLoongArchQemuPkgTokenSpaceGuid.PcdFlashDxeFvSize|0x0|UINT32|0x00000009
+  gLoongArchQemuPkgTokenSpaceGuid.PcdDeviceTreeBase|0x0|UINT64|0x00000018
+  gLoongArchQemuPkgTokenSpaceGuid.PcdDeviceTreePadding|256|UINT32|0x00000019
+
   gLoongArchQemuPkgTokenSpaceGuid.PcdSecPeiTempRamBase|0|UINT64|0x0000001c
   gLoongArchQemuPkgTokenSpaceGuid.PcdSecPeiTempRamSize|0|UINT32|0x0000001d
+  gLoongArchQemuPkgTokenSpaceGuid.PcdUefiRamTop|0x0|UINT64|0x0000001e
+  gLoongArchQemuPkgTokenSpaceGuid.PcdRamRegionsBottom|0x0|UINT64|0x00000022
   gLoongArchQemuPkgTokenSpaceGuid.PcdFlashSecFvBase|0x0|UINT64|0x00000028
   gLoongArchQemuPkgTokenSpaceGuid.PcdFlashSecFvSize|0x0|UINT32|0x00000029
+
+[PcdsFixedAtBuild.LOONGARCH64]
+   gEmbeddedTokenSpaceGuid.PcdPrePiCpuMemorySize|32|UINT8|0x00000010
+   gEmbeddedTokenSpaceGuid.PcdPrePiCpuIoSize|0|UINT8|0x00000011
+
+[PcdsDynamic]
+  gLoongArchQemuPkgTokenSpaceGuid.PcdRamSize|0x40000000|UINT64|0x00000041
+  gLoongArchQemuPkgTokenSpaceGuid.PcdFwCfgSelectorAddress|0x0|UINT64|0x00000042
+  gLoongArchQemuPkgTokenSpaceGuid.PcdFwCfgDataAddress|0x0|UINT64|0x00000043
+  gLoongArchQemuPkgTokenSpaceGuid.PcdSwapPageDir|0x0|UINT64|0x00000044
+  gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPgd|0x0|UINT64|0x00000045
+  gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPud|0x0|UINT64|0x00000046
+  gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPmd|0x0|UINT64|0x00000047
+  gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPte|0x0|UINT64|0x00000048
+
diff --git a/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc b/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc
index 52ab11de9a..1ca8cc09f7 100644
--- a/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc
+++ b/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc
@@ -57,22 +57,58 @@
 
 [LibraryClasses.common]
   PcdLib                           | MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
+  TimerLib                         | Platform/Loongson/LoongArchQemuPkg/Library/StableTimerLib/TimerLib.inf
   PrintLib                         | MdePkg/Library/BasePrintLib/BasePrintLib.inf
   BaseMemoryLib                    | MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
 
 
   BaseLib                          | MdePkg/Library/BaseLib/BaseLib.inf
+  PerformanceLib                   | MdePkg/Library/BasePerformanceLibNull/BasePerformanceLibNull.inf
   PeCoffLib                        | MdePkg/Library/BasePeCoffLib/BasePeCoffLib.inf
+  CacheMaintenanceLib              | MdePkg/Library/BaseCacheMaintenanceLib/BaseCacheMaintenanceLib.inf
+  UefiDecompressLib                | MdePkg/Library/BaseUefiDecompressLib/BaseUefiDecompressLib.inf
   PeCoffGetEntryPointLib           | MdePkg/Library/BasePeCoffGetEntryPointLib/BasePeCoffGetEntryPointLib.inf
   IoLib                            | MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf
   SerialPortLib                    | Platform/Loongson/LoongArchQemuPkg/Library/SerialPortLib/SerialPortLib.inf
   DebugPrintErrorLevelLib          | MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf
+  FdtLib                           | EmbeddedPkg/Library/FdtLib/FdtLib.inf
   PeCoffExtraActionLib             | MdePkg/Library/BasePeCoffExtraActionLibNull/BasePeCoffExtraActionLibNull.inf
   DebugAgentLib                    | MdeModulePkg/Library/DebugAgentLibNull/DebugAgentLibNull.inf
 
   DebugLib                         | MdePkg/Library/BaseDebugLibSerialPort/BaseDebugLibSerialPort.inf
 
+  PeiServicesLib                   | MdePkg/Library/PeiServicesLib/PeiServicesLib.inf
+[LibraryClasses.common.SEC]
+  ReportStatusCodeLib              | MdeModulePkg/Library/PeiReportStatusCodeLib/PeiReportStatusCodeLib.inf
+  HobLib                           | MdePkg/Library/PeiHobLib/PeiHobLib.inf
+  MemoryAllocationLib              | MdePkg/Library/PeiMemoryAllocationLib/PeiMemoryAllocationLib.inf
+
+[LibraryClasses.common.PEI_CORE]
+  HobLib                           | MdePkg/Library/PeiHobLib/PeiHobLib.inf
+  PeiServicesTablePointerLib       | Platform/Loongson/LoongArchQemuPkg/Library/PeiServicesTablePointerLib/PeiServicesTablePointerLib.inf
+  MemoryAllocationLib              | MdePkg/Library/PeiMemoryAllocationLib/PeiMemoryAllocationLib.inf
+  PeiCoreEntryPoint                | MdePkg/Library/PeiCoreEntryPoint/PeiCoreEntryPoint.inf
+  ReportStatusCodeLib              | MdeModulePkg/Library/PeiReportStatusCodeLib/PeiReportStatusCodeLib.inf
+  OemHookStatusCodeLib             | MdeModulePkg/Library/OemHookStatusCodeLibNull/OemHookStatusCodeLibNull.inf
+  PeCoffGetEntryPointLib           | MdePkg/Library/BasePeCoffGetEntryPointLib/BasePeCoffGetEntryPointLib.inf
+  QemuFwCfgLib                     | Platform/Loongson/LoongArchQemuPkg/Library/QemuFwCfgLib/QemuFwCfgLib.inf
+  MmuLib                           | Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLibPei.inf
+
+[LibraryClasses.common.PEIM]
+  HobLib                           | MdePkg/Library/PeiHobLib/PeiHobLib.inf
+  PeiServicesTablePointerLib       | Platform/Loongson/LoongArchQemuPkg/Library/PeiServicesTablePointerLib/PeiServicesTablePointerLib.inf
+  MemoryAllocationLib              | MdePkg/Library/PeiMemoryAllocationLib/PeiMemoryAllocationLib.inf
+  PeimEntryPoint                   | MdePkg/Library/PeimEntryPoint/PeimEntryPoint.inf
+  ReportStatusCodeLib              | MdeModulePkg/Library/PeiReportStatusCodeLib/PeiReportStatusCodeLib.inf
+  OemHookStatusCodeLib             | MdeModulePkg/Library/OemHookStatusCodeLibNull/OemHookStatusCodeLibNull.inf
+  PeCoffGetEntryPointLib           | MdePkg/Library/BasePeCoffGetEntryPointLib/BasePeCoffGetEntryPointLib.inf
+  PeiResourcePublicationLib        | MdePkg/Library/PeiResourcePublicationLib/PeiResourcePublicationLib.inf
+  ExtractGuidedSectionLib          | MdePkg/Library/PeiExtractGuidedSectionLib/PeiExtractGuidedSectionLib.inf
+  PcdLib                           | MdePkg/Library/PeiPcdLib/PeiPcdLib.inf
+  QemuFwCfgS3Lib                   | OvmfPkg/Library/QemuFwCfgS3Lib/PeiQemuFwCfgS3LibFwCfg.inf
+  QemuFwCfgLib                     | Platform/Loongson/LoongArchQemuPkg/Library/QemuFwCfgLib/QemuFwCfgLib.inf
 
+  MmuLib                       | Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLibPei.inf
 
 
 ################################################################################
@@ -119,9 +155,16 @@
   # ASSERT_BREAKPOINT_ENABLED  0x10
   # ASSERT_DEADLOOP_ENABLED    0x20
 
+#######################################################################################
   gLoongArchQemuPkgTokenSpaceGuid.PcdSecPeiTempRamBase                 | 0x10000
   gLoongArchQemuPkgTokenSpaceGuid.PcdSecPeiTempRamSize                 | 0x10000
-
+  gLoongArchQemuPkgTokenSpaceGuid.PcdDeviceTreeBase                    | 0x1c400000
+  #
+  # minimal memory for uefi bios should be 512M
+  # 0x00000000 - 0x10000000
+  # 0x90000000 - 0xA0000000
+  #
+  gLoongArchQemuPkgTokenSpaceGuid.PcdUefiRamTop                        | 0x10000000
 
 [Components]
 
@@ -129,3 +172,23 @@
   # SEC Phase modules
   #
   Platform/Loongson/LoongArchQemuPkg/Sec/SecMain.inf
+
+  #
+  # PEI Phase modules
+  #
+  MdeModulePkg/Core/Pei/PeiMain.inf
+  MdeModulePkg/Universal/PCD/Pei/Pcd.inf  {
+    <LibraryClasses>
+      PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
+  }
+  MdePkg/Library/PeiExtractGuidedSectionLib/PeiExtractGuidedSectionLib.inf
+  MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf {
+    <LibraryClasses>
+      NULL|MdeModulePkg/Library/LzmaCustomDecompressLib/LzmaCustomDecompressLib.inf
+  }
+
+  Platform/Loongson/LoongArchQemuPkg/PlatformPei/PlatformPei.inf {
+    <LibraryClasses>
+      PcdLib|MdePkg/Library/PeiPcdLib/PeiPcdLib.inf
+  }
+
diff --git a/Platform/Loongson/LoongArchQemuPkg/Loongson.fdf b/Platform/Loongson/LoongArchQemuPkg/Loongson.fdf
index 128b3843db..f964304fdc 100644
--- a/Platform/Loongson/LoongArchQemuPkg/Loongson.fdf
+++ b/Platform/Loongson/LoongArchQemuPkg/Loongson.fdf
@@ -45,9 +45,60 @@ READ_LOCK_STATUS   = TRUE
 
 INF  Platform/Loongson/LoongArchQemuPkg/Sec/SecMain.inf
 
+#####################################################################################################
+[FV.PEIFV]
+FvNameGuid         = 6f856a84-de7d-4af9-93a3-342b4ecb46eb
+BlockSize          = $(BLOCK_SIZE)
+FvAlignment        = 16
+ERASE_POLARITY     = 1
+MEMORY_MAPPED      = TRUE
+STICKY_WRITE       = TRUE
+LOCK_CAP           = TRUE
+LOCK_STATUS        = TRUE
+READ_DISABLED_CAP  = TRUE
+READ_ENABLED_CAP   = TRUE
+READ_STATUS        = TRUE
+READ_LOCK_CAP      = TRUE
+READ_LOCK_STATUS   = TRUE
+WRITE_DISABLED_CAP = TRUE
+WRITE_ENABLED_CAP  = TRUE
+WRITE_STATUS       = TRUE
+WRITE_LOCK_CAP     = TRUE
+WRITE_LOCK_STATUS  = TRUE
+
+APRIORI PEI {
+  INF  MdeModulePkg/Universal/PCD/Pei/Pcd.inf
+}
+
+#
+#  PEI Phase modules
+#
+
+INF  MdeModulePkg/Core/Pei/PeiMain.inf
+INF  MdeModulePkg/Universal/PCD/Pei/Pcd.inf
+INF  MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf
+INF  Platform/Loongson/LoongArchQemuPkg/PlatformPei/PlatformPei.inf
+
 #####################################################################################################
 [Rule.Common.SEC]
   FILE SEC = $(NAMED_GUID) {
     TE       TE Align = Auto   $(INF_OUTPUT)/$(MODULE_NAME).efi
     UI       STRING ="$(MODULE_NAME)" Optional
   }
+
+#####################################################################################################
+[Rule.Common.PEI_CORE]
+  FILE PEI_CORE = $(NAMED_GUID) {
+    TE       TE   Align=Auto    $(INF_OUTPUT)/$(MODULE_NAME).efi
+    UI       STRING ="$(MODULE_NAME)" Optional
+  }
+
+#####################################################################################################
+[Rule.Common.PEIM]
+  FILE PEIM = $(NAMED_GUID) {
+     PEI_DEPEX PEI_DEPEX Optional        $(INF_OUTPUT)/$(MODULE_NAME).depex
+     PE32      PE32   Align=Auto         $(INF_OUTPUT)/$(MODULE_NAME).efi
+     UI        STRING="$(MODULE_NAME)" Optional
+  }
+
+#####################################################################################################
diff --git a/Platform/Loongson/LoongArchQemuPkg/PlatformPei/Fv.c b/Platform/Loongson/LoongArchQemuPkg/PlatformPei/Fv.c
new file mode 100644
index 0000000000..f79d4506b1
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/PlatformPei/Fv.c
@@ -0,0 +1,61 @@
+/** @file
+  Build FV related hobs for platform.
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+
+#include "PiPei.h"
+#include "Platform.h"
+#include <Library/DebugLib.h>
+#include <Library/HobLib.h>
+#include <Library/PeiServicesLib.h>
+#include <Library/PcdLib.h>
+
+
+/**
+  Publish PEI & DXE (Decompressed) Memory based FVs to let PEI
+  and DXE know about them.
+
+  @retval EFI_SUCCESS   Platform PEI FVs were initialized successfully.
+
+**/
+EFI_STATUS
+PeiFvInitialization (
+  VOID
+  )
+{
+  DEBUG ((DEBUG_INFO, "Platform PEI Firmware Volume Initialization\n"));
+
+  //
+  // Create a memory allocation HOB for the PEI FV.
+  //
+  BuildMemoryAllocationHob (
+    PcdGet64 (PcdSecPeiTempRamBase),
+    PcdGet32 (PcdSecPeiTempRamSize),
+    EfiBootServicesData
+    );
+
+  //
+  // Let DXE know about the DXE FV
+  //
+  BuildFvHob (PcdGet64 (PcdFlashDxeFvBase), PcdGet32 (PcdFlashDxeFvSize));
+
+  //
+  // Let PEI know about the DXE FV so it can find the DXE Core
+  //
+  DEBUG ((DEBUG_INFO, "DXEFV base:%p size:%x\n", (VOID *) (UINTN)PcdGet64 (PcdFlashDxeFvBase),
+    PcdGet32 (PcdFlashDxeFvSize)));
+  PeiServicesInstallFvInfoPpi (
+    NULL,
+    (VOID *) (UINTN)PcdGet64 (PcdFlashDxeFvBase),
+    PcdGet32 (PcdFlashDxeFvSize),
+    NULL,
+    NULL
+    );
+
+  return EFI_SUCCESS;
+}
diff --git a/Platform/Loongson/LoongArchQemuPkg/PlatformPei/MemDetect.c b/Platform/Loongson/LoongArchQemuPkg/PlatformPei/MemDetect.c
new file mode 100644
index 0000000000..ef9b3ccf12
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/PlatformPei/MemDetect.c
@@ -0,0 +1,108 @@
+/** @file
+  Memory Detection for Virtual Machines.
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+
+//
+// The package level header files this module uses
+//
+#include <PiPei.h>
+
+//
+// The Library classes this module consumes
+//
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/DebugLib.h>
+#include <Library/HobLib.h>
+#include <Library/IoLib.h>
+#include <Library/PcdLib.h>
+#include <Library/PeimEntryPoint.h>
+#include <Library/ResourcePublicationLib.h>
+#include <Library/QemuFwCfgLib.h>
+#include "Platform.h"
+
+/**
+  Publish PEI core memory
+
+  @return EFI_SUCCESS     The PEIM initialized successfully.
+
+**/
+EFI_STATUS
+PublishPeiMemory (
+  VOID
+  )
+{
+  EFI_STATUS  Status;
+  UINT64 Base;
+  UINT64      Size;
+  UINT64      RamTop;
+
+  //
+  // Determine the range of memory to use during PEI
+  //
+  Base = PcdGet64 (PcdSecPeiTempRamBase) + PcdGet32 (PcdSecPeiTempRamSize);
+  RamTop = PcdGet64 (PcdUefiRamTop);
+  Size = RamTop - Base;
+
+
+  //
+  // Publish this memory to the PEI Core
+  //
+  Status = PublishSystemMemory (Base, Size);
+  ASSERT_EFI_ERROR (Status);
+
+  DEBUG ((DEBUG_INFO, "Publish Memory Initialize done.\n"));
+  return Status;
+}
+
+/**
+  Peform Memory Detection
+  Publish system RAM and reserve memory regions
+**/
+VOID
+InitializeRamRegions (
+  VOID
+  )
+{
+  EFI_STATUS           Status;
+  FIRMWARE_CONFIG_ITEM FwCfgItem;
+  UINTN                FwCfgSize;
+  LOONGARCH_MEMMAP_ENTRY  MemoryMapEntry;
+  LOONGARCH_MEMMAP_ENTRY  *StartEntry;
+  LOONGARCH_MEMMAP_ENTRY  *pEntry;
+  UINTN                Processed;
+
+  Status = QemuFwCfgFindFile ("etc/memmap", &FwCfgItem, &FwCfgSize);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a %d read etc/memmap error Status %d \n", __func__, __LINE__, Status));
+    return ;
+  }
+  if (FwCfgSize % sizeof MemoryMapEntry != 0) {
+    DEBUG ((DEBUG_ERROR, "no MemoryMapEntry FwCfgSize:%d\n", FwCfgSize));
+    return ;
+  }
+
+  QemuFwCfgSelectItem (FwCfgItem);
+  StartEntry = AllocatePages  (EFI_SIZE_TO_PAGES (FwCfgSize));
+  QemuFwCfgReadBytes (FwCfgSize, StartEntry);
+  for (Processed = 0; Processed < (FwCfgSize / sizeof MemoryMapEntry); Processed++) {
+    pEntry = StartEntry + Processed;
+    if (pEntry->Length == 0) {
+      continue;
+    }
+
+    DEBUG ((DEBUG_INFO, "MemmapEntry Base %p length %p  type %d\n", pEntry->BaseAddr, pEntry->Length, pEntry->Type));
+    if (pEntry->Type != EfiAcpiAddressRangeMemory) {
+      continue;
+    }
+
+    AddMemoryRangeHob ( pEntry->BaseAddr, pEntry->BaseAddr + pEntry->Length);
+  }
+
+}
diff --git a/Platform/Loongson/LoongArchQemuPkg/PlatformPei/Platform.c b/Platform/Loongson/LoongArchQemuPkg/PlatformPei/Platform.c
new file mode 100644
index 0000000000..6f6c53fcab
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/PlatformPei/Platform.c
@@ -0,0 +1,264 @@
+/** @file
+  Platform PEI driver
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Glossary:
+    - Mem - Memory
+**/
+
+
+//
+// The package level header files this module uses
+//
+#include <PiPei.h>
+//
+// The Library classes this module consumes
+//
+#include <Library/DebugLib.h>
+#include <Library/HobLib.h>
+#include <Library/IoLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/PcdLib.h>
+#include <Library/PeimEntryPoint.h>
+#include <Library/PeiServicesLib.h>
+#include <Library/ResourcePublicationLib.h>
+#include <Guid/MemoryTypeInformation.h>
+#include <Library/QemuFwCfgLib.h>
+#include <Library/MmuLib.h>
+#include <Guid/FdtHob.h>
+#include <libfdt.h>
+#include <Ppi/MasterBootMode.h>
+
+#include "Platform.h"
+
+/* TODO */
+EFI_MEMORY_TYPE_INFORMATION mDefaultMemoryTypeInformation[] = {
+  { EfiReservedMemoryType,  0x004 },
+  { EfiRuntimeServicesData, 0x024 },
+  { EfiRuntimeServicesCode, 0x030 },
+  { EfiBootServicesCode,    0x180 },
+  { EfiBootServicesData,    0xF00 },
+  { EfiMaxMemoryType,       0x000 }
+};
+
+//
+// Module globals
+//
+CONST EFI_PEI_PPI_DESCRIPTOR  mPpiListBootMode = {
+  (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
+  &gEfiPeiMasterBootModePpiGuid,
+  NULL
+};
+
+/**
+  Create Reserved type memory range hand off block.
+
+  @param  MemoryBase    memory base address.
+  @param  MemoryLimit  memory length.
+
+  @return  VOID
+**/
+VOID
+AddReservedMemoryBaseSizeHob (
+  EFI_PHYSICAL_ADDRESS        MemoryBase,
+  UINT64                      MemorySize
+  )
+{
+  BuildResourceDescriptorHob (
+    EFI_RESOURCE_MEMORY_RESERVED,
+    EFI_RESOURCE_ATTRIBUTE_PRESENT     |
+    EFI_RESOURCE_ATTRIBUTE_INITIALIZED |
+    EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE |
+    EFI_RESOURCE_ATTRIBUTE_TESTED,
+    MemoryBase,
+    MemorySize
+    );
+}
+/**
+  Create system type  memory range hand off block.
+
+  @param  MemoryBase    memory base address.
+  @param  MemoryLimit  memory length.
+
+  @return  VOID
+**/
+VOID
+AddMemoryBaseSizeHob (
+  EFI_PHYSICAL_ADDRESS        MemoryBase,
+  UINT64                      MemorySize
+  )
+{
+  BuildResourceDescriptorHob (
+    EFI_RESOURCE_SYSTEM_MEMORY,
+    EFI_RESOURCE_ATTRIBUTE_PRESENT |
+    EFI_RESOURCE_ATTRIBUTE_INITIALIZED |
+    EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE |
+    EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE |
+    EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE |
+    EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE |
+    EFI_RESOURCE_ATTRIBUTE_TESTED,
+    MemoryBase,
+    MemorySize
+    );
+}
+
+/**
+  Create  memory range hand off block.
+
+  @param  MemoryBase    memory base address.
+  @param  MemoryLimit  memory length.
+
+  @return  VOID
+**/
+VOID
+AddMemoryRangeHob (
+  EFI_PHYSICAL_ADDRESS        MemoryBase,
+  EFI_PHYSICAL_ADDRESS        MemoryLimit
+  )
+{
+  AddMemoryBaseSizeHob (MemoryBase, (UINT64) (MemoryLimit - MemoryBase));
+}
+/**
+  Create  memory type information hand off block.
+
+  @param  VOID
+
+  @return  VOID
+**/
+VOID
+MemMapInitialization (
+  VOID
+  )
+{
+  DEBUG ((DEBUG_INFO, "==%a==\n", __func__));
+  //
+  // Create Memory Type Information HOB
+  //
+  BuildGuidDataHob (
+    &gEfiMemoryTypeInformationGuid,
+    mDefaultMemoryTypeInformation,
+    sizeof (mDefaultMemoryTypeInformation)
+    );
+}
+
+/**
+  Misc Initialization.
+
+  @param  VOID
+
+  @return  VOID
+**/
+VOID
+MiscInitialization (
+  VOID
+  )
+{
+  DEBUG ((DEBUG_INFO, "==%a==\n", __func__));
+  //
+  // Creat CPU HOBs.
+  //
+  BuildCpuHob (PcdGet8 (PcdPrePiCpuMemorySize), PcdGet8 (PcdPrePiCpuIoSize));
+}
+/**
+  add fdt hand off block.
+
+  @param  VOID
+
+  @return  VOID
+**/
+VOID
+AddFdtHob (VOID)
+{
+  VOID    *Base;
+  VOID    *NewBase;
+  UINTN   FdtSize;
+  UINTN   FdtPages;
+  UINT64  *FdtHobData;
+
+  Base = (VOID*)(UINTN)PcdGet64 (PcdDeviceTreeBase);
+  ASSERT (Base != NULL);
+
+  FdtSize = fdt_totalsize (Base) + PcdGet32 (PcdDeviceTreePadding);
+  FdtPages = EFI_SIZE_TO_PAGES (FdtSize);
+  NewBase = AllocatePages (FdtPages);
+  ASSERT (NewBase != NULL);
+  fdt_open_into (Base, NewBase, EFI_PAGES_TO_SIZE (FdtPages));
+
+  FdtHobData = BuildGuidHob (&gFdtHobGuid, sizeof *FdtHobData);
+  ASSERT (FdtHobData != NULL);
+  *FdtHobData = (UINTN)NewBase;
+
+}
+
+/**
+  Fetch the size of system memory from QEMU.
+
+  @param  VOID
+
+  @return  VOID
+**/
+VOID
+SystemMemorySizeInitialization (
+  VOID
+  )
+{
+  UINT64        RamSize;
+  RETURN_STATUS PcdStatus;
+
+  QemuFwCfgSelectItem (QemuFwCfgItemRamSize);
+  RamSize= QemuFwCfgRead64 ();
+  DEBUG ((DEBUG_INFO, "%a: QEMU reports %dM system memory\n", __FUNCTION__,
+    RamSize/1024/1024));
+
+  //
+  // If the fw_cfg key or fw_cfg entirely is unavailable, no change to PCD.
+  //
+  if (RamSize == 0) {
+    return;
+  }
+
+  //
+  // Otherwise, set RamSize to PCD.
+  //
+  PcdStatus =  PcdSet64S (PcdRamSize, RamSize);
+  ASSERT_RETURN_ERROR (PcdStatus);
+}
+
+/**
+  Perform Platform PEI initialization.
+
+  @param  FileHandle      Handle of the file being invoked.
+  @param  PeiServices     Describes the list of possible PEI Services.
+
+  @return EFI_SUCCESS     The PEIM initialized successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+InitializePlatform (
+  IN       EFI_PEI_FILE_HANDLE  FileHandle,
+  IN CONST EFI_PEI_SERVICES     **PeiServices
+  )
+{
+  EFI_STATUS Status;
+
+  DEBUG ((DEBUG_INFO, "Platform PEIM Loaded\n"));
+
+  Status = PeiServicesInstallPpi (&mPpiListBootMode);
+  ASSERT_EFI_ERROR (Status);
+
+  SystemMemorySizeInitialization ();
+  PublishPeiMemory ();
+  PeiFvInitialization ();
+  InitializeRamRegions ();
+  MemMapInitialization ();
+  MiscInitialization ();
+  AddFdtHob ();
+  ConfigureMmu ();
+
+  return EFI_SUCCESS;
+}
diff --git a/Platform/Loongson/LoongArchQemuPkg/PlatformPei/Platform.h b/Platform/Loongson/LoongArchQemuPkg/PlatformPei/Platform.h
new file mode 100644
index 0000000000..bc44f233e7
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/PlatformPei/Platform.h
@@ -0,0 +1,87 @@
+/** @file
+  Platform PEI module include file.
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef PLATFORM_H_
+#define PLATFORM_H_
+
+#include <IndustryStandard/Pci22.h>
+
+/**
+  Create system type  memory range hand off block.
+
+  @param  MemoryBase    memory base address.
+  @param  MemoryLimit  memory length.
+
+  @return  VOID
+**/
+VOID
+AddMemoryBaseSizeHob (
+  EFI_PHYSICAL_ADDRESS        MemoryBase,
+  UINT64                      MemorySize
+  );
+
+/**
+  Create  memory range hand off block.
+
+  @param  MemoryBase    memory base address.
+  @param  MemoryLimit  memory length.
+
+  @return  VOID
+**/
+VOID
+AddMemoryRangeHob (
+  EFI_PHYSICAL_ADDRESS        MemoryBase,
+  EFI_PHYSICAL_ADDRESS        MemoryLimit
+  );
+
+/**
+  Create Reserved type memory range hand off block.
+
+  @param  MemoryBase    memory base address.
+  @param  MemoryLimit  memory length.
+
+  @return  VOID
+**/
+VOID
+AddReservedMemoryBaseSizeHob (
+  EFI_PHYSICAL_ADDRESS        MemoryBase,
+  UINT64                      MemorySize
+  );
+/**
+  Publish PEI core memory
+
+  @return EFI_SUCCESS     The PEIM initialized successfully.
+
+**/
+EFI_STATUS
+PublishPeiMemory (
+  VOID
+  );
+/**
+  Publish system RAM and reserve memory regions
+
+**/
+VOID
+InitializeRamRegions (
+  VOID
+  );
+
+/**
+  Publish PEI & DXE (Decompressed) Memory based FVs to let PEI
+  and DXE know about them.
+
+  @retval EFI_SUCCESS   Platform PEI FVs were initialized successfully.
+
+**/
+EFI_STATUS
+PeiFvInitialization (
+  VOID
+  );
+
+#endif // _PLATFORM_PEI_H_INCLUDED_
diff --git a/Platform/Loongson/LoongArchQemuPkg/PlatformPei/PlatformPei.inf b/Platform/Loongson/LoongArchQemuPkg/PlatformPei/PlatformPei.inf
new file mode 100644
index 0000000000..940bb82258
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/PlatformPei/PlatformPei.inf
@@ -0,0 +1,71 @@
+
+## @file
+#  Platform PEI driver
+#
+#  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = PlatformPei
+  FILE_GUID                      = 4c0e81e5-e8e3-4eef-b24b-19b686e9ab53
+  MODULE_TYPE                    = PEIM
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = InitializePlatform
+
+[Sources]
+  Fv.c
+  MemDetect.c
+  Platform.c
+
+
+[Packages]
+  ArmVirtPkg/ArmVirtPkg.dec
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  EmbeddedPkg/EmbeddedPkg.dec
+  Platform/Loongson/LoongArchQemuPkg/Loongson.dec
+  OvmfPkg/OvmfPkg.dec
+
+[Ppis]
+  gEfiPeiMasterBootModePpiGuid
+
+[Guids]
+  gEfiMemoryTypeInformationGuid
+  gFdtHobGuid
+
+[LibraryClasses]
+  DebugLib
+  BaseMemoryLib
+  HobLib
+  IoLib
+  PeiResourcePublicationLib
+  PeiServicesLib
+  PeiServicesTablePointerLib
+  PeimEntryPoint
+  QemuFwCfgLib
+  PcdLib
+  TimerLib
+  MmuLib
+  MemoryAllocationLib
+
+[Pcd]
+  gLoongArchQemuPkgTokenSpaceGuid.PcdRamSize
+  gLoongArchQemuPkgTokenSpaceGuid.PcdDeviceTreeBase
+  gLoongArchQemuPkgTokenSpaceGuid.PcdDeviceTreePadding
+
+[FixedPcd]
+  gLoongArchQemuPkgTokenSpaceGuid.PcdFlashDxeFvBase
+  gLoongArchQemuPkgTokenSpaceGuid.PcdFlashDxeFvSize
+  gLoongArchQemuPkgTokenSpaceGuid.PcdRamRegionsBottom
+  gLoongArchQemuPkgTokenSpaceGuid.PcdUefiRamTop
+  gLoongArchQemuPkgTokenSpaceGuid.PcdSecPeiTempRamBase
+  gLoongArchQemuPkgTokenSpaceGuid.PcdSecPeiTempRamSize
+  gEmbeddedTokenSpaceGuid.PcdPrePiCpuMemorySize
+  gEmbeddedTokenSpaceGuid.PcdPrePiCpuIoSize
+
+[Depex]
+  TRUE
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [edk2-platforms][PATCH V3 08/16] Platform/Loongson: Add CPU DXE driver.
  2022-09-29  7:07 [edk2-platforms][PATCH V3 00/16] Platform: Add Loongson support xianglai
                   ` (6 preceding siblings ...)
  2022-09-29  7:07 ` [edk2-platforms][PATCH V3 07/16] Platform/Loongson: Support PEI phase xianglai
@ 2022-09-29  7:07 ` xianglai
  2022-09-29  7:07 ` [edk2-platforms][PATCH V3 09/16] Platform/Loongson: Add PciCpuIoDxe driver xianglai
                   ` (7 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: xianglai @ 2022-09-29  7:07 UTC (permalink / raw)
  To: devel; +Cc: quic_llindhol, michael.d.kinney, kraxel, ardb, maobibo

The driver produces EFI_CPU_ARCH_PROTOCOL,
Initialize the exception entry address.

REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4054

Signed-off-by: xianglai li <lixianglai@loongson.cn>
---
 .../LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.c  | 382 ++++++++++++++++++
 .../LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.h  | 151 +++++++
 .../Drivers/CpuDxe/CpuDxe.inf                 |  56 +++
 .../Drivers/CpuDxe/LoongArch64/Exception.c    | 338 ++++++++++++++++
 .../Drivers/CpuDxe/LoongArch64/Fpu.S          |  67 +++
 .../Drivers/CpuDxe/LoongArch64/LoongArch.S    | 292 +++++++++++++
 6 files changed, 1286 insertions(+)
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.inf
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Exception.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Fpu.S
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/LoongArch.S

diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.c
new file mode 100644
index 0000000000..bff2bd0c0a
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.c
@@ -0,0 +1,382 @@
+/** @file
+  CPU DXE Module to produce CPU ARCH Protocol
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+
+#include <Guid/IdleLoopEvent.h>
+#include <Uefi.h>
+#include <Library/CacheMaintenanceLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/CpuLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseLib.h>
+#include <Library/MmuLib.h>
+#include "CpuDxe.h"
+
+BOOLEAN mInterruptState   = FALSE;
+
+/*
+  This function flushes the range of addresses from Start to Start+Length
+  from the processor's data cache. If Start is not aligned to a cache line
+  boundary, then the bytes before Start to the preceding cache line boundary
+  are also flushed. If Start+Length is not aligned to a cache line boundary,
+  then the bytes past Start+Length to the end of the next cache line boundary
+  are also flushed. The FlushType of EfiCpuFlushTypeWriteBackInvalidate must be
+  supported. If the data cache is fully coherent with all DMA operations, then
+  this function can just return EFI_SUCCESS. If the processor does not support
+  flushing a range of the data cache, then the entire data cache can be flushed.
+
+  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
+  @param  Start            The beginning physical address to flush from the processor's data
+                           cache.
+  @param  Length           The number of bytes to flush from the processor's data cache. This
+                           function may flush more bytes than Length specifies depending upon
+                           the granularity of the flush operation that the processor supports.
+  @param  FlushType        Specifies the type of flush operation to perform.
+
+  @retval EFI_SUCCESS           The address range from Start to Start+Length was flushed from
+                                the processor's data cache.
+  @retval EFI_UNSUPPORTEDT      The processor does not support the cache flush type specified
+                                by FlushType.
+  @retval EFI_DEVICE_ERROR      The address range from Start to Start+Length could not be flushed
+                                from the processor's data cache.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuFlushCpuDataCache (
+  IN EFI_CPU_ARCH_PROTOCOL           *This,
+  IN EFI_PHYSICAL_ADDRESS            Start,
+  IN UINT64                          Length,
+  IN EFI_CPU_FLUSH_TYPE              FlushType
+  )
+{
+
+  switch (FlushType) {
+    case EfiCpuFlushTypeWriteBack:
+      WriteBackDataCacheRange ((VOID *) (UINTN)Start, (UINTN)Length);
+      break;
+    case EfiCpuFlushTypeInvalidate:
+      InvalidateDataCacheRange ((VOID *) (UINTN)Start, (UINTN)Length);
+      break;
+    case EfiCpuFlushTypeWriteBackInvalidate:
+      WriteBackInvalidateDataCacheRange ((VOID *) (UINTN)Start, (UINTN)Length);
+      break;
+    default:
+      return EFI_INVALID_PARAMETER;
+  }
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  This function enables interrupt processing by the processor.
+
+  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
+
+  @retval EFI_SUCCESS           Interrupts are enabled on the processor.
+  @retval EFI_DEVICE_ERROR      Interrupts could not be enabled on the processor.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuEnableInterrupt (
+  IN EFI_CPU_ARCH_PROTOCOL          *This
+  )
+{
+  EnableInterrupts ();
+
+  mInterruptState  = TRUE;
+  return EFI_SUCCESS;
+}
+
+
+/**
+  This function disables interrupt processing by the processor.
+
+  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
+
+  @retval EFI_SUCCESS           Interrupts are disabled on the processor.
+  @retval EFI_DEVICE_ERROR      Interrupts could not be disabled on the processor.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuDisableInterrupt (
+  IN EFI_CPU_ARCH_PROTOCOL          *This
+  )
+{
+  DisableInterrupts ();
+
+  mInterruptState = FALSE;
+  return EFI_SUCCESS;
+}
+
+
+/**
+  This function retrieves the processor's current interrupt state a returns it in
+  State. If interrupts are currently enabled, then TRUE is returned. If interrupts
+  are currently disabled, then FALSE is returned.
+
+  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
+  @param  State            A pointer to the processor's current interrupt state. Set to TRUE if
+                           interrupts are enabled and FALSE if interrupts are disabled.
+
+  @retval EFI_SUCCESS           The processor's current interrupt state was returned in State.
+  @retval EFI_INVALID_PARAMETER State is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuGetInterruptState (
+  IN  EFI_CPU_ARCH_PROTOCOL         *This,
+  OUT BOOLEAN                       *State
+  )
+{
+  if (State == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *State = mInterruptState;
+  return EFI_SUCCESS;
+}
+
+
+/**
+  This function generates an INIT on the processor. If this function succeeds, then the
+  processor will be reset, and control will not be returned to the caller. If InitType is
+  not supported by this processor, or the processor cannot programmatically generate an
+  INIT without help from external hardware, then EFI_UNSUPPORTED is returned. If an error
+  occurs attempting to generate an INIT, then EFI_DEVICE_ERROR is returned.
+
+  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
+  @param  InitType         The type of processor INIT to perform.
+
+  @retval EFI_SUCCESS           The processor INIT was performed. This return code should never be seen.
+  @retval EFI_UNSUPPORTED       The processor INIT operation specified by InitType is not supported
+                                by this processor.
+  @retval EFI_DEVICE_ERROR      The processor INIT failed.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuInit (
+  IN EFI_CPU_ARCH_PROTOCOL           *This,
+  IN EFI_CPU_INIT_TYPE               InitType
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+/**
+  This function registers and enables the handler specified by InterruptHandler for a processor
+  interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the
+  handler for the processor interrupt or exception type specified by InterruptType is uninstalled.
+  The installed handler is called once for each processor interrupt or exception.
+
+  @param  InterruptType   Interrupt Type.
+  @param  InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
+  when a processor interrupt occurs. If this parameter is NULL, then the handler
+  will be uninstalled.
+
+  @retval EFI_SUCCESS           The handler for the processor interrupt was successfully installed or uninstalled.
+  @retval EFI_ALREADY_STARTED   InterruptHandler is not NULL, and a handler for InterruptType was
+  previously installed.
+  @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not
+  previously installed.
+  @retval EFI_UNSUPPORTED       The interrupt specified by InterruptType is not supported.
+**/
+EFI_STATUS
+EFIAPI
+CpuRegisterInterruptHandler (
+  IN EFI_CPU_ARCH_PROTOCOL          *This,
+  IN EFI_EXCEPTION_TYPE             InterruptType,
+  IN EFI_CPU_INTERRUPT_HANDLER      InterruptHandler
+  )
+{
+
+  return RegisterInterruptHandler (InterruptType, InterruptHandler);
+}
+
+/**
+  Returns a timer value from one of the CPU's internal timers. There is no
+  inherent time interval between ticks but is a function of the CPU frequency.
+
+  @param  This                - Protocol instance structure.
+  @param  TimerIndex          - Specifies which CPU timer is requested.
+  @param  TimerValue          - Pointer to the returned timer value.
+  @param  TimerPeriod         - A pointer to the amount of time that passes
+                                in femtoseconds (10-15) for each increment
+                                of TimerValue. If TimerValue does not
+                                increment at a predictable rate, then 0 is
+                                returned.  The amount of time that has
+                                passed between two calls to GetTimerValue()
+                                can be calculated with the formula
+                                (TimerValue2 - TimerValue1) * TimerPeriod.
+                                This parameter is optional and may be NULL.
+
+  @retval EFI_SUCCESS           - If the CPU timer count was returned.
+  @retval EFI_UNSUPPORTED       - If the CPU does not have any readable timers.
+  @retval EFI_DEVICE_ERROR      - If an error occurred while reading the timer.
+  @retval EFI_INVALID_PARAMETER - TimerIndex is not valid or TimerValue is NULL.
+**/
+EFI_STATUS
+EFIAPI
+CpuGetTimerValue (
+  IN  EFI_CPU_ARCH_PROTOCOL          *This,
+  IN  UINT32                         TimerIndex,
+  OUT UINT64                         *TimerValue,
+  OUT UINT64                         *TimerPeriod   OPTIONAL
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+/**
+  This function modifies the attributes for the memory region specified by BaseAddress and
+  Length from their current attributes to the attributes specified by Attributes.
+
+  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
+  @param  BaseAddress      The physical address that is the start address of a memory region.
+  @param  Length           The size in bytes of the memory region.
+  @param  Attributes       The bit mask of attributes to set for the memory region.
+
+  @retval EFI_SUCCESS           The attributes were set for the memory region.
+  @retval EFI_ACCESS_DENIED     The attributes for the memory resource range specified by
+                                BaseAddress and Length cannot be modified.
+  @retval EFI_INVALID_PARAMETER Length is zero.
+  @retval EFI_OUT_OF_RESOURCES  There are not enough system resources to modify the attributes of
+                                the memory resource range.
+  @retval EFI_UNSUPPORTED       The processor does not support one or more bytes of the memory
+                                resource range specified by BaseAddress and Length.
+                                The bit mask of attributes is not support for the memory resource
+                                range specified by BaseAddress and Length.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuSetMemoryAttributes (
+  IN EFI_CPU_ARCH_PROTOCOL    *This,
+  IN EFI_PHYSICAL_ADDRESS      BaseAddress,
+  IN UINT64                    Length,
+  IN UINT64                    EfiAttributes
+  )
+{
+  EFI_STATUS  Status;
+  UINTN       LoongArchAttributes;
+  UINTN       RegionBaseAddress;
+  UINTN       RegionLength;
+  UINTN       RegionLoongArchAttributes;
+
+  if ((BaseAddress & (SIZE_4KB - 1)) != 0) {
+    // Minimum granularity is SIZE_4KB (4KB on ARM)
+    DEBUG ((DEBUG_PAGE, "CpuSetMemoryAttributes(%lx, %lx, %lx): Minimum granularity is SIZE_4KB\n",
+      BaseAddress,
+      Length,
+      EfiAttributes));
+
+    return EFI_UNSUPPORTED;
+  }
+  // Convert the 'Attribute' into LoongArch Attribute
+  LoongArchAttributes = EfiAttributeToLoongArchAttribute (EfiAttributes);
+
+  // Get the region starting from 'BaseAddress' and its 'Attribute'
+  RegionBaseAddress = BaseAddress;
+  Status = GetLoongArchMemoryRegion (RegionBaseAddress, BaseAddress + Length,
+             &RegionLength, &RegionLoongArchAttributes);
+
+  LoongArchSetMemoryAttributes (BaseAddress, Length, EfiAttributes);
+  // Data & Instruction Caches are flushed when we set new memory attributes.
+  // So, we only set the attributes if the new region is different.
+  if (EFI_ERROR (Status) || (RegionLoongArchAttributes != LoongArchAttributes) ||
+      ((BaseAddress + Length) > (RegionBaseAddress + RegionLength)))
+  {
+    return LoongArchSetMemoryAttributes (BaseAddress, Length, EfiAttributes);
+  }
+  return EFI_SUCCESS;
+}
+
+/**
+  Callback function for idle events.
+
+  @param  Event                 Event whose notification function is being invoked.
+  @param  Context               The pointer to the notification function's context,
+                                which is implementation-dependent.
+
+  @param VOID
+**/
+VOID
+EFIAPI
+IdleLoopEventCallback (
+  IN EFI_EVENT                Event,
+  IN VOID                     *Context
+  )
+{
+  CpuSleep ();
+}
+
+//
+// Globals used to initialize the protocol
+//
+EFI_HANDLE            CpuHandle = NULL;
+EFI_CPU_ARCH_PROTOCOL Cpu = {
+  CpuFlushCpuDataCache,
+  CpuEnableInterrupt,
+  CpuDisableInterrupt,
+  CpuGetInterruptState,
+  CpuInit,
+  CpuRegisterInterruptHandler,
+  CpuGetTimerValue,
+  CpuSetMemoryAttributes,
+  0,          // NumberOfTimers
+  4,          // DmaBufferAlignment
+};
+
+
+/**
+  Initialize the state information for the CPU Architectural Protocol.
+
+  @param ImageHandle     Image handle this driver.
+  @param SystemTable     Pointer to the System Table.
+
+  @retval EFI_SUCCESS           Thread can be successfully created
+  @retval EFI_OUT_OF_RESOURCES  Cannot allocate protocol data structure
+  @retval EFI_DEVICE_ERROR      Cannot create the thread
+**/
+EFI_STATUS
+CpuDxeInitialize (
+  IN EFI_HANDLE         ImageHandle,
+  IN EFI_SYSTEM_TABLE   *SystemTable
+  )
+{
+  EFI_STATUS  Status;
+  EFI_EVENT    IdleLoopEvent;
+
+  InitializeExceptions (&Cpu);
+
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &CpuHandle,
+                  &gEfiCpuArchProtocolGuid, &Cpu,
+                  NULL
+                  );
+
+  //
+  // Setup a callback for idle events
+  //
+  Status = gBS->CreateEventEx (
+                  EVT_NOTIFY_SIGNAL,
+                  TPL_NOTIFY,
+                  IdleLoopEventCallback,
+                  NULL,
+                  &gIdleLoopEventGuid,
+                  &IdleLoopEvent
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.h b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.h
new file mode 100644
index 0000000000..1bb51bce24
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.h
@@ -0,0 +1,151 @@
+/** @file
+  CPU DXE Module to produce CPU ARCH Protocol and CPU MP Protocol
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef CPU_DXE_H_
+#define CPU_DXE_H_
+
+#include <Protocol/Cpu.h>
+
+/**
+  This function registers and enables the handler specified by InterruptHandler for a processor
+  interrupt or exception type specified by InteruptNum. If InterruptHandler is NULL, then the
+  handler for the processor interrupt or exception type specified by InteruptNum is uninstalled.
+  The installed handler is called once for each processor interrupt or exception.
+
+  @param  InteruptNum    A number of the processor's current interrupt.
+  @param  InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
+                           when a processor interrupt occurs. If this parameter is NULL, then the handler
+                           will be uninstalled.
+
+  @retval EFI_SUCCESS           The handler for the processor interrupt was successfully installed or uninstalled.
+  @retval EFI_ALREADY_STARTED   InterruptHandler is not NULL, and a handler for InteruptNum was
+                                previously installed.
+  @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InteruptNum was not
+                                previously installed.
+  @retval EFI_UNSUPPORTED       The interrupt specified by InteruptNum is not supported.
+
+**/
+EFI_STATUS
+RegisterInterruptHandler (
+  IN EFI_EXCEPTION_TYPE             InteruptNum,
+  IN EFI_CPU_INTERRUPT_HANDLER      InterruptHandler
+  );
+
+
+/**
+  This function registers and enables the handler specified by InterruptHandler for a processor
+  interrupt or exception type specified by InteruptNum. If InterruptHandler is NULL, then the
+  handler for the processor interrupt or exception type specified by InteruptNum is uninstalled.
+  The installed handler is called once for each processor interrupt or exception.
+
+  @param  InteruptNum    A number of the processor's current interrupt.
+  @param  InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
+                           when a processor interrupt occurs. If this parameter is NULL, then the handler
+                           will be uninstalled.
+
+  @retval EFI_SUCCESS           The handler for the processor interrupt was successfully installed or uninstalled.
+  @retval EFI_ALREADY_STARTED   InterruptHandler is not NULL, and a handler for InteruptNum was
+                                previously installed.
+  @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InteruptNum was not
+                                previously installed.
+  @retval EFI_UNSUPPORTED       The interrupt specified by InteruptNum is not supported.
+
+**/
+EFI_STATUS
+RegisterDebuggerInterruptHandler (
+  IN EFI_EXCEPTION_TYPE             InteruptNum,
+  IN EFI_CPU_INTERRUPT_HANDLER      InterruptHandler
+  );
+
+/**
+  This function modifies the attributes for the memory region specified by BaseAddress and
+  Length from their current attributes to the attributes specified by Attributes.
+
+  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
+  @param  BaseAddress      The physical address that is the start address of a memory region.
+  @param  Length           The size in bytes of the memory region.
+  @param  Attributes       The bit mask of attributes to set for the memory region.
+
+  @retval EFI_SUCCESS           The attributes were set for the memory region.
+  @retval EFI_ACCESS_DENIED     The attributes for the memory resource range specified by
+                                BaseAddress and Length cannot be modified.
+  @retval EFI_INVALID_PARAMETER Length is zero.
+  @retval EFI_OUT_OF_RESOURCES  There are not enough system resources to modify the attributes of
+                                the memory resource range.
+  @retval EFI_UNSUPPORTED       The processor does not support one or more bytes of the memory
+                                resource range specified by BaseAddress and Length.
+                                The bit mask of attributes is not support for the memory resource
+                                range specified by BaseAddress and Length.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuSetMemoryAttributes (
+  IN EFI_CPU_ARCH_PROTOCOL     *This,
+  IN EFI_PHYSICAL_ADDRESS      BaseAddress,
+  IN UINT64                    Length,
+  IN UINT64                    Attributes
+  );
+
+/** Exception module initialization
+  This function sets the exception base address.
+
+  @param  Cpu   A pointer to the CPU architecture protocol structure.
+
+  @retval EFI_SUCCESS           Initialization succeeded
+  @retval EFI_NOT_FOUND          Could not Found resources.
+  @retval EFI_OUT_OF_RESOURCES   No enough resources.
+**/
+EFI_STATUS
+InitializeExceptions (
+  IN EFI_CPU_ARCH_PROTOCOL    *Cpu
+  );
+
+/** Common exception entry
+  Exception handling is the entry point for the C environment,
+  This function does different things depending on the exception type.
+
+  @param  SystemContext  The system context at the time of the exception.
+
+  @retval            VOID.
+**/
+VOID
+EFIAPI
+CommonExceptionEntry (
+  IN OUT EFI_SYSTEM_CONTEXT           SystemContext
+  );
+
+extern CHAR8 LoongArchException[], LoongArchExceptionEnd[];
+/** Set Exception Base Address
+
+  @param  addr Exception Base Address.
+
+  @retval      The Old Exception Base Address.
+**/
+extern
+UINT64
+SetEbase (
+  EFI_PHYSICAL_ADDRESS addr
+  );
+/*
+  Load the FPU with signalling NANS.  This bit pattern we're using has
+  the property that no matter whether considered as single or as double
+  precision represents signaling NANS.
+
+  @param  fcsr  The value to initialize FCSR0
+
+  @retval      The Old Exception Base Address.
+ */
+extern
+VOID
+InitFpu (
+  UINT32 fcsr
+  );
+
+#endif // __CPU_DXE_H__
diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.inf b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.inf
new file mode 100644
index 0000000000..a5e93efdeb
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.inf
@@ -0,0 +1,56 @@
+## @file
+#  CPU driver installs CPU Architecture Protocol and CPU MP protocol.
+#
+#  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = CpuDxe
+  FILE_GUID                      = bf954921-25c1-48c0-9bfb-8d0cd7ee92da
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+
+  ENTRY_POINT                    = CpuDxeInitialize
+
+[Sources.Common]
+  CpuDxe.c
+  CpuDxe.h
+
+[Sources.LOONGARCH64]
+  LoongArch64/Exception.c
+  LoongArch64/LoongArch.S
+  LoongArch64/Fpu.S
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  EmbeddedPkg/EmbeddedPkg.dec
+  Platform/Loongson/LoongArchQemuPkg/Loongson.dec
+
+[LibraryClasses]
+  BaseLib
+  BaseMemoryLib
+  CacheMaintenanceLib
+  CpuLib
+  DebugLib
+  DxeServicesTableLib
+  HobLib
+  PeCoffGetEntryPointLib
+  UefiDriverEntryPoint
+  UefiLib
+  MmuLib
+
+[Protocols]
+  gEfiCpuArchProtocolGuid
+  gEfiMpServiceProtocolGuid
+
+[Guids]
+  gEfiDebugImageInfoTableGuid
+  gIdleLoopEventGuid
+
+[Depex]
+ TRUE
diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Exception.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Exception.c
new file mode 100644
index 0000000000..431aa39607
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Exception.c
@@ -0,0 +1,338 @@
+/** @file
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Glossary:
+    - ESTAT     - Exception Status
+    - ECFG      - Exception Configure
+    - ERA       - Exception Return Address
+    - BADV      - Bad Virtual Address
+    - BADI      - Bad Instructions
+    - Epc or EPC or epc   - Exception Program Counter
+    - pc or PC or pc      - Program Counter
+    - CRMD      - Current Mode
+    - PRMD      - Previous Mode
+    - CsrEuen      - Cpu Status Register Extern Unit Enable
+    - fpu or fp or FP   - Float Point Unit
+    - LOONGARCH   - Loongson Arch
+    - Irq   - Interrupt ReQuest
+**/
+
+#include "Library/Cpu.h"
+#include <Library/BaseMemoryLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/CacheMaintenanceLib.h>
+#include <Library/DebugLib.h>
+#include "CpuDxe.h"
+#include <Library/PeCoffGetEntryPointLib.h>
+#include <Library/UefiLib.h>
+#include <Guid/DebugImageInfoTable.h>
+
+
+
+EFI_EXCEPTION_CALLBACK  gInterruptHandler[MAX_LOONGARCH_INTERRUPT + 1];
+EFI_EXCEPTION_CALLBACK  gDebuggerExceptionHandlers[MAX_LOONGARCH_INTERRUPT + 1];
+
+/**
+  This function registers and enables the handler specified by InterruptHandler for a processor
+  interrupt or exception type specified by InteruptNum. If InterruptHandler is NULL, then the
+  handler for the processor interrupt or exception type specified by InteruptNum is uninstalled.
+  The installed handler is called once for each processor interrupt or exception.
+
+  @param  InteruptNum    A number of the processor's current interrupt.
+  @param  InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
+                           when a processor interrupt occurs. If this parameter is NULL, then the handler
+                           will be uninstalled.
+
+  @retval EFI_SUCCESS           The handler for the processor interrupt was successfully installed or uninstalled.
+  @retval EFI_ALREADY_STARTED   InterruptHandler is not NULL, and a handler for InteruptNum was
+                                previously installed.
+  @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InteruptNum was not
+                                previously installed.
+  @retval EFI_UNSUPPORTED       The interrupt specified by InteruptNum is not supported.
+
+**/
+EFI_STATUS
+RegisterInterruptHandler (
+  IN EFI_EXCEPTION_TYPE             InteruptNum,
+  IN EFI_CPU_INTERRUPT_HANDLER      InterruptHandler
+  )
+{
+  if (InteruptNum > MAX_LOONGARCH_INTERRUPT) {
+    return EFI_UNSUPPORTED;
+  }
+
+  if ((InterruptHandler != NULL)
+    && (gInterruptHandler[InteruptNum] != NULL))
+  {
+    return EFI_ALREADY_STARTED;
+  }
+
+  gInterruptHandler[InteruptNum] = InterruptHandler;
+
+  return EFI_SUCCESS;
+}
+/**
+  This function calls the corresponding exception handler based on the exception type.
+
+  @param  SystemContext  The system context at the time of the exception.
+
+  @retval VOID
+**/
+STATIC VOID
+EFIAPI
+CommonInterruptHandler (
+  IN OUT EFI_SYSTEM_CONTEXT           SystemContext
+  )
+{
+  INT32 Pending;
+  INT32 InterruptNum;
+  /*Interrupt [13-0] NMI IPI TI PCOV hw IP10-IP2 soft IP1-IP0*/
+  Pending = ((SystemContext.SystemContextLoongArch64->ESTAT) &
+             (SystemContext.SystemContextLoongArch64->ECFG) & 0x1fff);
+  for (InterruptNum = 0; InterruptNum < MAX_LOONGARCH_INTERRUPT; InterruptNum++) {
+    if (Pending & (1 << InterruptNum)) {
+      if (gInterruptHandler[InterruptNum] != NULL) {
+        gInterruptHandler[InterruptNum] (InterruptNum, SystemContext);
+      } else {
+        DEBUG ((DEBUG_INFO, "Pending: 0x%0x, InterruptNum: 0x%0x\n", Pending, InterruptNum));
+      }
+    }
+  }
+}
+
+/**
+  Use the EFI Debug Image Table to lookup the FaultAddress and find which PE/COFF image
+  it came from. As long as the PE/COFF image contains a debug directory entry a
+  string can be returned. For ELF and Mach-O images the string points to the Mach-O or ELF
+  image. Microsoft tools contain a pointer to the PDB file that contains the debug information.
+
+  @param  FaultAddress         Address to find PE/COFF image for.
+  @param  ImageBase            Return load address of found image
+  @param  PeCoffSizeOfHeaders  Return the size of the PE/COFF header for the image that was found
+
+  @retval NULL                 FaultAddress not in a loaded PE/COFF image.
+  @retval                      Path and file name of PE/COFF image.
+
+**/
+CHAR8 *
+GetImageName (
+  IN  UINTN  FaultAddress,
+  OUT UINTN  *ImageBase,
+  OUT UINTN  *PeCoffSizeOfHeaders
+  )
+{
+  EFI_STATUS                          Status;
+  EFI_DEBUG_IMAGE_INFO_TABLE_HEADER   *DebugTableHeader;
+  EFI_DEBUG_IMAGE_INFO                *DebugTable;
+  UINTN                               Entry;
+  CHAR8                               *Address;
+
+  Status = EfiGetSystemConfigurationTable (&gEfiDebugImageInfoTableGuid, (VOID **)&DebugTableHeader);
+  if (EFI_ERROR (Status)) {
+    return NULL;
+  }
+
+  DebugTable = DebugTableHeader->EfiDebugImageInfoTable;
+  if (DebugTable == NULL) {
+    return NULL;
+  }
+
+  Address = (CHAR8 *)(UINTN)FaultAddress;
+  for (Entry = 0; Entry < DebugTableHeader->TableSize; Entry++, DebugTable++) {
+    if (DebugTable->NormalImage != NULL) {
+      if ((DebugTable->NormalImage->ImageInfoType == EFI_DEBUG_IMAGE_INFO_TYPE_NORMAL) &&
+          (DebugTable->NormalImage->LoadedImageProtocolInstance != NULL)) {
+        if ((Address >= (CHAR8 *)DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase) &&
+            (Address <= ((CHAR8 *)DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase + DebugTable->NormalImage->LoadedImageProtocolInstance->ImageSize))) {
+          *ImageBase = (UINTN)DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase;
+          *PeCoffSizeOfHeaders = PeCoffGetSizeOfHeaders ((VOID *)(UINTN)*ImageBase);
+          return PeCoffLoaderGetPdbPointer (DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase);
+        }
+      }
+    }
+  }
+
+  return NULL;
+}
+
+/**
+  pass a file name string that contains the path, return file name.
+
+  @param  FullName   Path and file name
+
+  @retval            file name.
+**/
+STATIC
+CONST CHAR8 *
+BaseName (
+  IN  CONST CHAR8 *FullName
+  )
+{
+  CONST CHAR8 *Str;
+
+  Str = FullName + AsciiStrLen (FullName);
+
+  while (--Str > FullName) {
+    if (*Str == '/' || *Str == '\\') {
+      return Str + 1;
+    }
+  }
+  return Str;
+}
+
+/** Default Exception Handler Function
+  This function is called when an exception occurs that cannot be handled,
+  and this function prints the system context information when the interrupt occurred
+
+  @param  SystemContext  The system context at the time of the exception.
+
+  @retval            VOID.
+**/
+STATIC
+VOID
+EFIAPI
+DefaultHandler (
+  IN OUT EFI_SYSTEM_CONTEXT           SystemContext
+  )
+{
+  CHAR8  *ImageName;
+  UINTN  ImageBase, Epc;
+  UINTN  PeCoffSizeOfHeader;
+
+  DEBUG ((DEBUG_ERROR, "CRMD   0x%llx\n",  SystemContext.SystemContextLoongArch64->CRMD));
+  DEBUG ((DEBUG_ERROR, "PRMD   0x%llx\n",  SystemContext.SystemContextLoongArch64->PRMD));
+  DEBUG ((DEBUG_ERROR, "ECFG  0x%llx\n",  SystemContext.SystemContextLoongArch64->ECFG));
+  DEBUG ((DEBUG_ERROR, "ESTAT   0x%llx\n",  SystemContext.SystemContextLoongArch64->ESTAT));
+  DEBUG ((DEBUG_ERROR, "ERA    0x%llx\n",  SystemContext.SystemContextLoongArch64->ERA));
+  DEBUG ((DEBUG_ERROR, "BADV    0x%llx\n",  SystemContext.SystemContextLoongArch64->BADV));
+  DEBUG ((DEBUG_ERROR, "BADI 0x%llx\n",  SystemContext.SystemContextLoongArch64->BADI));
+
+  Epc = SystemContext.SystemContextLoongArch64->ERA;
+  ImageName = GetImageName (Epc, &ImageBase, &PeCoffSizeOfHeader);
+  if (ImageName != NULL) {
+    DEBUG ((DEBUG_ERROR, "PC 0x%012lx (0x%012lx+0x%08x) [ 0] %a\n",
+           Epc, ImageBase,
+           Epc - ImageBase, BaseName (ImageName)));
+  } else {
+    DEBUG ((DEBUG_ERROR, "PC 0x%012lx\n", Epc));
+  }
+
+  while (1);
+}
+
+/** Common exception entry
+  Exception handling is the entry point for the C environment,
+  This function does different things depending on the exception type.
+
+  @param  SystemContext  The system context at the time of the exception.
+
+  @retval            VOID.
+**/
+VOID
+EFIAPI
+CommonExceptionEntry (
+  IN OUT EFI_SYSTEM_CONTEXT           SystemContext
+  )
+{
+  INT32    ExceptionType;
+  UINT64   CsrEuen, FpuStatus;
+
+  ExceptionType = SystemContext.SystemContextLoongArch64->ESTAT & CSR_ESTAT_EXC;
+  ExceptionType = ExceptionType >> CSR_ESTAT_EXC_SHIFT;
+
+  LOONGARCH_CSR_READQ (CsrEuen, LOONGARCH_CSR_EUEN);
+  FpuStatus = CsrEuen & CSR_EUEN_FPEN;
+  switch (ExceptionType) {
+    case EXC_INT:
+      /*
+       * handle interrupt exception
+       */
+      CommonInterruptHandler (SystemContext);
+      if (!FpuStatus) {
+        LOONGARCH_CSR_READQ (CsrEuen, LOONGARCH_CSR_EUEN);
+        if (CsrEuen & CSR_EUEN_FPEN) {
+          /*
+           * Since Hw FP is enabled during interrupt handler,
+           * disable FP
+           */
+           CsrEuen &= ~CSR_EUEN_FPEN;
+           LOONGARCH_CSR_WRITEQ (CsrEuen, LOONGARCH_CSR_EUEN);
+        }
+      }
+      break;
+    case EXC_FPDIS:
+      /*
+       * Hardware FP disabled exception,
+       * Enable and init FP registers here
+       */
+      LOONGARCH_ENABLR_FPU ();
+      InitFpu(FPU_CSR_RN);
+      break;
+    default:
+      DefaultHandler(SystemContext);
+      break;
+  }
+}
+
+/** Exception module initialization
+  This function sets the exception base address.
+
+  @param  Cpu   A pointer to the CPU architecture protocol structure.
+
+  @retval EFI_SUCCESS           Initialization succeeded
+  @retval EFI_NOT_FOUND          Could not Found resources.
+  @retval EFI_OUT_OF_RESOURCES   No enough resources.
+**/
+EFI_STATUS
+InitializeExceptions (
+  IN EFI_CPU_ARCH_PROTOCOL    *Cpu
+  )
+{
+  EFI_STATUS           Status;
+  BOOLEAN              IrqEnabled;
+  EFI_PHYSICAL_ADDRESS Address;
+
+  ZeroMem (gInterruptHandler, sizeof (*gInterruptHandler));
+
+  //
+  // Disable interrupts
+  //
+  Cpu->GetInterruptState (Cpu, &IrqEnabled);
+  Cpu->DisableInterrupt (Cpu);
+
+  //
+  // EFI does not use the FIQ, but a debugger might so we must disable
+  // as we take over the exception vectors.
+  //
+  Status = gBS->AllocatePages (
+                 AllocateAnyPages,
+                 EfiRuntimeServicesData,
+                 1,
+                 &Address
+                 );
+  if (EFI_ERROR (Status)) {
+         return Status;
+  }
+
+  DEBUG ((DEBUG_INFO, "Set Exception Base Address\n"));
+  CopyMem ((char *)Address, LoongArchException, (LoongArchExceptionEnd - LoongArchException));
+  InvalidateInstructionCacheRange ((char *)Address, (LoongArchExceptionEnd - LoongArchException));
+
+  SetEbase (Address);
+  DEBUG ((DEBUG_INFO, "LoongArchException address: 0x%p\n", Address));
+  DEBUG ((DEBUG_INFO, "LoongArchExceptionEnd address: 0x%p\n", Address + (LoongArchExceptionEnd - LoongArchException)));
+
+  DEBUG ((DEBUG_INFO, "InitializeExceptions, IrqEnabled = %x\n", IrqEnabled));
+  if (IrqEnabled) {
+    //
+    // Restore interrupt state
+    //
+    Status = Cpu->EnableInterrupt (Cpu);
+  }
+
+  return Status;
+}
diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Fpu.S b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Fpu.S
new file mode 100644
index 0000000000..f6cab5c9e7
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/Fpu.S
@@ -0,0 +1,67 @@
+#------------------------------------------------------------------------------
+#
+# Fpu for LoongArch
+#
+# Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#  @par Glossary:
+#    - CsrEuen   - Cpu Status Register Extern Unit Enable
+#    - FPEN      - FPU Enable
+#    - fpu or fp or FP   - Float Point Unit
+#-----------------------------------------------------------------------------
+#ifndef __ASSEMBLY__
+#define  __ASSEMBLY__
+#endif
+#include "Library/Cpu.h"
+#include "CpuDxe.h"
+#include "LoongArchAsmMacro.h"
+
+/*
+  Load the FPU with signalling NANS.  This bit pattern we're using has
+  the property that no matter whether considered as single or as double
+  precision represents signaling NANS.
+
+  The value to initialize FCSR0 to comes in $A0.
+ */
+ASM_FUNC(InitFpu)
+  li.d    T1, CSR_EUEN_FPEN
+  csrxchg T1, T1, LOONGARCH_CSR_EUEN
+
+  movgr2fcsr      FCSR0, A0
+  li.d    T1, -1                          # SNaN
+  movgr2fr.d      $f0, T1
+  movgr2fr.d      $f1, T1
+  movgr2fr.d      $f2, T1
+  movgr2fr.d      $f3, T1
+  movgr2fr.d      $f4, T1
+  movgr2fr.d      $f5, T1
+  movgr2fr.d      $f6, T1
+  movgr2fr.d      $f7, T1
+  movgr2fr.d      $f8, T1
+  movgr2fr.d      $f9, T1
+  movgr2fr.d      $f10, T1
+  movgr2fr.d      $f11, T1
+  movgr2fr.d      $f12, T1
+  movgr2fr.d      $f13, T1
+  movgr2fr.d      $f14, T1
+  movgr2fr.d      $f15, T1
+  movgr2fr.d      $f16, T1
+  movgr2fr.d      $f17, T1
+  movgr2fr.d      $f18, T1
+  movgr2fr.d      $f19, T1
+  movgr2fr.d      $f20, T1
+  movgr2fr.d      $f21, T1
+  movgr2fr.d      $f22, T1
+  movgr2fr.d      $f23, T1
+  movgr2fr.d      $f24, T1
+  movgr2fr.d      $f25, T1
+  movgr2fr.d      $f26, T1
+  movgr2fr.d      $f27, T1
+  movgr2fr.d      $f28, T1
+  movgr2fr.d      $f29, T1
+  movgr2fr.d      $f30, T1
+  movgr2fr.d      $f31, T1
+
+  jirl ZERO, RA, 0
diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/LoongArch.S b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/LoongArch.S
new file mode 100644
index 0000000000..ae3e00b870
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/LoongArch64/LoongArch.S
@@ -0,0 +1,292 @@
+#------------------------------------------------------------------------------
+#
+# LoongArch for LoongArch
+#
+# Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#  @par Glossary:
+#    - CsrEuen      - Cpu Status Register Extern Unit Enable
+#    - fpu    - Float Point Unit
+#    - LOONGARCH   - Loongson Arch
+#    - Ebase - Exception Base Address
+#-----------------------------------------------------------------------------
+
+#ifndef __ASSEMBLY__
+#define  __ASSEMBLY__
+#endif
+
+#include "Library/Cpu.h"
+#include "CpuDxe.h"
+#include "LoongArchAsmMacro.h"
+
+#define RSIZE   8       /* 64 bit mode register size */
+#define RLOGSIZE 3
+
+
+/*
+   Main exception handler. Not really a leaf routine but not a normal
+   function either. Save away the entire cpu state end enter exception mode.
+ */
+ASM_FUNC(Exception_handler)
+
+    csrrd   SP, LOONGARCH_CSR_KS1
+
+    addi.d  T0, $r0, -0x10
+    and     SP, SP, T0
+    addi.d  SP, SP,  -((CSR_NUM + BASE_NUM + FP_BASE_NUM)  * RSIZE)
+
+    st.d   RA, SP, RA_NUM * RSIZE
+    st.d   GP, SP, GP_NUM * RSIZE
+    st.d   A0, SP, A0_NUM * RSIZE
+    st.d   A1, SP, A1_NUM * RSIZE
+    st.d   A2, SP, A2_NUM * RSIZE
+    st.d   A3, SP, A3_NUM * RSIZE
+    st.d   A4, SP, A4_NUM * RSIZE
+    st.d   A5, SP, A5_NUM * RSIZE
+    st.d   A6, SP, A6_NUM * RSIZE
+    st.d   A7, SP, A7_NUM * RSIZE
+    st.d   T1, SP, T1_NUM * RSIZE
+    st.d   T2, SP, T2_NUM * RSIZE
+    st.d   T3, SP, T3_NUM * RSIZE
+    st.d   T4, SP, T4_NUM * RSIZE
+    st.d   T5, SP, T5_NUM * RSIZE
+    st.d   T6, SP, T6_NUM * RSIZE
+    st.d   T7, SP, T7_NUM * RSIZE
+    st.d   T8, SP, T8_NUM * RSIZE
+    st.d   TP, SP, TP_NUM * RSIZE
+    st.d   FP, SP, FP_NUM * RSIZE
+    st.d   S0, SP, S0_NUM * RSIZE
+    st.d   S1, SP, S1_NUM * RSIZE
+    st.d   S2, SP, S2_NUM * RSIZE
+    st.d   S3, SP, S3_NUM * RSIZE
+    st.d   S4, SP, S4_NUM * RSIZE
+    st.d   S5, SP, S5_NUM * RSIZE
+    st.d   S6, SP, S6_NUM * RSIZE
+    st.d   S7, SP, S7_NUM * RSIZE
+    st.d   S8, SP, S8_NUM * RSIZE
+
+    /*
+     * save T0/SP from scratch registers on stack
+     */
+    csrrd  T0, LOONGARCH_CSR_KS0
+    st.d   T0, SP, T0_NUM * RSIZE
+    csrrd  T0, LOONGARCH_CSR_KS1
+    st.d   T0, SP, SP_NUM * RSIZE
+
+    csrrd   T0, LOONGARCH_CSR_CRMD
+    st.d    T0, SP, (LOONGARCH_CSR_CRMD + BASE_NUM)  * RSIZE
+    csrrd   T0, LOONGARCH_CSR_PRMD
+    st.d    T0, SP, (LOONGARCH_CSR_PRMD + BASE_NUM)  * RSIZE
+    csrrd   T0, LOONGARCH_CSR_ECFG
+    st.d    T0, SP, (LOONGARCH_CSR_ECFG + BASE_NUM) * RSIZE
+    csrrd   T0, LOONGARCH_CSR_ESTAT
+    st.d    T0, SP, (LOONGARCH_CSR_ESTAT + BASE_NUM)  * RSIZE
+    csrrd   T0, LOONGARCH_CSR_EPC
+    st.d    T0, SP, (LOONGARCH_CSR_EPC+ BASE_NUM)    * RSIZE
+    csrrd   T0, LOONGARCH_CSR_BADV
+    st.d    T0, SP, (LOONGARCH_CSR_BADV + BASE_NUM)  * RSIZE
+    csrrd   T0, LOONGARCH_CSR_BADI
+    st.d    T0, SP, (LOONGARCH_CSR_BADI + BASE_NUM)  * RSIZE
+    csrrd   T0, LOONGARCH_CSR_EUEN
+    st.d    T0, SP, (LOONGARCH_CSR_EUEN + BASE_NUM)  * RSIZE
+
+    /* Save FPU context */
+    ori     T1, ZERO, CSR_EUEN_FPEN
+    and     T2, T0, T1
+    beqz    T2, 1f
+
+    fst.d   $f0,  SP, (FP0_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f1,  SP, (FP1_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f2,  SP, (FP2_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f3,  SP, (FP3_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f4,  SP, (FP4_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f5,  SP, (FP5_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f6,  SP, (FP6_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f7,  SP, (FP7_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f8,  SP, (FP8_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f9,  SP, (FP9_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f10, SP, (FP10_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f11, SP, (FP11_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f12, SP, (FP12_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f13, SP, (FP13_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f14, SP, (FP14_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f15, SP, (FP15_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f16, SP, (FP16_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f17, SP, (FP17_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f18, SP, (FP18_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f19, SP, (FP19_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f20, SP, (FP20_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f21, SP, (FP21_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f22, SP, (FP22_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f23, SP, (FP23_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f24, SP, (FP24_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f25, SP, (FP25_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f26, SP, (FP26_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f27, SP, (FP27_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f28, SP, (FP28_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f29, SP, (FP29_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f30, SP, (FP30_NUM + FP_BASE_INDEX) * RSIZE
+    fst.d   $f31, SP, (FP31_NUM + FP_BASE_INDEX) * RSIZE
+
+    movfcsr2gr      T3, FCSR0
+    st.d            T3, SP, (FCSR_NUM + FP_BASE_INDEX) * RSIZE
+    movcf2gr        T3, $fcc0
+    or              T2, T3, ZERO
+    movcf2gr        T3, $fcc1
+    bstrins.d       T2, T3, 0xf, 0x8
+    movcf2gr        T3, $fcc2
+    bstrins.d       T2, T3, 0x17, 0x10
+    movcf2gr        T3, $fcc3
+    bstrins.d       T2, T3, 0x1f, 0x18
+    movcf2gr        T3, $fcc4
+    bstrins.d       T2, T3, 0x27, 0x20
+    movcf2gr        T3, $fcc5
+    bstrins.d       T2, T3, 0x2f, 0x28
+    movcf2gr        T3, $fcc6
+    bstrins.d       T2, T3, 0x37, 0x30
+    movcf2gr        T3, $fcc7
+    bstrins.d       T2, T3, 0x3f, 0x38
+    st.d            T2, SP, (FCC_NUM + FP_BASE_INDEX)  * RSIZE
+1:
+    or      A0, SP, ZERO
+    bl      CommonExceptionEntry
+    /*disable interrupt*/
+    li.d     T0, (1 << 2)
+    csrxchg ZERO, T0, LOONGARCH_CSR_CRMD
+
+    ld.d    T0, SP, (LOONGARCH_CSR_PRMD + BASE_NUM) * RSIZE
+    csrwr   T0, LOONGARCH_CSR_PRMD
+    ld.d    T0, SP, (LOONGARCH_CSR_ECFG + BASE_NUM) * RSIZE
+    csrwr   T0, LOONGARCH_CSR_ECFG
+    ld.d    T0, SP, (LOONGARCH_CSR_EPC + BASE_NUM) * RSIZE
+    csrwr   T0, LOONGARCH_CSR_EPC
+
+    ld.d    T0, SP, (LOONGARCH_CSR_EUEN + BASE_NUM) * RSIZE
+    ori     T1, ZERO, CSR_EUEN_FPEN
+    and     T2, T0, T1
+    beqz    T2, 2f
+
+    /*
+     * check previous FP state
+     * restore FP contect if FP enabled
+     */
+    fld.d   $f0,  SP, (FP0_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f1,  SP, (FP1_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f2,  SP, (FP2_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f3,  SP, (FP3_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f4,  SP, (FP4_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f5,  SP, (FP5_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f6,  SP, (FP6_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f7,  SP, (FP7_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f8,  SP, (FP8_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f9,  SP, (FP9_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f10, SP, (FP10_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f11, SP, (FP11_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f12, SP, (FP12_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f13, SP, (FP13_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f14, SP, (FP14_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f15, SP, (FP15_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f16, SP, (FP16_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f17, SP, (FP17_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f18, SP, (FP18_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f19, SP, (FP19_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f20, SP, (FP20_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f21, SP, (FP21_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f22, SP, (FP22_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f23, SP, (FP23_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f24, SP, (FP24_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f25, SP, (FP25_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f26, SP, (FP26_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f27, SP, (FP27_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f28, SP, (FP28_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f29, SP, (FP29_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f30, SP, (FP30_NUM + FP_BASE_INDEX) * RSIZE
+    fld.d   $f31, SP, (FP31_NUM + FP_BASE_INDEX) * RSIZE
+
+    ld.d    T0, SP, (FCSR_NUM + FP_BASE_INDEX) * RSIZE
+    movgr2fcsr      FCSR0, T0
+    ld.d    T0, SP, (FCC_NUM + FP_BASE_INDEX) * RSIZE
+    bstrpick.d      T1, T0, 7, 0
+    movgr2cf        $fcc0, T1
+    bstrpick.d      T1, T0, 15, 8
+    movgr2cf        $fcc1, T1
+    bstrpick.d      T1, T0, 23, 16
+    movgr2cf        $fcc2, T1
+    bstrpick.d      T1, T0, 31, 24
+    movgr2cf        $fcc3, T1
+    bstrpick.d      T1, T0, 39, 32
+    movgr2cf        $fcc4, T1
+    bstrpick.d      T1, T0, 47, 40
+    movgr2cf        $fcc5, T1
+    bstrpick.d      T1, T0, 55, 48
+    movgr2cf        $fcc6, T1
+    bstrpick.d      T1, T0, 63, 56
+    movgr2cf        $fcc7, T1
+2:
+    ld.d    RA, SP, RA_NUM * RSIZE
+    ld.d    GP, SP, GP_NUM * RSIZE
+    ld.d    A0, SP, A0_NUM * RSIZE
+    ld.d    A1, SP, A1_NUM * RSIZE
+    ld.d    A2, SP, A2_NUM * RSIZE
+    ld.d    A3, SP, A3_NUM * RSIZE
+    ld.d    A4, SP, A4_NUM * RSIZE
+    ld.d    A5, SP, A5_NUM * RSIZE
+    ld.d    A6, SP, A6_NUM * RSIZE
+    ld.d    A7, SP, A7_NUM * RSIZE
+    ld.d    T0, SP, T0_NUM * RSIZE
+    ld.d    T1, SP, T1_NUM * RSIZE
+    ld.d    T2, SP, T2_NUM * RSIZE
+    ld.d    T3, SP, T3_NUM * RSIZE
+    ld.d    T4, SP, T4_NUM * RSIZE
+    ld.d    T5, SP, T5_NUM * RSIZE
+    ld.d    T6, SP, T6_NUM * RSIZE
+    ld.d    T7, SP, T7_NUM * RSIZE
+    ld.d    T8, SP, T8_NUM * RSIZE
+    ld.d    TP, SP, TP_NUM * RSIZE
+    ld.d    FP, SP, FP_NUM * RSIZE
+    ld.d    S0, SP, S0_NUM * RSIZE
+    ld.d    S1, SP, S1_NUM * RSIZE
+    ld.d    S2, SP, S2_NUM * RSIZE
+    ld.d    S3, SP, S3_NUM * RSIZE
+    ld.d    S4, SP, S4_NUM * RSIZE
+    ld.d    S5, SP, S5_NUM * RSIZE
+    ld.d    S6, SP, S6_NUM * RSIZE
+    ld.d    S7, SP, S7_NUM * RSIZE
+    ld.d    S8, SP, S8_NUM * RSIZE
+
+    ld.d    SP, SP, SP_NUM * RSIZE
+    ertn
+
+/*
+   Exception trampoline copied down to RAM after initialization.
+ */
+ASM_FUNC(LoongArchException)
+    csrwr   T0, LOONGARCH_CSR_KS0
+    csrwr   SP, LOONGARCH_CSR_KS1
+    pcaddi   T0, 0
+    ld.d     T0, T0, 16
+    jirl    ZERO, T0, 0
+    nop
+1:
+    .quad  Exception_handler
+.globl  LoongArchExceptionEnd
+LoongArchExceptionEnd:
+
+/*
+   Set Exception Base Address.
+ */
+ASM_FUNC(SetEbase)
+    /*
+     * clear Vint cofigure
+     * all exceptions share the same interrupt entry
+     */
+    csrrd   T0, LOONGARCH_CSR_ECFG
+    li.d    T1, ~0x70000
+    and     T0, T0, T1
+    csrwr   T0, LOONGARCH_CSR_ECFG
+
+    /*set ebase*/
+    csrwr   A0, LOONGARCH_CSR_EBASE
+    jirl    ZERO, RA, 0
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [edk2-platforms][PATCH V3 09/16] Platform/Loongson: Add PciCpuIoDxe driver.
  2022-09-29  7:07 [edk2-platforms][PATCH V3 00/16] Platform: Add Loongson support xianglai
                   ` (7 preceding siblings ...)
  2022-09-29  7:07 ` [edk2-platforms][PATCH V3 08/16] Platform/Loongson: Add CPU DXE driver xianglai
@ 2022-09-29  7:07 ` xianglai
  2022-09-29  7:07 ` [edk2-platforms][PATCH V3 10/16] Platform/Loongson: Add timer Dxe driver xianglai
                   ` (6 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: xianglai @ 2022-09-29  7:07 UTC (permalink / raw)
  To: devel; +Cc: quic_llindhol, michael.d.kinney, kraxel, ardb, maobibo

Add PCI CpuIo protocol.there is no fix translation
offset between I/O port accesses and MMIO accesses.
Add PciCpuIo2Dxe driver to implement EFI_CPU_IO2_PROTOCOL
to add the translation for IO access.

REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4054

Signed-off-by: xianglai li <lixianglai@loongson.cn>
---
 .../Drivers/PciCpuIo2Dxe/PciCpuIo2Dxe.c       | 548 ++++++++++++++++++
 .../Drivers/PciCpuIo2Dxe/PciCpuIo2Dxe.h       | 219 +++++++
 .../Drivers/PciCpuIo2Dxe/PciCpuIo2Dxe.inf     |  40 ++
 3 files changed, 807 insertions(+)
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/PciCpuIo2Dxe/PciCpuIo2Dxe.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/PciCpuIo2Dxe/PciCpuIo2Dxe.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/PciCpuIo2Dxe/PciCpuIo2Dxe.inf

diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/PciCpuIo2Dxe/PciCpuIo2Dxe.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/PciCpuIo2Dxe/PciCpuIo2Dxe.c
new file mode 100644
index 0000000000..f5154eea7a
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/PciCpuIo2Dxe/PciCpuIo2Dxe.c
@@ -0,0 +1,548 @@
+/** @file
+  Produces the CPU I/O 2 Protocol.
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiDxe.h>
+
+#include <Protocol/CpuIo2.h>
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/IoLib.h>
+#include <Library/PcdLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include "PciCpuIo2Dxe.h"
+
+//
+// Handle for the CPU I/O 2 Protocol
+//
+STATIC EFI_HANDLE  mHandle = NULL;
+
+//
+// Lookup table for increment values based on transfer widths
+//
+STATIC CONST UINT8 mInStride[] = {
+  1, // EfiCpuIoWidthUint8
+  2, // EfiCpuIoWidthUint16
+  4, // EfiCpuIoWidthUint32
+  8, // EfiCpuIoWidthUint64
+  0, // EfiCpuIoWidthFifoUint8
+  0, // EfiCpuIoWidthFifoUint16
+  0, // EfiCpuIoWidthFifoUint32
+  0, // EfiCpuIoWidthFifoUint64
+  1, // EfiCpuIoWidthFillUint8
+  2, // EfiCpuIoWidthFillUint16
+  4, // EfiCpuIoWidthFillUint32
+  8  // EfiCpuIoWidthFillUint64
+};
+
+//
+// Lookup table for increment values based on transfer widths
+//
+STATIC CONST UINT8 mOutStride[] = {
+  1, // EfiCpuIoWidthUint8
+  2, // EfiCpuIoWidthUint16
+  4, // EfiCpuIoWidthUint32
+  8, // EfiCpuIoWidthUint64
+  1, // EfiCpuIoWidthFifoUint8
+  2, // EfiCpuIoWidthFifoUint16
+  4, // EfiCpuIoWidthFifoUint32
+  8, // EfiCpuIoWidthFifoUint64
+  0, // EfiCpuIoWidthFillUint8
+  0, // EfiCpuIoWidthFillUint16
+  0, // EfiCpuIoWidthFillUint32
+  0  // EfiCpuIoWidthFillUint64
+};
+
+/**
+  Check parameters to a CPU I/O 2 Protocol service request.
+
+  The I/O operations are carried out exactly as requested. The caller is responsible
+  for satisfying any alignment and I/O width restrictions that a PI System on a
+  platform might require. For example on some platforms, width requests of
+  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will
+  be handled by the driver.
+
+  @param[in] MmioOperation  TRUE for an MMIO operation, FALSE for I/O Port operation.
+  @param[in] Width          Signifies the width of the I/O or Memory operation.
+  @param[in] Address        The base address of the I/O operation.
+  @param[in] Count          The number of I/O operations to perform. The number of
+                            bytes moved is Width size * Count, starting at Address.
+  @param[in] Buffer         For read operations, the destination buffer to store the results.
+                            For write operations, the source buffer from which to write data.
+
+  @retval EFI_SUCCESS            The parameters for this request pass the checks.
+  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.
+  @retval EFI_INVALID_PARAMETER  Buffer is NULL.
+  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.
+  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,
+                                 and Count is not valid for this PI system.
+
+**/
+EFI_STATUS
+CpuIoCheckParameter (
+  IN BOOLEAN                    MmioOperation,
+  IN EFI_CPU_IO_PROTOCOL_WIDTH  Width,
+  IN UINT64                     Address,
+  IN UINTN                      Count,
+  IN VOID                       *Buffer
+  )
+{
+  UINT64  MaxCount;
+  UINT64  Limit;
+
+  //
+  // Check to see if Buffer is NULL
+  //
+  if (Buffer == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Check to see if Width is in the valid range
+  //
+  if ((UINT32)Width >= EfiCpuIoWidthMaximum) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // For FIFO type, the target address won't increase during the access,
+  // so treat Count as 1
+  //
+  if ((Width >= EfiCpuIoWidthFifoUint8)
+    && (Width <= EfiCpuIoWidthFifoUint64))
+  {
+    Count = 1;
+  }
+
+  //
+  // Check to see if Width is in the valid range for I/O Port operations
+  //
+  Width = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);
+  if ((!MmioOperation)
+    && (Width == EfiCpuIoWidthUint64))
+  {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Check to see if Address is aligned
+  //
+  if ((Address & (UINT64) (mInStride[Width] - 1)) != 0) {
+    return EFI_UNSUPPORTED;
+  }
+
+  //
+  // Check to see if any address associated with this transfer exceeds the maximum
+  // allowed address.  The maximum address implied by the parameters passed in is
+  // Address + Size * Count.  If the following condition is met, then the transfer
+  // is not supported.
+  //
+  //    Address + Size * Count > (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS) + 1
+  //
+  // Since MAX_ADDRESS can be the maximum integer value supported by the CPU and Count
+  // can also be the maximum integer value supported by the CPU, this range
+  // check must be adjusted to avoid all overflow conditions.
+  //
+  // The following form of the range check is equivalent but assumes that
+  // MAX_ADDRESS and MAX_IO_PORT_ADDRESS are of the form (2^n - 1).
+  //
+  Limit = (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS);
+  if (Count == 0) {
+    if (Address > Limit) {
+      return EFI_UNSUPPORTED;
+    }
+  } else {
+    MaxCount = RShiftU64 (Limit, Width);
+    if (MaxCount < (Count - 1)) {
+      return EFI_UNSUPPORTED;
+    }
+    if (Address > LShiftU64 (MaxCount - Count + 1, Width)) {
+      return EFI_UNSUPPORTED;
+    }
+  }
+
+  //
+  // Check to see if Buffer is aligned
+  //
+  if (((UINTN)Buffer & ((MIN (sizeof (UINTN), mInStride[Width])  - 1))) != 0) {
+    return EFI_UNSUPPORTED;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Reads memory-mapped registers.
+
+  The I/O operations are carried out exactly as requested. The caller is responsible
+  for satisfying any alignment and I/O width restrictions that a PI System on a
+  platform might require. For example on some platforms, width requests of
+  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will
+  be handled by the driver.
+
+  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,
+  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for
+  each of the Count operations that is performed.
+
+  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,
+  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is
+  incremented for each of the Count operations that is performed. The read or
+  write operation is performed Count times on the same Address.
+
+  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,
+  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is
+  incremented for each of the Count operations that is performed. The read or
+  write operation is performed Count times from the first element of Buffer.
+
+  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.
+  @param[in]  Width    Signifies the width of the I/O or Memory operation.
+  @param[in]  Address  The base address of the I/O operation.
+  @param[in]  Count    The number of I/O operations to perform. The number of
+                       bytes moved is Width size * Count, starting at Address.
+  @param[out] Buffer   For read operations, the destination buffer to store the results.
+                       For write operations, the source buffer from which to write data.
+
+  @retval EFI_SUCCESS            The data was read from or written to the PI system.
+  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.
+  @retval EFI_INVALID_PARAMETER  Buffer is NULL.
+  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.
+  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,
+                                 and Count is not valid for this PI system.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuMemoryServiceRead (
+  IN  EFI_CPU_IO2_PROTOCOL       *This,
+  IN  EFI_CPU_IO_PROTOCOL_WIDTH  Width,
+  IN  UINT64                     Address,
+  IN  UINTN                      Count,
+  OUT VOID                       *Buffer
+  )
+{
+  EFI_STATUS                 Status;
+  UINT8                      InStride;
+  UINT8                      OutStride;
+  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;
+  UINT8                      *Uint8Buffer;
+  Status = CpuIoCheckParameter (TRUE, Width, Address, Count, Buffer);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Select loop based on the width of the transfer
+  //
+  InStride = mInStride[Width];
+  OutStride = mOutStride[Width];
+  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);
+  for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {
+    if (OperationWidth == EfiCpuIoWidthUint8) {
+      *Uint8Buffer = MmioRead8 ((UINTN)Address);
+    } else if (OperationWidth == EfiCpuIoWidthUint16) {
+      *((UINT16 *)Uint8Buffer) = MmioRead16 ((UINTN)Address);
+    } else if (OperationWidth == EfiCpuIoWidthUint32) {
+      *((UINT32 *)Uint8Buffer) = MmioRead32 ((UINTN)Address);
+    } else if (OperationWidth == EfiCpuIoWidthUint64) {
+      *((UINT64 *)Uint8Buffer) = MmioRead64 ((UINTN)Address);
+    }
+  }
+  return EFI_SUCCESS;
+}
+
+/**
+  Writes memory-mapped registers.
+
+  The I/O operations are carried out exactly as requested. The caller is responsible
+  for satisfying any alignment and I/O width restrictions that a PI System on a
+  platform might require. For example on some platforms, width requests of
+  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will
+  be handled by the driver.
+
+  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,
+  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for
+  each of the Count operations that is performed.
+
+  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,
+  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is
+  incremented for each of the Count operations that is performed. The read or
+  write operation is performed Count times on the same Address.
+
+  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,
+  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is
+  incremented for each of the Count operations that is performed. The read or
+  write operation is performed Count times from the first element of Buffer.
+
+  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.
+  @param[in]  Width    Signifies the width of the I/O or Memory operation.
+  @param[in]  Address  The base address of the I/O operation.
+  @param[in]  Count    The number of I/O operations to perform. The number of
+                       bytes moved is Width size * Count, starting at Address.
+  @param[in]  Buffer   For read operations, the destination buffer to store the results.
+                       For write operations, the source buffer from which to write data.
+
+  @retval EFI_SUCCESS            The data was read from or written to the PI system.
+  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.
+  @retval EFI_INVALID_PARAMETER  Buffer is NULL.
+  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.
+  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,
+                                 and Count is not valid for this PI system.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuMemoryServiceWrite (
+  IN EFI_CPU_IO2_PROTOCOL       *This,
+  IN EFI_CPU_IO_PROTOCOL_WIDTH  Width,
+  IN UINT64                     Address,
+  IN UINTN                      Count,
+  IN VOID                       *Buffer
+  )
+{
+  EFI_STATUS                 Status;
+  UINT8                      InStride;
+  UINT8                      OutStride;
+  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;
+  UINT8                      *Uint8Buffer;
+
+  Status = CpuIoCheckParameter (TRUE, Width, Address, Count, Buffer);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Select loop based on the width of the transfer
+  //
+  InStride = mInStride[Width];
+  OutStride = mOutStride[Width];
+  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);
+  for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {
+    if (OperationWidth == EfiCpuIoWidthUint8) {
+      MmioWrite8 ((UINTN)Address, *Uint8Buffer);
+    } else if (OperationWidth == EfiCpuIoWidthUint16) {
+      MmioWrite16 ((UINTN)Address, *((UINT16 *)Uint8Buffer));
+    } else if (OperationWidth == EfiCpuIoWidthUint32) {
+      MmioWrite32 ((UINTN)Address, *((UINT32 *)Uint8Buffer));
+    } else if (OperationWidth == EfiCpuIoWidthUint64) {
+      MmioWrite64 ((UINTN)Address, *((UINT64 *)Uint8Buffer));
+    }
+  }
+  return EFI_SUCCESS;
+}
+
+/**
+  Reads I/O registers.
+
+  The I/O operations are carried out exactly as requested. The caller is responsible
+  for satisfying any alignment and I/O width restrictions that a PI System on a
+  platform might require. For example on some platforms, width requests of
+  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will
+  be handled by the driver.
+
+  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,
+  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for
+  each of the Count operations that is performed.
+
+  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,
+  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is
+  incremented for each of the Count operations that is performed. The read or
+  write operation is performed Count times on the same Address.
+
+  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,
+  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is
+  incremented for each of the Count operations that is performed. The read or
+  write operation is performed Count times from the first element of Buffer.
+
+  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.
+  @param[in]  Width    Signifies the width of the I/O or Memory operation.
+  @param[in]  Address  The base address of the I/O operation.
+  @param[in]  Count    The number of I/O operations to perform. The number of
+                       bytes moved is Width size * Count, starting at Address.
+  @param[out] Buffer   For read operations, the destination buffer to store the results.
+                       For write operations, the source buffer from which to write data.
+
+  @retval EFI_SUCCESS            The data was read from or written to the PI system.
+  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.
+  @retval EFI_INVALID_PARAMETER  Buffer is NULL.
+  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.
+  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,
+                                 and Count is not valid for this PI system.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuIoServiceRead (
+  IN  EFI_CPU_IO2_PROTOCOL       *This,
+  IN  EFI_CPU_IO_PROTOCOL_WIDTH  Width,
+  IN  UINT64                     Address,
+  IN  UINTN                      Count,
+  OUT VOID                       *Buffer
+  )
+{
+  EFI_STATUS                 Status;
+  UINT8                      InStride;
+  UINT8                      OutStride;
+  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;
+  UINT8                      *Uint8Buffer;
+
+  Status = CpuIoCheckParameter (FALSE, Width, Address, Count, Buffer);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  Address += PcdGet64 (PcdPciIoTranslation);
+
+  //
+  // Select loop based on the width of the transfer
+  //
+  InStride = mInStride[Width];
+  OutStride = mOutStride[Width];
+  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);
+
+  for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {
+    if (OperationWidth == EfiCpuIoWidthUint8) {
+      *Uint8Buffer = MmioRead8 ((UINTN)Address);
+    } else if (OperationWidth == EfiCpuIoWidthUint16) {
+      *((UINT16 *)Uint8Buffer) = MmioRead16 ((UINTN)Address);
+    } else if (OperationWidth == EfiCpuIoWidthUint32) {
+      *((UINT32 *)Uint8Buffer) = MmioRead32 ((UINTN)Address);
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Write I/O registers.
+
+  The I/O operations are carried out exactly as requested. The caller is responsible
+  for satisfying any alignment and I/O width restrictions that a PI System on a
+  platform might require. For example on some platforms, width requests of
+  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will
+  be handled by the driver.
+
+  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,
+  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for
+  each of the Count operations that is performed.
+
+  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,
+  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is
+  incremented for each of the Count operations that is performed. The read or
+  write operation is performed Count times on the same Address.
+
+  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,
+  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is
+  incremented for each of the Count operations that is performed. The read or
+  write operation is performed Count times from the first element of Buffer.
+
+  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.
+  @param[in]  Width    Signifies the width of the I/O or Memory operation.
+  @param[in]  Address  The base address of the I/O operation.
+  @param[in]  Count    The number of I/O operations to perform. The number of
+                       bytes moved is Width size * Count, starting at Address.
+  @param[in]  Buffer   For read operations, the destination buffer to store the results.
+                       For write operations, the source buffer from which to write data.
+
+  @retval EFI_SUCCESS            The data was read from or written to the PI system.
+  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.
+  @retval EFI_INVALID_PARAMETER  Buffer is NULL.
+  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.
+  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,
+                                 and Count is not valid for this PI system.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuIoServiceWrite (
+  IN EFI_CPU_IO2_PROTOCOL       *This,
+  IN EFI_CPU_IO_PROTOCOL_WIDTH  Width,
+  IN UINT64                     Address,
+  IN UINTN                      Count,
+  IN VOID                       *Buffer
+  )
+{
+  EFI_STATUS                 Status;
+  UINT8                      InStride;
+  UINT8                      OutStride;
+  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;
+  UINT8                      *Uint8Buffer;
+
+  //
+  // Make sure the parameters are valid
+  //
+  Status = CpuIoCheckParameter (FALSE, Width, Address, Count, Buffer);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Address += PcdGet64 (PcdPciIoTranslation);
+
+  //
+  // Select loop based on the width of the transfer
+  //
+  InStride = mInStride[Width];
+  OutStride = mOutStride[Width];
+  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);
+
+  for (Uint8Buffer = (UINT8 *)Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {
+    if (OperationWidth == EfiCpuIoWidthUint8) {
+      MmioWrite8 ((UINTN)Address, *Uint8Buffer);
+    } else if (OperationWidth == EfiCpuIoWidthUint16) {
+      MmioWrite16 ((UINTN)Address, *((UINT16 *)Uint8Buffer));
+    } else if (OperationWidth == EfiCpuIoWidthUint32) {
+      MmioWrite32 ((UINTN)Address, *((UINT32 *)Uint8Buffer));
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+//
+// CPU I/O 2 Protocol instance
+//
+STATIC EFI_CPU_IO2_PROTOCOL mCpuIo2 = {
+  {
+    CpuMemoryServiceRead,
+    CpuMemoryServiceWrite
+  },
+  {
+    CpuIoServiceRead,
+    CpuIoServiceWrite
+  }
+};
+
+
+/**
+  The user Entry Point for module CpuIo2Dxe. The user code starts with this function.
+
+  @param[in] ImageHandle    The firmware allocated handle for the EFI image.
+  @param[in] SystemTable    A pointer to the EFI System Table.
+
+  @retval EFI_SUCCESS       The entry point is executed successfully.
+  @retval other             Some error occurs when executing this entry point.
+
+**/
+EFI_STATUS
+EFIAPI
+PciCpuIo2Initialize (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+{
+  EFI_STATUS Status;
+
+  ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiCpuIo2ProtocolGuid);
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &mHandle,
+                  &gEfiCpuIo2ProtocolGuid, &mCpuIo2,
+                  NULL
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/PciCpuIo2Dxe/PciCpuIo2Dxe.h b/Platform/Loongson/LoongArchQemuPkg/Drivers/PciCpuIo2Dxe/PciCpuIo2Dxe.h
new file mode 100644
index 0000000000..a3f8cf7cbb
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/PciCpuIo2Dxe/PciCpuIo2Dxe.h
@@ -0,0 +1,219 @@
+/** @file
+  Internal include file for the CPU I/O 2 Protocol.
+
+Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef PCI_CPU_IO2_DXE_H_
+#define PCI_CPU_IO2_DXE_H_
+
+//#include <PiDxe.h>
+//
+//#include <Protocol/CpuIo2.h>
+//
+//#include <Library/BaseLib.h>
+//#include <Library/DebugLib.h>
+//#include <Library/IoLib.h>
+//#include <Library/UefiBootServicesTableLib.h>
+
+#define MAX_IO_PORT_ADDRESS   0xFFFF
+
+/**
+  Reads memory-mapped registers.
+
+  The I/O operations are carried out exactly as requested. The caller is responsible
+  for satisfying any alignment and I/O width restrictions that a PI System on a
+  platform might require. For example on some platforms, width requests of
+  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will
+  be handled by the driver.
+
+  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,
+  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for
+  each of the Count operations that is performed.
+
+  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,
+  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is
+  incremented for each of the Count operations that is performed. The read or
+  write operation is performed Count times on the same Address.
+
+  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,
+  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is
+  incremented for each of the Count operations that is performed. The read or
+  write operation is performed Count times from the first element of Buffer.
+
+  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.
+  @param[in]  Width    Signifies the width of the I/O or Memory operation.
+  @param[in]  Address  The base address of the I/O operation.
+  @param[in]  Count    The number of I/O operations to perform. The number of
+                       bytes moved is Width size * Count, starting at Address.
+  @param[out] Buffer   For read operations, the destination buffer to store the results.
+                       For write operations, the source buffer from which to write data.
+
+  @retval EFI_SUCCESS            The data was read from or written to the PI system.
+  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.
+  @retval EFI_INVALID_PARAMETER  Buffer is NULL.
+  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.
+  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,
+                                 and Count is not valid for this PI system.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuMemoryServiceRead (
+  IN  EFI_CPU_IO2_PROTOCOL       *This,
+  IN  EFI_CPU_IO_PROTOCOL_WIDTH  Width,
+  IN  UINT64                     Address,
+  IN  UINTN                      Count,
+  OUT VOID                       *Buffer
+  );
+
+/**
+  Writes memory-mapped registers.
+
+  The I/O operations are carried out exactly as requested. The caller is responsible
+  for satisfying any alignment and I/O width restrictions that a PI System on a
+  platform might require. For example on some platforms, width requests of
+  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will
+  be handled by the driver.
+
+  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,
+  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for
+  each of the Count operations that is performed.
+
+  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,
+  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is
+  incremented for each of the Count operations that is performed. The read or
+  write operation is performed Count times on the same Address.
+
+  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,
+  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is
+  incremented for each of the Count operations that is performed. The read or
+  write operation is performed Count times from the first element of Buffer.
+
+  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.
+  @param[in]  Width    Signifies the width of the I/O or Memory operation.
+  @param[in]  Address  The base address of the I/O operation.
+  @param[in]  Count    The number of I/O operations to perform. The number of
+                       bytes moved is Width size * Count, starting at Address.
+  @param[in]  Buffer   For read operations, the destination buffer to store the results.
+                       For write operations, the source buffer from which to write data.
+
+  @retval EFI_SUCCESS            The data was read from or written to the PI system.
+  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.
+  @retval EFI_INVALID_PARAMETER  Buffer is NULL.
+  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.
+  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,
+                                 and Count is not valid for this PI system.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuMemoryServiceWrite (
+  IN EFI_CPU_IO2_PROTOCOL       *This,
+  IN EFI_CPU_IO_PROTOCOL_WIDTH  Width,
+  IN UINT64                     Address,
+  IN UINTN                      Count,
+  IN VOID                       *Buffer
+  );
+
+/**
+  Reads I/O registers.
+
+  The I/O operations are carried out exactly as requested. The caller is responsible
+  for satisfying any alignment and I/O width restrictions that a PI System on a
+  platform might require. For example on some platforms, width requests of
+  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will
+  be handled by the driver.
+
+  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,
+  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for
+  each of the Count operations that is performed.
+
+  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,
+  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is
+  incremented for each of the Count operations that is performed. The read or
+  write operation is performed Count times on the same Address.
+
+  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,
+  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is
+  incremented for each of the Count operations that is performed. The read or
+  write operation is performed Count times from the first element of Buffer.
+
+  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.
+  @param[in]  Width    Signifies the width of the I/O or Memory operation.
+  @param[in]  Address  The base address of the I/O operation.
+  @param[in]  Count    The number of I/O operations to perform. The number of
+                       bytes moved is Width size * Count, starting at Address.
+  @param[out] Buffer   For read operations, the destination buffer to store the results.
+                       For write operations, the source buffer from which to write data.
+
+  @retval EFI_SUCCESS            The data was read from or written to the PI system.
+  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.
+  @retval EFI_INVALID_PARAMETER  Buffer is NULL.
+  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.
+  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,
+                                 and Count is not valid for this PI system.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuIoServiceRead (
+  IN  EFI_CPU_IO2_PROTOCOL       *This,
+  IN  EFI_CPU_IO_PROTOCOL_WIDTH  Width,
+  IN  UINT64                     Address,
+  IN  UINTN                      Count,
+  OUT VOID                       *Buffer
+  );
+
+/**
+  Write I/O registers.
+
+  The I/O operations are carried out exactly as requested. The caller is responsible
+  for satisfying any alignment and I/O width restrictions that a PI System on a
+  platform might require. For example on some platforms, width requests of
+  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will
+  be handled by the driver.
+
+  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,
+  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for
+  each of the Count operations that is performed.
+
+  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,
+  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is
+  incremented for each of the Count operations that is performed. The read or
+  write operation is performed Count times on the same Address.
+
+  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,
+  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is
+  incremented for each of the Count operations that is performed. The read or
+  write operation is performed Count times from the first element of Buffer.
+
+  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.
+  @param[in]  Width    Signifies the width of the I/O or Memory operation.
+  @param[in]  Address  The base address of the I/O operation.
+  @param[in]  Count    The number of I/O operations to perform. The number of
+                       bytes moved is Width size * Count, starting at Address.
+  @param[in]  Buffer   For read operations, the destination buffer to store the results.
+                       For write operations, the source buffer from which to write data.
+
+  @retval EFI_SUCCESS            The data was read from or written to the PI system.
+  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.
+  @retval EFI_INVALID_PARAMETER  Buffer is NULL.
+  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.
+  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,
+                                 and Count is not valid for this PI system.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuIoServiceWrite (
+  IN EFI_CPU_IO2_PROTOCOL       *This,
+  IN EFI_CPU_IO_PROTOCOL_WIDTH  Width,
+  IN UINT64                     Address,
+  IN UINTN                      Count,
+  IN VOID                       *Buffer
+  );
+
+#endif
diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/PciCpuIo2Dxe/PciCpuIo2Dxe.inf b/Platform/Loongson/LoongArchQemuPkg/Drivers/PciCpuIo2Dxe/PciCpuIo2Dxe.inf
new file mode 100644
index 0000000000..9fb7160ae7
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/PciCpuIo2Dxe/PciCpuIo2Dxe.inf
@@ -0,0 +1,40 @@
+## @file
+#  Produces the CPU I/O 2 Protocol by using the services of the I/O Library.
+#
+#  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = PciCpuIo2Dxe
+  FILE_GUID                      = 168D1A6E-F4A5-448A-9E95-795661BB3067
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = PciCpuIo2Initialize
+
+[Sources]
+  PciCpuIo2Dxe.c
+
+[Packages]
+  Platform/Loongson/LoongArchQemuPkg/Loongson.dec
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  UefiDriverEntryPoint
+  BaseLib
+  DebugLib
+  IoLib
+  PcdLib
+  UefiBootServicesTableLib
+
+[Pcd]
+  gEfiMdePkgTokenSpaceGuid.PcdPciIoTranslation
+
+[Protocols]
+  gEfiCpuIo2ProtocolGuid                         ## PRODUCES
+
+[Depex]
+  TRUE
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [edk2-platforms][PATCH V3 10/16] Platform/Loongson: Add timer Dxe driver.
  2022-09-29  7:07 [edk2-platforms][PATCH V3 00/16] Platform: Add Loongson support xianglai
                   ` (8 preceding siblings ...)
  2022-09-29  7:07 ` [edk2-platforms][PATCH V3 09/16] Platform/Loongson: Add PciCpuIoDxe driver xianglai
@ 2022-09-29  7:07 ` xianglai
  2022-09-29  7:07 ` [edk2-platforms][PATCH V3 11/16] Platform/Loongson: Add RealTime Clock lib xianglai
                   ` (5 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: xianglai @ 2022-09-29  7:07 UTC (permalink / raw)
  To: devel; +Cc: quic_llindhol, michael.d.kinney, kraxel, ardb, maobibo

This driver produces Timer Architectural Protocol,
Registers a timer interrupt and initializes the timer.

REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4054

Signed-off-by: xianglai li <lixianglai@loongson.cn>
---
 .../Drivers/StableTimerDxe/Timer.c            | 405 ++++++++++++++++++
 .../Drivers/StableTimerDxe/Timer.h            | 165 +++++++
 .../Drivers/StableTimerDxe/TimerDxe.inf       |  40 ++
 3 files changed, 610 insertions(+)
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/StableTimerDxe/Timer.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/StableTimerDxe/Timer.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/StableTimerDxe/TimerDxe.inf

diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/StableTimerDxe/Timer.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/StableTimerDxe/Timer.c
new file mode 100644
index 0000000000..68742551ec
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/StableTimerDxe/Timer.c
@@ -0,0 +1,405 @@
+/** @file
+  Timer Architectural Protocol as defined in the DXE CIS
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Protocol/Cpu.h>
+#include "Library/Cpu.h"
+#include <Library/DebugLib.h>
+#include <Library/StableTimer.h>
+#include "Timer.h"
+#include <Library/TimerLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+//
+// The handle onto which the Timer Architectural Protocol will be installed
+//
+EFI_HANDLE            mTimerHandle = NULL;
+EFI_EVENT             EfiExitBootServicesEvent = (EFI_EVENT)NULL;
+
+//
+// The Timer Architectural Protocol that this driver produces
+//
+EFI_TIMER_ARCH_PROTOCOL   mTimer = {
+  TimerDriverRegisterHandler,
+  TimerDriverSetTimerPeriod,
+  TimerDriverGetTimerPeriod,
+  TimerDriverGenerateSoftInterrupt
+};
+
+//
+// Pointer to the CPU Architectural Protocol instance
+//
+EFI_CPU_ARCH_PROTOCOL     *mCpu;
+
+//
+// The notification function to call on every timer interrupt.
+// A bug in the compiler prevents us from initializing this here.
+//
+EFI_TIMER_NOTIFY mTimerNotifyFunction;
+
+//
+// The current period of the timer interrupt
+//
+volatile UINT64           mTimerPeriod = 0;
+volatile UINT64           mTimerTicks = 0;
+
+//
+// Const frequence in Hz
+//
+extern UINT32 StableTimerFreq;
+
+/**
+  Sets the counter value for timer.
+
+  @param Count    The 16-bit counter value to program into stable timer.
+
+  @retval VOID
+**/
+VOID
+SetPitCount (
+  IN UINT64  Count
+  )
+{
+  if (Count <= 4) {
+    return;
+  }
+
+  Count &= LOONGARCH_CSR_TMCFG_TIMEVAL;
+  Count |= LOONGARCH_CSR_TMCFG_EN | LOONGARCH_CSR_TMCFG_PERIOD;
+  LOONGARCH_CSR_WRITEQ (Count, LOONGARCH_CSR_TMCFG);
+}
+
+/**
+  Timer Interrupt Handler.
+
+  @param InterruptType    The type of interrupt that occurred
+  @param SystemContext    A pointer to the system context when the interrupt occurred
+
+  @retval VOID
+**/
+VOID
+EFIAPI
+TimerInterruptHandler (
+  IN EFI_EXCEPTION_TYPE   InterruptType,
+  IN EFI_SYSTEM_CONTEXT   SystemContext
+  )
+{
+  EFI_TPL OriginalTPL;
+
+  OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL);
+
+  //
+  // Clear interrupt.
+  //
+  LOONGARCH_CSR_WRITEQ (0x1, LOONGARCH_CSR_TINTCLR);
+
+  if (mTimerNotifyFunction != NULL) {
+    //
+    // @bug : This does not handle missed timer interrupts
+    //
+    mTimerNotifyFunction (mTimerPeriod);
+  }
+
+  gBS->RestoreTPL (OriginalTPL);
+}
+
+/**
+
+  This function registers the handler NotifyFunction so it is called every time
+  the timer interrupt fires.  It also passes the amount of time since the last
+  handler call to the NotifyFunction.  If NotifyFunction is NULL, then the
+  handler is unregistered.  If the handler is registered, then EFI_SUCCESS is
+  returned.  If the CPU does not support registering a timer interrupt handler,
+  then EFI_UNSUPPORTED is returned.  If an attempt is made to register a handler
+  when a handler is already registered, then EFI_ALREADY_STARTED is returned.
+  If an attempt is made to unregister a handler when a handler is not registered,
+  then EFI_INVALID_PARAMETER is returned.  If an error occurs attempting to
+  register the NotifyFunction with the timer interrupt, then EFI_DEVICE_ERROR
+  is returned.
+
+
+  @param This             The EFI_TIMER_ARCH_PROTOCOL instance.
+  @param NotifyFunction   The function to call when a timer interrupt fires.  This
+                          function executes at TPL_HIGH_LEVEL.  The DXE Core will
+                          register a handler for the timer interrupt, so it can know
+                          how much time has passed.  This information is used to
+                          signal timer based events.  NULL will unregister the handler.
+
+  @retval        EFI_SUCCESS            The timer handler was registered.
+  @retval        EFI_UNSUPPORTED        The platform does not support timer interrupts.
+  @retval        EFI_ALREADY_STARTED    NotifyFunction is not NULL, and a handler is already
+                                        registered.
+  @retval        EFI_INVALID_PARAMETER  NotifyFunction is NULL, and a handler was not
+                                        previously registered.
+  @retval        EFI_DEVICE_ERROR       The timer handler could not be registered.
+
+**/
+EFI_STATUS
+EFIAPI
+TimerDriverRegisterHandler (
+  IN EFI_TIMER_ARCH_PROTOCOL  *This,
+  IN EFI_TIMER_NOTIFY         NotifyFunction
+  )
+{
+  //
+  // Check for invalid parameters
+  //
+  if ((NotifyFunction == NULL)
+    && (mTimerNotifyFunction == NULL))
+  {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if ((NotifyFunction != NULL)
+    && mTimerNotifyFunction != NULL)
+  {
+    return EFI_ALREADY_STARTED;
+  }
+
+  mTimerNotifyFunction = NotifyFunction;
+
+  return EFI_SUCCESS;
+}
+
+/**
+
+  This function adjusts the period of timer interrupts to the value specified
+  by TimerPeriod.  If the timer period is updated, then the selected timer
+  period is stored in EFI_TIMER.TimerPeriod, and EFI_SUCCESS is returned.  If
+  the timer hardware is not programmable, then EFI_UNSUPPORTED is returned.
+  If an error occurs while attempting to update the timer period, then the
+  timer hardware will be put back in its state prior to this call, and
+  EFI_DEVICE_ERROR is returned.  If TimerPeriod is 0, then the timer interrupt
+  is disabled.  This is not the same as disabling the CPU's interrupts.
+  Instead, it must either turn off the timer hardware, or it must adjust the
+  interrupt controller so that a CPU interrupt is not generated when the timer
+  interrupt fires.
+
+
+  @param This            The EFI_TIMER_ARCH_PROTOCOL instance.
+  @param TimerPeriod     The rate to program the timer interrupt in 100 nS units.  If
+                         the timer hardware is not programmable, then EFI_UNSUPPORTED is
+                         returned.  If the timer is programmable, then the timer period
+                         will be rounded up to the nearest timer period that is supported
+                         by the timer hardware.  If TimerPeriod is set to 0, then the
+                         timer interrupts will be disabled.
+
+  @retval        EFI_SUCCESS       The timer period was changed.
+  @retval        EFI_UNSUPPORTED   The platform cannot change the period of the timer interrupt.
+  @retval        EFI_DEVICE_ERROR  The timer period could not be changed due to a device error.
+
+**/
+EFI_STATUS
+EFIAPI
+TimerDriverSetTimerPeriod (
+  IN EFI_TIMER_ARCH_PROTOCOL  *This,
+  IN UINT64                   TimerPeriod
+  )
+{
+  UINT64  TimerCount;
+
+  if (TimerPeriod == 0) {
+    //
+    // Disable timer interrupt for a TimerPeriod of 0
+    //
+    mCpu->DisableInterrupt (mCpu);
+  } else {
+
+    TimerCount = TimerPeriod * StableTimerFreq / 10000000ULL;
+
+    if (TimerCount >= BIT48) {
+      TimerCount = 0;
+    }
+
+    //
+    // Program the stable timer with the new count value
+    //
+    mTimerTicks = TimerCount;
+    SetPitCount (TimerCount);
+
+    //
+    // Enable timer interrupt
+    //
+    mCpu->EnableInterrupt (mCpu);
+  }
+
+  //
+  // Save the new timer period
+  //
+  mTimerPeriod = TimerPeriod;
+
+  return EFI_SUCCESS;
+}
+
+/**
+
+  This function retrieves the period of timer interrupts in 100 ns units,
+  returns that value in TimerPeriod, and returns EFI_SUCCESS.  If TimerPeriod
+  is NULL, then EFI_INVALID_PARAMETER is returned.  If a TimerPeriod of 0 is
+  returned, then the timer is currently disabled.
+
+
+  @param This            The EFI_TIMER_ARCH_PROTOCOL instance.
+  @param TimerPeriod     A pointer to the timer period to retrieve in 100 ns units.  If
+                         0 is returned, then the timer is currently disabled.
+
+  @retval EFI_SUCCESS            The timer period was returned in TimerPeriod.
+  @retval EFI_INVALID_PARAMETER  TimerPeriod is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+TimerDriverGetTimerPeriod (
+  IN EFI_TIMER_ARCH_PROTOCOL   *This,
+  OUT UINT64                   *TimerPeriod
+  )
+{
+  if (TimerPeriod == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *TimerPeriod = mTimerPeriod;
+
+  return EFI_SUCCESS;
+}
+
+/**
+    Disable the timer
+**/
+
+/**
+  Disable the timer
+  DXE Core will disable the timer after all the event handlers have run.
+
+  @param[in]  Event   The Event that is being processed
+  @param[in]  Context Event Context
+**/
+VOID
+EFIAPI
+ExitBootServicesEvent (
+  IN EFI_EVENT  Event,
+  IN VOID       *Context
+  )
+{
+  /*
+   * Disable timer interrupt when exiting boot service
+   */
+  LOONGARCH_CSR_WRITEQ (0, LOONGARCH_CSR_TMCFG);
+}
+
+/**
+
+  This function generates a soft timer interrupt. If the platform does not support soft
+  timer interrupts, then EFI_UNSUPPORTED is returned. Otherwise, EFI_SUCCESS is returned.
+  If a handler has been registered through the EFI_TIMER_ARCH_PROTOCOL.RegisterHandler ()
+  service, then a soft timer interrupt will be generated. If the timer interrupt is
+  enabled when this service is called, then the registered handler will be invoked. The
+  registered handler should not be able to distinguish a hardware-generated timer
+  interrupt from a software-generated timer interrupt.
+
+
+  @param This              The EFI_TIMER_ARCH_PROTOCOL instance.
+
+  @retval EFI_SUCCESS       The soft timer interrupt was generated.
+  @retval EFI_UNSUPPORTED   The platform does not support the generation of soft timer interrupts.
+
+**/
+EFI_STATUS
+EFIAPI
+TimerDriverGenerateSoftInterrupt (
+  IN EFI_TIMER_ARCH_PROTOCOL  *This
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+/**
+  Initialize the Timer Architectural Protocol driver
+
+  @param ImageHandle     ImageHandle of the loaded driver
+  @param SystemTable     Pointer to the System Table
+
+  @retval EFI_SUCCESS            Timer Architectural Protocol created
+  @retval EFI_OUT_OF_RESOURCES   Not enough resources available to initialize driver.
+  @retval EFI_DEVICE_ERROR       A device error occurred attempting to initialize the driver.
+
+**/
+EFI_STATUS
+EFIAPI
+StableTimerDriverInitialize (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      TimerVector;
+
+  //
+  // Initialize the pointer to our notify function.
+  //
+  mTimerNotifyFunction = NULL;
+
+  //
+  // Make sure the Timer Architectural Protocol is not already installed in the system
+  //
+  ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiTimerArchProtocolGuid);
+
+  //
+  // Find the CPU architectural protocol.
+  //
+  Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **) &mCpu);
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Force the timer to be disabled
+  //
+  Status = TimerDriverSetTimerPeriod (&mTimer, 0);
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Calculate const frequence
+  //
+  StableTimerFreq = CalcConstFreq ();
+  DEBUG ((DEBUG_INFO, "===========Stable timer freq %d Hz=============\n", StableTimerFreq));
+
+  //
+  // Install interrupt handler for Stable Timer #0 (ISA IRQ0)
+  //
+  TimerVector = EXCEPT_LOONGARCH_INT_TIMER;
+  Status = mCpu->RegisterInterruptHandler (mCpu, TimerVector, TimerInterruptHandler);
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Enable TI local timer interrupt
+  //
+  CpuSetIP (1 << 11);
+
+  //
+  // Force the timer to be enabled at its default period
+  //
+  Status = TimerDriverSetTimerPeriod (&mTimer, DEFAULT_TIMER_TICK_DURATION);
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Install the Timer Architectural Protocol onto a new handle
+  //
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &mTimerHandle,
+                  &gEfiTimerArchProtocolGuid, &mTimer,
+                  NULL
+                  );
+
+  ASSERT_EFI_ERROR (Status);
+
+  // Register for an ExitBootServicesEvent
+  Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_NOTIFY, ExitBootServicesEvent, NULL,
+                  &EfiExitBootServicesEvent);
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/StableTimerDxe/Timer.h b/Platform/Loongson/LoongArchQemuPkg/Drivers/StableTimerDxe/Timer.h
new file mode 100644
index 0000000000..7fe5c9c179
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/StableTimerDxe/Timer.h
@@ -0,0 +1,165 @@
+/** @file
+  Private data structures
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef TIMER_H_
+#define TIMER_H_
+
+#include <Protocol/Timer.h>
+
+
+#define DEFAULT_TIMER_TICK_DURATION 100000 //10ms = 100000 100 ns units
+#define SR_IP7                      (1 << 15)
+//
+// Function Prototypes
+//
+extern UINT32 EFIAPI CpuGetCompare(VOID);
+extern VOID  EFIAPI CpuSetCompare(IN UINT32 val);
+extern VOID  EFIAPI CpuSetIP(IN UINT32 val);
+extern VOID  EFIAPI ClearC0Cause(IN UINT32 val);
+extern VOID  EFIAPI ClearC0Status(IN UINT32 val);
+/**
+  Initialize the Timer Architectural Protocol driver
+
+  @param ImageHandle     ImageHandle of the loaded driver
+  @param SystemTable     Pointer to the System Table
+
+  @retval EFI_SUCCESS            Timer Architectural Protocol created
+  @retval EFI_OUT_OF_RESOURCES   Not enough resources available to initialize driver.
+  @retval EFI_DEVICE_ERROR       A device error occurred attempting to initialize the driver.
+
+**/
+EFI_STATUS
+EFIAPI
+TimerDriverInitialize (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+;
+
+/**
+
+  This function adjusts the period of timer interrupts to the value specified
+  by TimerPeriod.  If the timer period is updated, then the selected timer
+  period is stored in EFI_TIMER.TimerPeriod, and EFI_SUCCESS is returned.  If
+  the timer hardware is not programmable, then EFI_UNSUPPORTED is returned.
+  If an error occurs while attempting to update the timer period, then the
+  timer hardware will be put back in its state prior to this call, and
+  EFI_DEVICE_ERROR is returned.  If TimerPeriod is 0, then the timer interrupt
+  is disabled.  This is not the same as disabling the CPU's interrupts.
+  Instead, it must either turn off the timer hardware, or it must adjust the
+  interrupt controller so that a CPU interrupt is not generated when the timer
+  interrupt fires.
+
+
+  @param This            The EFI_TIMER_ARCH_PROTOCOL instance.
+  @param NotifyFunction  The rate to program the timer interrupt in 100 nS units.  If
+                         the timer hardware is not programmable, then EFI_UNSUPPORTED is
+                         returned.  If the timer is programmable, then the timer period
+                         will be rounded up to the nearest timer period that is supported
+                         by the timer hardware.  If TimerPeriod is set to 0, then the
+                         timer interrupts will be disabled.
+
+  @retval        EFI_SUCCESS       The timer period was changed.
+  @retval        EFI_UNSUPPORTED   The platform cannot change the period of the timer interrupt.
+  @retval        EFI_DEVICE_ERROR  The timer period could not be changed due to a device error.
+
+**/
+EFI_STATUS
+EFIAPI
+TimerDriverRegisterHandler (
+  IN EFI_TIMER_ARCH_PROTOCOL  *This,
+  IN EFI_TIMER_NOTIFY         NotifyFunction
+  )
+;
+
+/**
+
+  This function adjusts the period of timer interrupts to the value specified
+  by TimerPeriod.  If the timer period is updated, then the selected timer
+  period is stored in EFI_TIMER.TimerPeriod, and EFI_SUCCESS is returned.  If
+  the timer hardware is not programmable, then EFI_UNSUPPORTED is returned.
+  If an error occurs while attempting to update the timer period, then the
+  timer hardware will be put back in its state prior to this call, and
+  EFI_DEVICE_ERROR is returned.  If TimerPeriod is 0, then the timer interrupt
+  is disabled.  This is not the same as disabling the CPU's interrupts.
+  Instead, it must either turn off the timer hardware, or it must adjust the
+  interrupt controller so that a CPU interrupt is not generated when the timer
+  interrupt fires.
+
+
+  @param This            The EFI_TIMER_ARCH_PROTOCOL instance.
+  @param TimerPeriod     The rate to program the timer interrupt in 100 nS units.  If
+                         the timer hardware is not programmable, then EFI_UNSUPPORTED is
+                         returned.  If the timer is programmable, then the timer period
+                         will be rounded up to the nearest timer period that is supported
+                         by the timer hardware.  If TimerPeriod is set to 0, then the
+                         timer interrupts will be disabled.
+
+  @retval        EFI_SUCCESS       The timer period was changed.
+  @retval        EFI_UNSUPPORTED   The platform cannot change the period of the timer interrupt.
+  @retval        EFI_DEVICE_ERROR  The timer period could not be changed due to a device error.
+
+**/
+EFI_STATUS
+EFIAPI
+TimerDriverSetTimerPeriod (
+  IN EFI_TIMER_ARCH_PROTOCOL  *This,
+  IN UINT64                   TimerPeriod
+  )
+;
+
+/**
+
+  This function retrieves the period of timer interrupts in 100 ns units,
+  returns that value in TimerPeriod, and returns EFI_SUCCESS.  If TimerPeriod
+  is NULL, then EFI_INVALID_PARAMETER is returned.  If a TimerPeriod of 0 is
+  returned, then the timer is currently disabled.
+
+
+  @param This            The EFI_TIMER_ARCH_PROTOCOL instance.
+  @param TimerPeriod     A pointer to the timer period to retrieve in 100 ns units.  If
+                         0 is returned, then the timer is currently disabled.
+
+  @retval EFI_SUCCESS            The timer period was returned in TimerPeriod.
+  @retval EFI_INVALID_PARAMETER  TimerPeriod is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+TimerDriverGetTimerPeriod (
+  IN EFI_TIMER_ARCH_PROTOCOL   *This,
+  OUT UINT64                   *TimerPeriod
+  )
+;
+
+/**
+
+  This function generates a soft timer interrupt. If the platform does not support soft
+  timer interrupts, then EFI_UNSUPPORTED is returned. Otherwise, EFI_SUCCESS is returned.
+  If a handler has been registered through the EFI_TIMER_ARCH_PROTOCOL.RegisterHandler()
+  service, then a soft timer interrupt will be generated. If the timer interrupt is
+  enabled when this service is called, then the registered handler will be invoked. The
+  registered handler should not be able to distinguish a hardware-generated timer
+  interrupt from a software-generated timer interrupt.
+
+
+  @param This              The EFI_TIMER_ARCH_PROTOCOL instance.
+
+  @retval EFI_SUCCESS       The soft timer interrupt was generated.
+  @retval EFI_UNSUPPORTED   The platform does not support the generation of soft timer interrupts.
+
+**/
+EFI_STATUS
+EFIAPI
+TimerDriverGenerateSoftInterrupt (
+  IN EFI_TIMER_ARCH_PROTOCOL  *This
+  )
+;
+
+#endif
diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/StableTimerDxe/TimerDxe.inf b/Platform/Loongson/LoongArchQemuPkg/Drivers/StableTimerDxe/TimerDxe.inf
new file mode 100644
index 0000000000..10e6b9a85e
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/StableTimerDxe/TimerDxe.inf
@@ -0,0 +1,40 @@
+## @file
+#  Stable timer driver that provides Timer Arch protocol.
+#
+#  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = Timer
+  MODULE_UNI_FILE                = Timer.uni
+  FILE_GUID                      = AEBE2648-47A9-40FA-83FD-06AA88443BB2
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+
+  ENTRY_POINT                    = StableTimerDriverInitialize
+
+[Packages]
+  MdePkg/MdePkg.dec
+  Platform/Loongson/LoongArchQemuPkg/Loongson.dec
+
+[LibraryClasses]
+  UefiBootServicesTableLib
+  BaseLib
+  DebugLib
+  UefiDriverEntryPoint
+  IoLib
+  TimerLib
+
+[Sources]
+  Timer.h
+  Timer.c
+
+[Protocols]
+  gEfiCpuArchProtocolGuid       ## CONSUMES
+  gEfiTimerArchProtocolGuid     ## PRODUCES
+
+[depex]
+  TRUE
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [edk2-platforms][PATCH V3 11/16] Platform/Loongson: Add RealTime Clock lib.
  2022-09-29  7:07 [edk2-platforms][PATCH V3 00/16] Platform: Add Loongson support xianglai
                   ` (9 preceding siblings ...)
  2022-09-29  7:07 ` [edk2-platforms][PATCH V3 10/16] Platform/Loongson: Add timer Dxe driver xianglai
@ 2022-09-29  7:07 ` xianglai
  2022-09-29  7:08 ` [edk2-platforms][PATCH V3 12/16] Platform/Loongson: Add Platform Boot Manager Lib xianglai
                   ` (4 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: xianglai @ 2022-09-29  7:07 UTC (permalink / raw)
  To: devel; +Cc: quic_llindhol, michael.d.kinney, kraxel, ardb, maobibo

This library provides interfaces such as
real-time clock initialization
to get time and setting time.

REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4054

Signed-off-by: xianglai li <lixianglai@loongson.cn>
---
 .../LsRealTimeClockLib/LsRealTimeClock.h      |  41 +++
 .../LsRealTimeClockLib/LsRealTimeClockLib.c   | 343 ++++++++++++++++++
 .../LsRealTimeClockLib/LsRealTimeClockLib.inf |  41 +++
 3 files changed, 425 insertions(+)
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/LsRealTimeClockLib/LsRealTimeClock.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/LsRealTimeClockLib/LsRealTimeClockLib.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/LsRealTimeClockLib/LsRealTimeClockLib.inf

diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/LsRealTimeClockLib/LsRealTimeClock.h b/Platform/Loongson/LoongArchQemuPkg/Library/LsRealTimeClockLib/LsRealTimeClock.h
new file mode 100644
index 0000000000..c98881e5d0
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/LsRealTimeClockLib/LsRealTimeClock.h
@@ -0,0 +1,41 @@
+/** @file
+  Implement EFI RealTimeClock runtime services via RTC Lib.
+
+  Copyright (c) 2021, Loongson Limited. All rights reserved.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+
+#ifndef LS_REAL_TIME_CLOCK_H_
+#define LS_REAL_TIME_CLOCK_H_
+
+#define TOY_WRITE0_REG        0x24
+#define TOY_WRITE1_REG        0x28
+#define TOY_READ0_REG         0x2c
+#define TOY_READ1_REG         0x30
+#define RTC_CTRL_REG          0x40
+
+/* TOY Enable bits */
+#define RTC_ENABLE_BIT        (1UL << 13)
+#define TOY_ENABLE_BIT        (1UL << 11)
+#define OSC_ENABLE_BIT        (1UL << 8)
+
+/*
+ * shift bits and filed mask
+ */
+#define TOY_MON_MASK          0x3f
+#define TOY_DAY_MASK          0x1f
+#define TOY_HOUR_MASK         0x1f
+#define TOY_MIN_MASK          0x3f
+#define TOY_SEC_MASK          0x3f
+#define TOY_MSEC_MASK         0xf
+
+#define TOY_MON_SHIFT         26
+#define TOY_DAY_SHIFT         21
+#define TOY_HOUR_SHIFT        16
+#define TOY_MIN_SHIFT         10
+#define TOY_SEC_SHIFT         4
+
+#endif //__LS_REAL_TIME_CLOCK_H__
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/LsRealTimeClockLib/LsRealTimeClockLib.c b/Platform/Loongson/LoongArchQemuPkg/Library/LsRealTimeClockLib/LsRealTimeClockLib.c
new file mode 100644
index 0000000000..78420ddfab
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/LsRealTimeClockLib/LsRealTimeClockLib.c
@@ -0,0 +1,343 @@
+/** @file
+  Implement EFI RealTimeClock runtime services via RTC Lib.
+
+  Copyright (c) 2021, Loongson Limited. All rights reserved.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Uefi.h>
+#include <PiDxe.h>
+
+#include <Guid/EventGroup.h>
+#include <Guid/GlobalVariable.h>
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/IoLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PcdLib.h>
+#include <Library/RealTimeClockLib.h>
+#include <Library/TimeBaseLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiRuntimeLib.h>
+#include <Protocol/RealTimeClock.h>
+#include "LsRealTimeClock.h"
+
+STATIC BOOLEAN                mInitialized = FALSE;
+STATIC EFI_EVENT              mRtcVirtualAddrChangeEvent;
+STATIC UINTN                  mRtcBase = 0X100d0100;
+/*
+  Enable Real-time clock.
+
+  @param VOID
+
+  @retval  VOID
+ */
+VOID
+InitRtc (
+  VOID
+  )
+{
+ UINTN  Val;
+
+  if (!mInitialized) {
+    /* enable rtc */
+    Val  = MmioRead32 (mRtcBase + RTC_CTRL_REG);
+    Val |= TOY_ENABLE_BIT | OSC_ENABLE_BIT;
+    MmioWrite32 (mRtcBase + RTC_CTRL_REG, Val);
+    mInitialized = TRUE;
+  }
+}
+
+/**
+  Returns the current time and date information, and the time-keeping capabilities
+  of the hardware platform.
+
+  @param  Time                   A pointer to storage to receive a snapshot of the current time.
+  @param  Capabilities           An optional pointer to a buffer to receive the real time clock
+                                 device's capabilities.
+
+  @retval EFI_SUCCESS            The operation completed successfully.
+  @retval EFI_INVALID_PARAMETER  Time is NULL.
+  @retval EFI_DEVICE_ERROR       The time could not be retrieved due to hardware error.
+  @retval EFI_SECURITY_VIOLATION The time could not be retrieved due to an authentication failure.
+
+**/
+EFI_STATUS
+EFIAPI
+LibGetTime (
+  OUT EFI_TIME                *Time,
+  OUT EFI_TIME_CAPABILITIES   *Capabilities
+  )
+{
+  UINT32      Val;
+
+  // Ensure Time is a valid pointer
+  if (Time == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  InitRtc ();
+  Val = MmioRead32 (mRtcBase + TOY_READ1_REG);
+  Time->Year = Val + 1900;
+
+  Val = MmioRead32 (mRtcBase + TOY_READ0_REG);
+  Time->Month =  (Val >> TOY_MON_SHIFT) & TOY_MON_MASK;
+  Time->Day   =  (Val >> TOY_DAY_SHIFT) & TOY_DAY_MASK;
+  Time->Hour  =  (Val >> TOY_HOUR_SHIFT) & TOY_HOUR_MASK;
+  Time->Minute = (Val >> TOY_MIN_SHIFT) & TOY_MIN_MASK;
+  Time->Second = (Val >> TOY_SEC_SHIFT) & TOY_SEC_MASK;
+  Time->Nanosecond  = 0;
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Sets the current local time and date information.
+
+  @param  Time                  A pointer to the current time.
+
+  @retval EFI_SUCCESS           The operation completed successfully.
+  @retval EFI_INVALID_PARAMETER A time field is out of range.
+  @retval EFI_DEVICE_ERROR      The time could not be set due due to hardware error.
+
+**/
+EFI_STATUS
+EFIAPI
+LibSetTime (
+  IN  EFI_TIME                *Time
+  )
+{
+  UINT32      Val;
+
+  // Initialize the hardware if not already done
+  InitRtc ();
+
+  Val = 0;
+  Val |= (Time->Second << TOY_SEC_SHIFT);
+  Val |= (Time->Minute << TOY_MIN_SHIFT);
+  Val |= (Time->Hour   << TOY_HOUR_SHIFT);
+  Val |= (Time->Day    << TOY_DAY_SHIFT);
+  Val |= (Time->Month  << TOY_MON_SHIFT);
+  MmioWrite32 (mRtcBase + TOY_WRITE0_REG, Val);
+
+  Val = Time->Year - 1900;
+  MmioWrite32 (mRtcBase + TOY_WRITE1_REG, Val);
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Returns the current wakeup alarm clock setting.
+
+  @param  Enabled               Indicates if the alarm is currently enabled or disabled.
+  @param  Pending               Indicates if the alarm signal is pending and requires acknowledgement.
+  @param  Time                  The current alarm setting.
+
+  @retval EFI_SUCCESS           The alarm settings were returned.
+  @retval EFI_INVALID_PARAMETER Any parameter is NULL.
+  @retval EFI_DEVICE_ERROR      The wakeup time could not be retrieved due to a hardware error.
+
+**/
+EFI_STATUS
+EFIAPI
+LibGetWakeupTime (
+  OUT BOOLEAN     *Enabled,
+  OUT BOOLEAN     *Pending,
+  OUT EFI_TIME    *Time
+  )
+{
+  // Not a required feature
+  return EFI_UNSUPPORTED;
+}
+
+
+/**
+  Sets the system wakeup alarm clock time.
+
+  @param  Enabled               Enable or disable the wakeup alarm.
+  @param  Time                  If Enable is TRUE, the time to set the wakeup alarm for.
+
+  @retval EFI_SUCCESS           If Enable is TRUE, then the wakeup alarm was enabled. If
+                                Enable is FALSE, then the wakeup alarm was disabled.
+  @retval EFI_INVALID_PARAMETER A time field is out of range.
+  @retval EFI_DEVICE_ERROR      The wakeup time could not be set due to a hardware error.
+  @retval EFI_UNSUPPORTED       A wakeup timer is not supported on this platform.
+
+**/
+EFI_STATUS
+EFIAPI
+LibSetWakeupTime (
+  IN BOOLEAN      Enabled,
+  OUT EFI_TIME    *Time
+  )
+{
+  // Not a required feature
+  return EFI_UNSUPPORTED;
+}
+
+/**
+  Fixup internal data so that EFI can be call in virtual mode.
+  Call the passed in Child Notify event and convert any pointers in
+  lib to virtual mode.
+
+  @param[in]    Event   The Event that is being processed
+  @param[in]    Context Event Context
+**/
+VOID
+EFIAPI
+LibRtcVirtualNotifyEvent (
+  IN EFI_EVENT        Event,
+  IN VOID             *Context
+  )
+{
+  //
+  // Only needed if you are going to support the OS calling RTC functions in virtual mode.
+  // You will need to call EfiConvertPointer (). To convert any stored physical addresses
+  // to virtual address. After the OS transitions to calling in virtual mode, all future
+  // runtime calls will be made in virtual mode.
+  //
+  EfiConvertPointer (0x0, (VOID**)&mRtcBase);
+  return;
+}
+
+/** Add the RTC controller address range to the memory map.
+
+  @param [in]  ImageHandle  The handle to the image.
+  @param [in]  RtcPageBase  Base address of the RTC controller.
+
+  @retval EFI_SUCCESS             Success.
+  @retval EFI_INVALID_PARAMETER   A parameter is invalid.
+  @retval EFI_NOT_FOUND           Flash device not found.
+**/
+EFI_STATUS
+KvmtoolRtcMapMemory (
+  IN EFI_HANDLE               ImageHandle,
+  IN EFI_PHYSICAL_ADDRESS     RtcPageBase
+  )
+{
+  EFI_STATUS  Status;
+
+  Status = gDS->AddMemorySpace (
+                  EfiGcdMemoryTypeMemoryMappedIo,
+                  RtcPageBase,
+                  EFI_PAGE_SIZE,
+                  EFI_MEMORY_UC | EFI_MEMORY_RUNTIME
+                  );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((
+      DEBUG_ERROR, "Failed to add memory space. Status = %r\n",
+      Status
+      ));
+    return Status;
+  }
+
+  Status = gDS->AllocateMemorySpace (
+                  EfiGcdAllocateAddress,
+                  EfiGcdMemoryTypeMemoryMappedIo,
+                  0,
+                  EFI_PAGE_SIZE,
+                  &RtcPageBase,
+                  ImageHandle,
+                  NULL
+                  );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "Failed to allocate memory space. Status = %r\n",
+      Status
+      ));
+    gDS->RemoveMemorySpace (
+           RtcPageBase,
+           EFI_PAGE_SIZE
+           );
+    return Status;
+  }
+
+  Status = gDS->SetMemorySpaceAttributes (
+                  RtcPageBase,
+                  EFI_PAGE_SIZE,
+                  EFI_MEMORY_UC | EFI_MEMORY_RUNTIME
+                  );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "Failed to set memory attributes. Status = %r\n",
+      Status
+      ));
+
+    gDS->FreeMemorySpace (
+           RtcPageBase,
+           EFI_PAGE_SIZE
+           );
+
+    gDS->RemoveMemorySpace (
+           RtcPageBase,
+           EFI_PAGE_SIZE
+           );
+  }
+
+  return Status;
+}
+
+/**
+  This is the declaration of an EFI image entry point. This can be the entry point to an application
+  written to this specification, an EFI boot service driver, or an EFI runtime driver.
+
+  @param  ImageHandle           Handle that identifies the loaded image.
+  @param  SystemTable           System Table for this image.
+
+  @retval EFI_SUCCESS           The operation completed successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+LibRtcInitialize (
+  IN EFI_HANDLE                            ImageHandle,
+  IN EFI_SYSTEM_TABLE                      *SystemTable
+  )
+{
+  EFI_STATUS    Status;
+  EFI_HANDLE    Handle;
+
+  Status = KvmtoolRtcMapMemory (ImageHandle, (mRtcBase & ~EFI_PAGE_MASK));
+  if (EFI_ERROR (Status)) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "Failed to map memory for loongson 7A RTC. Status = %r\n",
+      Status
+      ));
+    return Status;
+  }
+
+  // Setup the setters and getters
+  gRT->GetTime       = LibGetTime;
+  gRT->SetTime       = LibSetTime;
+
+  // Install the protocol
+  Handle = NULL;
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &Handle,
+                  &gEfiRealTimeClockArchProtocolGuid,  NULL,
+                  NULL
+                 );
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Register for the virtual address change event
+  //
+  Status = gBS->CreateEventEx (
+                  EVT_NOTIFY_SIGNAL,
+                  TPL_NOTIFY,
+                  LibRtcVirtualNotifyEvent,
+                  NULL,
+                  &gEfiEventVirtualAddressChangeGuid,
+                  &mRtcVirtualAddrChangeEvent
+                  );
+  ASSERT_EFI_ERROR (Status);
+  return Status;
+}
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/LsRealTimeClockLib/LsRealTimeClockLib.inf b/Platform/Loongson/LoongArchQemuPkg/Library/LsRealTimeClockLib/LsRealTimeClockLib.inf
new file mode 100644
index 0000000000..c985f7a727
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/LsRealTimeClockLib/LsRealTimeClockLib.inf
@@ -0,0 +1,41 @@
+## @file
+#
+#  Copyright (c) 2021, Loongson Limited. All rights reserved.
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = LsRealTimeClockLib
+  FILE_GUID                      = 9793a3da-1869-4fdf-88b1-c6484341f50b
+  MODULE_TYPE                    = BASE
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = RealTimeClockLib
+
+[Sources.common]
+  LsRealTimeClockLib.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  EmbeddedPkg/EmbeddedPkg.dec
+  Platform/Loongson/LoongArchQemuPkg/Loongson.dec
+
+[LibraryClasses]
+  IoLib
+  UefiLib
+  DebugLib
+  PcdLib
+  DxeServicesTableLib
+  UefiRuntimeLib
+
+[Guids]
+  gEfiEventVirtualAddressChangeGuid
+
+[Protocols]
+  gEfiRealTimeClockArchProtocolGuid             # PROTOCOL ALWAYS_PRODUCED
+
+[Depex]
+  TRUE
+
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [edk2-platforms][PATCH V3 12/16] Platform/Loongson: Add Platform Boot Manager Lib.
  2022-09-29  7:07 [edk2-platforms][PATCH V3 00/16] Platform: Add Loongson support xianglai
                   ` (10 preceding siblings ...)
  2022-09-29  7:07 ` [edk2-platforms][PATCH V3 11/16] Platform/Loongson: Add RealTime Clock lib xianglai
@ 2022-09-29  7:08 ` xianglai
  2022-09-29  7:08 ` [edk2-platforms][PATCH V3 13/16] Platform/Loongson: Add Reset System Lib xianglai
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: xianglai @ 2022-09-29  7:08 UTC (permalink / raw)
  To: devel; +Cc: quic_llindhol, michael.d.kinney, kraxel, ardb, maobibo

The Library provides Boot Manager interfaces.

REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4054

Signed-off-by: xianglai li <lixianglai@loongson.cn>
---
 .../PlatformBootManagerLib/PlatformBm.c       | 760 ++++++++++++++++++
 .../PlatformBootManagerLib/PlatformBm.h       | 112 +++
 .../PlatformBootManagerLib.inf                |  78 ++
 .../PlatformBootManagerLib/QemuKernel.c       |  81 ++
 4 files changed, 1031 insertions(+)
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/PlatformBm.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/PlatformBm.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/QemuKernel.c

diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/PlatformBm.c b/Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/PlatformBm.c
new file mode 100644
index 0000000000..f58c43fa89
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/PlatformBm.c
@@ -0,0 +1,760 @@
+/** @file
+  Implementation for PlatformBootManagerLib library class interfaces.
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <IndustryStandard/Pci22.h>
+#include <Library/BootLogoLib.h>
+#include <Library/PcdLib.h>
+#include <Library/QemuBootOrderLib.h>
+#include <Library/UefiBootManagerLib.h>
+#include <Protocol/FirmwareVolume2.h>
+#include <Protocol/LoadedImage.h>
+#include <Protocol/PciIo.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseMemoryLib.h>
+#include "PlatformBm.h"
+
+STATIC PLATFORM_SERIAL_CONSOLE mSerialConsole = {
+    //
+    // VENDOR_DEVICE_PATH SerialDxe
+    //
+    {
+        { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, DP_NODE_LEN (VENDOR_DEVICE_PATH) },
+        SERIAL_DXE_FILE_GUID
+    },
+
+    //
+    // UART_DEVICE_PATH Uart
+    //
+    {
+        { MESSAGING_DEVICE_PATH, MSG_UART_DP, DP_NODE_LEN (UART_DEVICE_PATH) },
+    0,                                      // Reserved
+    FixedPcdGet64 (PcdUartDefaultBaudRate), // BaudRate
+    FixedPcdGet8 (PcdUartDefaultDataBits),  // DataBits
+    FixedPcdGet8 (PcdUartDefaultParity),    // Parity
+    FixedPcdGet8 (PcdUartDefaultStopBits)   // StopBits
+  },
+
+  //
+  // VENDOR_DEFINED_DEVICE_PATH TermType
+  //
+  {
+    {
+      MESSAGING_DEVICE_PATH, MSG_VENDOR_DP,
+      DP_NODE_LEN (VENDOR_DEFINED_DEVICE_PATH)
+    }
+    //
+    // Guid to be filled in dynamically
+    //
+  },
+
+  //
+  // EFI_DEVICE_PATH_PROTOCOL End
+  //
+  {
+    END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,
+    DP_NODE_LEN (EFI_DEVICE_PATH_PROTOCOL)
+  }
+};
+
+STATIC PLATFORM_USB_KEYBOARD mUsbKeyboard = {
+  //
+  // USB_CLASS_DEVICE_PATH Keyboard
+  //
+  {
+    {
+      MESSAGING_DEVICE_PATH, MSG_USB_CLASS_DP,
+      DP_NODE_LEN (USB_CLASS_DEVICE_PATH)
+    },
+    0xFFFF, // VendorId: any
+    0xFFFF, // ProductId: any
+    3,      // DeviceClass: HID
+    1,      // DeviceSubClass: boot
+    1       // DeviceProtocol: keyboard
+  },
+
+  //
+  // EFI_DEVICE_PATH_PROTOCOL End
+  //
+  {
+    END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,
+    DP_NODE_LEN (EFI_DEVICE_PATH_PROTOCOL)
+  }
+};
+
+/**
+  Locate all handles that carry the specified protocol, filter them with a
+  callback function, and pass each handle that passes the filter to another
+  callback.
+
+  @param[in] ProtocolGuid  The protocol to look for.
+
+  @param[in] Filter        The filter function to pass each handle to. If this
+                           parameter is NULL, then all handles are processed.
+
+  @param[in] Process       The callback function to pass each handle to that
+                           clears the filter.
+**/
+VOID
+FilterAndProcess (
+  IN EFI_GUID          *ProtocolGuid,
+  IN FILTER_FUNCTION   Filter         OPTIONAL,
+  IN CALLBACK_FUNCTION Process
+  )
+{
+  EFI_STATUS Status;
+  EFI_HANDLE *Handles;
+  UINTN      NoHandles;
+  UINTN      Idx;
+
+  Status = gBS->LocateHandleBuffer (ByProtocol, ProtocolGuid,
+                  NULL /* SearchKey */, &NoHandles, &Handles);
+  if (EFI_ERROR (Status)) {
+    //
+    // This is not an error, just an informative condition.
+    //
+    DEBUG ((DEBUG_VERBOSE, "%a: %g: %r\n", __FUNCTION__, ProtocolGuid,
+      Status));
+    return;
+  }
+
+  ASSERT (NoHandles > 0);
+  for (Idx = 0; Idx < NoHandles; ++Idx) {
+    CHAR16        *DevicePathText;
+    STATIC CHAR16 Fallback[] = L"<device path unavailable>";
+
+    //
+    // The ConvertDevicePathToText () function handles NULL input transparently.
+    //
+    DevicePathText = ConvertDevicePathToText (
+                       DevicePathFromHandle (Handles[Idx]),
+                       FALSE, // DisplayOnly
+                       FALSE  // AllowShortcuts
+                       );
+    if (DevicePathText == NULL) {
+      DevicePathText = Fallback;
+    }
+
+    if ((Filter == NULL)
+      || (Filter (Handles[Idx], DevicePathText)))
+    {
+      Process (Handles[Idx], DevicePathText);
+    }
+
+    if (DevicePathText != Fallback) {
+      FreePool (DevicePathText);
+    }
+  }
+  gBS->FreePool (Handles);
+}
+
+
+/**
+  This FILTER_FUNCTION checks if a handle corresponds to a PCI display device.
+
+  @param  Handle   The handle to check
+  @param  ReportText   A pointer to a string at the time of the error.
+
+  @retval    TURE     THe  handle corresponds to a PCI display device.
+  @retval    FALSE    THe  handle does not corresponds to a PCI display device.
+**/
+BOOLEAN
+EFIAPI
+IsPciDisplay (
+  IN EFI_HANDLE   Handle,
+  IN CONST CHAR16 *ReportText
+  )
+{
+  EFI_STATUS          Status;
+  EFI_PCI_IO_PROTOCOL *PciIo;
+  PCI_TYPE00          Pci;
+
+  Status = gBS->HandleProtocol (Handle, &gEfiPciIoProtocolGuid,
+                  (VOID**)&PciIo);
+  if (EFI_ERROR (Status)) {
+    //
+    // This is not an error worth reporting.
+    //
+    return FALSE;
+  }
+
+  Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, 0 /* Offset */,
+                        sizeof Pci / sizeof (UINT32), &Pci);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: %s: %r\n", __FUNCTION__, ReportText, Status));
+    return FALSE;
+  }
+
+  return IS_PCI_DISPLAY (&Pci);
+}
+
+
+/**
+  This CALLBACK_FUNCTION attempts to connect a handle non-recursively, asking
+  the matching driver to produce all first-level child handles.
+
+  @param  Handle   The handle to connect.
+  @param  ReportText   A pointer to a string at the time of the error.
+
+  @retval  VOID
+**/
+VOID
+EFIAPI
+Connect (
+  IN EFI_HANDLE   Handle,
+  IN CONST CHAR16 *ReportText
+  )
+{
+  EFI_STATUS Status;
+
+  Status = gBS->ConnectController (
+                  Handle, // ControllerHandle
+                  NULL,   // DriverImageHandle
+                  NULL,   // RemainingDevicePath -- produce all children
+                  FALSE   // Recursive
+                  );
+  DEBUG ((EFI_ERROR (Status) ? DEBUG_ERROR : DEBUG_VERBOSE, "%a: %s: %r\n",
+    __FUNCTION__, ReportText, Status));
+}
+
+
+/**
+  This CALLBACK_FUNCTION retrieves the EFI_DEVICE_PATH_PROTOCOL from the
+  handle, and adds it to ConOut and ErrOut.
+
+  @param  Handle   The handle to retrieves.
+  @param  ReportText   A pointer to a string at the time of the error.
+
+  @retval  VOID
+**/
+VOID
+EFIAPI
+AddOutput (
+  IN EFI_HANDLE   Handle,
+  IN CONST CHAR16 *ReportText
+  )
+{
+  EFI_STATUS               Status;
+  EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+
+  DevicePath = DevicePathFromHandle (Handle);
+  if (DevicePath == NULL) {
+    DEBUG ((DEBUG_ERROR, "%a: %s: handle %p: device path not found\n",
+      __FUNCTION__, ReportText, Handle));
+    return;
+  }
+
+  Status = EfiBootManagerUpdateConsoleVariable (ConOut, DevicePath, NULL);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: %s: adding to ConOut: %r\n", __FUNCTION__,
+      ReportText, Status));
+    return;
+  }
+
+  Status = EfiBootManagerUpdateConsoleVariable (ErrOut, DevicePath, NULL);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: %s: adding to ErrOut: %r\n", __FUNCTION__,
+      ReportText, Status));
+    return;
+  }
+
+  DEBUG ((DEBUG_VERBOSE, "%a: %s: added to ConOut and ErrOut\n", __FUNCTION__,
+    ReportText));
+}
+/**
+  Register the boot option.
+
+  @param  FileGuid      File Guid.
+  @param  Description   Option descriptor.
+  @param  Attributes    Option  Attributes.
+
+  @retval  VOID
+**/
+VOID
+PlatformRegisterFvBootOption (
+  IN EFI_GUID                         *FileGuid,
+  IN CHAR16                           *Description,
+  IN UINT32                           Attributes
+  )
+{
+  EFI_STATUS                        Status;
+  INTN                              OptionIndex;
+  EFI_BOOT_MANAGER_LOAD_OPTION      NewOption;
+  EFI_BOOT_MANAGER_LOAD_OPTION      *BootOptions;
+  UINTN                             BootOptionCount;
+  MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode;
+  EFI_LOADED_IMAGE_PROTOCOL         *LoadedImage;
+  EFI_DEVICE_PATH_PROTOCOL          *DevicePath;
+
+  Status = gBS->HandleProtocol (
+                  gImageHandle,
+                  &gEfiLoadedImageProtocolGuid,
+                  (VOID **) &LoadedImage
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  EfiInitializeFwVolDevicepathNode (&FileNode, FileGuid);
+  DevicePath = DevicePathFromHandle (LoadedImage->DeviceHandle);
+  ASSERT (DevicePath != NULL);
+  DevicePath = AppendDevicePathNode (
+                 DevicePath,
+                 (EFI_DEVICE_PATH_PROTOCOL *) &FileNode
+                 );
+  ASSERT (DevicePath != NULL);
+
+  Status = EfiBootManagerInitializeLoadOption (
+             &NewOption,
+             LoadOptionNumberUnassigned,
+             LoadOptionTypeBoot,
+             Attributes,
+             Description,
+             DevicePath,
+             NULL,
+             0
+             );
+  ASSERT_EFI_ERROR (Status);
+  FreePool (DevicePath);
+
+  BootOptions = EfiBootManagerGetLoadOptions (
+                  &BootOptionCount, LoadOptionTypeBoot
+                  );
+
+  OptionIndex = EfiBootManagerFindLoadOption (
+                  &NewOption, BootOptions, BootOptionCount
+                  );
+
+  if (OptionIndex == -1) {
+    Status = EfiBootManagerAddLoadOptionVariable (&NewOption, MAX_UINTN);
+    ASSERT_EFI_ERROR (Status);
+  }
+  EfiBootManagerFreeLoadOption (&NewOption);
+  EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
+}
+
+
+/**
+  Remove all MemoryMapped (...)/FvFile (...) and Fv (...)/FvFile (...) boot options
+  whose device paths do not resolve exactly to an FvFile in the system.
+
+  This removes any boot options that point to binaries built into the firmware
+  and have become stale due to any of the following:
+  - FvMain's base address or size changed (historical),
+  - FvMain's FvNameGuid changed,
+  - the FILE_GUID of the pointed-to binary changed,
+  - the referenced binary is no longer built into the firmware.
+
+  EfiBootManagerFindLoadOption () used in PlatformRegisterFvBootOption () only
+  avoids exact duplicates.
+**/
+VOID
+RemoveStaleFvFileOptions (
+  VOID
+  )
+{
+  EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;
+  UINTN                        BootOptionCount;
+  UINTN                        Index;
+
+  BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount,
+                  LoadOptionTypeBoot);
+
+  for (Index = 0; Index < BootOptionCount; ++Index) {
+    EFI_DEVICE_PATH_PROTOCOL *Node1, *Node2, *SearchNode;
+    EFI_STATUS               Status;
+    EFI_HANDLE               FvHandle;
+
+    //
+    // If the device path starts with neither MemoryMapped (...) nor Fv (...),
+    // then keep the boot option.
+    //
+    Node1 = BootOptions[Index].FilePath;
+    if (!(DevicePathType (Node1) == HARDWARE_DEVICE_PATH
+      && DevicePathSubType (Node1) == HW_MEMMAP_DP)
+      && !(DevicePathType (Node1) == MEDIA_DEVICE_PATH
+      && DevicePathSubType (Node1) == MEDIA_PIWG_FW_VOL_DP))
+    {
+      continue;
+    }
+
+    //
+    // If the second device path node is not FvFile (...), then keep the boot
+    // option.
+    //
+    Node2 = NextDevicePathNode (Node1);
+    if ((DevicePathType (Node2) != MEDIA_DEVICE_PATH)
+      || (DevicePathSubType (Node2) != MEDIA_PIWG_FW_FILE_DP))
+    {
+      continue;
+    }
+
+    //
+    // Locate the Firmware Volume2 protocol instance that is denoted by the
+    // boot option. If this lookup fails (i.e., the boot option references a
+    // firmware volume that doesn't exist), then we'll proceed to delete the
+    // boot option.
+    //
+    SearchNode = Node1;
+    Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid,
+                    &SearchNode, &FvHandle);
+
+    if (!EFI_ERROR (Status)) {
+      //
+      // The firmware volume was found; now let's see if it contains the FvFile
+      // identified by GUID.
+      //
+      EFI_FIRMWARE_VOLUME2_PROTOCOL     *FvProtocol;
+      MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *FvFileNode;
+      UINTN                             BufferSize;
+      EFI_FV_FILETYPE                   FoundType;
+      EFI_FV_FILE_ATTRIBUTES            FileAttributes;
+      UINT32                            AuthenticationStatus;
+
+      Status = gBS->HandleProtocol (FvHandle, &gEfiFirmwareVolume2ProtocolGuid,
+                      (VOID **)&FvProtocol);
+      ASSERT_EFI_ERROR (Status);
+
+      FvFileNode = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)Node2;
+      //
+      // Buffer==NULL means we request metadata only: BufferSize, FoundType,
+      // FileAttributes.
+      //
+      Status = FvProtocol->ReadFile (
+                             FvProtocol,
+                             &FvFileNode->FvFileName, // NameGuid
+                             NULL,                    // Buffer
+                             &BufferSize,
+                             &FoundType,
+                             &FileAttributes,
+                             &AuthenticationStatus
+                             );
+      if (!EFI_ERROR (Status)) {
+        //
+        // The FvFile was found. Keep the boot option.
+        //
+        continue;
+      }
+    }
+
+    //
+    // Delete the boot option.
+    //
+    Status = EfiBootManagerDeleteLoadOptionVariable (
+               BootOptions[Index].OptionNumber, LoadOptionTypeBoot);
+    DEBUG_CODE (
+      CHAR16 *DevicePathString;
+
+      DevicePathString = ConvertDevicePathToText (BootOptions[Index].FilePath,
+                           FALSE, FALSE);
+      DEBUG ((
+        EFI_ERROR (Status) ? EFI_D_WARN : DEBUG_VERBOSE,
+        "%a: removing stale Boot#%04x %s: %r\n",
+        __FUNCTION__,
+        (UINT32)BootOptions[Index].OptionNumber,
+        DevicePathString == NULL ? L"<unavailable>" : DevicePathString,
+        Status
+        ));
+      if (DevicePathString != NULL) {
+        FreePool (DevicePathString);
+      }
+      );
+  }
+
+  EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
+}
+
+/**
+  Register the boot option And Keys.
+
+  @param  VOID
+
+  @retval  VOID
+**/
+VOID
+PlatformRegisterOptionsAndKeys (
+  VOID
+  )
+{
+  EFI_STATUS                   Status;
+  EFI_INPUT_KEY                Enter;
+  EFI_INPUT_KEY                F2;
+  EFI_INPUT_KEY                Esc;
+  EFI_BOOT_MANAGER_LOAD_OPTION BootOption;
+
+  //
+  // Register ENTER as CONTINUE key
+  //
+  Enter.ScanCode    = SCAN_NULL;
+  Enter.UnicodeChar = CHAR_CARRIAGE_RETURN;
+  Status = EfiBootManagerRegisterContinueKeyOption (0, &Enter, NULL);
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Map F2 and ESC to Boot Manager Menu
+  //
+  F2.ScanCode     = SCAN_F2;
+  F2.UnicodeChar  = CHAR_NULL;
+  Esc.ScanCode    = SCAN_ESC;
+  Esc.UnicodeChar = CHAR_NULL;
+  Status = EfiBootManagerGetBootManagerMenu (&BootOption);
+  ASSERT_EFI_ERROR (Status);
+  Status = EfiBootManagerAddKeyOptionVariable (
+             NULL, (UINT16) BootOption.OptionNumber, 0, &F2, NULL
+             );
+  ASSERT (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED);
+  Status = EfiBootManagerAddKeyOptionVariable (
+             NULL, (UINT16) BootOption.OptionNumber, 0, &Esc, NULL
+             );
+  ASSERT (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED);
+}
+
+
+//
+// BDS Platform Functions
+//
+/**
+  Do the platform init, can be customized by OEM/IBV
+  Possible things that can be done in PlatformBootManagerBeforeConsole:
+  > Update console variable: 1. include hot-plug devices;
+  >                          2. Clear ConIn and add SOL for AMT
+  > Register new Driver#### or Boot####
+  > Register new Key####: e.g.: F12
+  > Signal ReadyToLock event
+  > Authentication action: 1. connect Auth devices;
+  >                        2. Identify auto logon user.
+**/
+VOID
+EFIAPI
+PlatformBootManagerBeforeConsole (
+  VOID
+  )
+{
+  RETURN_STATUS PcdStatus;
+
+  //
+  // Signal EndOfDxe PI Event
+  //
+  EfiEventGroupSignal (&gEfiEndOfDxeEventGroupGuid);
+
+  //
+  // Dispatch deferred images after EndOfDxe event.
+  //
+  EfiBootManagerDispatchDeferredImages ();
+
+  //
+  // Locate the PCI root bridges and make the PCI bus driver connect each,
+  // non-recursively. This will produce a number of child handles with PciIo on
+  // them.
+  //
+  FilterAndProcess (&gEfiPciRootBridgeIoProtocolGuid, NULL, Connect);
+
+  //
+  // Signal the ACPI platform driver that it can download QEMU ACPI tables.
+  //
+  EfiEventGroupSignal (&gRootBridgesConnectedEventGroupGuid);
+
+  //
+  // Find all display class PCI devices (using the handles from the previous
+  // step), and connect them non-recursively. This should produce a number of
+  // child handles with GOPs on them.
+  //
+  FilterAndProcess (&gEfiPciIoProtocolGuid, IsPciDisplay, Connect);
+
+  //
+  // Now add the device path of all handles with GOP on them to ConOut and
+  // ErrOut.
+  //
+  FilterAndProcess (&gEfiGraphicsOutputProtocolGuid, NULL, AddOutput);
+
+  //
+  // Add the hardcoded short-form USB keyboard device path to ConIn.
+  //
+  EfiBootManagerUpdateConsoleVariable (ConIn,
+    (EFI_DEVICE_PATH_PROTOCOL *)&mUsbKeyboard, NULL);
+
+  //
+  // Add the hardcoded serial console device path to ConIn, ConOut, ErrOut.
+  //
+  CopyGuid (&mSerialConsole.TermType.Guid, &gEfiTtyTermGuid);
+  EfiBootManagerUpdateConsoleVariable (ConIn,
+    (EFI_DEVICE_PATH_PROTOCOL *)&mSerialConsole, NULL);
+  EfiBootManagerUpdateConsoleVariable (ConOut,
+    (EFI_DEVICE_PATH_PROTOCOL *)&mSerialConsole, NULL);
+  EfiBootManagerUpdateConsoleVariable (ErrOut,
+    (EFI_DEVICE_PATH_PROTOCOL *)&mSerialConsole, NULL);
+
+  //
+  // Set the front page timeout from the QEMU configuration.
+  //
+  PcdStatus = PcdSet16S (PcdPlatformBootTimeOut,
+                GetFrontPageTimeoutFromQemu ());
+  ASSERT_RETURN_ERROR (PcdStatus);
+
+  //
+  // Register platform-specific boot options and keyboard shortcuts.
+  //
+  PlatformRegisterOptionsAndKeys ();
+}
+
+/**
+  Do the platform specific action after the console is ready
+  Possible things that can be done in PlatformBootManagerAfterConsole:
+  > Console post action:
+    > Dynamically switch output mode from 100x31 to 80x25 for certain senarino
+    > Signal console ready platform customized event
+  > Run diagnostics like memory testing
+  > Connect certain devices
+  > Dispatch aditional option roms
+  > Special boot: e.g.: USB boot, enter UI
+**/
+VOID
+EFIAPI
+PlatformBootManagerAfterConsole (
+  VOID
+  )
+{
+  //
+  // Show the splash screen.
+  //
+  DEBUG ((DEBUG_INFO, "PlatformBootManagerAfterConsole, func: %a, line: %d\n", __func__, __LINE__));
+  BootLogoEnableLogo ();
+  DEBUG ((DEBUG_INFO, "PlatformBootManagerAfterConsole, func: %a, line: %d\n", __func__, __LINE__));
+
+  //
+  // Connect the rest of the devices.
+  //
+  DEBUG ((DEBUG_INFO, "PlatformBootManagerAfterConsole, func: %a, line: %d\n", __func__, __LINE__));
+  EfiBootManagerConnectAll ();
+  DEBUG ((DEBUG_INFO, "PlatformBootManagerAfterConsole, func: %a, line: %d\n", __func__, __LINE__));
+
+  //
+  // Process QEMU's -kernel command line option. Note that the kernel booted
+  // this way should receive ACPI tables, which is why we connect all devices
+  // first (see above) -- PCI enumeration blocks ACPI table installation, if
+  // there is a PCI host.
+  //
+  DEBUG ((DEBUG_INFO, "PlatformBootManagerAfterConsole, func: %a, line: %d\n", __func__, __LINE__));
+  TryRunningQemuKernel ();
+  DEBUG ((DEBUG_INFO, "PlatformBootManagerAfterConsole, func: %a, line: %d\n", __func__, __LINE__));
+
+  //
+  // Enumerate all possible boot options, then filter and reorder them based on
+  // the QEMU configuration.
+  //
+  DEBUG ((DEBUG_INFO, "PlatformBootManagerAfterConsole, func: %a, line: %d\n", __func__, __LINE__));
+  EfiBootManagerRefreshAllBootOption ();
+  DEBUG ((DEBUG_INFO, "PlatformBootManagerAfterConsole, func: %a, line: %d\n", __func__, __LINE__));
+
+  //
+  // Register UEFI Shell
+  //
+  DEBUG ((DEBUG_INFO, "PlatformBootManagerAfterConsole, func: %a, line: %d\n", __func__, __LINE__));
+  PlatformRegisterFvBootOption (
+    &gUefiShellFileGuid, L"EFI Internal Shell", LOAD_OPTION_ACTIVE
+    );
+  DEBUG ((DEBUG_INFO, "PlatformBootManagerAfterConsole, func: %a, line: %d\n", __func__, __LINE__));
+
+  RemoveStaleFvFileOptions ();
+  DEBUG ((DEBUG_INFO, "PlatformBootManagerAfterConsole, func: %a, line: %d\n", __func__, __LINE__));
+  SetBootOrderFromQemu ();
+  DEBUG ((DEBUG_INFO, "PlatformBootManagerAfterConsole, func: %a, line: %d\n", __func__, __LINE__));
+}
+
+/**
+  This function is called each second during the boot manager waits the
+  timeout.
+
+  @param TimeoutRemain  The remaining timeout.
+**/
+VOID
+EFIAPI
+PlatformBootManagerWaitCallback (
+  IN UINT16          TimeoutRemain
+  )
+{
+  EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION Black;
+  EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION White;
+  UINT16                              Timeout;
+
+  Timeout = PcdGet16 (PcdPlatformBootTimeOut);
+
+  Black.Raw = 0x00000000;
+  White.Raw = 0x00FFFFFF;
+
+  BootLogoUpdateProgress (
+    White.Pixel,
+    Black.Pixel,
+    L"Start boot option",
+    White.Pixel,
+    (Timeout - TimeoutRemain) * 100 / Timeout,
+    0
+    );
+}
+
+/**
+  The function is called when no boot option could be launched,
+  including platform recovery options and options pointing to applications
+  built into firmware volumes.
+
+  If this function returns, BDS attempts to enter an infinite loop.
+**/
+VOID
+EFIAPI
+PlatformBootManagerUnableToBoot (
+  VOID
+  )
+{
+  EFI_STATUS                   Status;
+  EFI_INPUT_KEY                Key;
+  EFI_BOOT_MANAGER_LOAD_OPTION BootManagerMenu;
+  UINTN                        Index;
+
+  //
+  // BootManagerMenu doesn't contain the correct information when return status
+  // is EFI_NOT_FOUND.
+  //
+  Status = EfiBootManagerGetBootManagerMenu (&BootManagerMenu);
+  if (EFI_ERROR (Status)) {
+    return;
+  }
+  //
+  // Normally BdsDxe does not print anything to the system console, but this is
+  // a last resort -- the end-user will likely not see any DEBUG messages
+  // logged in this situation.
+  //
+  // AsciiPrint () will NULL-check gST->ConOut internally. We check gST->ConIn
+  // here to see if it makes sense to request and wait for a keypress.
+  //
+  if (gST->ConIn != NULL) {
+    AsciiPrint (
+      "%a: No bootable option or device was found.\n"
+      "%a: Press any key to enter the Boot Manager Menu.\n",
+      gEfiCallerBaseName,
+      gEfiCallerBaseName
+      );
+    Status = gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &Index);
+    ASSERT_EFI_ERROR (Status);
+    ASSERT (Index == 0);
+
+    //
+    // Drain any queued keys.
+    //
+    while (!EFI_ERROR (gST->ConIn->ReadKeyStroke (gST->ConIn, &Key))) {
+      //
+      // just throw away Key
+      //
+    }
+  }
+
+  for (;;) {
+    EfiBootManagerBoot (&BootManagerMenu);
+  }
+}
+
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/PlatformBm.h b/Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/PlatformBm.h
new file mode 100644
index 0000000000..427ecf1154
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/PlatformBm.h
@@ -0,0 +1,112 @@
+/** @file
+  Head file for BDS Platform specific code
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef PLATFORM_BM_H_
+#define PLATFORM_BM_H_
+
+#include <Library/DevicePathLib.h>
+
+#define DP_NODE_LEN(Type) { (UINT8)sizeof (Type), (UINT8)(sizeof (Type) >> 8) }
+
+#define SERIAL_DXE_FILE_GUID { \
+          0xD3987D4B, 0x971A, 0x435F, \
+          { 0x8C, 0xAF, 0x49, 0x67, 0xEB, 0x62, 0x72, 0x41 } \
+          }
+
+#define ALIGN_UP(addr, align) \
+    ((addr + (typeof (addr)) align - 1) & ~((typeof (addr)) align - 1))
+
+#pragma pack (1)
+typedef struct {
+  VENDOR_DEVICE_PATH         SerialDxe;
+  UART_DEVICE_PATH           Uart;
+  VENDOR_DEFINED_DEVICE_PATH TermType;
+  EFI_DEVICE_PATH_PROTOCOL   End;
+} PLATFORM_SERIAL_CONSOLE;
+#pragma pack ()
+
+#pragma pack (1)
+typedef struct {
+  USB_CLASS_DEVICE_PATH    Keyboard;
+  EFI_DEVICE_PATH_PROTOCOL End;
+} PLATFORM_USB_KEYBOARD;
+#pragma pack ()
+
+/**
+  Check if the handle satisfies a particular condition.
+
+  @param[in] Handle      The handle to check.
+  @param[in] ReportText  A caller-allocated string passed in for reporting
+                         purposes. It must never be NULL.
+
+  @retval TRUE   The condition is satisfied.
+  @retval FALSE  Otherwise. This includes the case when the condition could not
+                 be fully evaluated due to an error.
+**/
+typedef
+BOOLEAN
+(EFIAPI *FILTER_FUNCTION) (
+  IN EFI_HANDLE   Handle,
+  IN CONST CHAR16 *ReportText
+  );
+
+/**
+  Process a handle.
+
+  @param[in] Handle      The handle to process.
+  @param[in] ReportText  A caller-allocated string passed in for reporting
+                         purposes. It must never be NULL.
+**/
+typedef
+VOID
+(EFIAPI *CALLBACK_FUNCTION)  (
+  IN EFI_HANDLE   Handle,
+  IN CONST CHAR16 *ReportText
+  );
+
+/**
+ * execute from kernel entry point.
+ *
+ * @param[in] Argc  The count of args.
+ * @param[in] Argv  The pointer to args array.
+ * @param[in] Bpi   The pointer to bootparaminterface struct.
+ * @param[in] Vec   The fourth args for kernel.
+ ***/
+typedef
+VOID
+(EFIAPI *EFI_KERNEL_ENTRY_POINT) (
+  IN UINTN  Argc,
+  IN VOID   *Argv,
+  IN VOID   *Bpi,
+  IN VOID   *Vec
+  );
+
+/**
+  Download the kernel, the initial ramdisk, and the kernel command line from
+  QEMU's fw_cfg. Construct a minimal SimpleFileSystem that contains the two
+  image files, and load and start the kernel from it.
+
+  The kernel will be instructed via its command line to load the initrd from
+  the same Simple FileSystem.
+
+  @retval EFI_NOT_FOUND         Kernel image was not found.
+  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.
+  @retval EFI_PROTOCOL_ERROR    Unterminated kernel command line.
+
+  @return                       Error codes from any of the underlying
+                                functions. On success, the function doesn't
+                                return.
+**/
+EFI_STATUS
+EFIAPI
+TryRunningQemuKernel (
+  VOID
+  );
+
+#endif // _PLATFORM_BM_H_
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf b/Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
new file mode 100644
index 0000000000..0f322b30f6
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
@@ -0,0 +1,78 @@
+## @file
+#  Implementation for PlatformBootManagerLib library class interfaces.
+#
+#  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = PlatformBootManagerLib
+  FILE_GUID                      = 469184E8-FADA-41E4-8823-012CA19B40D4
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = PlatformBootManagerLib|DXE_DRIVER
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = LOONGARCH64
+#
+
+[Sources]
+  PlatformBm.c
+  QemuKernel.c
+
+[Packages]
+  Platform/Loongson/LoongArchQemuPkg/Loongson.dec
+  MdeModulePkg/MdeModulePkg.dec
+  MdePkg/MdePkg.dec
+  OvmfPkg/OvmfPkg.dec
+  ShellPkg/ShellPkg.dec
+
+[LibraryClasses]
+  BaseLib
+  BaseMemoryLib
+  BootLogoLib
+  DebugLib
+  DevicePathLib
+  MemoryAllocationLib
+  PcdLib
+  PrintLib
+  QemuBootOrderLib
+  QemuLoadImageLib
+  QemuFwCfgLib
+  UefiBootManagerLib
+  UefiBootServicesTableLib
+  UefiLib
+  UefiRuntimeServicesTableLib
+
+[FixedPcd]
+  gEfiMdePkgTokenSpaceGuid.PcdUartDefaultBaudRate
+  gEfiMdePkgTokenSpaceGuid.PcdUartDefaultDataBits
+  gEfiMdePkgTokenSpaceGuid.PcdUartDefaultParity
+  gEfiMdePkgTokenSpaceGuid.PcdUartDefaultStopBits
+
+[Pcd]
+  gEfiMdePkgTokenSpaceGuid.PcdPlatformBootTimeOut
+
+[Guids]
+  gEfiFileInfoGuid
+  gEfiFileSystemInfoGuid
+  gEfiFileSystemVolumeLabelInfoIdGuid
+  gEfiEndOfDxeEventGroupGuid
+  gRootBridgesConnectedEventGroupGuid
+  gUefiShellFileGuid
+  gEfiLoongsonBootparamsTableGuid                               ## SOMETIMES_PRODUCES ## SystemTable
+  gEfiTtyTermGuid
+
+[Protocols]
+  gEfiDevicePathProtocolGuid
+  gEfiFirmwareVolume2ProtocolGuid
+  gEfiGraphicsOutputProtocolGuid
+  gEfiLoadedImageProtocolGuid
+  gEfiPciRootBridgeIoProtocolGuid
+  gEfiSimpleFileSystemProtocolGuid
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/QemuKernel.c b/Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/QemuKernel.c
new file mode 100644
index 0000000000..ceb131d566
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/QemuKernel.c
@@ -0,0 +1,81 @@
+/** @file
+  Try to run Linux kernel.
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Glossary:
+    - mem     - Memory
+    - Bpi    - Boot Parameter Interface
+    - FwCfg    - FirmWare Configure
+**/
+
+#include <Library/QemuLoadImageLib.h>
+#include <Library/ReportStatusCodeLib.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+
+/**
+  Download the kernel, the initial ramdisk, and the kernel command line from
+  QEMU's fw_cfg. Construct a minimal SimpleFileSystem that contains the two
+  image files, and load and start the kernel from it.
+
+  The kernel will be instructed via its command line to load the initrd from
+  the same Simple FileSystem.
+
+  @retval EFI_NOT_FOUND         Kernel image was not found.
+  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.
+  @retval EFI_PROTOCOL_ERROR    Unterminated kernel command line.
+
+  @return                       Error codes from any of the underlying
+                                functions. On success, the function doesn't
+                                return.
+**/
+EFI_STATUS
+TryRunningQemuKernel (
+  VOID
+  )
+{
+  EFI_STATUS  Status;
+  EFI_HANDLE  KernelImageHandle;
+
+  Status = QemuLoadKernelImage (&KernelImageHandle);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Signal the EFI_EVENT_GROUP_READY_TO_BOOT event.
+  //
+  EfiSignalEventReadyToBoot ();
+
+  REPORT_STATUS_CODE (
+    EFI_PROGRESS_CODE,
+    (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_READY_TO_BOOT_EVENT)
+    );
+
+  //
+  // Start the image.
+  //
+  Status = QemuStartKernelImage (&KernelImageHandle);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "%a: QemuStartKernelImage(): %r\n",
+      __FUNCTION__,
+      Status
+      ));
+  }
+
+  QemuUnloadKernelImage (KernelImageHandle);
+
+  return Status;
+}
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [edk2-platforms][PATCH V3 13/16] Platform/Loongson: Add Reset System Lib.
  2022-09-29  7:07 [edk2-platforms][PATCH V3 00/16] Platform: Add Loongson support xianglai
                   ` (11 preceding siblings ...)
  2022-09-29  7:08 ` [edk2-platforms][PATCH V3 12/16] Platform/Loongson: Add Platform Boot Manager Lib xianglai
@ 2022-09-29  7:08 ` xianglai
  2022-09-29  7:08 ` [edk2-platforms][PATCH V3 14/16] Platform/Loongson: Support Dxe xianglai
                   ` (2 subsequent siblings)
  15 siblings, 0 replies; 21+ messages in thread
From: xianglai @ 2022-09-29  7:08 UTC (permalink / raw)
  To: devel; +Cc: quic_llindhol, michael.d.kinney, kraxel, ardb, maobibo

This library provides interfaces related to restart and shutdown.

REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4054

Signed-off-by: xianglai li <lixianglai@loongson.cn>
---
 .../BaseResetSystemAcpiGed.c                  | 155 ++++++++++
 .../BaseResetSystemAcpiGedLib.inf             |  42 +++
 .../DxeResetSystemAcpiGed.c                   | 270 ++++++++++++++++++
 .../DxeResetSystemAcpiGedLib.inf              |  47 +++
 .../ResetSystemAcpiLib/ResetSystemAcpiGed.c   | 128 +++++++++
 .../ResetSystemAcpiLib/ResetSystemAcpiGed.h   |  20 ++
 6 files changed, 662 insertions(+)
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/ResetSystemAcpiLib/BaseResetSystemAcpiGed.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/ResetSystemAcpiLib/BaseResetSystemAcpiGedLib.inf
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/ResetSystemAcpiLib/DxeResetSystemAcpiGed.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/ResetSystemAcpiLib/DxeResetSystemAcpiGedLib.inf
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/ResetSystemAcpiLib/ResetSystemAcpiGed.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/ResetSystemAcpiLib/ResetSystemAcpiGed.h

diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/ResetSystemAcpiLib/BaseResetSystemAcpiGed.c b/Platform/Loongson/LoongArchQemuPkg/Library/ResetSystemAcpiLib/BaseResetSystemAcpiGed.c
new file mode 100644
index 0000000000..a50672410b
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/ResetSystemAcpiLib/BaseResetSystemAcpiGed.c
@@ -0,0 +1,155 @@
+/** @file
+  Base ResetSystem library implementation.
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Base.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include "ResetSystemAcpiGed.h"
+#include <Library/QemuFwCfgLib.h>
+
+/**
+  Get configuration item data by the firmware configuration file name.
+
+  @param[in]  Name - Name of file to look up.
+
+  @return    VOID*       The Pointer of Value of Firmware Configuration item read.
+
+**/
+VOID *
+GetFwCfgData(
+CONST CHAR8           *Name
+)
+{
+  FIRMWARE_CONFIG_ITEM FwCfgItem;
+  EFI_STATUS           Status;
+  UINTN                FwCfgSize;
+  VOID                 *Data;
+
+  Status = QemuFwCfgFindFile (Name, &FwCfgItem, &FwCfgSize);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a %d read  %s error Status %d \n", __func__, __LINE__, Name, Status));
+    return NULL;
+  }
+
+  Data = AllocatePool (FwCfgSize);
+  if (Data == NULL) {
+    return NULL;
+  }
+
+  QemuFwCfgSelectItem (FwCfgItem);
+  QemuFwCfgReadBytes (FwCfgSize, Data);
+
+  return Data;
+}
+
+/**
+  Find the power manager related info from ACPI table
+
+
+  @retval RETURN_SUCCESS     Successfully find out all the required information.
+  @retval RETURN_NOT_FOUND   Failed to find the required info.
+
+**/
+STATIC EFI_STATUS
+GetPowerManagerByParseAcpiInfo (VOID)
+{
+  EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE    *Fadt = NULL;
+  EFI_ACPI_3_0_ROOT_SYSTEM_DESCRIPTION_POINTER *Rsdp = NULL;
+  EFI_ACPI_DESCRIPTION_HEADER                  *Xsdt = NULL;
+  EFI_ACPI_DESCRIPTION_HEADER                  *Rsdt = NULL;
+  VOID                                         *AcpiTables = NULL;
+  UINT32                                       *Entry32 = NULL;
+  UINTN                                         Entry32Num;
+  UINT32                                       *Signature = NULL;
+  UINTN                                         Idx;
+
+  Rsdp = GetFwCfgData ("etc/acpi/rsdp");
+  if (Rsdp == NULL) {
+    DEBUG ((DEBUG_ERROR, "%a %d read etc/acpi/rsdp error \n", __func__, __LINE__));
+    return RETURN_NOT_FOUND;
+  }
+
+  AcpiTables = GetFwCfgData ("etc/acpi/tables");
+  if (AcpiTables == NULL) {
+    DEBUG ((DEBUG_ERROR, "%a %d read etc/acpi/tables error \n", __func__, __LINE__));
+    FreePool (Rsdp);
+    return RETURN_NOT_FOUND;
+  }
+
+  Rsdt = (EFI_ACPI_DESCRIPTION_HEADER *)((UINTN)AcpiTables +  Rsdp->RsdtAddress);
+  Entry32    = (UINT32 *)(Rsdt + 1);
+  Entry32Num = (Rsdt->Length - sizeof (EFI_ACPI_DESCRIPTION_HEADER)) >> 2;
+  for (Idx = 0; Idx < Entry32Num; Idx++) {
+    Signature = (UINT32 *)((UINTN)Entry32[Idx] + (UINTN)AcpiTables);
+    if (*Signature == EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE) {
+      Fadt = (EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE *)Signature;
+      DEBUG ((DEBUG_INFO, "Found Fadt in Rsdt\n"));
+      goto Done;
+    }
+  }
+
+
+  Xsdt = (EFI_ACPI_DESCRIPTION_HEADER *)((UINTN)AcpiTables +  Rsdp->XsdtAddress);
+  Entry32    = (UINT32 *)(Xsdt + 1);
+  Entry32Num = (Xsdt->Length - sizeof (EFI_ACPI_DESCRIPTION_HEADER)) >> 2;
+  for (Idx = 0; Idx < Entry32Num; Idx++) {
+    Signature = (UINT32 *)((UINTN)Entry32[Idx] + (UINTN)AcpiTables);
+    if (*Signature == EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE) {
+      Fadt = (EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE *)Signature;
+      DEBUG ((DEBUG_INFO, "Found Fadt in Xsdt\n"));
+      goto Done;
+    }
+  }
+
+
+  FreePool (Rsdp);
+  FreePool (AcpiTables);
+  DEBUG ((DEBUG_ERROR, " Fadt Not Found\n"));
+  return RETURN_NOT_FOUND;
+
+Done:
+
+  mPowerManager.ResetRegAddr        = Fadt->ResetReg.Address;
+  mPowerManager.ResetValue          = Fadt->ResetValue;
+  mPowerManager.SleepControlRegAddr = Fadt->SleepControlReg.Address;
+  mPowerManager.SleepStatusRegAddr  = Fadt->SleepStatusReg.Address;
+
+  FreePool (Rsdp);
+  FreePool (AcpiTables);
+
+  return RETURN_SUCCESS;
+}
+
+/**
+  The constructor function to initialize mPowerManager.
+
+  @retval EFI_SUCCESS   initialize mPowerManager success.
+  @retval RETURN_NOT_FOUND   Failed to initialize mPowerManager.
+
+**/
+EFI_STATUS
+ResetSystemLibConstructor (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+{
+  EFI_STATUS  Status;
+
+  Status = GetPowerManagerByParseAcpiInfo ();
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_INFO, "%a:%d\n",  __FUNCTION__, __LINE__));
+  }
+
+  ASSERT (mPowerManager.SleepControlRegAddr);
+  ASSERT (mPowerManager.SleepStatusRegAddr);
+  ASSERT (mPowerManager.ResetRegAddr);
+
+  return Status;
+}
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/ResetSystemAcpiLib/BaseResetSystemAcpiGedLib.inf b/Platform/Loongson/LoongArchQemuPkg/Library/ResetSystemAcpiLib/BaseResetSystemAcpiGedLib.inf
new file mode 100644
index 0000000000..2336732e35
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/ResetSystemAcpiLib/BaseResetSystemAcpiGedLib.inf
@@ -0,0 +1,42 @@
+## @file
+#  Base library instance for ResetSystem library class for loongarhch
+#
+#  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 1.29
+  BASE_NAME                      = ResetSystemLib
+  FILE_GUID                      = 3d6faf60-804a-4ca9-a36a-1a92416919d0
+  MODULE_TYPE                    = BASE
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = ResetSystemLib|SEC PEI_CORE PEIM DXE_CORE
+  CONSTRUCTOR                    = ResetSystemLibConstructor
+
+#
+# The following information is for reference only and not required by the build
+# tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64
+#
+
+[Sources]
+  BaseResetSystemAcpiGed.c
+  ResetSystemAcpiGed.c
+
+
+[Packages]
+  MdeModulePkg/MdeModulePkg.dec
+  MdePkg/MdePkg.dec
+  OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+  BaseLib
+  DebugLib
+  QemuFwCfgLib
+  MemoryAllocationLib
+  IoLib
+
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/ResetSystemAcpiLib/DxeResetSystemAcpiGed.c b/Platform/Loongson/LoongArchQemuPkg/Library/ResetSystemAcpiLib/DxeResetSystemAcpiGed.c
new file mode 100644
index 0000000000..7d2aea53f4
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/ResetSystemAcpiLib/DxeResetSystemAcpiGed.c
@@ -0,0 +1,270 @@
+/** @file
+  Dxe ResetSystem library implementation.
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Base.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiRuntimeLib.h> // EfiConvertPointer()
+#include <Library/DxeServicesTableLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include "ResetSystemAcpiGed.h"
+#include <Library/UefiLib.h>
+
+/**
+  Modifies the attributes to Runtime type for a page size memory region.
+
+  @param  BaseAddress            Specified start address
+
+  @retval EFI_SUCCESS           The attributes were set for the memory region.
+  @retval EFI_INVALID_PARAMETER Length is zero.
+  @retval EFI_UNSUPPORTED       The processor does not support one or more bytes of the memory
+                                resource range specified by BaseAddress and Length.
+  @retval EFI_UNSUPPORTED       The bit mask of attributes is not support for the memory resource
+                                range specified by BaseAddress and Length.
+  @retval EFI_ACCESS_DEFINED    The attributes for the memory resource range specified by
+                                BaseAddress and Length cannot be modified.
+  @retval EFI_OUT_OF_RESOURCES  There are not enough system resources to modify the attributes of
+                                the memory resource range.
+  @retval EFI_NOT_AVAILABLE_YET The attributes cannot be set because CPU architectural protocol is
+                                not available yet.
+
+**/
+EFI_STATUS
+SetMemoryAttributesRunTime (
+  UINTN Address
+  )
+{
+  EFI_STATUS  Status;
+  EFI_GCD_MEMORY_SPACE_DESCRIPTOR  Descriptor;
+
+  Address &= ~EFI_PAGE_MASK;
+
+  Status = gDS->GetMemorySpaceDescriptor (Address, &Descriptor);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_INFO, "%a: GetMemorySpaceDescriptor failed\n", __FUNCTION__));
+    return Status;
+  }
+
+  if (Descriptor.GcdMemoryType == EfiGcdMemoryTypeNonExistent) {
+    Status = gDS->AddMemorySpace (
+                  EfiGcdMemoryTypeMemoryMappedIo,
+                  Address,
+                  EFI_PAGE_SIZE,
+                  EFI_MEMORY_UC | EFI_MEMORY_RUNTIME
+                  );
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_INFO, "%a: AddMemorySpace failed\n", __FUNCTION__));
+      return Status;
+    }
+
+    Status = gDS->SetMemorySpaceAttributes (
+                  Address,
+                  EFI_PAGE_SIZE,
+                  EFI_MEMORY_RUNTIME
+                  );
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_INFO, "%a:%d SetMemorySpaceAttributes failed\n", __FUNCTION__, __LINE__));
+      return Status;
+    }
+  } else if (!(Descriptor.Attributes & EFI_MEMORY_RUNTIME)) {
+    Status = gDS->SetMemorySpaceAttributes (
+                  Address,
+                  EFI_PAGE_SIZE,
+                  Descriptor.Attributes | EFI_MEMORY_RUNTIME
+                  );
+
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_INFO, "%a:%d SetMemorySpaceAttributes failed\n", __FUNCTION__, __LINE__));
+      return Status;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Find the power manager related info from ACPI table
+
+
+  @retval RETURN_SUCCESS     Successfully find out all the required information.
+  @retval RETURN_NOT_FOUND   Failed to find the required info.
+
+**/
+STATIC EFI_STATUS
+GetPowerManagerByParseAcpiInfo (
+  VOID
+)
+{
+  EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE    *Fadt = NULL;
+  EFI_ACPI_3_0_ROOT_SYSTEM_DESCRIPTION_POINTER *Rsdp = NULL;
+  EFI_ACPI_DESCRIPTION_HEADER                  *Xsdt = NULL;
+  EFI_ACPI_DESCRIPTION_HEADER                  *Rsdt = NULL;
+  UINT32                                       *Entry32 = NULL;
+  UINTN                                         Entry32Num;
+  UINT32                                       *Signature = NULL;
+  UINTN                                         Idx;
+  EFI_STATUS           Status;
+
+  Status = EfiGetSystemConfigurationTable (&gEfiAcpiTableGuid, (VOID **)&Rsdp);
+  if (EFI_ERROR (Status)) {
+    Status = EfiGetSystemConfigurationTable (&gEfiAcpi10TableGuid, (VOID **)&Rsdp);
+  }
+
+  if (EFI_ERROR (Status) || (Rsdp == NULL)) {
+    DEBUG ((DEBUG_ERROR, "EFI_ERROR or Rsdp == NULL\n"));
+    return RETURN_NOT_FOUND;
+  }
+
+  Rsdt = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN)Rsdp->RsdtAddress;
+  Entry32    = (UINT32 *)(UINTN)(Rsdt + 1);
+  Entry32Num = (Rsdt->Length - sizeof (EFI_ACPI_DESCRIPTION_HEADER)) >> 2;
+  for (Idx = 0; Idx < Entry32Num; Idx++) {
+    Signature = (UINT32 *)(UINTN)Entry32[Idx];
+    if (*Signature == EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE) {
+      Fadt = (EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE *)Signature;
+      DEBUG ((DEBUG_INFO, "Found Fadt in Rsdt\n"));
+      goto Done;
+    }
+  }
+
+
+  Xsdt = (EFI_ACPI_DESCRIPTION_HEADER *)Rsdp->XsdtAddress;
+  Entry32    = (UINT32 *)(Xsdt + 1);
+  Entry32Num = (Xsdt->Length - sizeof (EFI_ACPI_DESCRIPTION_HEADER)) >> 2;
+  for (Idx = 0; Idx < Entry32Num; Idx++) {
+    Signature = (UINT32 *)(UINTN)Entry32[Idx];
+    if (*Signature == EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE) {
+      Fadt = (EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE *)Signature;
+      DEBUG ((DEBUG_INFO, "Found Fadt in Xsdt\n"));
+      goto Done;
+    }
+  }
+
+
+  DEBUG ((DEBUG_ERROR, " Fadt Not Found\n"));
+  return RETURN_NOT_FOUND;
+
+Done:
+
+  mPowerManager.ResetRegAddr        = Fadt->ResetReg.Address;
+  mPowerManager.ResetValue          = Fadt->ResetValue;
+  mPowerManager.SleepControlRegAddr = Fadt->SleepControlReg.Address;
+  mPowerManager.SleepStatusRegAddr  = Fadt->SleepStatusReg.Address;
+
+  return RETURN_SUCCESS;
+
+}
+
+/**
+  This is a notification function registered on EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE
+  event. It converts a pointer to a new virtual address.
+
+  @param[in] Event        Event whose notification function is being invoked.
+  @param[in] Context      Pointer to the notification function's context
+**/
+VOID
+EFIAPI
+ResetSystemLibAddressChangeEvent (
+  IN EFI_EVENT  Event,
+  IN VOID       *Context
+  )
+{
+  EfiConvertPointer (0, (VOID **)&mPowerManager.SleepControlRegAddr);
+  EfiConvertPointer (0, (VOID **)&mPowerManager.SleepStatusRegAddr);
+  EfiConvertPointer (0, (VOID **)&mPowerManager.ResetRegAddr);
+}
+
+/**
+  Notification function of ACPI Table change.
+
+  This is a notification function registered on ACPI Table change event.
+  It saves the Century address stored in ACPI FADT table.
+
+  @param  Event        Event whose notification function is being invoked.
+  @param  Context      Pointer to the notification function's context.
+
+**/
+STATIC VOID
+AcpiNotificationEvent (
+  IN EFI_EVENT Event,
+  IN VOID      *Context
+  )
+{
+  EFI_STATUS                       Status;
+
+  Status = GetPowerManagerByParseAcpiInfo ();
+  if (EFI_ERROR (Status)) {
+    return ;
+  }
+
+  DEBUG ((DEBUG_INFO, "%a: sleepControl %llx\n", __FUNCTION__, mPowerManager.SleepControlRegAddr));
+  ASSERT (mPowerManager.SleepControlRegAddr);
+  Status =  SetMemoryAttributesRunTime (mPowerManager.SleepControlRegAddr);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_INFO, "%a:%d\n",  __FUNCTION__, __LINE__));
+    return ;
+  }
+
+  DEBUG ((DEBUG_INFO, "%a: sleepStatus %llx\n", __FUNCTION__, mPowerManager.SleepStatusRegAddr));
+  ASSERT (mPowerManager.SleepStatusRegAddr);
+  Status =  SetMemoryAttributesRunTime (mPowerManager.SleepStatusRegAddr);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_INFO, "%a:%d\n",  __FUNCTION__, __LINE__));
+    return ;
+  }
+
+  DEBUG ((DEBUG_INFO, "%a: ResetReg %llx\n", __FUNCTION__, mPowerManager.ResetRegAddr));
+  ASSERT (mPowerManager.ResetRegAddr);
+  Status =  SetMemoryAttributesRunTime (mPowerManager.ResetRegAddr);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_INFO, "%a:%d\n",  __FUNCTION__, __LINE__));
+  }
+
+  return ;
+}
+
+/**
+  The constructor function to Register ACPI Table change event and Address Change Event.
+
+  @retval EFI_SUCCESS   The constructor always returns RETURN_SUCCESS.
+
+**/
+EFI_STATUS
+EFIAPI
+ResetSystemLibConstructor (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+{
+  EFI_STATUS  Status;
+  EFI_EVENT   Event;
+  EFI_EVENT   ResetSystemVirtualNotifyEvent;
+
+  Status = gBS->CreateEventEx (
+                    EVT_NOTIFY_SIGNAL,
+                    TPL_CALLBACK,
+                    AcpiNotificationEvent,
+                    NULL,
+                    &gEfiAcpiTableGuid,
+                    &Event
+                    );
+
+  //
+  // Register SetVirtualAddressMap () notify function
+  //
+  Status = gBS->CreateEvent (
+                  EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE,
+                  TPL_NOTIFY,
+                  ResetSystemLibAddressChangeEvent,
+                  NULL,
+                  &ResetSystemVirtualNotifyEvent
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/ResetSystemAcpiLib/DxeResetSystemAcpiGedLib.inf b/Platform/Loongson/LoongArchQemuPkg/Library/ResetSystemAcpiLib/DxeResetSystemAcpiGedLib.inf
new file mode 100644
index 0000000000..6ba78574b5
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/ResetSystemAcpiLib/DxeResetSystemAcpiGedLib.inf
@@ -0,0 +1,47 @@
+## @file
+#  DXE library instance for ResetSystem library class for loongarch.
+#
+#  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 1.29
+  BASE_NAME                      = ResetSystemLib
+  FILE_GUID                      = 3d6faf60-804a-4ca9-a36a-1a92416919d0
+  MODULE_TYPE                    = DXE_RUNTIME_DRIVER
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = ResetSystemLib|DXE_DRIVER DXE_RUNTIME_DRIVER SMM_CORE DXE_SMM_DRIVER UEFI_DRIVER UEFI_APPLICATION
+  CONSTRUCTOR                    = ResetSystemLibConstructor
+
+#
+# The following information is for reference only and not required by the build
+# tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64
+#
+
+[Sources]
+  DxeResetSystemAcpiGed.c
+  ResetSystemAcpiGed.c
+
+
+[Packages]
+  MdeModulePkg/MdeModulePkg.dec
+  MdePkg/MdePkg.dec
+  OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+  BaseLib
+  DebugLib
+  DxeServicesTableLib
+  IoLib
+  UefiLib
+
+[Guids]
+  gEfiAcpi10TableGuid                           ## PRODUCES           ## SystemTable
+  gEfiAcpiTableGuid                             ## PRODUCES           ## SystemTable
+
+
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/ResetSystemAcpiLib/ResetSystemAcpiGed.c b/Platform/Loongson/LoongArchQemuPkg/Library/ResetSystemAcpiLib/ResetSystemAcpiGed.c
new file mode 100644
index 0000000000..7f88269f13
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/ResetSystemAcpiLib/ResetSystemAcpiGed.c
@@ -0,0 +1,128 @@
+/** @file
+  ResetSystem library implementation.
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Base.h>
+#include <Uefi.h>
+#include <PiPei.h>
+#include <Library/BaseLib.h>        // CpuDeadLoop()
+#include <Library/DebugLib.h>
+#include <Library/ResetSystemLib.h> // ResetCold()
+#include <Library/IoLib.h>
+#include "ResetSystemAcpiGed.h"
+
+POWER_MANAGER mPowerManager;
+
+/**
+  Calling this function causes a system-wide reset. This sets
+  all circuitry within the system to its initial state. This type of reset
+  is asynchronous to system operation and operates without regard to
+  cycle boundaries.
+
+  System reset should not return, if it returns, it means the system does
+  not support cold reset.
+**/
+STATIC VOID
+AcpiGedReset (
+  VOID
+  )
+{
+  MmioWrite8 (
+    (UINTN)mPowerManager.ResetRegAddr,
+    mPowerManager.ResetValue
+    );
+
+  CpuDeadLoop ();
+}
+
+/**
+  This function causes the system to enter a power state equivalent
+    to the ACPI S5 states.
+
+ * */
+STATIC VOID
+AcpiGedShutdown (
+  VOID
+  )
+{
+  MmioWrite8 (
+    (UINTN)mPowerManager.SleepControlRegAddr,
+    (1 << 5) /* enable bit */ |
+    (5 << 2) /* typ == S5  */
+    );
+
+  CpuDeadLoop ();
+}
+
+/**
+  This function causes a system-wide reset (cold reset), in which
+  all circuitry within the system returns to its initial state. This type of
+  reset is asynchronous to system operation and operates without regard to
+  cycle boundaries.
+
+  If this function returns, it means that the system does not support cold
+  reset.
+**/
+VOID EFIAPI
+ResetCold (
+  VOID
+  )
+{
+  AcpiGedReset ();
+}
+
+/**
+  This function causes a system-wide initialization (warm reset), in which all
+  processors are set to their initial state. Pending cycles are not corrupted.
+
+  If this function returns, it means that the system does not support warm
+  reset.
+**/
+VOID EFIAPI
+ResetWarm (
+  VOID
+  )
+{
+  AcpiGedReset ();
+}
+
+/**
+  This function causes a systemwide reset. The exact type of the reset is
+  defined by the EFI_GUID that follows the Null-terminated Unicode string passed
+  into ResetData. If the platform does not recognize the EFI_GUID in ResetData
+  the platform must pick a supported reset type to perform.The platform may
+  optionally log the parameters from any non-normal reset that occurs.
+
+  @param[in]  DataSize   The size, in bytes, of ResetData.
+  @param[in]  ResetData  The data buffer starts with a Null-terminated string,
+                         followed by the EFI_GUID.
+**/
+VOID
+EFIAPI
+ResetPlatformSpecific (
+  IN UINTN  DataSize,
+  IN VOID   *ResetData
+  )
+{
+  AcpiGedReset ();
+}
+
+/**
+  This function causes the system to enter a power state equivalent
+  to the ACPI G2/S5 or G3 states.
+
+  If this function returns, it means that the system does not support shut down
+  reset.
+**/
+VOID EFIAPI
+ResetShutdown (
+  VOID
+  )
+{
+  AcpiGedShutdown ();
+}
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/ResetSystemAcpiLib/ResetSystemAcpiGed.h b/Platform/Loongson/LoongArchQemuPkg/Library/ResetSystemAcpiLib/ResetSystemAcpiGed.h
new file mode 100644
index 0000000000..08288da1e8
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/ResetSystemAcpiLib/ResetSystemAcpiGed.h
@@ -0,0 +1,20 @@
+/** @file
+  ResetSystem lib head file.
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Base.h>
+
+typedef struct {
+  UINT64  SleepControlRegAddr;
+  UINT64  SleepStatusRegAddr;
+  UINT64  ResetRegAddr;
+  UINT8   ResetValue;
+} POWER_MANAGER;
+
+extern POWER_MANAGER mPowerManager;
+
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [edk2-platforms][PATCH V3 14/16] Platform/Loongson: Support Dxe
  2022-09-29  7:07 [edk2-platforms][PATCH V3 00/16] Platform: Add Loongson support xianglai
                   ` (12 preceding siblings ...)
  2022-09-29  7:08 ` [edk2-platforms][PATCH V3 13/16] Platform/Loongson: Add Reset System Lib xianglai
@ 2022-09-29  7:08 ` xianglai
  2022-09-29  7:08 ` [edk2-platforms][PATCH V3 15/16] Platform/Loongson: Add QemuFlashFvbServicesRuntimeDxe driver xianglai
  2022-09-29  7:08 ` [edk2-platforms][PATCH V3 16/16] Platform/Loongson: Support for saving variables to flash xianglai
  15 siblings, 0 replies; 21+ messages in thread
From: xianglai @ 2022-09-29  7:08 UTC (permalink / raw)
  To: devel; +Cc: quic_llindhol, michael.d.kinney, kraxel, ardb, maobibo

Support Dxe for LoogArch.

REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4054

Signed-off-by: xianglai li <lixianglai@loongson.cn>
---
 .../Loongson/LoongArchQemuPkg/Loongson.dec    |  14 +-
 .../Loongson/LoongArchQemuPkg/Loongson.dsc    | 420 +++++++++++++++++-
 .../Loongson/LoongArchQemuPkg/Loongson.fdf    | 243 ++++++++++
 .../LoongArchQemuPkg/Loongson.fdf.inc         |  42 ++
 .../LoongArchQemuPkg/VarStore.fdf.inc         |  64 +++
 5 files changed, 778 insertions(+), 5 deletions(-)
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/VarStore.fdf.inc

diff --git a/Platform/Loongson/LoongArchQemuPkg/Loongson.dec b/Platform/Loongson/LoongArchQemuPkg/Loongson.dec
index 6a423f3ed6..5065f99042 100644
--- a/Platform/Loongson/LoongArchQemuPkg/Loongson.dec
+++ b/Platform/Loongson/LoongArchQemuPkg/Loongson.dec
@@ -18,7 +18,8 @@
 #                   Comments are used for Keywords and Module Types.
 #
 # Supported Module Types:
-#  BASE SEC PEI_CORE PEIM DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER DXE_SAL_DRIVER UEFI_DRIVER UEFI_APPLICATION
+#  BASE SEC PEI_CORE PEIM DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER
+#  DXE_SAL_DRIVER UEFI_DRIVER UEFI_APPLICATION
 #
 ################################################################################
 [Includes.common]
@@ -26,12 +27,20 @@
 
 [Guids]
   gLoongArchQemuPkgTokenSpaceGuid  = { 0x0e0383ce, 0x0151, 0x4d01, { 0x80, 0x0e, 0x3f, 0xef, 0x8b, 0x27, 0x6d, 0x52 } }
+  gEfiLoongsonBootparamsTableGuid  = { 0x4660f721, 0x2ec5, 0x416a, { 0x89, 0x9a, 0x43, 0x18, 0x02, 0x50, 0xa0, 0xc9 } }
+
+[Protocols]
 
 [PcdsFixedAtBuild, PcdsDynamic]
   gLoongArchQemuPkgTokenSpaceGuid.PcdFlashPeiFvBase|0x0|UINT64|0x00000003
   gLoongArchQemuPkgTokenSpaceGuid.PcdFlashPeiFvSize|0x0|UINT32|0x00000004
+  gLoongArchQemuPkgTokenSpaceGuid.PcdFlashDxeFvOffset|0x0|UINT64|0x00000007
   gLoongArchQemuPkgTokenSpaceGuid.PcdFlashDxeFvBase|0x0|UINT64|0x00000008
   gLoongArchQemuPkgTokenSpaceGuid.PcdFlashDxeFvSize|0x0|UINT32|0x00000009
+  gLoongArchQemuPkgTokenSpaceGuid.PcdFlashSecModuleBase|0x0|UINT64|0x0000000a
+  gLoongArchQemuPkgTokenSpaceGuid.PcdFlashSecModuleSize|0x0|UINT32|0x0000000b
+  gLoongArchQemuPkgTokenSpaceGuid.PcdFlashEventLogBase|0x0|UINT64|0x0000000c
+  gLoongArchQemuPkgTokenSpaceGuid.PcdFlashEventLogSize|0x0|UINT32|0x0000000d
   gLoongArchQemuPkgTokenSpaceGuid.PcdDeviceTreeBase|0x0|UINT64|0x00000018
   gLoongArchQemuPkgTokenSpaceGuid.PcdDeviceTreePadding|256|UINT32|0x00000019
 
@@ -45,6 +54,8 @@
 [PcdsFixedAtBuild.LOONGARCH64]
    gEmbeddedTokenSpaceGuid.PcdPrePiCpuMemorySize|32|UINT8|0x00000010
    gEmbeddedTokenSpaceGuid.PcdPrePiCpuIoSize|0|UINT8|0x00000011
+   gLoongArchQemuPkgTokenSpaceGuid.PcdDeviceSpaceStartAddress|0x10000000|UINT32|0x00000012
+   gLoongArchQemuPkgTokenSpaceGuid.PcdDeviceSpaceLength|0x80000000|UINT32|0x00000013
 
 [PcdsDynamic]
   gLoongArchQemuPkgTokenSpaceGuid.PcdRamSize|0x40000000|UINT64|0x00000041
@@ -56,3 +67,4 @@
   gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPmd|0x0|UINT64|0x00000047
   gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPte|0x0|UINT64|0x00000048
 
+[PcdsFeatureFlag]
diff --git a/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc b/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc
index 1ca8cc09f7..74c83720b7 100644
--- a/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc
+++ b/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc
@@ -24,6 +24,25 @@
   FLASH_DEFINITION               = Platform/Loongson/LoongArchQemuPkg/Loongson.fdf
   TTY_TERMINAL                   = FALSE
 
+  #
+  # Defines for default states.  These can be changed on the command line.
+  # -D FLAG=VALUE
+  DEFINE TTY_TERMINAL            = FALSE
+  DEFINE SECURE_BOOT_ENABLE      = FALSE
+  DEFINE TPM2_ENABLE             = FALSE
+  DEFINE TPM2_CONFIG_ENABLE      = FALSE
+
+  #
+  # Network definition
+  #
+  DEFINE NETWORK_IP6_ENABLE              = FALSE
+  DEFINE NETWORK_HTTP_BOOT_ENABLE        = FALSE
+  DEFINE NETWORK_SNP_ENABLE              = FALSE
+  DEFINE NETWORK_TLS_ENABLE              = FALSE
+  DEFINE NETWORK_ALLOW_HTTP_CONNECTIONS  = TRUE
+  DEFINE NETWORK_ISCSI_ENABLE            = FALSE
+
+!include NetworkPkg/NetworkDefines.dsc.inc
 ############################################################################
 #
 # Defines for default states.  These can be changed on the command line.
@@ -37,16 +56,25 @@
   #
   GCC:*_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES
 
+!include NetworkPkg/NetworkBuildOptions.dsc.inc
 
 [BuildOptions.LOONGARCH64.EDKII.SEC]
   *_*_*_CC_FLAGS                 =
 
-#[BuildOptions.common.EDKII.DXE_CORE,BuildOptions.common.EDKII.DXE_DRIVER,BuildOptions.common.EDKII.UEFI_DRIVER,BuildOptions.common.EDKII.UEFI_APPLICATION]
-#  GCC:*_*_*_DLINK_FLAGS = -z common-page-size=0x1000
+[BuildOptions.common.EDKII.DXE_CORE,BuildOptions.common.EDKII.DXE_DRIVER,BuildOptions.common.EDKII.UEFI_DRIVER,BuildOptions.common.EDKII.UEFI_APPLICATION]
+  GCC:*_*_*_DLINK_FLAGS = -z common-page-size=0x1000
 
 [BuildOptions.common.EDKII.DXE_RUNTIME_DRIVER]
   GCC:*_*_LOONGARCH64_DLINK_FLAGS = -z common-page-size=0x10000
 
+################################################################################
+#
+# SKU Identification section - list of all SKU IDs supported by this Platform.
+#
+################################################################################
+[SkuIds]
+  0|DEFAULT
+
 ################################################################################
 #
 # Library Class section - list of all Library Classes needed by this Platform.
@@ -61,19 +89,88 @@
   PrintLib                         | MdePkg/Library/BasePrintLib/BasePrintLib.inf
   BaseMemoryLib                    | MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
 
+  # Networking Requirements
+!include NetworkPkg/NetworkLibs.dsc.inc
+!if $(NETWORK_TLS_ENABLE) == TRUE
+  TlsLib|CryptoPkg/Library/TlsLib/TlsLib.inf
+!endif
 
   BaseLib                          | MdePkg/Library/BaseLib/BaseLib.inf
+  SafeIntLib                       | MdePkg/Library/BaseSafeIntLib/BaseSafeIntLib.inf
+  TimeBaseLib                      | EmbeddedPkg/Library/TimeBaseLib/TimeBaseLib.inf
+  BmpSupportLib                    | MdeModulePkg/Library/BaseBmpSupportLib/BaseBmpSupportLib.inf
+  SynchronizationLib               | MdePkg/Library/BaseSynchronizationLib/BaseSynchronizationLib.inf
+  CpuLib                           | MdePkg/Library/BaseCpuLib/BaseCpuLib.inf
   PerformanceLib                   | MdePkg/Library/BasePerformanceLibNull/BasePerformanceLibNull.inf
   PeCoffLib                        | MdePkg/Library/BasePeCoffLib/BasePeCoffLib.inf
   CacheMaintenanceLib              | MdePkg/Library/BaseCacheMaintenanceLib/BaseCacheMaintenanceLib.inf
   UefiDecompressLib                | MdePkg/Library/BaseUefiDecompressLib/BaseUefiDecompressLib.inf
+  UefiHiiServicesLib               | MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesLib.inf
+  HiiLib                           | MdeModulePkg/Library/UefiHiiLib/UefiHiiLib.inf
+  CapsuleLib                       | MdeModulePkg/Library/DxeCapsuleLibNull/DxeCapsuleLibNull.inf
+  DxeServicesLib                   | MdePkg/Library/DxeServicesLib/DxeServicesLib.inf
+  DxeServicesTableLib              | MdePkg/Library/DxeServicesTableLib/DxeServicesTableLib.inf
   PeCoffGetEntryPointLib           | MdePkg/Library/BasePeCoffGetEntryPointLib/BasePeCoffGetEntryPointLib.inf
+  PciLib                           | MdePkg/Library/BasePciLibPciExpress/BasePciLibPciExpress.inf
+  PciExpressLib                    | OvmfPkg/Library/BaseCachingPciExpressLib/BaseCachingPciExpressLib.inf
+  PciCapLib                        | OvmfPkg/Library/BasePciCapLib/BasePciCapLib.inf
+  PciCapPciSegmentLib              | OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.inf
+  PciCapPciIoLib                   | OvmfPkg/Library/UefiPciCapPciIoLib/UefiPciCapPciIoLib.inf
   IoLib                            | MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf
   SerialPortLib                    | Platform/Loongson/LoongArchQemuPkg/Library/SerialPortLib/SerialPortLib.inf
+  EfiResetSystemLib                | Platform/Loongson/LoongArchQemuPkg/Library/ResetSystemAcpiLib/BaseResetSystemAcpiGedLib.inf
+  ResetSystemLib                   | Platform/Loongson/LoongArchQemuPkg/Library/ResetSystemAcpiLib/BaseResetSystemAcpiGedLib.inf
+
+  UefiLib                          | MdePkg/Library/UefiLib/UefiLib.inf
+  UefiBootServicesTableLib         | MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf
+  UefiRuntimeServicesTableLib      | MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf
+  UefiDriverEntryPoint             | MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf
+  UefiApplicationEntryPoint        | MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf
+  DevicePathLib                    | MdePkg/Library/UefiDevicePathLibDevicePathProtocol/UefiDevicePathLibDevicePathProtocol.inf
+  FileHandleLib                    | MdePkg/Library/UefiFileHandleLib/UefiFileHandleLib.inf
+  SecurityManagementLib            | MdeModulePkg/Library/DxeSecurityManagementLib/DxeSecurityManagementLib.inf
+  UefiUsbLib                       | MdePkg/Library/UefiUsbLib/UefiUsbLib.inf
+  SerializeVariablesLib            | OvmfPkg/Library/SerializeVariablesLib/SerializeVariablesLib.inf
+  CustomizedDisplayLib             | MdeModulePkg/Library/CustomizedDisplayLib/CustomizedDisplayLib.inf
   DebugPrintErrorLevelLib          | MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf
+  TpmMeasurementLib                | MdeModulePkg/Library/TpmMeasurementLibNull/TpmMeasurementLibNull.inf
+  AuthVariableLib                  | MdeModulePkg/Library/AuthVariableLibNull/AuthVariableLibNull.inf
+  VarCheckLib                      | MdeModulePkg/Library/VarCheckLib/VarCheckLib.inf
+  VariablePolicyLib                | MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf
+  VariablePolicyHelperLib          | MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.inf
+  SortLib                          | MdeModulePkg/Library/UefiSortLib/UefiSortLib.inf
   FdtLib                           | EmbeddedPkg/Library/FdtLib/FdtLib.inf
+  PciPcdProducerLib                | OvmfPkg/Fdt/FdtPciPcdProducerLib/FdtPciPcdProducerLib.inf
+  PciSegmentLib                    | MdePkg/Library/BasePciSegmentLibPci/BasePciSegmentLibPci.inf
+  PciHostBridgeLib                 | OvmfPkg/Fdt/FdtPciHostBridgeLib/FdtPciHostBridgeLib.inf
+  PciHostBridgeUtilityLib          | ArmVirtPkg/Library/ArmVirtPciHostBridgeUtilityLib/ArmVirtPciHostBridgeUtilityLib.inf
+  MmuLib                           | Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLib.inf
+  FileExplorerLib                  | MdeModulePkg/Library/FileExplorerLib/FileExplorerLib.inf
+
+!if $(HTTP_BOOT_ENABLE) == TRUE
+  HttpLib                          | MdeModulePkg/Library/DxeHttpLib/DxeHttpLib.inf
+!endif
+  UefiBootManagerLib               | MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.inf
+  OrderedCollectionLib             | MdePkg/Library/BaseOrderedCollectionRedBlackTreeLib/BaseOrderedCollectionRedBlackTreeLib.inf
+  ReportStatusCodeLib              | MdePkg/Library/BaseReportStatusCodeLibNull/BaseReportStatusCodeLibNull.inf
+
+  PeCoffGetEntryPointLib           | MdePkg/Library/BasePeCoffGetEntryPointLib/BasePeCoffGetEntryPointLib.inf
   PeCoffExtraActionLib             | MdePkg/Library/BasePeCoffExtraActionLibNull/BasePeCoffExtraActionLibNull.inf
   DebugAgentLib                    | MdeModulePkg/Library/DebugAgentLibNull/DebugAgentLibNull.inf
+  CpuExceptionHandlerLib           | MdeModulePkg/Library/CpuExceptionHandlerLibNull/CpuExceptionHandlerLibNull.inf
+
+  PlatformBootManagerLib           | Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
+  BootLogoLib                      | MdeModulePkg/Library/BootLogoLib/BootLogoLib.inf
+  QemuBootOrderLib                 | OvmfPkg/Library/QemuBootOrderLib/QemuBootOrderLib.inf
+  QemuFwCfgSimpleParserLib         | OvmfPkg/Library/QemuFwCfgSimpleParserLib/QemuFwCfgSimpleParserLib.inf
+  QemuLoadImageLib                 | OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf
+
+  #
+  # Virtio Support
+  #
+  VirtioLib                        | OvmfPkg/Library/VirtioLib/VirtioLib.inf
+  FrameBufferBltLib                | MdeModulePkg/Library/FrameBufferBltLib/FrameBufferBltLib.inf
+  QemuFwCfgLib                     | OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibMmio.inf
 
   DebugLib                         | MdePkg/Library/BaseDebugLibSerialPort/BaseDebugLibSerialPort.inf
 
@@ -91,7 +188,7 @@
   ReportStatusCodeLib              | MdeModulePkg/Library/PeiReportStatusCodeLib/PeiReportStatusCodeLib.inf
   OemHookStatusCodeLib             | MdeModulePkg/Library/OemHookStatusCodeLibNull/OemHookStatusCodeLibNull.inf
   PeCoffGetEntryPointLib           | MdePkg/Library/BasePeCoffGetEntryPointLib/BasePeCoffGetEntryPointLib.inf
-  QemuFwCfgLib                     | Platform/Loongson/LoongArchQemuPkg/Library/QemuFwCfgLib/QemuFwCfgLib.inf
+  QemuFwCfgLib                     | Platform/Loongson/LoongArchQemuPkg/Library/QemuFwCfgLib/QemuFwCfgPeiLib.inf
   MmuLib                           | Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLibPei.inf
 
 [LibraryClasses.common.PEIM]
@@ -106,9 +203,59 @@
   ExtractGuidedSectionLib          | MdePkg/Library/PeiExtractGuidedSectionLib/PeiExtractGuidedSectionLib.inf
   PcdLib                           | MdePkg/Library/PeiPcdLib/PeiPcdLib.inf
   QemuFwCfgS3Lib                   | OvmfPkg/Library/QemuFwCfgS3Lib/PeiQemuFwCfgS3LibFwCfg.inf
-  QemuFwCfgLib                     | Platform/Loongson/LoongArchQemuPkg/Library/QemuFwCfgLib/QemuFwCfgLib.inf
+  QemuFwCfgLib                     | Platform/Loongson/LoongArchQemuPkg/Library/QemuFwCfgLib/QemuFwCfgPeiLib.inf
 
   MmuLib                       | Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLibPei.inf
+[LibraryClasses.common.DXE_CORE]
+  HobLib                           | MdePkg/Library/DxeCoreHobLib/DxeCoreHobLib.inf
+  DxeCoreEntryPoint                | MdePkg/Library/DxeCoreEntryPoint/DxeCoreEntryPoint.inf
+  MemoryAllocationLib              | MdeModulePkg/Library/DxeCoreMemoryAllocationLib/DxeCoreMemoryAllocationLib.inf
+  ReportStatusCodeLib              | MdeModulePkg/Library/DxeReportStatusCodeLib/DxeReportStatusCodeLib.inf
+
+[LibraryClasses.common.DXE_RUNTIME_DRIVER]
+  PcdLib                           | MdePkg/Library/DxePcdLib/DxePcdLib.inf
+  HobLib                           | MdePkg/Library/DxeHobLib/DxeHobLib.inf
+  DxeCoreEntryPoint                | MdePkg/Library/DxeCoreEntryPoint/DxeCoreEntryPoint.inf
+  MemoryAllocationLib              | MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
+  ReportStatusCodeLib              | MdeModulePkg/Library/RuntimeDxeReportStatusCodeLib/RuntimeDxeReportStatusCodeLib.inf
+  UefiRuntimeLib                   | MdePkg/Library/UefiRuntimeLib/UefiRuntimeLib.inf
+  ExtractGuidedSectionLib          | MdePkg/Library/PeiExtractGuidedSectionLib/PeiExtractGuidedSectionLib.inf
+  QemuFwCfgS3Lib                   | OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf
+  RealTimeClockLib                 | Platform/Loongson/LoongArchQemuPkg/Library/LsRealTimeClockLib/LsRealTimeClockLib.inf
+  VariablePolicyLib                | MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLibRuntimeDxe.inf
+  QemuFwCfgLib                     | Platform/Loongson/LoongArchQemuPkg/Library/QemuFwCfgLib/QemuFwCfgPeiLib.inf
+  EfiResetSystemLib                | Platform/Loongson/LoongArchQemuPkg/Library/ResetSystemAcpiLib/DxeResetSystemAcpiGedLib.inf
+  ResetSystemLib                   | Platform/Loongson/LoongArchQemuPkg/Library/ResetSystemAcpiLib/DxeResetSystemAcpiGedLib.inf
+!if $(TARGET) != RELEASE
+  DebugLib                         | MdePkg/Library/DxeRuntimeDebugLibSerialPort/DxeRuntimeDebugLibSerialPort.inf
+!endif
+
+[LibraryClasses.common.UEFI_DRIVER]
+  PcdLib                           | MdePkg/Library/DxePcdLib/DxePcdLib.inf
+  HobLib                           | MdePkg/Library/DxeHobLib/DxeHobLib.inf
+  DxeCoreEntryPoint                | MdePkg/Library/DxeCoreEntryPoint/DxeCoreEntryPoint.inf
+  MemoryAllocationLib              | MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
+  ReportStatusCodeLib              | MdeModulePkg/Library/DxeReportStatusCodeLib/DxeReportStatusCodeLib.inf
+  UefiScsiLib                      | MdePkg/Library/UefiScsiLib/UefiScsiLib.inf
+  ExtractGuidedSectionLib          | MdePkg/Library/PeiExtractGuidedSectionLib/PeiExtractGuidedSectionLib.inf
+  QemuFwCfgLib                     | OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibMmio.inf
+
+[LibraryClasses.common.DXE_DRIVER]
+  PcdLib                           | MdePkg/Library/DxePcdLib/DxePcdLib.inf
+  HobLib                           | MdePkg/Library/DxeHobLib/DxeHobLib.inf
+  MemoryAllocationLib              | MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
+  ReportStatusCodeLib              | MdeModulePkg/Library/DxeReportStatusCodeLib/DxeReportStatusCodeLib.inf
+  UefiScsiLib                      | MdePkg/Library/UefiScsiLib/UefiScsiLib.inf
+  CpuExceptionHandlerLib           | UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf
+  ExtractGuidedSectionLib          | MdePkg/Library/DxeExtractGuidedSectionLib/DxeExtractGuidedSectionLib.inf
+  QemuFwCfgS3Lib                   | OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf
+  QemuFwCfgLib                     | OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibMmio.inf
+
+[LibraryClasses.common.UEFI_APPLICATION]
+  PcdLib                           | MdePkg/Library/DxePcdLib/DxePcdLib.inf
+  HobLib                           | MdePkg/Library/DxeHobLib/DxeHobLib.inf
+  MemoryAllocationLib              | MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
+  ExtractGuidedSectionLib          | MdePkg/Library/DxeExtractGuidedSectionLib/DxeExtractGuidedSectionLib.inf
 
 
 ################################################################################
@@ -116,12 +263,32 @@
 # Pcd Section - list of all EDK II PCD Entries defined by this Platform.
 #
 ################################################################################
+[PcdsFeatureFlag]
+   gEfiMdeModulePkgTokenSpaceGuid.PcdHiiOsRuntimeSupport               | FALSE
+#  gEfiMdeModulePkgTokenSpaceGuid.PcdStatusCodeUseSerial               | TRUE
+#  gEfiMdeModulePkgTokenSpaceGuid.PcdStatusCodeUseMemory               | TRUE
+  gEfiMdeModulePkgTokenSpaceGuid.PcdDxeIplSupportUefiDecompress        | TRUE
+  gEfiMdeModulePkgTokenSpaceGuid.PcdConOutGopSupport                   | TRUE
+  gEfiMdeModulePkgTokenSpaceGuid.PcdConOutUgaSupport                   | FALSE
+  gEfiMdeModulePkgTokenSpaceGuid.PcdPciBusHotplugDeviceSupport         | FALSE
+  gUefiOvmfPkgTokenSpaceGuid.PcdQemuBootOrderPciTranslation            | TRUE
+  gUefiOvmfPkgTokenSpaceGuid.PcdQemuBootOrderMmioTranslation           | TRUE
 [PcdsFixedAtBuild]
 ## BaseLib ##
   gEfiMdePkgTokenSpaceGuid.PcdMaximumUnicodeStringLength               | 1000000
   gEfiMdePkgTokenSpaceGuid.PcdMaximumAsciiStringLength                 | 1000000
   gEfiMdePkgTokenSpaceGuid.PcdMaximumLinkedListLength                  | 1000000
+  gEfiMdePkgTokenSpaceGuid.PcdSpinLockTimeout                          | 10000000
 
+  gEfiMdeModulePkgTokenSpaceGuid.PcdStatusCodeMemorySize               | 1
+  gEfiMdeModulePkgTokenSpaceGuid.PcdResetOnMemoryTypeInformationChange | FALSE
+  gEfiMdePkgTokenSpaceGuid.PcdMaximumGuidedExtractHandler              | 0x10
+  gEfiMdeModulePkgTokenSpaceGuid.PcdMaxVariableSize                    | 0x2000
+  gEfiMdeModulePkgTokenSpaceGuid.PcdMaxHardwareErrorVariableSize       | 0x8000
+  gEfiMdeModulePkgTokenSpaceGuid.PcdVpdBaseAddress                     | 0x0
+  gEmbeddedTokenSpaceGuid.PcdPrePiCpuMemorySize                        | 48
+  gEmbeddedTokenSpaceGuid.PcdPrePiCpuIoSize                            | 32
+  gEfiMdePkgTokenSpaceGuid.PcdReportStatusCodePropertyMask             | 0x07
   gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel                     | 0x8000004F
   # DEBUG_INIT      0x00000001  // Initialization
   # DEBUG_WARN      0x00000002  // Warnings
@@ -165,6 +332,60 @@
   # 0x90000000 - 0xA0000000
   #
   gLoongArchQemuPkgTokenSpaceGuid.PcdUefiRamTop                        | 0x10000000
+  gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiExposedTableVersions           | 0x06
+
+  gEfiMdeModulePkgTokenSpaceGuid.PcdBootManagerMenuFile                | { 0x21, 0xaa, 0x2c, 0x46, 0x14, 0x76, 0x03, 0x45, 0x83, 0x6e, 0x8a, 0xb6, 0xf4, 0x66, 0x23, 0x31 }
+
+  #
+  # Network Pcds
+  #
+!include NetworkPkg/NetworkPcds.dsc.inc
+################################################################################
+#
+# Pcd Dynamic Section - list of all EDK II PCD Entries defined by this Platform
+#
+################################################################################
+[PcdsDynamicDefault]
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase         | 0
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase64       | 0
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase64       | 0
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase       | 0
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase64     | 0
+  gEfiMdeModulePkgTokenSpaceGuid.PcdEmuVariableNvStoreReserved         | 0
+  gEfiMdeModulePkgTokenSpaceGuid.PcdPciDisableBusEnumeration           | FALSE
+  gEfiMdeModulePkgTokenSpaceGuid.PcdVideoHorizontalResolution          | 800
+  gEfiMdeModulePkgTokenSpaceGuid.PcdVideoVerticalResolution            | 600
+  gEfiMdePkgTokenSpaceGuid.PcdPlatformBootTimeOut                      | 2
+
+  # Set video resolution for text setup.
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSetupVideoHorizontalResolution     | 640
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSetupVideoVerticalResolution       | 480
+
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSmbiosVersion                      | 0x0300
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSmbiosDocRev                       | 0x0
+
+  gLoongArchQemuPkgTokenSpaceGuid.PcdRamSize                           | 0x40000000
+
+  ## If TRUE, OvmfPkg/AcpiPlatformDxe will not wait for PCI
+  #  enumeration to complete before installing ACPI tables.
+  gEfiMdeModulePkgTokenSpaceGuid.PcdPciDisableBusEnumeration           |TRUE
+  gEfiMdePkgTokenSpaceGuid.PcdPciIoTranslation                         |0x0
+  # set PcdPciExpressBaseAddress to MAX_UINT64, which signifies that this
+  # PCD and PcdPciDisableBusEnumeration above have not been assigned yet
+  gEfiMdePkgTokenSpaceGuid.PcdPciExpressBaseAddress                    |0xFFFFFFFFFFFFFFFF
+
+  #
+  # IPv4 and IPv6 PXE Boot support.
+  #
+  gEfiNetworkPkgTokenSpaceGuid.PcdIPv4PXESupport                       | 0x01
+  gEfiNetworkPkgTokenSpaceGuid.PcdIPv6PXESupport                       | 0x01
+
+  #
+  # SMBIOS entry point version
+  #
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSmbiosVersion|0x0300
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSmbiosDocRev|0x0
+  gUefiOvmfPkgTokenSpaceGuid.PcdQemuSmbiosValidated|TRUE
 
 [Components]
 
@@ -192,3 +413,194 @@
       PcdLib|MdePkg/Library/PeiPcdLib/PeiPcdLib.inf
   }
 
+  #
+  # DXE Phase modules
+  #
+  MdeModulePkg/Core/Dxe/DxeMain.inf {
+    <LibraryClasses>
+      NULL                             | MdeModulePkg/Library/LzmaCustomDecompressLib/LzmaCustomDecompressLib.inf
+      DevicePathLib                    | MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf
+      ExtractGuidedSectionLib          | MdePkg/Library/DxeExtractGuidedSectionLib/DxeExtractGuidedSectionLib.inf
+  }
+
+  MdeModulePkg/Universal/ReportStatusCodeRouter/RuntimeDxe/ReportStatusCodeRouterRuntimeDxe.inf
+  MdeModulePkg/Universal/StatusCodeHandler/RuntimeDxe/StatusCodeHandlerRuntimeDxe.inf
+  MdeModulePkg/Universal/PCD/Dxe/Pcd.inf  {
+   <LibraryClasses>
+      PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
+  }
+
+  MdeModulePkg/Core/RuntimeDxe/RuntimeDxe.inf
+  Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.inf
+  MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf
+  MdeModulePkg/Universal/MonotonicCounterRuntimeDxe/MonotonicCounterRuntimeDxe.inf
+  MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleRuntimeDxe.inf
+  MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxe.inf
+  Platform/Loongson/LoongArchQemuPkg/Drivers/StableTimerDxe/TimerDxe.inf
+  MdeModulePkg/Universal/ResetSystemRuntimeDxe/ResetSystemRuntimeDxe.inf
+  MdeModulePkg/Universal/Metronome/Metronome.inf
+  EmbeddedPkg/RealTimeClockRuntimeDxe/RealTimeClockRuntimeDxe.inf
+
+  #
+  # Variable
+  #
+#  Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
+  OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.inf {
+    <LibraryClasses>
+      PlatformFvbLib|OvmfPkg/Library/EmuVariableFvbLib/EmuVariableFvbLib.inf
+  }
+  MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf
+  MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf {
+    <LibraryClasses>
+      NULL|MdeModulePkg/Library/VarCheckUefiLib/VarCheckUefiLib.inf
+      BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
+  }
+
+  #
+  # Platform Driver
+  #
+  OvmfPkg/VirtioBlkDxe/VirtioBlk.inf
+  OvmfPkg/VirtioScsiDxe/VirtioScsi.inf
+  OvmfPkg/VirtioRngDxe/VirtioRng.inf
+
+  #
+  # File system
+  #
+  MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.inf
+  MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe.inf
+  MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxe.inf
+  FatPkg/EnhancedFatDxe/Fat.inf
+
+  #
+  #BDS
+  #
+  MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.inf {
+    <LibraryClasses>
+      DevicePathLib                    | MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf
+      PcdLib                           | MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
+  }
+  MdeModulePkg/Universal/DisplayEngineDxe/DisplayEngineDxe.inf
+  MdeModulePkg/Universal/SetupBrowserDxe/SetupBrowserDxe.inf
+  MdeModulePkg/Universal/BdsDxe/BdsDxe.inf
+  MdeModulePkg/Logo/LogoDxe.inf
+  MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabaseDxe.inf
+  MdeModulePkg/Application/UiApp/UiApp.inf {
+    <LibraryClasses>
+      NULL|MdeModulePkg/Library/DeviceManagerUiLib/DeviceManagerUiLib.inf
+      NULL|MdeModulePkg/Library/BootManagerUiLib/BootManagerUiLib.inf
+      NULL|MdeModulePkg/Library/BootMaintenanceManagerUiLib/BootMaintenanceManagerUiLib.inf
+  }
+
+  OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf {
+    <LibraryClasses>
+      NULL|OvmfPkg/Library/BlobVerifierLibNull/BlobVerifierLibNull.inf
+  }
+
+  #
+  # Network Support
+  #
+#!include NetworkPkg/NetworkComponents.dsc.inc
+
+#  NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf {
+#    <LibraryClasses>
+#      NULL|OvmfPkg/Library/PxeBcPcdProducerLib/PxeBcPcdProducerLib.inf
+#  }
+
+!if $(NETWORK_TLS_ENABLE) == TRUE
+  NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigDxe.inf {
+    <LibraryClasses>
+      NULL|OvmfPkg/Library/TlsAuthConfigLib/TlsAuthConfigLib.inf
+  }
+!endif
+  OvmfPkg/VirtioNetDxe/VirtioNet.inf
+
+
+  #
+  # IDE/SCSI
+  #
+  OvmfPkg/SataControllerDxe/SataControllerDxe.inf
+  MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AtaAtapiPassThru.inf
+  MdeModulePkg/Bus/Ata/AtaBusDxe/AtaBusDxe.inf
+  MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBusDxe.inf
+  MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDiskDxe.inf
+
+  #
+  # SMBIOS Support
+  #
+  MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.inf {
+    <LibraryClasses>
+      NULL                             | OvmfPkg/Library/SmbiosVersionLib/DetectSmbiosVersionLib.inf
+  }
+  OvmfPkg/SmbiosPlatformDxe/SmbiosPlatformDxe.inf
+
+  #
+  # PCI
+  #
+  Platform/Loongson/LoongArchQemuPkg/Drivers/PciCpuIo2Dxe/PciCpuIo2Dxe.inf
+  EmbeddedPkg/Drivers/FdtClientDxe/FdtClientDxe.inf
+  MdeModulePkg/Bus/Pci/PciHostBridgeDxe/PciHostBridgeDxe.inf
+  MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf
+  OvmfPkg/VirtioPciDeviceDxe/VirtioPciDeviceDxe.inf
+  OvmfPkg/Virtio10Dxe/Virtio10.inf
+
+  #
+  # Console
+  #
+  MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatformDxe.inf
+  MdeModulePkg/Universal/Console/ConSplitterDxe/ConSplitterDxe.inf
+  MdeModulePkg/Universal/Console/TerminalDxe/TerminalDxe.inf
+  MdeModulePkg/Universal/PrintDxe/PrintDxe.inf
+  MdeModulePkg/Universal/SerialDxe/SerialDxe.inf
+  MdeModulePkg/Universal/Console/GraphicsConsoleDxe/GraphicsConsoleDxe.inf {
+    <LibraryClasses>
+      PcdLib|MdePkg/Library/DxePcdLib/DxePcdLib.inf
+  }
+
+  #
+  # Video
+  #
+  OvmfPkg/QemuRamfbDxe/QemuRamfbDxe.inf
+  OvmfPkg/VirtioGpuDxe/VirtioGpu.inf
+  OvmfPkg/PlatformDxe/Platform.inf
+
+  #
+  # Usb Support
+  #
+  MdeModulePkg/Bus/Pci/UhciDxe/UhciDxe.inf
+  MdeModulePkg/Bus/Pci/EhciDxe/EhciDxe.inf
+  MdeModulePkg/Bus/Pci/XhciDxe/XhciDxe.inf
+  MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.inf
+  MdeModulePkg/Bus/Usb/UsbKbDxe/UsbKbDxe.inf
+  MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassStorageDxe.inf
+
+  #
+  # ACPI Support
+  #
+  MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTableDxe.inf
+  MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf
+  OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf
+
+  #
+  #app
+  #
+  ShellPkg/Application/Shell/Shell.inf {
+    <LibraryClasses>
+      ShellCommandLib|ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.inf
+      NULL|ShellPkg/Library/UefiShellLevel2CommandsLib/UefiShellLevel2CommandsLib.inf
+      NULL|ShellPkg/Library/UefiShellLevel1CommandsLib/UefiShellLevel1CommandsLib.inf
+      NULL|ShellPkg/Library/UefiShellLevel3CommandsLib/UefiShellLevel3CommandsLib.inf
+      NULL|ShellPkg/Library/UefiShellDriver1CommandsLib/UefiShellDriver1CommandsLib.inf
+      NULL|ShellPkg/Library/UefiShellDebug1CommandsLib/UefiShellDebug1CommandsLib.inf
+      NULL|ShellPkg/Library/UefiShellInstall1CommandsLib/UefiShellInstall1CommandsLib.inf
+      NULL|ShellPkg/Library/UefiShellNetwork1CommandsLib/UefiShellNetwork1CommandsLib.inf
+      HandleParsingLib|ShellPkg/Library/UefiHandleParsingLib/UefiHandleParsingLib.inf
+      ShellLib|ShellPkg/Library/UefiShellLib/UefiShellLib.inf
+      FileHandleLib|MdePkg/Library/UefiFileHandleLib/UefiFileHandleLib.inf
+      SortLib|MdeModulePkg/Library/UefiSortLib/UefiSortLib.inf
+      PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf
+      BcfgCommandLib|ShellPkg/Library/UefiShellBcfgCommandLib/UefiShellBcfgCommandLib.inf
+<PcdsFixedAtBuild>
+      gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0xFF
+      gEfiShellPkgTokenSpaceGuid.PcdShellLibAutoInitialize|FALSE
+      gEfiMdePkgTokenSpaceGuid.PcdUefiLibMaxPrintBufferSize|8000
+  }
diff --git a/Platform/Loongson/LoongArchQemuPkg/Loongson.fdf b/Platform/Loongson/LoongArchQemuPkg/Loongson.fdf
index f964304fdc..ba2942e455 100644
--- a/Platform/Loongson/LoongArchQemuPkg/Loongson.fdf
+++ b/Platform/Loongson/LoongArchQemuPkg/Loongson.fdf
@@ -22,6 +22,15 @@ $(SECFV_OFFSET)|$(SECFV_SIZE)
 gLoongArchQemuPkgTokenSpaceGuid.PcdFlashSecFvBase|gLoongArchQemuPkgTokenSpaceGuid.PcdFlashSecFvSize
 FV = SECFV
 
+$(PEIFV_OFFSET)|$(PEIFV_SIZE)
+gLoongArchQemuPkgTokenSpaceGuid.PcdFlashPeiFvBase|gLoongArchQemuPkgTokenSpaceGuid.PcdFlashPeiFvSize
+FV = PEIFV
+
+$(DXEFV_OFFSET)|$(DXEFV_SIZE)
+gLoongArchQemuPkgTokenSpaceGuid.PcdFlashDxeFvBase|gLoongArchQemuPkgTokenSpaceGuid.PcdFlashDxeFvSize
+FV = FVMAIN_COMPACT
+
+!include VarStore.fdf.inc
 #####################################################################################################
 [FV.SECFV]
 FvNameGuid         = 587d4265-5e71-41da-9c35-4258551f1e22
@@ -78,6 +87,178 @@ INF  MdeModulePkg/Core/Pei/PeiMain.inf
 INF  MdeModulePkg/Universal/PCD/Pei/Pcd.inf
 INF  MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf
 INF  Platform/Loongson/LoongArchQemuPkg/PlatformPei/PlatformPei.inf
+#####################################################################################################
+[FV.DXEFV]
+FvNameGuid         = 5d19a5b3-130f-459b-a292-9270a9e6bc62
+BlockSize          = $(BLOCK_SIZE)
+FvAlignment        = 16
+ERASE_POLARITY     = 1
+MEMORY_MAPPED      = TRUE
+STICKY_WRITE       = TRUE
+LOCK_CAP           = TRUE
+LOCK_STATUS        = TRUE
+READ_DISABLED_CAP  = TRUE
+READ_ENABLED_CAP   = TRUE
+READ_STATUS        = TRUE
+READ_LOCK_CAP      = TRUE
+READ_LOCK_STATUS   = TRUE
+WRITE_DISABLED_CAP = TRUE
+WRITE_ENABLED_CAP  = TRUE
+WRITE_STATUS       = TRUE
+WRITE_LOCK_CAP     = TRUE
+WRITE_LOCK_STATUS  = TRUE
+
+APRIORI DXE {
+  INF  MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.inf
+  INF  MdeModulePkg/Universal/PCD/Dxe/Pcd.inf
+}
+
+#
+# DXE Phase modules
+#
+INF  MdeModulePkg/Core/Dxe/DxeMain.inf
+
+INF  MdeModulePkg/Universal/ReportStatusCodeRouter/RuntimeDxe/ReportStatusCodeRouterRuntimeDxe.inf
+INF  MdeModulePkg/Universal/StatusCodeHandler/RuntimeDxe/StatusCodeHandlerRuntimeDxe.inf
+INF  MdeModulePkg/Universal/PCD/Dxe/Pcd.inf
+INF  MdeModulePkg/Core/RuntimeDxe/RuntimeDxe.inf
+INF  Platform/Loongson/LoongArchQemuPkg/Drivers/CpuDxe/CpuDxe.inf
+INF  MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxe.inf
+INF  Platform/Loongson/LoongArchQemuPkg/Drivers/StableTimerDxe/TimerDxe.inf
+INF  MdeModulePkg/Universal/ResetSystemRuntimeDxe/ResetSystemRuntimeDxe.inf
+INF  MdeModulePkg/Universal/Metronome/Metronome.inf
+INF  MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf
+INF  MdeModulePkg/Universal/MonotonicCounterRuntimeDxe/MonotonicCounterRuntimeDxe.inf
+INF  EmbeddedPkg/RealTimeClockRuntimeDxe/RealTimeClockRuntimeDxe.inf
+INF  MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleRuntimeDxe.inf
+
+#
+# Variable
+#
+INF  Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
+INF  OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.inf
+INF  MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf
+INF  MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf
+#
+# PCI
+#
+INF  Platform/Loongson/LoongArchQemuPkg/Drivers/PciCpuIo2Dxe/PciCpuIo2Dxe.inf
+INF  EmbeddedPkg/Drivers/FdtClientDxe/FdtClientDxe.inf
+INF  MdeModulePkg/Bus/Pci/PciHostBridgeDxe/PciHostBridgeDxe.inf
+INF  MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf
+INF  OvmfPkg/VirtioPciDeviceDxe/VirtioPciDeviceDxe.inf
+INF  OvmfPkg/Virtio10Dxe/Virtio10.inf
+
+#
+# Platform Driver
+#
+INF  OvmfPkg/VirtioBlkDxe/VirtioBlk.inf
+INF  OvmfPkg/VirtioScsiDxe/VirtioScsi.inf
+INF  OvmfPkg/VirtioRngDxe/VirtioRng.inf
+INF  OvmfPkg/VirtioNetDxe/VirtioNet.inf
+
+#
+# Console
+#
+INF  MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatformDxe.inf
+INF  MdeModulePkg/Universal/Console/ConSplitterDxe/ConSplitterDxe.inf
+INF  MdeModulePkg/Universal/Console/TerminalDxe/TerminalDxe.inf
+INF  MdeModulePkg/Universal/PrintDxe/PrintDxe.inf
+INF  MdeModulePkg/Universal/SerialDxe/SerialDxe.inf
+INF  MdeModulePkg/Universal/Console/GraphicsConsoleDxe/GraphicsConsoleDxe.inf
+
+#
+#Video
+#
+INF  OvmfPkg/QemuRamfbDxe/QemuRamfbDxe.inf
+INF  OvmfPkg/VirtioGpuDxe/VirtioGpu.inf
+INF  OvmfPkg/PlatformDxe/Platform.inf
+
+#
+# IDE
+#
+INF  OvmfPkg/SataControllerDxe/SataControllerDxe.inf
+INF  MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AtaAtapiPassThru.inf
+INF  MdeModulePkg/Bus/Ata/AtaBusDxe/AtaBusDxe.inf
+INF  MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBusDxe.inf
+INF  MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDiskDxe.inf
+
+#
+# Usb Support
+#
+INF  MdeModulePkg/Bus/Pci/UhciDxe/UhciDxe.inf
+INF  MdeModulePkg/Bus/Pci/EhciDxe/EhciDxe.inf
+INF  MdeModulePkg/Bus/Pci/XhciDxe/XhciDxe.inf
+INF  MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.inf
+INF  MdeModulePkg/Bus/Usb/UsbKbDxe/UsbKbDxe.inf
+INF  MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassStorageDxe.inf
+
+#
+#BDS
+#
+INF  MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabaseDxe.inf
+INF  MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.inf
+INF  MdeModulePkg/Universal/DisplayEngineDxe/DisplayEngineDxe.inf
+INF  MdeModulePkg/Universal/SetupBrowserDxe/SetupBrowserDxe.inf
+INF  MdeModulePkg/Universal/BdsDxe/BdsDxe.inf
+INF  MdeModulePkg/Logo/LogoDxe.inf
+INF MdeModulePkg/Application/UiApp/UiApp.inf
+INF OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
+#
+#Smbios
+#
+INF  MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.inf
+INF  OvmfPkg/SmbiosPlatformDxe/SmbiosPlatformDxe.inf
+
+#
+#Acpi
+#
+INF  MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTableDxe.inf
+INF  MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf
+INF  OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf
+
+#
+# Network modules
+#!include NetworkPkg/Network.fdf.inc
+
+#
+# File system
+#
+INF  MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.inf
+INF  MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe.inf
+INF  MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxe.inf
+INF  FatPkg/EnhancedFatDxe/Fat.inf
+
+#
+#Boot OS
+#
+INF ShellPkg/Application/Shell/Shell.inf
+
+#####################################################################################################
+[FV.FVMAIN_COMPACT]
+FvNameGuid         = af8c3fe8-9ce8-4548-884a-e3f4dd91f040
+FvAlignment        = 16
+ERASE_POLARITY     = 1
+MEMORY_MAPPED      = TRUE
+STICKY_WRITE       = TRUE
+LOCK_CAP           = TRUE
+LOCK_STATUS        = TRUE
+WRITE_DISABLED_CAP = TRUE
+WRITE_ENABLED_CAP  = TRUE
+WRITE_STATUS       = TRUE
+WRITE_LOCK_CAP     = TRUE
+WRITE_LOCK_STATUS  = TRUE
+READ_DISABLED_CAP  = TRUE
+READ_ENABLED_CAP   = TRUE
+READ_STATUS        = TRUE
+READ_LOCK_CAP      = TRUE
+READ_LOCK_STATUS   = TRUE
+
+FILE FV_IMAGE = 9E21FD93-9C72-4c15-8C4B-E77F1DB2D792 {
+   SECTION GUIDED EE4E5898-3914-4259-9D6E-DC7BD79403CF PROCESSING_REQUIRED = TRUE {
+     SECTION FV_IMAGE = DXEFV
+   }
+ }
 
 #####################################################################################################
 [Rule.Common.SEC]
@@ -102,3 +283,65 @@ INF  Platform/Loongson/LoongArchQemuPkg/PlatformPei/PlatformPei.inf
   }
 
 #####################################################################################################
+[Rule.Common.DXE_CORE]
+  FILE DXE_CORE = $(NAMED_GUID) {
+    PE32     PE32           $(INF_OUTPUT)/$(MODULE_NAME).efi
+    UI       STRING="$(MODULE_NAME)" Optional
+  }
+
+#####################################################################################################
+[Rule.Common.DXE_DRIVER]
+  FILE DRIVER = $(NAMED_GUID) {
+    DXE_DEPEX    DXE_DEPEX Optional      $(INF_OUTPUT)/$(MODULE_NAME).depex
+    PE32         PE32                    $(INF_OUTPUT)/$(MODULE_NAME).efi
+    UI       STRING="$(MODULE_NAME)" Optional
+    RAW          ACPI  Optional            |.acpi
+    RAW          ASL   Optional            |.aml
+  }
+
+#####################################################################################################
+[Rule.Common.DXE_RUNTIME_DRIVER]
+  FILE DRIVER = $(NAMED_GUID) {
+    DXE_DEPEX    DXE_DEPEX     Optional $(INF_OUTPUT)/$(MODULE_NAME).depex
+    PE32         PE32                   $(INF_OUTPUT)/$(MODULE_NAME).efi
+    UI           STRING="$(MODULE_NAME)" Optional
+  }
+
+#####################################################################################################
+[Rule.Common.UEFI_DRIVER]
+  FILE DRIVER = $(NAMED_GUID) {
+    DXE_DEPEX    DXE_DEPEX Optional  $(INF_OUTPUT)/$(MODULE_NAME).depex
+    PE32     PE32                    $(INF_OUTPUT)/$(MODULE_NAME).efi
+    UI       STRING="$(MODULE_NAME)" Optional
+  }
+
+#####################################################################################################
+[Rule.Common.UEFI_DRIVER.BINARY]
+  FILE DRIVER = $(NAMED_GUID) {
+    DXE_DEPEX DXE_DEPEX Optional      |.depex
+    PE32      PE32                    |.efi
+    UI        STRING="$(MODULE_NAME)" Optional
+    VERSION   STRING="$(INF_VERSION)" Optional BUILD_NUM=$(BUILD_NUMBER)
+  }
+
+#####################################################################################################
+[Rule.Common.UEFI_APPLICATION]
+  FILE APPLICATION = $(NAMED_GUID) {
+    PE32     PE32                    $(INF_OUTPUT)/$(MODULE_NAME).efi
+    UI       STRING="$(MODULE_NAME)" Optional
+  }
+
+#####################################################################################################
+[Rule.Common.UEFI_APPLICATION.BINARY]
+  FILE APPLICATION = $(NAMED_GUID) {
+    PE32      PE32                    |.efi
+    UI        STRING="$(MODULE_NAME)" Optional
+    VERSION   STRING="$(INF_VERSION)" Optional BUILD_NUM=$(BUILD_NUMBER)
+  }
+
+#####################################################################################################
+[Rule.Common.USER_DEFINED.ACPITABLE]
+  FILE FREEFORM = $(NAMED_GUID) {
+    RAW ACPI               |.acpi
+    RAW ASL                |.aml
+  }
diff --git a/Platform/Loongson/LoongArchQemuPkg/Loongson.fdf.inc b/Platform/Loongson/LoongArchQemuPkg/Loongson.fdf.inc
index a1a2d537e3..964d69ed44 100644
--- a/Platform/Loongson/LoongArchQemuPkg/Loongson.fdf.inc
+++ b/Platform/Loongson/LoongArchQemuPkg/Loongson.fdf.inc
@@ -19,3 +19,45 @@ DEFINE FD_SIZE                    = 0x400000
 #Set Sec base address and size in flash
 DEFINE SECFV_OFFSET               = 0x00000000
 DEFINE SECFV_SIZE                 = 0x00010000
+
+#Set Pei base address and size in flash
+DEFINE PEIFV_OFFSET               = 0x00010000
+DEFINE PEIFV_SIZE                 = 0x00040000
+
+#Set Dxe base address and size in flash
+DEFINE DXEFV_OFFSET               = 0x00050000
+DEFINE DXEFV_SIZE                 = 0x00350000
+
+#Set Var base address and size in flash
+DEFINE VARIABLE_OFFSET            = 0x003a0000
+DEFINE VAR_ALL_SIZE               = 0x60000
+
+############################################################################
+#Set Var Flash layout
+DEFINE VARIABLE_SIZE              = 0x00010000
+DEFINE VAR_ALL_BLOCKS             = 0x60
+
+DEFINE RESERVE1_OFFSET            = $(VARIABLE_OFFSET) + $(VARIABLE_SIZE)
+DEFINE RESERVE1_SIZE              = 0xB000
+
+DEFINE RESERVE2_OFFSET            = $(RESERVE1_OFFSET) + $(RESERVE1_SIZE)
+DEFINE RESERVE2_SIZE              = 0x14000
+
+DEFINE SPARE_OFFSET               = $(RESERVE2_OFFSET) + $(RESERVE2_SIZE)
+DEFINE SPARE_SIZE                 = 0x20000
+
+DEFINE FWTWORKING_OFFSET          = $(SPARE_OFFSET) + $(SPARE_SIZE)
+DEFINE FWTWORKING_SIZE            = 0x8000
+
+DEFINE EVENT_LOG_OFFSET           = $(FWTWORKING_OFFSET) + $(FWTWORKING_SIZE)
+DEFINE EVENT_LOG_SIZE             = 0x8000
+
+# Set Variable
+SET gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase64 = 0
+SET gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize = $(VARIABLE_SIZE)
+
+# Set FtwSpare
+SET gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize = $(SPARE_SIZE)
+
+# Set FtwWorking
+SET gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize = $(FWTWORKING_SIZE)
diff --git a/Platform/Loongson/LoongArchQemuPkg/VarStore.fdf.inc b/Platform/Loongson/LoongArchQemuPkg/VarStore.fdf.inc
new file mode 100644
index 0000000000..625fa23543
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/VarStore.fdf.inc
@@ -0,0 +1,64 @@
+## @file
+#
+#  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+$(VARIABLE_OFFSET)|$(VARIABLE_SIZE)
+#NV_VARIABLE_STORE
+DATA = {
+  #jmp to 0x1c060000
+  0x01, 0x0c, 0x38, 0x14, 0x21, 0x00, 0x80, 0x03,
+  0x21, 0x00, 0x00, 0x4c, 0x00, 0x10, 0x14, 0x00,
+  # FileSystemGuid: gEfiSystemNvDataFvGuid =
+  #   { 0xFFF12B8D, 0x7696, 0x4C8B,
+  #     { 0xA9, 0x85, 0x27, 0x47, 0x07, 0x5B, 0x4F, 0x50 }}
+  0x8D, 0x2B, 0xF1, 0xFF, 0x96, 0x76, 0x8B, 0x4C,
+  0xA9, 0x85, 0x27, 0x47, 0x07, 0x5B, 0x4F, 0x50,
+  # FvLength: 0x60000
+  0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+  # Signature "_FVH"       # Attributes
+  0x5f, 0x46, 0x56, 0x48, 0x36, 0x0c, 0x04, 0x00,
+  # HeaderLength # CheckSum # ExtHeaderOffset #Reserved #Revision
+  0x48, 0x00, 0x8F, 0x6B, 0x00, 0x00, 0x00, 0x02,
+  # Blockmap[0]: 0x10 Blocks * 0x1000 Bytes / Block
+  0x60, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
+  # Blockmap[1]: End
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  ## This is the VARIABLE_STORE_HEADER
+  # gEfiVariableGuid = {0xddcf3616, 0x3275, 0x4164, { 0x98, 0xb6, 0xfe, 0x85, 0x70, 0x7f, 0xfe, 0x7d}}
+  0x16, 0x36, 0xcf, 0xdd, 0x75, 0x32, 0x64, 0x41,
+  0x98, 0xb6, 0xfe, 0x85, 0x70, 0x7f, 0xfe, 0x7d,
+  # Size: 0x10000 (gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize) -
+  #         0x48 (size of EFI_FIRMWARE_VOLUME_HEADER) = 0xffb8
+  # This can speed up the Variable Dispatch a bit.
+  0xb8, 0xff, 0x00, 0x00,
+  # FORMATTED: 0x5A #HEALTHY: 0xFE #Reserved: UINT16 #Reserved1: UINT32
+  0x5a, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+}
+$(RESERVE1_OFFSET)|$(RESERVE1_SIZE)
+#NV_RESERVE1_STORE
+
+$(RESERVE2_OFFSET)|$(RESERVE2_SIZE)
+#NV_RESERVE2_STORE
+
+$(SPARE_OFFSET)|$(SPARE_SIZE)
+#NV_FTW_SPARE
+
+$(FWTWORKING_OFFSET)|$(FWTWORKING_SIZE)
+#NV_FTW_WORKING
+DATA = {
+  # EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER->Signature = gEdkiiWorkingBlockSignatureGuid =
+  #  { 0x9e58292b, 0x7c68, 0x497d, { 0xa0, 0xce, 0x65,  0x0, 0xfd, 0x9f, 0x1b, 0x95 }}
+  0x2b, 0x29, 0x58, 0x9e, 0x68, 0x7c, 0x7d, 0x49,
+  0xa0, 0xce, 0x65,  0x0, 0xfd, 0x9f, 0x1b, 0x95,
+  # Crc:UINT32            #WorkingBlockValid:1, WorkingBlockInvalid:1, Reserved
+  0x07, 0x7f, 0x44, 0x88, 0xfe, 0xff, 0xff, 0xff,
+  # WriteQueueSize: UINT64 #Size:0x8000 - 0x20(FTW_WORKING_HEADER) = 0x7FE0
+  0xe0, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+}
+
+$(EVENT_LOG_OFFSET)|$(EVENT_LOG_SIZE)
+#NV_EVENT_LOG
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [edk2-platforms][PATCH V3 15/16] Platform/Loongson: Add QemuFlashFvbServicesRuntimeDxe driver.
  2022-09-29  7:07 [edk2-platforms][PATCH V3 00/16] Platform: Add Loongson support xianglai
                   ` (13 preceding siblings ...)
  2022-09-29  7:08 ` [edk2-platforms][PATCH V3 14/16] Platform/Loongson: Support Dxe xianglai
@ 2022-09-29  7:08 ` xianglai
  2022-09-29  7:08 ` [edk2-platforms][PATCH V3 16/16] Platform/Loongson: Support for saving variables to flash xianglai
  15 siblings, 0 replies; 21+ messages in thread
From: xianglai @ 2022-09-29  7:08 UTC (permalink / raw)
  To: devel; +Cc: quic_llindhol, michael.d.kinney, kraxel, ardb, maobibo

This library provides flash read and write functionality
and supports writing variables to flash.

REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4054

Signed-off-by: xianglai li <lixianglai@loongson.cn>
---
 .../QemuFlashFvbServicesRuntimeDxe/FvbInfo.c  |  115 ++
 .../FvbServicesRuntimeDxe.inf                 |   73 ++
 .../FwBlockService.c                          | 1158 +++++++++++++++++
 .../FwBlockService.h                          |  178 +++
 .../FwBlockServiceDxe.c                       |  152 +++
 .../QemuFlash.c                               |  251 ++++
 .../QemuFlash.h                               |   86 ++
 .../QemuFlashDxe.c                            |   21 +
 .../Loongson/LoongArchQemuPkg/Loongson.dsc    |    4 +-
 9 files changed, 2036 insertions(+), 2 deletions(-)
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbInfo.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockServiceDxe.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c

diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbInfo.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbInfo.c
new file mode 100644
index 0000000000..df772f72be
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbInfo.c
@@ -0,0 +1,115 @@
+/** @file
+  Defines data structure that is the volume header found.These data is intent
+  to decouple FVB driver with FV header.
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+//
+// The package level header files this module uses
+//
+#include <Pi/PiFirmwareVolume.h>
+
+//
+// The protocols, PPI and GUID definitions for this module
+//
+#include <Guid/SystemNvDataGuid.h>
+//
+// The Library classes this module consumes
+//
+#include <Library/BaseLib.h>
+#include <Library/PcdLib.h>
+
+typedef struct {
+  UINT64                        FvLength;
+  EFI_FIRMWARE_VOLUME_HEADER    FvbInfo;
+  //
+  // EFI_FV_BLOCK_MAP_ENTRY    ExtraBlockMap[n];//n=0
+  //
+  EFI_FV_BLOCK_MAP_ENTRY        End[1];
+} EFI_FVB_MEDIA_INFO;
+
+EFI_FVB_MEDIA_INFO  mPlatformFvbMediaInfo[] = {
+  //
+  // System NvStorage FVB
+  //
+  {
+    FixedPcdGet32 (PcdAllVarSize),
+    {
+      {
+        0,
+      },  // ZeroVector[16]
+      EFI_SYSTEM_NV_DATA_FV_GUID,
+      FixedPcdGet32 (PcdAllVarSize),
+      EFI_FVH_SIGNATURE,
+      EFI_FVB2_MEMORY_MAPPED |
+      EFI_FVB2_READ_ENABLED_CAP |
+      EFI_FVB2_READ_STATUS |
+      EFI_FVB2_WRITE_ENABLED_CAP |
+      EFI_FVB2_WRITE_STATUS |
+      EFI_FVB2_ERASE_POLARITY |
+      EFI_FVB2_ALIGNMENT_16,
+      sizeof (EFI_FIRMWARE_VOLUME_HEADER) + sizeof (EFI_FV_BLOCK_MAP_ENTRY),
+      0,  // CheckSum
+      0,  // ExtHeaderOffset
+      {
+        0,
+      },  // Reserved[1]
+      2,  // Revision
+      {
+        {
+          (FixedPcdGet32 (PcdAllVarSize))/
+          FixedPcdGet32 (PcdFlashBlockSize),
+          FixedPcdGet32 (PcdFlashBlockSize),
+        }
+      } // BlockMap[1]
+    },
+    {
+      {
+        0,
+        0
+      }
+    }  // End[1]
+  }
+};
+
+EFI_STATUS
+GetFvbInfo (
+  IN  UINT64                      FvLength,
+  OUT EFI_FIRMWARE_VOLUME_HEADER  **FvbInfo
+  )
+{
+  STATIC BOOLEAN  Checksummed = FALSE;
+  UINTN           Index;
+
+  if (!Checksummed) {
+    for (Index = 0;
+         Index < sizeof (mPlatformFvbMediaInfo) / sizeof (EFI_FVB_MEDIA_INFO);
+         Index += 1)
+    {
+      UINT16  Checksum;
+      mPlatformFvbMediaInfo[Index].FvbInfo.Checksum = 0;
+      Checksum                                      = CalculateCheckSum16 (
+                                                        (UINT16 *)&mPlatformFvbMediaInfo[Index].FvbInfo,
+                                                        mPlatformFvbMediaInfo[Index].FvbInfo.HeaderLength
+                                                        );
+      mPlatformFvbMediaInfo[Index].FvbInfo.Checksum = Checksum;
+    }
+    Checksummed = TRUE;
+  }
+
+  for (Index = 0;
+       Index < sizeof (mPlatformFvbMediaInfo) / sizeof (EFI_FVB_MEDIA_INFO);
+       Index += 1)
+  {
+    if (mPlatformFvbMediaInfo[Index].FvLength == FvLength) {
+      *FvbInfo = &mPlatformFvbMediaInfo[Index].FvbInfo;
+      return EFI_SUCCESS;
+    }
+  }
+
+  return EFI_NOT_FOUND;
+}
diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
new file mode 100644
index 0000000000..4b0c42b075
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
@@ -0,0 +1,73 @@
+## @file
+# Component description file for Emu Fimware Volume Block DXE driver module.
+#
+#  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = FvbServicesRuntimeDxe
+  FILE_GUID                      = 733cbac2-b23f-4b92-bc8e-fb01ce5907b7
+  MODULE_TYPE                    = DXE_RUNTIME_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = FvbInitialize
+
+#
+# The following information is for reference only and not required by the build
+# tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64
+#
+
+[Sources]
+  FvbInfo.c
+  FwBlockService.c
+  FwBlockServiceDxe.c
+  QemuFlash.c
+  QemuFlashDxe.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  OvmfPkg/OvmfPkg.dec
+  Platform/Loongson/LoongArchQemuPkg/Loongson.dec
+
+[LibraryClasses]
+  BaseLib
+  BaseMemoryLib
+  DebugLib
+  DevicePathLib
+  DxeServicesTableLib
+  MemoryAllocationLib
+  PcdLib
+  UefiBootServicesTableLib
+  UefiDriverEntryPoint
+  UefiRuntimeLib
+
+[Guids]
+  gEfiEventVirtualAddressChangeGuid   # ALWAYS_CONSUMED
+  # gEfiEventVirtualAddressChangeGuid # Create Event: EVENT_GROUP_GUID
+
+[Protocols]
+  gEfiFirmwareVolumeBlockProtocolGuid           # PROTOCOL SOMETIMES_PRODUCED
+  gEfiDevicePathProtocolGuid                    # PROTOCOL SOMETIMES_PRODUCED
+
+[FixedPcd]
+  gLoongArchQemuPkgTokenSpaceGuid.PcdOvmfFlashNvStorageVariableBase
+  gLoongArchQemuPkgTokenSpaceGuid.PcdOvmfFlashNvStorageFtwWorkingBase
+  gLoongArchQemuPkgTokenSpaceGuid.PcdOvmfFlashNvStorageFtwSpareBase
+  gLoongArchQemuPkgTokenSpaceGuid.PcdFlashFdBase
+  gLoongArchQemuPkgTokenSpaceGuid.PcdAllVarSize
+  gLoongArchQemuPkgTokenSpaceGuid.PcdFlashBlockSize
+[Pcd]
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase64
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashVariablesEnable
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase64
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase64
+
+[Depex]
+  TRUE
diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
new file mode 100644
index 0000000000..d44f2b460c
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
@@ -0,0 +1,1158 @@
+/** @file
+  Implementations for Firmware Volume Block protocol.
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+//
+// The protocols, PPI and GUID definitions for this module
+//
+#include <Protocol/DevicePath.h>
+#include <Protocol/FirmwareVolumeBlock.h>
+
+//
+// The Library classes this module consumes
+//
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#include "FwBlockService.h"
+#include "QemuFlash.h"
+
+#define EFI_FVB2_STATUS \
+          (EFI_FVB2_READ_STATUS | EFI_FVB2_WRITE_STATUS | EFI_FVB2_LOCK_STATUS)
+
+ESAL_FWB_GLOBAL  *mFvbModuleGlobal;
+
+FV_MEMMAP_DEVICE_PATH  mFvMemmapDevicePathTemplate = {
+  {
+    {
+      HARDWARE_DEVICE_PATH,
+      HW_MEMMAP_DP,
+      {
+        (UINT8)(sizeof (MEMMAP_DEVICE_PATH)),
+        (UINT8)(sizeof (MEMMAP_DEVICE_PATH) >> 8)
+      }
+    },
+    EfiMemoryMappedIO,
+    (EFI_PHYSICAL_ADDRESS)0,
+    (EFI_PHYSICAL_ADDRESS)0,
+  },
+  {
+    END_DEVICE_PATH_TYPE,
+    END_ENTIRE_DEVICE_PATH_SUBTYPE,
+    {
+      END_DEVICE_PATH_LENGTH,
+      0
+    }
+  }
+};
+
+FV_PIWG_DEVICE_PATH  mFvPIWGDevicePathTemplate = {
+  {
+    {
+      MEDIA_DEVICE_PATH,
+      MEDIA_PIWG_FW_VOL_DP,
+      {
+        (UINT8)(sizeof (MEDIA_FW_VOL_DEVICE_PATH)),
+        (UINT8)(sizeof (MEDIA_FW_VOL_DEVICE_PATH) >> 8)
+      }
+    },
+    { 0 }
+  },
+  {
+    END_DEVICE_PATH_TYPE,
+    END_ENTIRE_DEVICE_PATH_SUBTYPE,
+    {
+      END_DEVICE_PATH_LENGTH,
+      0
+    }
+  }
+};
+
+EFI_FW_VOL_BLOCK_DEVICE  mFvbDeviceTemplate = {
+  FVB_DEVICE_SIGNATURE,
+  NULL,
+  0,
+  {
+    FvbProtocolGetAttributes,
+    FvbProtocolSetAttributes,
+    FvbProtocolGetPhysicalAddress,
+    FvbProtocolGetBlockSize,
+    FvbProtocolRead,
+    FvbProtocolWrite,
+    FvbProtocolEraseBlocks,
+    NULL
+  }
+};
+
+
+EFI_STATUS
+GetFvbInstance (
+  IN  UINTN                Instance,
+  IN  ESAL_FWB_GLOBAL      *Global,
+  OUT EFI_FW_VOL_INSTANCE  **FwhInstance
+  )
+/*++
+
+  Routine Description:
+    Retrieves the physical address of a memory mapped FV
+
+  Arguments:
+    Instance              - The FV instance whose base address is going to be
+                            returned
+    Global                - Pointer to ESAL_FWB_GLOBAL that contains all
+                            instance data
+    FwhInstance           - The EFI_FW_VOL_INSTANCE firmware instance structure
+
+  Returns:
+    EFI_SUCCESS           - Successfully returns
+    EFI_INVALID_PARAMETER - Instance not found
+
+--*/
+{
+  EFI_FW_VOL_INSTANCE  *FwhRecord;
+
+  *FwhInstance = NULL;
+  if (Instance >= Global->NumFv) {
+    return EFI_INVALID_PARAMETER;
+  }
+  //
+  // Find the right instance of the FVB private data
+  //
+  FwhRecord = Global->FvInstance;
+  while (Instance > 0) {
+    FwhRecord = (EFI_FW_VOL_INSTANCE *)
+                (
+                 (UINTN)((UINT8 *)FwhRecord) + FwhRecord->VolumeHeader.HeaderLength +
+                 (sizeof (EFI_FW_VOL_INSTANCE) - sizeof (EFI_FIRMWARE_VOLUME_HEADER))
+                );
+    Instance--;
+  }
+
+  *FwhInstance = FwhRecord;
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+FvbGetPhysicalAddress (
+  IN UINTN                  Instance,
+  OUT EFI_PHYSICAL_ADDRESS  *Address,
+  IN ESAL_FWB_GLOBAL        *Global
+  )
+/*++
+
+  Routine Description:
+    Retrieves the physical address of a memory mapped FV
+
+  Arguments:
+    Instance              - The FV instance whose base address is going to be
+                            returned
+    Address               - Pointer to a caller allocated EFI_PHYSICAL_ADDRESS
+                            that on successful return, contains the base
+                            address of the firmware volume.
+    Global                - Pointer to ESAL_FWB_GLOBAL that contains all
+                            instance data
+
+  Returns:
+    EFI_SUCCESS           - Successfully returns
+    EFI_INVALID_PARAMETER - Instance not found
+
+--*/
+{
+  EFI_FW_VOL_INSTANCE  *FwhInstance;
+  EFI_STATUS           Status;
+
+  //
+  // Find the right instance of the FVB private data
+  //
+  Status = GetFvbInstance (Instance, Global, &FwhInstance);
+  ASSERT_EFI_ERROR (Status);
+  *Address = FwhInstance->FvBase;
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+FvbGetVolumeAttributes (
+  IN UINTN                  Instance,
+  OUT EFI_FVB_ATTRIBUTES_2  *Attributes,
+  IN ESAL_FWB_GLOBAL        *Global
+  )
+/*++
+
+  Routine Description:
+    Retrieves attributes, insures positive polarity of attribute bits, returns
+    resulting attributes in output parameter
+
+  Arguments:
+    Instance              - The FV instance whose attributes is going to be
+                            returned
+    Attributes            - Output buffer which contains attributes
+    Global                - Pointer to ESAL_FWB_GLOBAL that contains all
+                            instance data
+
+  Returns:
+    EFI_SUCCESS           - Successfully returns
+    EFI_INVALID_PARAMETER - Instance not found
+
+--*/
+{
+  EFI_FW_VOL_INSTANCE  *FwhInstance;
+  EFI_STATUS           Status;
+
+  //
+  // Find the right instance of the FVB private data
+  //
+  Status = GetFvbInstance (Instance, Global, &FwhInstance);
+  ASSERT_EFI_ERROR (Status);
+  *Attributes = FwhInstance->VolumeHeader.Attributes;
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+FvbGetLbaAddress (
+  IN  UINTN            Instance,
+  IN  EFI_LBA          Lba,
+  OUT UINTN            *LbaAddress,
+  OUT UINTN            *LbaLength,
+  OUT UINTN            *NumOfBlocks,
+  IN  ESAL_FWB_GLOBAL  *Global
+  )
+/*++
+
+  Routine Description:
+    Retrieves the starting address of an LBA in an FV
+
+  Arguments:
+    Instance              - The FV instance which the Lba belongs to
+    Lba                   - The logical block address
+    LbaAddress            - On output, contains the physical starting address
+                            of the Lba
+    LbaLength             - On output, contains the length of the block
+    NumOfBlocks           - A pointer to a caller allocated UINTN in which the
+                            number of consecutive blocks starting with Lba is
+                            returned. All blocks in this range have a size of
+                            BlockSize
+    Global                - Pointer to ESAL_FWB_GLOBAL that contains all
+                            instance data
+
+  Returns:
+    EFI_SUCCESS           - Successfully returns
+    EFI_INVALID_PARAMETER - Instance not found
+
+--*/
+{
+  UINT32                  NumBlocks;
+  UINT32                  BlockLength;
+  UINTN                   Offset;
+  EFI_LBA                 StartLba;
+  EFI_LBA                 NextLba;
+  EFI_FW_VOL_INSTANCE     *FwhInstance;
+  EFI_FV_BLOCK_MAP_ENTRY  *BlockMap;
+  EFI_STATUS              Status;
+
+  //
+  // Find the right instance of the FVB private data
+  //
+  Status = GetFvbInstance (Instance, Global, &FwhInstance);
+  ASSERT_EFI_ERROR (Status);
+
+  StartLba = 0;
+  Offset   = 0;
+  BlockMap = &(FwhInstance->VolumeHeader.BlockMap[0]);
+
+  //
+  // Parse the blockmap of the FV to find which map entry the Lba belongs to
+  //
+  while (TRUE) {
+    NumBlocks   = BlockMap->NumBlocks;
+    BlockLength = BlockMap->Length;
+
+    if ((NumBlocks == 0) || (BlockLength == 0)) {
+      return EFI_INVALID_PARAMETER;
+    }
+
+    NextLba = StartLba + NumBlocks;
+
+    //
+    // The map entry found
+    //
+    if ((Lba >= StartLba) && (Lba < NextLba)) {
+      Offset = Offset + (UINTN)MultU64x32 ((Lba - StartLba), BlockLength);
+      if (LbaAddress != NULL) {
+        *LbaAddress = FwhInstance->FvBase + Offset;
+      }
+
+      if (LbaLength != NULL) {
+        *LbaLength = BlockLength;
+      }
+
+      if (NumOfBlocks != NULL) {
+        *NumOfBlocks = (UINTN)(NextLba - Lba);
+      }
+
+      return EFI_SUCCESS;
+    }
+
+    StartLba = NextLba;
+    Offset   = Offset + NumBlocks * BlockLength;
+    BlockMap++;
+  }
+}
+
+EFI_STATUS
+FvbSetVolumeAttributes (
+  IN UINTN                     Instance,
+  IN OUT EFI_FVB_ATTRIBUTES_2  *Attributes,
+  IN ESAL_FWB_GLOBAL           *Global
+  )
+/*++
+
+  Routine Description:
+    Modifies the current settings of the firmware volume according to the
+    input parameter, and returns the new setting of the volume
+
+  Arguments:
+    Instance              - The FV instance whose attributes is going to be
+                            modified
+    Attributes            - On input, it is a pointer to EFI_FVB_ATTRIBUTES_2
+                            containing the desired firmware volume settings.
+                            On successful return, it contains the new settings
+                            of the firmware volume
+    Global                - Pointer to ESAL_FWB_GLOBAL that contains all
+                            instance data
+
+  Returns:
+    EFI_SUCCESS           - Successfully returns
+    EFI_ACCESS_DENIED     - The volume setting is locked and cannot be modified
+    EFI_INVALID_PARAMETER - Instance not found, or The attributes requested are
+                            in conflict with the capabilities as declared in
+                            the firmware volume header
+
+--*/
+{
+  EFI_FW_VOL_INSTANCE   *FwhInstance;
+  EFI_FVB_ATTRIBUTES_2  OldAttributes;
+  EFI_FVB_ATTRIBUTES_2  *AttribPtr;
+  UINT32                Capabilities;
+  UINT32                OldStatus;
+  UINT32                NewStatus;
+  EFI_STATUS            Status;
+  EFI_FVB_ATTRIBUTES_2  UnchangedAttributes;
+
+  //
+  // Find the right instance of the FVB private data
+  //
+  Status = GetFvbInstance (Instance, Global, &FwhInstance);
+  ASSERT_EFI_ERROR (Status);
+
+  AttribPtr =
+    (EFI_FVB_ATTRIBUTES_2 *)&(FwhInstance->VolumeHeader.Attributes);
+  OldAttributes = *AttribPtr;
+  Capabilities  = OldAttributes & (EFI_FVB2_READ_DISABLED_CAP | \
+                                   EFI_FVB2_READ_ENABLED_CAP | \
+                                   EFI_FVB2_WRITE_DISABLED_CAP | \
+                                   EFI_FVB2_WRITE_ENABLED_CAP | \
+                                   EFI_FVB2_LOCK_CAP \
+                                   );
+  OldStatus = OldAttributes & EFI_FVB2_STATUS;
+  NewStatus = *Attributes & EFI_FVB2_STATUS;
+
+  UnchangedAttributes = EFI_FVB2_READ_DISABLED_CAP  | \
+                        EFI_FVB2_READ_ENABLED_CAP   | \
+                        EFI_FVB2_WRITE_DISABLED_CAP | \
+                        EFI_FVB2_WRITE_ENABLED_CAP  | \
+                        EFI_FVB2_LOCK_CAP           | \
+                        EFI_FVB2_STICKY_WRITE       | \
+                        EFI_FVB2_MEMORY_MAPPED      | \
+                        EFI_FVB2_ERASE_POLARITY     | \
+                        EFI_FVB2_READ_LOCK_CAP      | \
+                        EFI_FVB2_WRITE_LOCK_CAP     | \
+                        EFI_FVB2_ALIGNMENT;
+
+  //
+  // Some attributes of FV is read only can *not* be set
+  //
+  if ((OldAttributes & UnchangedAttributes) ^
+      (*Attributes & UnchangedAttributes))
+  {
+    return EFI_INVALID_PARAMETER;
+  }
+  //
+  // If firmware volume is locked, no status bit can be updated
+  //
+  if (OldAttributes & EFI_FVB2_LOCK_STATUS) {
+    if (OldStatus ^ NewStatus) {
+      return EFI_ACCESS_DENIED;
+    }
+  }
+  //
+  // Test read disable
+  //
+  if ((Capabilities & EFI_FVB2_READ_DISABLED_CAP) == 0) {
+    if ((NewStatus & EFI_FVB2_READ_STATUS) == 0) {
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+  //
+  // Test read enable
+  //
+  if ((Capabilities & EFI_FVB2_READ_ENABLED_CAP) == 0) {
+    if (NewStatus & EFI_FVB2_READ_STATUS) {
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+  //
+  // Test write disable
+  //
+  if ((Capabilities & EFI_FVB2_WRITE_DISABLED_CAP) == 0) {
+    if ((NewStatus & EFI_FVB2_WRITE_STATUS) == 0) {
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+  //
+  // Test write enable
+  //
+  if ((Capabilities & EFI_FVB2_WRITE_ENABLED_CAP) == 0) {
+    if (NewStatus & EFI_FVB2_WRITE_STATUS) {
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+  //
+  // Test lock
+  //
+  if ((Capabilities & EFI_FVB2_LOCK_CAP) == 0) {
+    if (NewStatus & EFI_FVB2_LOCK_STATUS) {
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+
+  *AttribPtr  = (*AttribPtr) & (0xFFFFFFFF & (~EFI_FVB2_STATUS));
+  *AttribPtr  = (*AttribPtr) | NewStatus;
+  *Attributes = *AttribPtr;
+
+  return EFI_SUCCESS;
+}
+
+//
+// FVB protocol APIs
+//
+EFI_STATUS
+EFIAPI
+FvbProtocolGetPhysicalAddress (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
+  OUT EFI_PHYSICAL_ADDRESS                     *Address
+  )
+/*++
+
+  Routine Description:
+
+    Retrieves the physical address of the device.
+
+  Arguments:
+
+    This                  - Calling context
+    Address               - Output buffer containing the address.
+
+  Returns:
+    EFI_SUCCESS           - Successfully returns
+
+--*/
+{
+  EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice;
+
+  FvbDevice = FVB_DEVICE_FROM_THIS (This);
+
+  return FvbGetPhysicalAddress (
+           FvbDevice->Instance,
+           Address,
+           mFvbModuleGlobal
+           );
+}
+
+EFI_STATUS
+EFIAPI
+FvbProtocolGetBlockSize (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
+  IN CONST EFI_LBA                             Lba,
+  OUT UINTN                                    *BlockSize,
+  OUT UINTN                                    *NumOfBlocks
+  )
+/*++
+
+  Routine Description:
+    Retrieve the size of a logical block
+
+  Arguments:
+    This                  - Calling context
+    Lba                   - Indicates which block to return the size for.
+    BlockSize             - A pointer to a caller allocated UINTN in which
+                            the size of the block is returned
+    NumOfBlocks           - a pointer to a caller allocated UINTN in which the
+                            number of consecutive blocks starting with Lba is
+                            returned. All blocks in this range have a size of
+                            BlockSize
+
+  Returns:
+    EFI_SUCCESS           - The firmware volume was read successfully and
+                            contents are in Buffer
+
+--*/
+{
+  EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice;
+
+  FvbDevice = FVB_DEVICE_FROM_THIS (This);
+
+  return FvbGetLbaAddress (
+           FvbDevice->Instance,
+           Lba,
+           NULL,
+           BlockSize,
+           NumOfBlocks,
+           mFvbModuleGlobal
+           );
+}
+
+EFI_STATUS
+EFIAPI
+FvbProtocolGetAttributes (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
+  OUT EFI_FVB_ATTRIBUTES_2                     *Attributes
+  )
+/*++
+
+  Routine Description:
+      Retrieves Volume attributes.  No polarity translations are done.
+
+  Arguments:
+      This                - Calling context
+      Attributes          - output buffer which contains attributes
+
+  Returns:
+    EFI_SUCCESS           - Successfully returns
+
+--*/
+{
+  EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice;
+
+  FvbDevice = FVB_DEVICE_FROM_THIS (This);
+
+  return FvbGetVolumeAttributes (
+           FvbDevice->Instance,
+           Attributes,
+           mFvbModuleGlobal
+           );
+}
+
+EFI_STATUS
+EFIAPI
+FvbProtocolSetAttributes (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
+  IN OUT EFI_FVB_ATTRIBUTES_2                  *Attributes
+  )
+/*++
+
+  Routine Description:
+    Sets Volume attributes. No polarity translations are done.
+
+  Arguments:
+    This                  - Calling context
+    Attributes            - output buffer which contains attributes
+
+  Returns:
+    EFI_SUCCESS           - Successfully returns
+
+--*/
+{
+  EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice;
+
+  FvbDevice = FVB_DEVICE_FROM_THIS (This);
+
+  return FvbSetVolumeAttributes (
+           FvbDevice->Instance,
+           Attributes,
+           mFvbModuleGlobal
+           );
+}
+
+EFI_STATUS
+EFIAPI
+FvbProtocolEraseBlocks (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
+  ...
+  )
+/*++
+
+  Routine Description:
+
+    The EraseBlock() function erases one or more blocks as denoted by the
+    variable argument list. The entire parameter list of blocks must be
+    verified prior to erasing any blocks.  If a block is requested that does
+    not exist within the associated firmware volume (it has a larger index than
+    the last block of the firmware volume), the EraseBlock() function must
+    return EFI_INVALID_PARAMETER without modifying the contents of the firmware
+    volume.
+
+  Arguments:
+    This                  - Calling context
+    ...                   - Starting LBA followed by Number of Lba to erase.
+                            a -1 to terminate the list.
+
+  Returns:
+    EFI_SUCCESS           - The erase request was successfully completed
+    EFI_ACCESS_DENIED     - The firmware volume is in the WriteDisabled state
+    EFI_DEVICE_ERROR      - The block device is not functioning correctly and
+                            could not be written. Firmware device may have been
+                            partially erased
+
+--*/
+{
+  EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice;
+  EFI_FW_VOL_INSTANCE      *FwhInstance;
+  UINTN                    NumOfBlocks;
+  VA_LIST                  args;
+  EFI_LBA                  StartingLba;
+  UINTN                    NumOfLba;
+  EFI_STATUS               Status;
+
+  FvbDevice = FVB_DEVICE_FROM_THIS (This);
+
+  Status = GetFvbInstance (
+             FvbDevice->Instance,
+             mFvbModuleGlobal,
+             &FwhInstance
+             );
+  ASSERT_EFI_ERROR (Status);
+
+  NumOfBlocks = FwhInstance->NumOfBlocks;
+
+  VA_START (args, This);
+
+  do {
+    StartingLba = VA_ARG (args, EFI_LBA);
+    if (StartingLba == EFI_LBA_LIST_TERMINATOR) {
+      break;
+    }
+
+    NumOfLba = VA_ARG (args, UINTN);
+
+    //
+    // Check input parameters
+    //
+    if ((NumOfLba == 0) || ((StartingLba + NumOfLba) > NumOfBlocks)) {
+      VA_END (args);
+      return EFI_INVALID_PARAMETER;
+    }
+  } while (1);
+
+  VA_END (args);
+
+  VA_START (args, This);
+  do {
+    StartingLba = VA_ARG (args, EFI_LBA);
+    if (StartingLba == EFI_LBA_LIST_TERMINATOR) {
+      break;
+    }
+
+    NumOfLba = VA_ARG (args, UINTN);
+
+    while (NumOfLba > 0) {
+      Status = QemuFlashEraseBlock (StartingLba);
+      if (EFI_ERROR (Status)) {
+        VA_END (args);
+        return Status;
+      }
+
+      StartingLba++;
+      NumOfLba--;
+    }
+
+  } while (1);
+
+  VA_END (args);
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+FvbProtocolWrite (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
+  IN       EFI_LBA                             Lba,
+  IN       UINTN                               Offset,
+  IN OUT   UINTN                               *NumBytes,
+  IN       UINT8                               *Buffer
+  )
+/*++
+
+  Routine Description:
+
+    Writes data beginning at Lba:Offset from FV. The write terminates either
+    when *NumBytes of data have been written, or when a block boundary is
+    reached.  *NumBytes is updated to reflect the actual number of bytes
+    written. The write operation does not include erase. This routine will
+    attempt to write only the specified bytes. If the writes do not stick,
+    it will return an error.
+
+  Arguments:
+    This                  - Calling context
+    Lba                   - Block in which to begin write
+    Offset                - Offset in the block at which to begin write
+    NumBytes              - On input, indicates the requested write size. On
+                            output, indicates the actual number of bytes
+                            written
+    Buffer                - Buffer containing source data for the write.
+
+  Returns:
+    EFI_SUCCESS           - The firmware volume was written successfully
+    EFI_BAD_BUFFER_SIZE   - Write attempted across a LBA boundary. On output,
+                            NumBytes contains the total number of bytes
+                            actually written
+    EFI_ACCESS_DENIED     - The firmware volume is in the WriteDisabled state
+    EFI_DEVICE_ERROR      - The block device is not functioning correctly and
+                            could not be written
+    EFI_INVALID_PARAMETER - NumBytes or Buffer are NULL
+
+--*/
+{
+  return QemuFlashWrite (
+           (EFI_LBA)Lba,
+           (UINTN)Offset,
+           NumBytes,
+           (UINT8 *)Buffer
+           );
+}
+
+EFI_STATUS
+EFIAPI
+FvbProtocolRead (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
+  IN CONST EFI_LBA                             Lba,
+  IN CONST UINTN                               Offset,
+  IN OUT UINTN                                 *NumBytes,
+  IN UINT8                                     *Buffer
+  )
+/*++
+
+  Routine Description:
+
+    Reads data beginning at Lba:Offset from FV. The Read terminates either
+    when *NumBytes of data have been read, or when a block boundary is
+    reached.  *NumBytes is updated to reflect the actual number of bytes
+    written. The write operation does not include erase. This routine will
+    attempt to write only the specified bytes. If the writes do not stick,
+    it will return an error.
+
+  Arguments:
+    This                  - Calling context
+    Lba                   - Block in which to begin Read
+    Offset                - Offset in the block at which to begin Read
+    NumBytes              - On input, indicates the requested write size. On
+                            output, indicates the actual number of bytes Read
+    Buffer                - Buffer containing source data for the Read.
+
+  Returns:
+    EFI_SUCCESS           - The firmware volume was read successfully and
+                            contents are in Buffer
+    EFI_BAD_BUFFER_SIZE   - Read attempted across a LBA boundary. On output,
+                            NumBytes contains the total number of bytes
+                            returned in Buffer
+    EFI_ACCESS_DENIED     - The firmware volume is in the ReadDisabled state
+    EFI_DEVICE_ERROR      - The block device is not functioning correctly and
+                            could not be read
+    EFI_INVALID_PARAMETER - NumBytes or Buffer are NULL
+
+--*/
+{
+  return QemuFlashRead (
+           (EFI_LBA)Lba,
+           (UINTN)Offset,
+           NumBytes,
+           (UINT8 *)Buffer
+           );
+}
+
+EFI_STATUS
+ValidateFvHeader (
+  EFI_FIRMWARE_VOLUME_HEADER  *FwVolHeader
+  )
+/*++
+
+  Routine Description:
+    Check the integrity of firmware volume header
+
+  Arguments:
+    FwVolHeader           - A pointer to a firmware volume header
+
+  Returns:
+    EFI_SUCCESS           - The firmware volume is consistent
+    EFI_NOT_FOUND         - The firmware volume has corrupted. So it is not an
+                            FV
+
+--*/
+{
+  UINT16  Checksum;
+
+  //
+  // Verify the header revision, header signature, length
+  // Length of FvBlock cannot be 2**64-1
+  // HeaderLength cannot be an odd number
+  //
+  if ((FwVolHeader->Revision != EFI_FVH_REVISION) ||
+      (FwVolHeader->Signature != EFI_FVH_SIGNATURE) ||
+      (FwVolHeader->FvLength == ((UINTN)-1)) ||
+      ((FwVolHeader->HeaderLength & 0x01) != 0)
+      )
+  {
+    return EFI_NOT_FOUND;
+  }
+
+  //
+  // Verify the header checksum
+  //
+
+  Checksum = CalculateSum16 (
+               (UINT16 *)FwVolHeader,
+               FwVolHeader->HeaderLength
+               );
+  if (Checksum != 0) {
+    UINT16  Expected;
+
+    Expected =
+      (UINT16)(((UINTN)FwVolHeader->Checksum + 0x10000 - Checksum) & 0xffff);
+
+    DEBUG ((
+      DEBUG_INFO,
+      "FV@%p Checksum is 0x%x, expected 0x%x\n",
+      FwVolHeader,
+      FwVolHeader->Checksum,
+      Expected
+      ));
+    return EFI_NOT_FOUND;
+  }
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+MarkMemoryRangeForRuntimeAccess (
+  EFI_PHYSICAL_ADDRESS                BaseAddress,
+  UINTN                               Length
+  )
+{
+  EFI_STATUS                          Status;
+
+  //
+  // Mark flash region as runtime memory
+  //
+  Status = gDS->RemoveMemorySpace (
+                  BaseAddress,
+                  Length
+                  );
+
+  Status = gDS->AddMemorySpace (
+                  EfiGcdMemoryTypeSystemMemory,
+                  BaseAddress,
+                  Length,
+                  EFI_MEMORY_UC | EFI_MEMORY_RUNTIME
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  Status = gBS->AllocatePages (
+                  AllocateAddress,
+                  EfiRuntimeServicesData,
+                  EFI_SIZE_TO_PAGES (Length),
+                  &BaseAddress
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
+
+STATIC
+EFI_STATUS
+InitializeVariableFvHeader (
+  VOID
+  )
+{
+  EFI_STATUS                  Status;
+  EFI_FIRMWARE_VOLUME_HEADER  *GoodFwVolHeader;
+  EFI_FIRMWARE_VOLUME_HEADER  *FwVolHeader;
+  UINTN                       Length;
+  UINTN                       WriteLength;
+  UINTN                       BlockSize;
+
+  FwVolHeader =
+    (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)
+    PcdGet64 (PcdOvmfFlashNvStorageVariableBase);
+
+  Length =  FixedPcdGet32 (PcdAllVarSize);
+  BlockSize = PcdGet32 (PcdFlashBlockSize);
+
+  Status = ValidateFvHeader (FwVolHeader);
+  if (!EFI_ERROR (Status)) {
+    if ((FwVolHeader->FvLength != Length) ||
+        (FwVolHeader->BlockMap[0].Length != BlockSize))
+    {
+      Status = EFI_VOLUME_CORRUPTED;
+    }
+  }
+  if (EFI_ERROR (Status)) {
+    UINTN  Offset;
+    UINTN  Start;
+
+    DEBUG ((
+      DEBUG_INFO,
+      "Variable FV header is not valid. It will be reinitialized.\n"
+      ));
+
+    //
+    // Get FvbInfo to provide in FwhInstance.
+    //
+    Status = GetFvbInfo (Length, &GoodFwVolHeader);
+    ASSERT (!EFI_ERROR (Status));
+
+    Start = (UINTN)(UINT8*) FwVolHeader - PcdGet64 (PcdFlashFdBase);
+    ASSERT (Start % BlockSize == 0 && Length % BlockSize == 0);
+    ASSERT (GoodFwVolHeader->HeaderLength <= BlockSize);
+
+    //
+    // Erase all the blocks
+    //
+    for (Offset = Start; Offset < Start + Length; Offset += BlockSize) {
+      Status = QemuFlashEraseBlock (Offset / BlockSize);
+      ASSERT_EFI_ERROR (Status);
+    }
+
+    //
+    // Write good FV header
+    //
+    WriteLength = GoodFwVolHeader->HeaderLength;
+    Status      = QemuFlashWrite (
+                    Start / BlockSize,
+                    0,
+                    &WriteLength,
+                    (UINT8 *)GoodFwVolHeader
+                    );
+    ASSERT_EFI_ERROR (Status);
+    ASSERT (WriteLength == GoodFwVolHeader->HeaderLength);
+  }
+
+  return Status;
+}
+
+EFI_STATUS
+EFIAPI
+FvbInitialize (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+/*++
+
+  Routine Description:
+    This function does common initialization for FVB services
+
+  Arguments:
+
+  Returns:
+
+--*/
+{
+  EFI_STATUS                  Status;
+  EFI_FW_VOL_INSTANCE         *FwhInstance;
+  EFI_FIRMWARE_VOLUME_HEADER  *FwVolHeader;
+  UINT32                      BufferSize;
+  EFI_FV_BLOCK_MAP_ENTRY      *PtrBlockMapEntry;
+  EFI_FW_VOL_BLOCK_DEVICE     *FvbDevice;
+  UINT32                      MaxLbaSize;
+  EFI_PHYSICAL_ADDRESS        BaseAddress;
+  UINTN                       Length;
+  UINTN                       NumOfBlocks;
+  RETURN_STATUS               PcdStatus;
+
+  if (EFI_ERROR (QemuFlashInitialize ())) {
+    //
+    // Return an error so image will be unloaded
+    //
+    DEBUG ((
+      DEBUG_INFO,
+      "QEMU flash was not detected. Writable FVB is not being installed.\n"
+      ));
+    return EFI_WRITE_PROTECTED;
+  }
+
+  //
+  // Allocate runtime services data for global variable, which contains
+  // the private data of all firmware volume block instances
+  //
+  mFvbModuleGlobal = AllocateRuntimePool (sizeof (ESAL_FWB_GLOBAL));
+  ASSERT (mFvbModuleGlobal != NULL);
+
+  BaseAddress = (UINTN) PcdGet64 (PcdOvmfFlashNvStorageVariableBase);
+  Length = PcdGet32 (PcdAllVarSize);
+
+  Status = InitializeVariableFvHeader ();
+  if (EFI_ERROR (Status)) {
+    DEBUG ((
+      DEBUG_INFO,
+      "QEMU Flash: Unable to initialize variable FV header\n"
+      ));
+    return EFI_WRITE_PROTECTED;
+  }
+
+  FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)BaseAddress;
+  Status      = ValidateFvHeader (FwVolHeader);
+  if (EFI_ERROR (Status)) {
+    //
+    // Get FvbInfo
+    //
+    Status = GetFvbInfo (Length, &FwVolHeader);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_INFO, "EFI_ERROR (GetFvbInfo (Length, &FwVolHeader))\n"));
+      return EFI_WRITE_PROTECTED;
+    }
+  }
+
+  BufferSize = (sizeof (EFI_FW_VOL_INSTANCE) +
+                FwVolHeader->HeaderLength -
+                sizeof (EFI_FIRMWARE_VOLUME_HEADER)
+                );
+  mFvbModuleGlobal->FvInstance = AllocateRuntimePool (BufferSize);
+  ASSERT (mFvbModuleGlobal->FvInstance != NULL);
+
+  FwhInstance = mFvbModuleGlobal->FvInstance;
+
+  mFvbModuleGlobal->NumFv = 0;
+  MaxLbaSize              = 0;
+
+  FwVolHeader =
+    (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)
+    PcdGet64 (PcdOvmfFlashNvStorageVariableBase);
+
+  FwhInstance->FvBase = (UINTN)BaseAddress;
+
+  CopyMem (
+    (UINTN *)&(FwhInstance->VolumeHeader),
+    (UINTN *)FwVolHeader,
+    FwVolHeader->HeaderLength
+    );
+  FwVolHeader = &(FwhInstance->VolumeHeader);
+
+  NumOfBlocks = 0;
+
+  for (PtrBlockMapEntry = FwVolHeader->BlockMap;
+       PtrBlockMapEntry->NumBlocks != 0;
+       PtrBlockMapEntry++)
+  {
+    //
+    // Get the maximum size of a block.
+    //
+    if (MaxLbaSize < PtrBlockMapEntry->Length) {
+      MaxLbaSize = PtrBlockMapEntry->Length;
+    }
+
+    NumOfBlocks = NumOfBlocks + PtrBlockMapEntry->NumBlocks;
+  }
+
+  //
+  // The total number of blocks in the FV.
+  //
+  FwhInstance->NumOfBlocks = NumOfBlocks;
+
+  //
+  // Add a FVB Protocol Instance
+  //
+  FvbDevice = AllocateRuntimePool (sizeof (EFI_FW_VOL_BLOCK_DEVICE));
+  ASSERT (FvbDevice != NULL);
+
+  CopyMem (FvbDevice, &mFvbDeviceTemplate, sizeof (EFI_FW_VOL_BLOCK_DEVICE));
+
+  FvbDevice->Instance = mFvbModuleGlobal->NumFv;
+  mFvbModuleGlobal->NumFv++;
+
+  //
+  // Set up the devicepath
+  //
+  if (FwVolHeader->ExtHeaderOffset == 0) {
+    FV_MEMMAP_DEVICE_PATH  *FvMemmapDevicePath;
+
+    //
+    // FV does not contains extension header, then produce MEMMAP_DEVICE_PATH
+    //
+    FvMemmapDevicePath = AllocateCopyPool (
+                           sizeof (FV_MEMMAP_DEVICE_PATH),
+                           &mFvMemmapDevicePathTemplate
+                           );
+    FvMemmapDevicePath->MemMapDevPath.StartingAddress = BaseAddress;
+    FvMemmapDevicePath->MemMapDevPath.EndingAddress   =
+      BaseAddress + FwVolHeader->FvLength - 1;
+    FvbDevice->DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)FvMemmapDevicePath;
+  } else {
+    FV_PIWG_DEVICE_PATH  *FvPiwgDevicePath;
+
+    FvPiwgDevicePath = AllocateCopyPool (
+                         sizeof (FV_PIWG_DEVICE_PATH),
+                         &mFvPIWGDevicePathTemplate
+                         );
+    CopyGuid (
+      &FvPiwgDevicePath->FvDevPath.FvName,
+      (GUID *)(UINTN)(BaseAddress + FwVolHeader->ExtHeaderOffset)
+      );
+    FvbDevice->DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)FvPiwgDevicePath;
+  }
+
+  //
+  // Module type specific hook.
+  //
+  InstallProtocolInterfaces (FvbDevice);
+
+  MarkMemoryRangeForRuntimeAccess (BaseAddress, Length);
+
+  //
+  // Set several PCD values to point to flash
+  //
+
+
+  PcdStatus = PcdSet64S (
+    PcdFlashNvStorageVariableBase64,
+    (UINTN) PcdGet64 (PcdOvmfFlashNvStorageVariableBase)
+    );
+  ASSERT_RETURN_ERROR (PcdStatus);
+  PcdStatus = PcdSet64S (
+    PcdFlashNvStorageFtwWorkingBase64,
+    PcdGet64 (PcdOvmfFlashNvStorageFtwWorkingBase)
+    );
+  ASSERT_RETURN_ERROR (PcdStatus);
+  PcdStatus = PcdSet64S (
+    PcdFlashNvStorageFtwSpareBase64,
+    PcdGet64 (PcdOvmfFlashNvStorageFtwSpareBase)
+    );
+  ASSERT_RETURN_ERROR (PcdStatus);
+
+  FwhInstance = (EFI_FW_VOL_INSTANCE *)
+                (
+                 (UINTN)((UINT8 *)FwhInstance) + FwVolHeader->HeaderLength +
+                 (sizeof (EFI_FW_VOL_INSTANCE) - sizeof (EFI_FIRMWARE_VOLUME_HEADER))
+                );
+
+  //
+  // Module type specific hook.
+  //
+  InstallVirtualAddressChangeHandler ();
+
+  PcdStatus = PcdSetBoolS (PcdOvmfFlashVariablesEnable, TRUE);
+  ASSERT_RETURN_ERROR (PcdStatus);
+  return EFI_SUCCESS;
+}
diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.h b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.h
new file mode 100644
index 0000000000..e7234a0c5e
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.h
@@ -0,0 +1,178 @@
+/** @file
+  Firmware volume block driver for Intel Firmware Hub (FWH) device
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+#ifndef _FW_BLOCK_SERVICE_H
+#define _FW_BLOCK_SERVICE_H
+
+typedef struct {
+  UINTN                         FvBase;
+  UINTN                         NumOfBlocks;
+  EFI_FIRMWARE_VOLUME_HEADER    VolumeHeader;
+} EFI_FW_VOL_INSTANCE;
+
+typedef struct {
+  UINT32                 NumFv;
+  EFI_FW_VOL_INSTANCE    *FvInstance;
+} ESAL_FWB_GLOBAL;
+
+extern ESAL_FWB_GLOBAL  *mFvbModuleGlobal;
+
+//
+// Fvb Protocol instance data
+//
+#define FVB_DEVICE_FROM_THIS(a)  CR (a, EFI_FW_VOL_BLOCK_DEVICE,\
+                                  FwVolBlockInstance, FVB_DEVICE_SIGNATURE)
+
+#define FVB_EXTEND_DEVICE_FROM_THIS(a)  CR (a, EFI_FW_VOL_BLOCK_DEVICE,\
+                                         FvbExtension, FVB_DEVICE_SIGNATURE)
+
+#define FVB_DEVICE_SIGNATURE  SIGNATURE_32 ('F', 'V', 'B', 'N')
+
+typedef struct {
+  MEDIA_FW_VOL_DEVICE_PATH    FvDevPath;
+  EFI_DEVICE_PATH_PROTOCOL    EndDevPath;
+} FV_PIWG_DEVICE_PATH;
+
+typedef struct {
+  MEMMAP_DEVICE_PATH          MemMapDevPath;
+  EFI_DEVICE_PATH_PROTOCOL    EndDevPath;
+} FV_MEMMAP_DEVICE_PATH;
+
+typedef struct {
+  UINTN                                 Signature;
+  EFI_DEVICE_PATH_PROTOCOL              *DevicePath;
+  UINTN                                 Instance;
+  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL    FwVolBlockInstance;
+} EFI_FW_VOL_BLOCK_DEVICE;
+
+EFI_STATUS
+GetFvbInfo (
+  IN  UINT64                      FvLength,
+  OUT EFI_FIRMWARE_VOLUME_HEADER  **FvbInfo
+  );
+
+EFI_STATUS
+FvbSetVolumeAttributes (
+  IN UINTN                     Instance,
+  IN OUT EFI_FVB_ATTRIBUTES_2  *Attributes,
+  IN ESAL_FWB_GLOBAL           *Global
+  );
+
+EFI_STATUS
+FvbGetVolumeAttributes (
+  IN UINTN                  Instance,
+  OUT EFI_FVB_ATTRIBUTES_2  *Attributes,
+  IN ESAL_FWB_GLOBAL        *Global
+  );
+
+EFI_STATUS
+FvbGetPhysicalAddress (
+  IN UINTN                  Instance,
+  OUT EFI_PHYSICAL_ADDRESS  *Address,
+  IN ESAL_FWB_GLOBAL        *Global
+  );
+
+EFI_STATUS
+EFIAPI
+FvbInitialize (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  );
+
+
+VOID
+EFIAPI
+FvbClassAddressChangeEvent (
+  IN EFI_EVENT  Event,
+  IN VOID       *Context
+  );
+
+EFI_STATUS
+FvbGetLbaAddress (
+  IN  UINTN            Instance,
+  IN  EFI_LBA          Lba,
+  OUT UINTN            *LbaAddress,
+  OUT UINTN            *LbaLength,
+  OUT UINTN            *NumOfBlocks,
+  IN  ESAL_FWB_GLOBAL  *Global
+  );
+
+//
+// Protocol APIs
+//
+EFI_STATUS
+EFIAPI
+FvbProtocolGetAttributes (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
+  OUT EFI_FVB_ATTRIBUTES_2                     *Attributes
+  );
+
+EFI_STATUS
+EFIAPI
+FvbProtocolSetAttributes (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
+  IN OUT EFI_FVB_ATTRIBUTES_2                  *Attributes
+  );
+
+EFI_STATUS
+EFIAPI
+FvbProtocolGetPhysicalAddress (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
+  OUT EFI_PHYSICAL_ADDRESS                     *Address
+  );
+
+EFI_STATUS
+EFIAPI
+FvbProtocolGetBlockSize (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
+  IN CONST EFI_LBA                             Lba,
+  OUT UINTN                                    *BlockSize,
+  OUT UINTN                                    *NumOfBlocks
+  );
+
+EFI_STATUS
+EFIAPI
+FvbProtocolRead (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
+  IN CONST EFI_LBA                             Lba,
+  IN CONST UINTN                               Offset,
+  IN OUT UINTN                                 *NumBytes,
+  IN UINT8                                     *Buffer
+  );
+
+EFI_STATUS
+EFIAPI
+FvbProtocolWrite (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
+  IN       EFI_LBA                             Lba,
+  IN       UINTN                               Offset,
+  IN OUT   UINTN                               *NumBytes,
+  IN       UINT8                               *Buffer
+  );
+
+EFI_STATUS
+EFIAPI
+FvbProtocolEraseBlocks (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
+  ...
+  );
+
+//
+// The following functions have different implementations dependent on the
+// module type chosen for building this driver.
+//
+VOID
+InstallProtocolInterfaces (
+  IN EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice
+  );
+
+VOID
+InstallVirtualAddressChangeHandler (
+  VOID
+  );
+#endif
diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockServiceDxe.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockServiceDxe.c
new file mode 100644
index 0000000000..cb85499539
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockServiceDxe.c
@@ -0,0 +1,152 @@
+/** @file
+  Functions related to the Firmware Volume Block service whose
+  implementation is specific to the runtime DXE driver build.
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Guid/EventGroup.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/PcdLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeLib.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/FirmwareVolumeBlock.h>
+
+#include "FwBlockService.h"
+#include "QemuFlash.h"
+
+VOID
+InstallProtocolInterfaces (
+  IN EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice
+  )
+{
+  EFI_STATUS                          Status;
+  EFI_HANDLE                          FwbHandle;
+  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *OldFwbInterface;
+
+  //
+  // Find a handle with a matching device path that has supports FW Block
+  // protocol
+  //
+  Status = gBS->LocateDevicePath (
+                  &gEfiFirmwareVolumeBlockProtocolGuid,
+                  &FvbDevice->DevicePath,
+                  &FwbHandle
+                  );
+  if (EFI_ERROR (Status)) {
+    //
+    // LocateDevicePath fails so install a new interface and device path
+    //
+    FwbHandle = NULL;
+    DEBUG ((DEBUG_INFO, "Installing QEMU flash FVB\n"));
+    Status = gBS->InstallMultipleProtocolInterfaces (
+                    &FwbHandle,
+                    &gEfiFirmwareVolumeBlockProtocolGuid,
+                    &FvbDevice->FwVolBlockInstance,
+                    &gEfiDevicePathProtocolGuid,
+                    FvbDevice->DevicePath,
+                    NULL
+                    );
+    ASSERT_EFI_ERROR (Status);
+  } else if (IsDevicePathEnd (FvbDevice->DevicePath)) {
+    //
+    // Device already exists, so reinstall the FVB protocol
+    //
+    Status = gBS->HandleProtocol (
+                    FwbHandle,
+                    &gEfiFirmwareVolumeBlockProtocolGuid,
+                    (VOID **)&OldFwbInterface
+                    );
+    ASSERT_EFI_ERROR (Status);
+
+    DEBUG ((DEBUG_INFO, "Reinstalling FVB for QEMU flash region\n"));
+    Status = gBS->ReinstallProtocolInterface (
+                    FwbHandle,
+                    &gEfiFirmwareVolumeBlockProtocolGuid,
+                    OldFwbInterface,
+                    &FvbDevice->FwVolBlockInstance
+                    );
+    ASSERT_EFI_ERROR (Status);
+  } else {
+    //
+    // There was a FVB protocol on an End Device Path node
+    //
+    ASSERT (FALSE);
+  }
+}
+
+
+STATIC
+VOID
+EFIAPI
+FvbVirtualAddressChangeEvent (
+  IN EFI_EVENT  Event,
+  IN VOID       *Context
+  )
+/*++
+
+  Routine Description:
+
+    Fixup internal data so that EFI and SAL can be call in virtual mode.
+    Call the passed in Child Notify event and convert the mFvbModuleGlobal
+    date items to there virtual address.
+
+  Arguments:
+
+    (Standard EFI notify event - EFI_EVENT_NOTIFY)
+
+  Returns:
+
+    None
+
+--*/
+{
+  EFI_FW_VOL_INSTANCE  *FwhInstance;
+  UINTN                Index;
+
+  FwhInstance = mFvbModuleGlobal->FvInstance;
+  EfiConvertPointer (0x0, (VOID **)&mFvbModuleGlobal->FvInstance);
+
+  //
+  // Convert the base address of all the instances
+  //
+  Index = 0;
+  while (Index < mFvbModuleGlobal->NumFv) {
+    EfiConvertPointer (0x0, (VOID **)&FwhInstance->FvBase);
+    FwhInstance = (EFI_FW_VOL_INSTANCE *)
+                  (
+                   (UINTN)((UINT8 *)FwhInstance) +
+                   FwhInstance->VolumeHeader.HeaderLength +
+                   (sizeof (EFI_FW_VOL_INSTANCE) - sizeof (EFI_FIRMWARE_VOLUME_HEADER))
+                  );
+    Index++;
+  }
+
+  EfiConvertPointer (0x0, (VOID **)&mFvbModuleGlobal);
+  QemuFlashConvertPointers ();
+}
+
+
+VOID
+InstallVirtualAddressChangeHandler (
+  VOID
+  )
+{
+  EFI_STATUS  Status;
+  EFI_EVENT   VirtualAddressChangeEvent;
+
+  Status = gBS->CreateEventEx (
+                  EVT_NOTIFY_SIGNAL,
+                  TPL_NOTIFY,
+                  FvbVirtualAddressChangeEvent,
+                  NULL,
+                  &gEfiEventVirtualAddressChangeGuid,
+                  &VirtualAddressChangeEvent
+                  );
+  ASSERT_EFI_ERROR (Status);
+}
diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c
new file mode 100644
index 0000000000..a85d736060
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c
@@ -0,0 +1,251 @@
+/** @file
+  LoongArch support for QEMU system firmware flash device
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/PcdLib.h>
+
+#include "QemuFlash.h"
+
+#define WRITE_BYTE_CMD           0x10
+#define BLOCK_ERASE_CMD          0x20
+#define CLEAR_STATUS_CMD         0x50
+#define READ_STATUS_CMD          0x70
+#define READ_DEVID_CMD           0x90
+#define BLOCK_ERASE_CONFIRM_CMD  0xd0
+#define READ_ARRAY_CMD           0xff
+
+#define CLEARED_ARRAY_STATUS  0x00
+
+UINT8  *mFlashBase;
+
+STATIC UINTN  mFdBlockSize  = 0;
+STATIC UINTN  mFdBlockCount = 0;
+
+STATIC
+volatile UINT8 *
+QemuFlashPtr (
+  IN        EFI_LBA  Lba,
+  IN        UINTN    Offset
+  )
+{
+  return mFlashBase + ((UINTN)Lba * mFdBlockSize) + Offset;
+}
+
+
+/**
+  Determines if the QEMU flash memory device is present.
+
+  @retval FALSE   The QEMU flash device is not present.
+  @retval TRUE    The QEMU flash device is present.
+
+**/
+STATIC
+BOOLEAN
+QemuFlashDetected (
+  VOID
+  )
+{
+  BOOLEAN         FlashDetected;
+  volatile UINT8  *Ptr;
+
+  UINTN  Offset;
+  UINT8  OriginalUint8;
+  UINT8  ProbeUint8;
+
+  FlashDetected = FALSE;
+  Ptr           = QemuFlashPtr (0, 0);
+
+  for (Offset = 0; Offset < mFdBlockSize; Offset++) {
+    Ptr        = QemuFlashPtr (0, Offset);
+    ProbeUint8 = *Ptr;
+    if ((ProbeUint8 != CLEAR_STATUS_CMD) &&
+        (ProbeUint8 != READ_STATUS_CMD) &&
+        (ProbeUint8 != CLEARED_ARRAY_STATUS))
+    {
+      break;
+    }
+  }
+
+  if (Offset >= mFdBlockSize) {
+    DEBUG ((DEBUG_INFO, "QEMU Flash: Failed to find probe location\n"));
+    return FALSE;
+  }
+
+  DEBUG ((DEBUG_INFO, "QEMU Flash: Attempting flash detection at %p\n", Ptr));
+
+  OriginalUint8 = *Ptr;
+  *Ptr          = CLEAR_STATUS_CMD;
+  ProbeUint8    = *Ptr;
+  if ((OriginalUint8 != CLEAR_STATUS_CMD) &&
+      (ProbeUint8 == CLEAR_STATUS_CMD))
+  {
+    DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as RAM\n"));
+    *Ptr = OriginalUint8;
+  } else {
+    *Ptr       = READ_STATUS_CMD;
+    ProbeUint8 = *Ptr;
+    if (ProbeUint8 == OriginalUint8) {
+      DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as ROM\n"));
+    } else if (ProbeUint8 == READ_STATUS_CMD) {
+      DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as RAM\n"));
+      *Ptr = OriginalUint8;
+    } else if (ProbeUint8 == CLEARED_ARRAY_STATUS) {
+      DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as FLASH\n"));
+      FlashDetected = TRUE;
+      *Ptr          = READ_ARRAY_CMD;
+    }
+  }
+
+  DEBUG ((
+    DEBUG_INFO,
+    "QemuFlashDetected => %a\n",
+    FlashDetected ? "Yes" : "No"
+    ));
+  return FlashDetected;
+}
+
+
+/**
+  Read from QEMU Flash
+
+  @param[in] Lba      The starting logical block index to read from.
+  @param[in] Offset   Offset into the block at which to begin reading.
+  @param[in] NumBytes On input, indicates the requested read size. On
+                      output, indicates the actual number of bytes read
+  @param[in] Buffer   Pointer to the buffer to read into.
+
+**/
+EFI_STATUS
+QemuFlashRead (
+  IN        EFI_LBA  Lba,
+  IN        UINTN    Offset,
+  IN        UINTN    *NumBytes,
+  IN        UINT8    *Buffer
+  )
+{
+  UINT8  *Ptr;
+
+  //
+  // Only write to the first 64k. We don't bother saving the FTW Spare
+  // block into the flash memory.
+  //
+  if (Lba >= mFdBlockCount) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Get flash address
+  //
+  Ptr = (UINT8 *)QemuFlashPtr (Lba, Offset);
+
+  CopyMem (Buffer, Ptr, *NumBytes);
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Write to QEMU Flash
+
+  @param[in] Lba      The starting logical block index to write to.
+  @param[in] Offset   Offset into the block at which to begin writing.
+  @param[in] NumBytes On input, indicates the requested write size. On
+                      output, indicates the actual number of bytes written
+  @param[in] Buffer   Pointer to the data to write.
+
+**/
+EFI_STATUS
+QemuFlashWrite (
+  IN        EFI_LBA  Lba,
+  IN        UINTN    Offset,
+  IN        UINTN    *NumBytes,
+  IN        UINT8    *Buffer
+  )
+{
+  volatile UINT8  *Ptr;
+  UINTN           Loop;
+
+  //
+  // Only write to the first 64k. We don't bother saving the FTW Spare
+  // block into the flash memory.
+  //
+  if (Lba >= mFdBlockCount) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Program flash
+  //
+  Ptr = QemuFlashPtr (Lba, Offset);
+  for (Loop = 0; Loop < *NumBytes; Loop++) {
+    *Ptr = WRITE_BYTE_CMD;
+    *Ptr = Buffer[Loop];
+    Ptr++;
+  }
+
+  //
+  // Restore flash to read mode
+  //
+  if (*NumBytes > 0) {
+    *(Ptr - 1) = READ_ARRAY_CMD;
+  }
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Erase a QEMU Flash block
+
+  @param Lba    The logical block index to erase.
+
+**/
+EFI_STATUS
+QemuFlashEraseBlock (
+  IN   EFI_LBA  Lba
+  )
+{
+  volatile UINT8  *Ptr;
+
+  if (Lba >= mFdBlockCount) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Ptr = QemuFlashPtr (Lba, 0);
+  *Ptr = BLOCK_ERASE_CMD;
+  *Ptr = BLOCK_ERASE_CONFIRM_CMD;
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Initializes QEMU flash memory support
+
+  @retval EFI_WRITE_PROTECTED   The QEMU flash device is not present.
+  @retval EFI_SUCCESS           The QEMU flash device is supported.
+
+**/
+EFI_STATUS
+QemuFlashInitialize (
+  VOID
+  )
+{
+  mFlashBase   = (UINT8 *)(UINTN)PcdGet64 (PcdOvmfFlashNvStorageVariableBase);
+  mFdBlockSize = PcdGet32 (PcdFlashBlockSize);
+  ASSERT(PcdGet32 (PcdAllVarSize) % mFdBlockSize == 0);
+  mFdBlockCount = PcdGet32 (PcdAllVarSize) / mFdBlockSize;
+
+  if (!QemuFlashDetected ()) {
+    return EFI_WRITE_PROTECTED;
+  }
+
+  return EFI_SUCCESS;
+}
+
diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h
new file mode 100644
index 0000000000..27caabc5f0
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h
@@ -0,0 +1,86 @@
+/** @file
+  LoongArch support for QEMU system firmware flash device
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __QEMU_FLASH_H__
+#define __QEMU_FLASH_H__
+
+#include <Protocol/FirmwareVolumeBlock.h>
+
+extern UINT8  *mFlashBase;
+
+/**
+  Read from QEMU Flash
+
+  @param[in] Lba      The starting logical block index to read from.
+  @param[in] Offset   Offset into the block at which to begin reading.
+  @param[in] NumBytes On input, indicates the requested read size. On
+                      output, indicates the actual number of bytes read
+  @param[in] Buffer   Pointer to the buffer to read into.
+
+**/
+EFI_STATUS
+QemuFlashRead (
+  IN        EFI_LBA  Lba,
+  IN        UINTN    Offset,
+  IN        UINTN    *NumBytes,
+  IN        UINT8    *Buffer
+  );
+
+
+/**
+  Write to QEMU Flash
+
+  @param[in] Lba      The starting logical block index to write to.
+  @param[in] Offset   Offset into the block at which to begin writing.
+  @param[in] NumBytes On input, indicates the requested write size. On
+                      output, indicates the actual number of bytes written
+  @param[in] Buffer   Pointer to the data to write.
+
+**/
+EFI_STATUS
+QemuFlashWrite (
+  IN        EFI_LBA  Lba,
+  IN        UINTN    Offset,
+  IN        UINTN    *NumBytes,
+  IN        UINT8    *Buffer
+  );
+
+
+/**
+  Erase a QEMU Flash block
+
+  @param Lba    The logical block index to erase.
+
+**/
+EFI_STATUS
+QemuFlashEraseBlock (
+  IN   EFI_LBA  Lba
+  );
+
+
+/**
+  Initializes QEMU flash memory support
+
+  @retval EFI_WRITE_PROTECTED   The QEMU flash device is not present.
+  @retval EFI_SUCCESS           The QEMU flash device is supported.
+
+**/
+EFI_STATUS
+QemuFlashInitialize (
+  VOID
+  );
+
+
+VOID
+QemuFlashConvertPointers (
+  VOID
+  );
+
+#endif
+
diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c
new file mode 100644
index 0000000000..b63314aac2
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c
@@ -0,0 +1,21 @@
+/** @file
+  LoongArch support for QEMU system firmware flash device: functions specific to the
+  runtime DXE driver build.
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/UefiRuntimeLib.h>
+
+#include "QemuFlash.h"
+
+VOID
+QemuFlashConvertPointers (
+  VOID
+  )
+{
+  EfiConvertPointer (0x0, (VOID **)&mFlashBase);
+}
diff --git a/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc b/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc
index 74c83720b7..59beafb34f 100644
--- a/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc
+++ b/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc
@@ -171,10 +171,10 @@
   VirtioLib                        | OvmfPkg/Library/VirtioLib/VirtioLib.inf
   FrameBufferBltLib                | MdeModulePkg/Library/FrameBufferBltLib/FrameBufferBltLib.inf
   QemuFwCfgLib                     | OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibMmio.inf
-
   DebugLib                         | MdePkg/Library/BaseDebugLibSerialPort/BaseDebugLibSerialPort.inf
-
   PeiServicesLib                   | MdePkg/Library/PeiServicesLib/PeiServicesLib.inf
+  VariableFlashInfoLib             | MdeModulePkg/Library/BaseVariableFlashInfoLib/BaseVariableFlashInfoLib.inf
+
 [LibraryClasses.common.SEC]
   ReportStatusCodeLib              | MdeModulePkg/Library/PeiReportStatusCodeLib/PeiReportStatusCodeLib.inf
   HobLib                           | MdePkg/Library/PeiHobLib/PeiHobLib.inf
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [edk2-platforms][PATCH V3 16/16] Platform/Loongson: Support for saving variables to flash.
  2022-09-29  7:07 [edk2-platforms][PATCH V3 00/16] Platform: Add Loongson support xianglai
                   ` (14 preceding siblings ...)
  2022-09-29  7:08 ` [edk2-platforms][PATCH V3 15/16] Platform/Loongson: Add QemuFlashFvbServicesRuntimeDxe driver xianglai
@ 2022-09-29  7:08 ` xianglai
  15 siblings, 0 replies; 21+ messages in thread
From: xianglai @ 2022-09-29  7:08 UTC (permalink / raw)
  To: devel; +Cc: quic_llindhol, michael.d.kinney, kraxel, ardb, maobibo

Added the function of saving non-volatile variables.

REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4054

Signed-off-by: xianglai li <lixianglai@loongson.cn>
---
 Platform/Loongson/LoongArchQemuPkg/Loongson.dec    |  6 ++++++
 Platform/Loongson/LoongArchQemuPkg/Loongson.dsc    |  2 +-
 Platform/Loongson/LoongArchQemuPkg/Loongson.fdf    | 14 ++++++++++++++
 .../Loongson/LoongArchQemuPkg/Loongson.fdf.inc     | 11 +++++++++++
 .../Loongson/LoongArchQemuPkg/VarStore.fdf.inc     | 10 +++++-----
 5 files changed, 37 insertions(+), 6 deletions(-)

diff --git a/Platform/Loongson/LoongArchQemuPkg/Loongson.dec b/Platform/Loongson/LoongArchQemuPkg/Loongson.dec
index 5065f99042..2fd80e7ea2 100644
--- a/Platform/Loongson/LoongArchQemuPkg/Loongson.dec
+++ b/Platform/Loongson/LoongArchQemuPkg/Loongson.dec
@@ -32,6 +32,8 @@
 [Protocols]
 
 [PcdsFixedAtBuild, PcdsDynamic]
+  gLoongArchQemuPkgTokenSpaceGuid.PcdFlashFdBase|0x0|UINT64|0
+  gLoongArchQemuPkgTokenSpaceGuid.PcdFlashBlockSize|0x0|UINT32|2
   gLoongArchQemuPkgTokenSpaceGuid.PcdFlashPeiFvBase|0x0|UINT64|0x00000003
   gLoongArchQemuPkgTokenSpaceGuid.PcdFlashPeiFvSize|0x0|UINT32|0x00000004
   gLoongArchQemuPkgTokenSpaceGuid.PcdFlashDxeFvOffset|0x0|UINT64|0x00000007
@@ -48,6 +50,10 @@
   gLoongArchQemuPkgTokenSpaceGuid.PcdSecPeiTempRamSize|0|UINT32|0x0000001d
   gLoongArchQemuPkgTokenSpaceGuid.PcdUefiRamTop|0x0|UINT64|0x0000001e
   gLoongArchQemuPkgTokenSpaceGuid.PcdRamRegionsBottom|0x0|UINT64|0x00000022
+  gLoongArchQemuPkgTokenSpaceGuid.PcdOvmfFlashNvStorageVariableBase|0x0|UINT64|0x00000024
+  gLoongArchQemuPkgTokenSpaceGuid.PcdAllVarSize|0x0|UINT32|0x00000025
+  gLoongArchQemuPkgTokenSpaceGuid.PcdOvmfFlashNvStorageFtwSpareBase|0x0|UINT64|0x00000026
+  gLoongArchQemuPkgTokenSpaceGuid.PcdOvmfFlashNvStorageFtwWorkingBase|0x0|UINT64|0x00000027
   gLoongArchQemuPkgTokenSpaceGuid.PcdFlashSecFvBase|0x0|UINT64|0x00000028
   gLoongArchQemuPkgTokenSpaceGuid.PcdFlashSecFvSize|0x0|UINT32|0x00000029
 
diff --git a/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc b/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc
index 59beafb34f..46b326f0a5 100644
--- a/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc
+++ b/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc
@@ -444,7 +444,7 @@
   #
   # Variable
   #
-#  Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
+  Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
   OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.inf {
     <LibraryClasses>
       PlatformFvbLib|OvmfPkg/Library/EmuVariableFvbLib/EmuVariableFvbLib.inf
diff --git a/Platform/Loongson/LoongArchQemuPkg/Loongson.fdf b/Platform/Loongson/LoongArchQemuPkg/Loongson.fdf
index ba2942e455..12920a0833 100644
--- a/Platform/Loongson/LoongArchQemuPkg/Loongson.fdf
+++ b/Platform/Loongson/LoongArchQemuPkg/Loongson.fdf
@@ -31,6 +31,19 @@ gLoongArchQemuPkgTokenSpaceGuid.PcdFlashDxeFvBase|gLoongArchQemuPkgTokenSpaceGui
 FV = FVMAIN_COMPACT
 
 !include VarStore.fdf.inc
+
+#####################################################################################################
+[FD.QEMU_EFI_VARS]
+BaseAddress   = $(VARIABLE_BASE_ADDRESS)
+Size          = $(VAR_ALL_SIZE)
+ErasePolarity = 1
+BlockSize     = $(BLOCK_SIZE)
+NumBlocks     = $(VAR_ALL_BLOCKS)
+
+DEFINE VARIABLE_OFFSET = 0
+!include VarStore.fdf.inc
+DEFINE VARIABLE_OFFSET = 0x003a0000
+
 #####################################################################################################
 [FV.SECFV]
 FvNameGuid         = 587d4265-5e71-41da-9c35-4258551f1e22
@@ -87,6 +100,7 @@ INF  MdeModulePkg/Core/Pei/PeiMain.inf
 INF  MdeModulePkg/Universal/PCD/Pei/Pcd.inf
 INF  MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf
 INF  Platform/Loongson/LoongArchQemuPkg/PlatformPei/PlatformPei.inf
+
 #####################################################################################################
 [FV.DXEFV]
 FvNameGuid         = 5d19a5b3-130f-459b-a292-9270a9e6bc62
diff --git a/Platform/Loongson/LoongArchQemuPkg/Loongson.fdf.inc b/Platform/Loongson/LoongArchQemuPkg/Loongson.fdf.inc
index 964d69ed44..4b926a85ff 100644
--- a/Platform/Loongson/LoongArchQemuPkg/Loongson.fdf.inc
+++ b/Platform/Loongson/LoongArchQemuPkg/Loongson.fdf.inc
@@ -34,6 +34,7 @@ DEFINE VAR_ALL_SIZE               = 0x60000
 
 ############################################################################
 #Set Var Flash layout
+DEFINE VARIABLE_BASE_ADDRESS      = 0x1c3a0000
 DEFINE VARIABLE_SIZE              = 0x00010000
 DEFINE VAR_ALL_BLOCKS             = 0x60
 
@@ -53,11 +54,21 @@ DEFINE EVENT_LOG_OFFSET           = $(FWTWORKING_OFFSET) + $(FWTWORKING_SIZE)
 DEFINE EVENT_LOG_SIZE             = 0x8000
 
 # Set Variable
+SET gLoongArchQemuPkgTokenSpaceGuid.PcdFlashFdBase = $(FD_BASE_ADDRESS)
+
+SET gLoongArchQemuPkgTokenSpaceGuid.PcdOvmfFlashNvStorageVariableBase = $(FD_BASE_ADDRESS) + $(VARIABLE_OFFSET)
+SET gLoongArchQemuPkgTokenSpaceGuid.PcdAllVarSize = $(VAR_ALL_SIZE)
+
+SET gLoongArchQemuPkgTokenSpaceGuid.PcdFlashBlockSize = $(BLOCK_SIZE)
 SET gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase64 = 0
 SET gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize = $(VARIABLE_SIZE)
 
 # Set FtwSpare
+SET gLoongArchQemuPkgTokenSpaceGuid.PcdOvmfFlashNvStorageFtwSpareBase  =  $(FD_BASE_ADDRESS) + $(SPARE_OFFSET)
+SET gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase64 = 0
 SET gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize = $(SPARE_SIZE)
 
 # Set FtwWorking
+SET gLoongArchQemuPkgTokenSpaceGuid.PcdOvmfFlashNvStorageFtwWorkingBase  =  $(FD_BASE_ADDRESS) + $(SPARE_OFFSET)  + $(SPARE_SIZE)
+SET gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase64 = 0
 SET gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize = $(FWTWORKING_SIZE)
diff --git a/Platform/Loongson/LoongArchQemuPkg/VarStore.fdf.inc b/Platform/Loongson/LoongArchQemuPkg/VarStore.fdf.inc
index 625fa23543..a4e0158cf8 100644
--- a/Platform/Loongson/LoongArchQemuPkg/VarStore.fdf.inc
+++ b/Platform/Loongson/LoongArchQemuPkg/VarStore.fdf.inc
@@ -38,16 +38,16 @@ DATA = {
   # FORMATTED: 0x5A #HEALTHY: 0xFE #Reserved: UINT16 #Reserved1: UINT32
   0x5a, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 }
-$(RESERVE1_OFFSET)|$(RESERVE1_SIZE)
+$(VARIABLE_OFFSET) + $(VARIABLE_SIZE)|$(RESERVE1_SIZE)
 #NV_RESERVE1_STORE
 
-$(RESERVE2_OFFSET)|$(RESERVE2_SIZE)
+$(VARIABLE_OFFSET) + $(VARIABLE_SIZE) + $(RESERVE1_SIZE)|$(RESERVE2_SIZE)
 #NV_RESERVE2_STORE
 
-$(SPARE_OFFSET)|$(SPARE_SIZE)
+$(VARIABLE_OFFSET) + $(VARIABLE_SIZE) + $(RESERVE1_SIZE) + $(RESERVE2_SIZE)|$(SPARE_SIZE)
 #NV_FTW_SPARE
 
-$(FWTWORKING_OFFSET)|$(FWTWORKING_SIZE)
+$(VARIABLE_OFFSET) + $(VARIABLE_SIZE) + $(RESERVE1_SIZE) + $(RESERVE2_SIZE) + $(SPARE_SIZE)|$(FWTWORKING_SIZE)
 #NV_FTW_WORKING
 DATA = {
   # EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER->Signature = gEdkiiWorkingBlockSignatureGuid =
@@ -60,5 +60,5 @@ DATA = {
   0xe0, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 }
 
-$(EVENT_LOG_OFFSET)|$(EVENT_LOG_SIZE)
+$(VARIABLE_OFFSET) + $(VARIABLE_SIZE) + $(RESERVE1_SIZE) + $(RESERVE2_SIZE) + $(SPARE_SIZE) + $(FWTWORKING_SIZE)|$(EVENT_LOG_SIZE)
 #NV_EVENT_LOG
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [edk2-platforms][PATCH V3 15/16] Platform/Loongson: Add QemuFlashFvbServicesRuntimeDxe driver.
  2022-10-14  4:01 [edk2-platforms][PATCH V3 00/16] Platform: Add Loongson support xianglai
@ 2022-10-14  4:01 ` xianglai
  2022-10-17 11:00   ` Ard Biesheuvel
  0 siblings, 1 reply; 21+ messages in thread
From: xianglai @ 2022-10-14  4:01 UTC (permalink / raw)
  To: devel; +Cc: quic_llindhol, michael.d.kinney, kraxel, ardb, maobibo

This library provides flash read and write functionality
and supports writing variables to flash.

REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4054

Signed-off-by: xianglai li <lixianglai@loongson.cn>
---
 .../QemuFlashFvbServicesRuntimeDxe/FvbInfo.c  |  115 ++
 .../FvbServicesRuntimeDxe.inf                 |   73 ++
 .../FwBlockService.c                          | 1158 +++++++++++++++++
 .../FwBlockService.h                          |  178 +++
 .../FwBlockServiceDxe.c                       |  152 +++
 .../QemuFlash.c                               |  251 ++++
 .../QemuFlash.h                               |   86 ++
 .../QemuFlashDxe.c                            |   21 +
 .../Loongson/LoongArchQemuPkg/Loongson.dsc    |    4 +-
 9 files changed, 2036 insertions(+), 2 deletions(-)
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbInfo.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockServiceDxe.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h
 create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c

diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbInfo.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbInfo.c
new file mode 100644
index 0000000000..df772f72be
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbInfo.c
@@ -0,0 +1,115 @@
+/** @file
+  Defines data structure that is the volume header found.These data is intent
+  to decouple FVB driver with FV header.
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+//
+// The package level header files this module uses
+//
+#include <Pi/PiFirmwareVolume.h>
+
+//
+// The protocols, PPI and GUID definitions for this module
+//
+#include <Guid/SystemNvDataGuid.h>
+//
+// The Library classes this module consumes
+//
+#include <Library/BaseLib.h>
+#include <Library/PcdLib.h>
+
+typedef struct {
+  UINT64                        FvLength;
+  EFI_FIRMWARE_VOLUME_HEADER    FvbInfo;
+  //
+  // EFI_FV_BLOCK_MAP_ENTRY    ExtraBlockMap[n];//n=0
+  //
+  EFI_FV_BLOCK_MAP_ENTRY        End[1];
+} EFI_FVB_MEDIA_INFO;
+
+EFI_FVB_MEDIA_INFO  mPlatformFvbMediaInfo[] = {
+  //
+  // System NvStorage FVB
+  //
+  {
+    FixedPcdGet32 (PcdAllVarSize),
+    {
+      {
+        0,
+      },  // ZeroVector[16]
+      EFI_SYSTEM_NV_DATA_FV_GUID,
+      FixedPcdGet32 (PcdAllVarSize),
+      EFI_FVH_SIGNATURE,
+      EFI_FVB2_MEMORY_MAPPED |
+      EFI_FVB2_READ_ENABLED_CAP |
+      EFI_FVB2_READ_STATUS |
+      EFI_FVB2_WRITE_ENABLED_CAP |
+      EFI_FVB2_WRITE_STATUS |
+      EFI_FVB2_ERASE_POLARITY |
+      EFI_FVB2_ALIGNMENT_16,
+      sizeof (EFI_FIRMWARE_VOLUME_HEADER) + sizeof (EFI_FV_BLOCK_MAP_ENTRY),
+      0,  // CheckSum
+      0,  // ExtHeaderOffset
+      {
+        0,
+      },  // Reserved[1]
+      2,  // Revision
+      {
+        {
+          (FixedPcdGet32 (PcdAllVarSize))/
+          FixedPcdGet32 (PcdFlashBlockSize),
+          FixedPcdGet32 (PcdFlashBlockSize),
+        }
+      } // BlockMap[1]
+    },
+    {
+      {
+        0,
+        0
+      }
+    }  // End[1]
+  }
+};
+
+EFI_STATUS
+GetFvbInfo (
+  IN  UINT64                      FvLength,
+  OUT EFI_FIRMWARE_VOLUME_HEADER  **FvbInfo
+  )
+{
+  STATIC BOOLEAN  Checksummed = FALSE;
+  UINTN           Index;
+
+  if (!Checksummed) {
+    for (Index = 0;
+         Index < sizeof (mPlatformFvbMediaInfo) / sizeof (EFI_FVB_MEDIA_INFO);
+         Index += 1)
+    {
+      UINT16  Checksum;
+      mPlatformFvbMediaInfo[Index].FvbInfo.Checksum = 0;
+      Checksum                                      = CalculateCheckSum16 (
+                                                        (UINT16 *)&mPlatformFvbMediaInfo[Index].FvbInfo,
+                                                        mPlatformFvbMediaInfo[Index].FvbInfo.HeaderLength
+                                                        );
+      mPlatformFvbMediaInfo[Index].FvbInfo.Checksum = Checksum;
+    }
+    Checksummed = TRUE;
+  }
+
+  for (Index = 0;
+       Index < sizeof (mPlatformFvbMediaInfo) / sizeof (EFI_FVB_MEDIA_INFO);
+       Index += 1)
+  {
+    if (mPlatformFvbMediaInfo[Index].FvLength == FvLength) {
+      *FvbInfo = &mPlatformFvbMediaInfo[Index].FvbInfo;
+      return EFI_SUCCESS;
+    }
+  }
+
+  return EFI_NOT_FOUND;
+}
diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
new file mode 100644
index 0000000000..4b0c42b075
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
@@ -0,0 +1,73 @@
+## @file
+# Component description file for Emu Fimware Volume Block DXE driver module.
+#
+#  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = FvbServicesRuntimeDxe
+  FILE_GUID                      = 733cbac2-b23f-4b92-bc8e-fb01ce5907b7
+  MODULE_TYPE                    = DXE_RUNTIME_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = FvbInitialize
+
+#
+# The following information is for reference only and not required by the build
+# tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64
+#
+
+[Sources]
+  FvbInfo.c
+  FwBlockService.c
+  FwBlockServiceDxe.c
+  QemuFlash.c
+  QemuFlashDxe.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  OvmfPkg/OvmfPkg.dec
+  Platform/Loongson/LoongArchQemuPkg/Loongson.dec
+
+[LibraryClasses]
+  BaseLib
+  BaseMemoryLib
+  DebugLib
+  DevicePathLib
+  DxeServicesTableLib
+  MemoryAllocationLib
+  PcdLib
+  UefiBootServicesTableLib
+  UefiDriverEntryPoint
+  UefiRuntimeLib
+
+[Guids]
+  gEfiEventVirtualAddressChangeGuid   # ALWAYS_CONSUMED
+  # gEfiEventVirtualAddressChangeGuid # Create Event: EVENT_GROUP_GUID
+
+[Protocols]
+  gEfiFirmwareVolumeBlockProtocolGuid           # PROTOCOL SOMETIMES_PRODUCED
+  gEfiDevicePathProtocolGuid                    # PROTOCOL SOMETIMES_PRODUCED
+
+[FixedPcd]
+  gLoongArchQemuPkgTokenSpaceGuid.PcdOvmfFlashNvStorageVariableBase
+  gLoongArchQemuPkgTokenSpaceGuid.PcdOvmfFlashNvStorageFtwWorkingBase
+  gLoongArchQemuPkgTokenSpaceGuid.PcdOvmfFlashNvStorageFtwSpareBase
+  gLoongArchQemuPkgTokenSpaceGuid.PcdFlashFdBase
+  gLoongArchQemuPkgTokenSpaceGuid.PcdAllVarSize
+  gLoongArchQemuPkgTokenSpaceGuid.PcdFlashBlockSize
+[Pcd]
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase64
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashVariablesEnable
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase64
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase64
+
+[Depex]
+  TRUE
diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
new file mode 100644
index 0000000000..d44f2b460c
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
@@ -0,0 +1,1158 @@
+/** @file
+  Implementations for Firmware Volume Block protocol.
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+//
+// The protocols, PPI and GUID definitions for this module
+//
+#include <Protocol/DevicePath.h>
+#include <Protocol/FirmwareVolumeBlock.h>
+
+//
+// The Library classes this module consumes
+//
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#include "FwBlockService.h"
+#include "QemuFlash.h"
+
+#define EFI_FVB2_STATUS \
+          (EFI_FVB2_READ_STATUS | EFI_FVB2_WRITE_STATUS | EFI_FVB2_LOCK_STATUS)
+
+ESAL_FWB_GLOBAL  *mFvbModuleGlobal;
+
+FV_MEMMAP_DEVICE_PATH  mFvMemmapDevicePathTemplate = {
+  {
+    {
+      HARDWARE_DEVICE_PATH,
+      HW_MEMMAP_DP,
+      {
+        (UINT8)(sizeof (MEMMAP_DEVICE_PATH)),
+        (UINT8)(sizeof (MEMMAP_DEVICE_PATH) >> 8)
+      }
+    },
+    EfiMemoryMappedIO,
+    (EFI_PHYSICAL_ADDRESS)0,
+    (EFI_PHYSICAL_ADDRESS)0,
+  },
+  {
+    END_DEVICE_PATH_TYPE,
+    END_ENTIRE_DEVICE_PATH_SUBTYPE,
+    {
+      END_DEVICE_PATH_LENGTH,
+      0
+    }
+  }
+};
+
+FV_PIWG_DEVICE_PATH  mFvPIWGDevicePathTemplate = {
+  {
+    {
+      MEDIA_DEVICE_PATH,
+      MEDIA_PIWG_FW_VOL_DP,
+      {
+        (UINT8)(sizeof (MEDIA_FW_VOL_DEVICE_PATH)),
+        (UINT8)(sizeof (MEDIA_FW_VOL_DEVICE_PATH) >> 8)
+      }
+    },
+    { 0 }
+  },
+  {
+    END_DEVICE_PATH_TYPE,
+    END_ENTIRE_DEVICE_PATH_SUBTYPE,
+    {
+      END_DEVICE_PATH_LENGTH,
+      0
+    }
+  }
+};
+
+EFI_FW_VOL_BLOCK_DEVICE  mFvbDeviceTemplate = {
+  FVB_DEVICE_SIGNATURE,
+  NULL,
+  0,
+  {
+    FvbProtocolGetAttributes,
+    FvbProtocolSetAttributes,
+    FvbProtocolGetPhysicalAddress,
+    FvbProtocolGetBlockSize,
+    FvbProtocolRead,
+    FvbProtocolWrite,
+    FvbProtocolEraseBlocks,
+    NULL
+  }
+};
+
+
+EFI_STATUS
+GetFvbInstance (
+  IN  UINTN                Instance,
+  IN  ESAL_FWB_GLOBAL      *Global,
+  OUT EFI_FW_VOL_INSTANCE  **FwhInstance
+  )
+/*++
+
+  Routine Description:
+    Retrieves the physical address of a memory mapped FV
+
+  Arguments:
+    Instance              - The FV instance whose base address is going to be
+                            returned
+    Global                - Pointer to ESAL_FWB_GLOBAL that contains all
+                            instance data
+    FwhInstance           - The EFI_FW_VOL_INSTANCE firmware instance structure
+
+  Returns:
+    EFI_SUCCESS           - Successfully returns
+    EFI_INVALID_PARAMETER - Instance not found
+
+--*/
+{
+  EFI_FW_VOL_INSTANCE  *FwhRecord;
+
+  *FwhInstance = NULL;
+  if (Instance >= Global->NumFv) {
+    return EFI_INVALID_PARAMETER;
+  }
+  //
+  // Find the right instance of the FVB private data
+  //
+  FwhRecord = Global->FvInstance;
+  while (Instance > 0) {
+    FwhRecord = (EFI_FW_VOL_INSTANCE *)
+                (
+                 (UINTN)((UINT8 *)FwhRecord) + FwhRecord->VolumeHeader.HeaderLength +
+                 (sizeof (EFI_FW_VOL_INSTANCE) - sizeof (EFI_FIRMWARE_VOLUME_HEADER))
+                );
+    Instance--;
+  }
+
+  *FwhInstance = FwhRecord;
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+FvbGetPhysicalAddress (
+  IN UINTN                  Instance,
+  OUT EFI_PHYSICAL_ADDRESS  *Address,
+  IN ESAL_FWB_GLOBAL        *Global
+  )
+/*++
+
+  Routine Description:
+    Retrieves the physical address of a memory mapped FV
+
+  Arguments:
+    Instance              - The FV instance whose base address is going to be
+                            returned
+    Address               - Pointer to a caller allocated EFI_PHYSICAL_ADDRESS
+                            that on successful return, contains the base
+                            address of the firmware volume.
+    Global                - Pointer to ESAL_FWB_GLOBAL that contains all
+                            instance data
+
+  Returns:
+    EFI_SUCCESS           - Successfully returns
+    EFI_INVALID_PARAMETER - Instance not found
+
+--*/
+{
+  EFI_FW_VOL_INSTANCE  *FwhInstance;
+  EFI_STATUS           Status;
+
+  //
+  // Find the right instance of the FVB private data
+  //
+  Status = GetFvbInstance (Instance, Global, &FwhInstance);
+  ASSERT_EFI_ERROR (Status);
+  *Address = FwhInstance->FvBase;
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+FvbGetVolumeAttributes (
+  IN UINTN                  Instance,
+  OUT EFI_FVB_ATTRIBUTES_2  *Attributes,
+  IN ESAL_FWB_GLOBAL        *Global
+  )
+/*++
+
+  Routine Description:
+    Retrieves attributes, insures positive polarity of attribute bits, returns
+    resulting attributes in output parameter
+
+  Arguments:
+    Instance              - The FV instance whose attributes is going to be
+                            returned
+    Attributes            - Output buffer which contains attributes
+    Global                - Pointer to ESAL_FWB_GLOBAL that contains all
+                            instance data
+
+  Returns:
+    EFI_SUCCESS           - Successfully returns
+    EFI_INVALID_PARAMETER - Instance not found
+
+--*/
+{
+  EFI_FW_VOL_INSTANCE  *FwhInstance;
+  EFI_STATUS           Status;
+
+  //
+  // Find the right instance of the FVB private data
+  //
+  Status = GetFvbInstance (Instance, Global, &FwhInstance);
+  ASSERT_EFI_ERROR (Status);
+  *Attributes = FwhInstance->VolumeHeader.Attributes;
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+FvbGetLbaAddress (
+  IN  UINTN            Instance,
+  IN  EFI_LBA          Lba,
+  OUT UINTN            *LbaAddress,
+  OUT UINTN            *LbaLength,
+  OUT UINTN            *NumOfBlocks,
+  IN  ESAL_FWB_GLOBAL  *Global
+  )
+/*++
+
+  Routine Description:
+    Retrieves the starting address of an LBA in an FV
+
+  Arguments:
+    Instance              - The FV instance which the Lba belongs to
+    Lba                   - The logical block address
+    LbaAddress            - On output, contains the physical starting address
+                            of the Lba
+    LbaLength             - On output, contains the length of the block
+    NumOfBlocks           - A pointer to a caller allocated UINTN in which the
+                            number of consecutive blocks starting with Lba is
+                            returned. All blocks in this range have a size of
+                            BlockSize
+    Global                - Pointer to ESAL_FWB_GLOBAL that contains all
+                            instance data
+
+  Returns:
+    EFI_SUCCESS           - Successfully returns
+    EFI_INVALID_PARAMETER - Instance not found
+
+--*/
+{
+  UINT32                  NumBlocks;
+  UINT32                  BlockLength;
+  UINTN                   Offset;
+  EFI_LBA                 StartLba;
+  EFI_LBA                 NextLba;
+  EFI_FW_VOL_INSTANCE     *FwhInstance;
+  EFI_FV_BLOCK_MAP_ENTRY  *BlockMap;
+  EFI_STATUS              Status;
+
+  //
+  // Find the right instance of the FVB private data
+  //
+  Status = GetFvbInstance (Instance, Global, &FwhInstance);
+  ASSERT_EFI_ERROR (Status);
+
+  StartLba = 0;
+  Offset   = 0;
+  BlockMap = &(FwhInstance->VolumeHeader.BlockMap[0]);
+
+  //
+  // Parse the blockmap of the FV to find which map entry the Lba belongs to
+  //
+  while (TRUE) {
+    NumBlocks   = BlockMap->NumBlocks;
+    BlockLength = BlockMap->Length;
+
+    if ((NumBlocks == 0) || (BlockLength == 0)) {
+      return EFI_INVALID_PARAMETER;
+    }
+
+    NextLba = StartLba + NumBlocks;
+
+    //
+    // The map entry found
+    //
+    if ((Lba >= StartLba) && (Lba < NextLba)) {
+      Offset = Offset + (UINTN)MultU64x32 ((Lba - StartLba), BlockLength);
+      if (LbaAddress != NULL) {
+        *LbaAddress = FwhInstance->FvBase + Offset;
+      }
+
+      if (LbaLength != NULL) {
+        *LbaLength = BlockLength;
+      }
+
+      if (NumOfBlocks != NULL) {
+        *NumOfBlocks = (UINTN)(NextLba - Lba);
+      }
+
+      return EFI_SUCCESS;
+    }
+
+    StartLba = NextLba;
+    Offset   = Offset + NumBlocks * BlockLength;
+    BlockMap++;
+  }
+}
+
+EFI_STATUS
+FvbSetVolumeAttributes (
+  IN UINTN                     Instance,
+  IN OUT EFI_FVB_ATTRIBUTES_2  *Attributes,
+  IN ESAL_FWB_GLOBAL           *Global
+  )
+/*++
+
+  Routine Description:
+    Modifies the current settings of the firmware volume according to the
+    input parameter, and returns the new setting of the volume
+
+  Arguments:
+    Instance              - The FV instance whose attributes is going to be
+                            modified
+    Attributes            - On input, it is a pointer to EFI_FVB_ATTRIBUTES_2
+                            containing the desired firmware volume settings.
+                            On successful return, it contains the new settings
+                            of the firmware volume
+    Global                - Pointer to ESAL_FWB_GLOBAL that contains all
+                            instance data
+
+  Returns:
+    EFI_SUCCESS           - Successfully returns
+    EFI_ACCESS_DENIED     - The volume setting is locked and cannot be modified
+    EFI_INVALID_PARAMETER - Instance not found, or The attributes requested are
+                            in conflict with the capabilities as declared in
+                            the firmware volume header
+
+--*/
+{
+  EFI_FW_VOL_INSTANCE   *FwhInstance;
+  EFI_FVB_ATTRIBUTES_2  OldAttributes;
+  EFI_FVB_ATTRIBUTES_2  *AttribPtr;
+  UINT32                Capabilities;
+  UINT32                OldStatus;
+  UINT32                NewStatus;
+  EFI_STATUS            Status;
+  EFI_FVB_ATTRIBUTES_2  UnchangedAttributes;
+
+  //
+  // Find the right instance of the FVB private data
+  //
+  Status = GetFvbInstance (Instance, Global, &FwhInstance);
+  ASSERT_EFI_ERROR (Status);
+
+  AttribPtr =
+    (EFI_FVB_ATTRIBUTES_2 *)&(FwhInstance->VolumeHeader.Attributes);
+  OldAttributes = *AttribPtr;
+  Capabilities  = OldAttributes & (EFI_FVB2_READ_DISABLED_CAP | \
+                                   EFI_FVB2_READ_ENABLED_CAP | \
+                                   EFI_FVB2_WRITE_DISABLED_CAP | \
+                                   EFI_FVB2_WRITE_ENABLED_CAP | \
+                                   EFI_FVB2_LOCK_CAP \
+                                   );
+  OldStatus = OldAttributes & EFI_FVB2_STATUS;
+  NewStatus = *Attributes & EFI_FVB2_STATUS;
+
+  UnchangedAttributes = EFI_FVB2_READ_DISABLED_CAP  | \
+                        EFI_FVB2_READ_ENABLED_CAP   | \
+                        EFI_FVB2_WRITE_DISABLED_CAP | \
+                        EFI_FVB2_WRITE_ENABLED_CAP  | \
+                        EFI_FVB2_LOCK_CAP           | \
+                        EFI_FVB2_STICKY_WRITE       | \
+                        EFI_FVB2_MEMORY_MAPPED      | \
+                        EFI_FVB2_ERASE_POLARITY     | \
+                        EFI_FVB2_READ_LOCK_CAP      | \
+                        EFI_FVB2_WRITE_LOCK_CAP     | \
+                        EFI_FVB2_ALIGNMENT;
+
+  //
+  // Some attributes of FV is read only can *not* be set
+  //
+  if ((OldAttributes & UnchangedAttributes) ^
+      (*Attributes & UnchangedAttributes))
+  {
+    return EFI_INVALID_PARAMETER;
+  }
+  //
+  // If firmware volume is locked, no status bit can be updated
+  //
+  if (OldAttributes & EFI_FVB2_LOCK_STATUS) {
+    if (OldStatus ^ NewStatus) {
+      return EFI_ACCESS_DENIED;
+    }
+  }
+  //
+  // Test read disable
+  //
+  if ((Capabilities & EFI_FVB2_READ_DISABLED_CAP) == 0) {
+    if ((NewStatus & EFI_FVB2_READ_STATUS) == 0) {
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+  //
+  // Test read enable
+  //
+  if ((Capabilities & EFI_FVB2_READ_ENABLED_CAP) == 0) {
+    if (NewStatus & EFI_FVB2_READ_STATUS) {
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+  //
+  // Test write disable
+  //
+  if ((Capabilities & EFI_FVB2_WRITE_DISABLED_CAP) == 0) {
+    if ((NewStatus & EFI_FVB2_WRITE_STATUS) == 0) {
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+  //
+  // Test write enable
+  //
+  if ((Capabilities & EFI_FVB2_WRITE_ENABLED_CAP) == 0) {
+    if (NewStatus & EFI_FVB2_WRITE_STATUS) {
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+  //
+  // Test lock
+  //
+  if ((Capabilities & EFI_FVB2_LOCK_CAP) == 0) {
+    if (NewStatus & EFI_FVB2_LOCK_STATUS) {
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+
+  *AttribPtr  = (*AttribPtr) & (0xFFFFFFFF & (~EFI_FVB2_STATUS));
+  *AttribPtr  = (*AttribPtr) | NewStatus;
+  *Attributes = *AttribPtr;
+
+  return EFI_SUCCESS;
+}
+
+//
+// FVB protocol APIs
+//
+EFI_STATUS
+EFIAPI
+FvbProtocolGetPhysicalAddress (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
+  OUT EFI_PHYSICAL_ADDRESS                     *Address
+  )
+/*++
+
+  Routine Description:
+
+    Retrieves the physical address of the device.
+
+  Arguments:
+
+    This                  - Calling context
+    Address               - Output buffer containing the address.
+
+  Returns:
+    EFI_SUCCESS           - Successfully returns
+
+--*/
+{
+  EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice;
+
+  FvbDevice = FVB_DEVICE_FROM_THIS (This);
+
+  return FvbGetPhysicalAddress (
+           FvbDevice->Instance,
+           Address,
+           mFvbModuleGlobal
+           );
+}
+
+EFI_STATUS
+EFIAPI
+FvbProtocolGetBlockSize (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
+  IN CONST EFI_LBA                             Lba,
+  OUT UINTN                                    *BlockSize,
+  OUT UINTN                                    *NumOfBlocks
+  )
+/*++
+
+  Routine Description:
+    Retrieve the size of a logical block
+
+  Arguments:
+    This                  - Calling context
+    Lba                   - Indicates which block to return the size for.
+    BlockSize             - A pointer to a caller allocated UINTN in which
+                            the size of the block is returned
+    NumOfBlocks           - a pointer to a caller allocated UINTN in which the
+                            number of consecutive blocks starting with Lba is
+                            returned. All blocks in this range have a size of
+                            BlockSize
+
+  Returns:
+    EFI_SUCCESS           - The firmware volume was read successfully and
+                            contents are in Buffer
+
+--*/
+{
+  EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice;
+
+  FvbDevice = FVB_DEVICE_FROM_THIS (This);
+
+  return FvbGetLbaAddress (
+           FvbDevice->Instance,
+           Lba,
+           NULL,
+           BlockSize,
+           NumOfBlocks,
+           mFvbModuleGlobal
+           );
+}
+
+EFI_STATUS
+EFIAPI
+FvbProtocolGetAttributes (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
+  OUT EFI_FVB_ATTRIBUTES_2                     *Attributes
+  )
+/*++
+
+  Routine Description:
+      Retrieves Volume attributes.  No polarity translations are done.
+
+  Arguments:
+      This                - Calling context
+      Attributes          - output buffer which contains attributes
+
+  Returns:
+    EFI_SUCCESS           - Successfully returns
+
+--*/
+{
+  EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice;
+
+  FvbDevice = FVB_DEVICE_FROM_THIS (This);
+
+  return FvbGetVolumeAttributes (
+           FvbDevice->Instance,
+           Attributes,
+           mFvbModuleGlobal
+           );
+}
+
+EFI_STATUS
+EFIAPI
+FvbProtocolSetAttributes (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
+  IN OUT EFI_FVB_ATTRIBUTES_2                  *Attributes
+  )
+/*++
+
+  Routine Description:
+    Sets Volume attributes. No polarity translations are done.
+
+  Arguments:
+    This                  - Calling context
+    Attributes            - output buffer which contains attributes
+
+  Returns:
+    EFI_SUCCESS           - Successfully returns
+
+--*/
+{
+  EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice;
+
+  FvbDevice = FVB_DEVICE_FROM_THIS (This);
+
+  return FvbSetVolumeAttributes (
+           FvbDevice->Instance,
+           Attributes,
+           mFvbModuleGlobal
+           );
+}
+
+EFI_STATUS
+EFIAPI
+FvbProtocolEraseBlocks (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
+  ...
+  )
+/*++
+
+  Routine Description:
+
+    The EraseBlock() function erases one or more blocks as denoted by the
+    variable argument list. The entire parameter list of blocks must be
+    verified prior to erasing any blocks.  If a block is requested that does
+    not exist within the associated firmware volume (it has a larger index than
+    the last block of the firmware volume), the EraseBlock() function must
+    return EFI_INVALID_PARAMETER without modifying the contents of the firmware
+    volume.
+
+  Arguments:
+    This                  - Calling context
+    ...                   - Starting LBA followed by Number of Lba to erase.
+                            a -1 to terminate the list.
+
+  Returns:
+    EFI_SUCCESS           - The erase request was successfully completed
+    EFI_ACCESS_DENIED     - The firmware volume is in the WriteDisabled state
+    EFI_DEVICE_ERROR      - The block device is not functioning correctly and
+                            could not be written. Firmware device may have been
+                            partially erased
+
+--*/
+{
+  EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice;
+  EFI_FW_VOL_INSTANCE      *FwhInstance;
+  UINTN                    NumOfBlocks;
+  VA_LIST                  args;
+  EFI_LBA                  StartingLba;
+  UINTN                    NumOfLba;
+  EFI_STATUS               Status;
+
+  FvbDevice = FVB_DEVICE_FROM_THIS (This);
+
+  Status = GetFvbInstance (
+             FvbDevice->Instance,
+             mFvbModuleGlobal,
+             &FwhInstance
+             );
+  ASSERT_EFI_ERROR (Status);
+
+  NumOfBlocks = FwhInstance->NumOfBlocks;
+
+  VA_START (args, This);
+
+  do {
+    StartingLba = VA_ARG (args, EFI_LBA);
+    if (StartingLba == EFI_LBA_LIST_TERMINATOR) {
+      break;
+    }
+
+    NumOfLba = VA_ARG (args, UINTN);
+
+    //
+    // Check input parameters
+    //
+    if ((NumOfLba == 0) || ((StartingLba + NumOfLba) > NumOfBlocks)) {
+      VA_END (args);
+      return EFI_INVALID_PARAMETER;
+    }
+  } while (1);
+
+  VA_END (args);
+
+  VA_START (args, This);
+  do {
+    StartingLba = VA_ARG (args, EFI_LBA);
+    if (StartingLba == EFI_LBA_LIST_TERMINATOR) {
+      break;
+    }
+
+    NumOfLba = VA_ARG (args, UINTN);
+
+    while (NumOfLba > 0) {
+      Status = QemuFlashEraseBlock (StartingLba);
+      if (EFI_ERROR (Status)) {
+        VA_END (args);
+        return Status;
+      }
+
+      StartingLba++;
+      NumOfLba--;
+    }
+
+  } while (1);
+
+  VA_END (args);
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+FvbProtocolWrite (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
+  IN       EFI_LBA                             Lba,
+  IN       UINTN                               Offset,
+  IN OUT   UINTN                               *NumBytes,
+  IN       UINT8                               *Buffer
+  )
+/*++
+
+  Routine Description:
+
+    Writes data beginning at Lba:Offset from FV. The write terminates either
+    when *NumBytes of data have been written, or when a block boundary is
+    reached.  *NumBytes is updated to reflect the actual number of bytes
+    written. The write operation does not include erase. This routine will
+    attempt to write only the specified bytes. If the writes do not stick,
+    it will return an error.
+
+  Arguments:
+    This                  - Calling context
+    Lba                   - Block in which to begin write
+    Offset                - Offset in the block at which to begin write
+    NumBytes              - On input, indicates the requested write size. On
+                            output, indicates the actual number of bytes
+                            written
+    Buffer                - Buffer containing source data for the write.
+
+  Returns:
+    EFI_SUCCESS           - The firmware volume was written successfully
+    EFI_BAD_BUFFER_SIZE   - Write attempted across a LBA boundary. On output,
+                            NumBytes contains the total number of bytes
+                            actually written
+    EFI_ACCESS_DENIED     - The firmware volume is in the WriteDisabled state
+    EFI_DEVICE_ERROR      - The block device is not functioning correctly and
+                            could not be written
+    EFI_INVALID_PARAMETER - NumBytes or Buffer are NULL
+
+--*/
+{
+  return QemuFlashWrite (
+           (EFI_LBA)Lba,
+           (UINTN)Offset,
+           NumBytes,
+           (UINT8 *)Buffer
+           );
+}
+
+EFI_STATUS
+EFIAPI
+FvbProtocolRead (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
+  IN CONST EFI_LBA                             Lba,
+  IN CONST UINTN                               Offset,
+  IN OUT UINTN                                 *NumBytes,
+  IN UINT8                                     *Buffer
+  )
+/*++
+
+  Routine Description:
+
+    Reads data beginning at Lba:Offset from FV. The Read terminates either
+    when *NumBytes of data have been read, or when a block boundary is
+    reached.  *NumBytes is updated to reflect the actual number of bytes
+    written. The write operation does not include erase. This routine will
+    attempt to write only the specified bytes. If the writes do not stick,
+    it will return an error.
+
+  Arguments:
+    This                  - Calling context
+    Lba                   - Block in which to begin Read
+    Offset                - Offset in the block at which to begin Read
+    NumBytes              - On input, indicates the requested write size. On
+                            output, indicates the actual number of bytes Read
+    Buffer                - Buffer containing source data for the Read.
+
+  Returns:
+    EFI_SUCCESS           - The firmware volume was read successfully and
+                            contents are in Buffer
+    EFI_BAD_BUFFER_SIZE   - Read attempted across a LBA boundary. On output,
+                            NumBytes contains the total number of bytes
+                            returned in Buffer
+    EFI_ACCESS_DENIED     - The firmware volume is in the ReadDisabled state
+    EFI_DEVICE_ERROR      - The block device is not functioning correctly and
+                            could not be read
+    EFI_INVALID_PARAMETER - NumBytes or Buffer are NULL
+
+--*/
+{
+  return QemuFlashRead (
+           (EFI_LBA)Lba,
+           (UINTN)Offset,
+           NumBytes,
+           (UINT8 *)Buffer
+           );
+}
+
+EFI_STATUS
+ValidateFvHeader (
+  EFI_FIRMWARE_VOLUME_HEADER  *FwVolHeader
+  )
+/*++
+
+  Routine Description:
+    Check the integrity of firmware volume header
+
+  Arguments:
+    FwVolHeader           - A pointer to a firmware volume header
+
+  Returns:
+    EFI_SUCCESS           - The firmware volume is consistent
+    EFI_NOT_FOUND         - The firmware volume has corrupted. So it is not an
+                            FV
+
+--*/
+{
+  UINT16  Checksum;
+
+  //
+  // Verify the header revision, header signature, length
+  // Length of FvBlock cannot be 2**64-1
+  // HeaderLength cannot be an odd number
+  //
+  if ((FwVolHeader->Revision != EFI_FVH_REVISION) ||
+      (FwVolHeader->Signature != EFI_FVH_SIGNATURE) ||
+      (FwVolHeader->FvLength == ((UINTN)-1)) ||
+      ((FwVolHeader->HeaderLength & 0x01) != 0)
+      )
+  {
+    return EFI_NOT_FOUND;
+  }
+
+  //
+  // Verify the header checksum
+  //
+
+  Checksum = CalculateSum16 (
+               (UINT16 *)FwVolHeader,
+               FwVolHeader->HeaderLength
+               );
+  if (Checksum != 0) {
+    UINT16  Expected;
+
+    Expected =
+      (UINT16)(((UINTN)FwVolHeader->Checksum + 0x10000 - Checksum) & 0xffff);
+
+    DEBUG ((
+      DEBUG_INFO,
+      "FV@%p Checksum is 0x%x, expected 0x%x\n",
+      FwVolHeader,
+      FwVolHeader->Checksum,
+      Expected
+      ));
+    return EFI_NOT_FOUND;
+  }
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+MarkMemoryRangeForRuntimeAccess (
+  EFI_PHYSICAL_ADDRESS                BaseAddress,
+  UINTN                               Length
+  )
+{
+  EFI_STATUS                          Status;
+
+  //
+  // Mark flash region as runtime memory
+  //
+  Status = gDS->RemoveMemorySpace (
+                  BaseAddress,
+                  Length
+                  );
+
+  Status = gDS->AddMemorySpace (
+                  EfiGcdMemoryTypeSystemMemory,
+                  BaseAddress,
+                  Length,
+                  EFI_MEMORY_UC | EFI_MEMORY_RUNTIME
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  Status = gBS->AllocatePages (
+                  AllocateAddress,
+                  EfiRuntimeServicesData,
+                  EFI_SIZE_TO_PAGES (Length),
+                  &BaseAddress
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
+
+STATIC
+EFI_STATUS
+InitializeVariableFvHeader (
+  VOID
+  )
+{
+  EFI_STATUS                  Status;
+  EFI_FIRMWARE_VOLUME_HEADER  *GoodFwVolHeader;
+  EFI_FIRMWARE_VOLUME_HEADER  *FwVolHeader;
+  UINTN                       Length;
+  UINTN                       WriteLength;
+  UINTN                       BlockSize;
+
+  FwVolHeader =
+    (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)
+    PcdGet64 (PcdOvmfFlashNvStorageVariableBase);
+
+  Length =  FixedPcdGet32 (PcdAllVarSize);
+  BlockSize = PcdGet32 (PcdFlashBlockSize);
+
+  Status = ValidateFvHeader (FwVolHeader);
+  if (!EFI_ERROR (Status)) {
+    if ((FwVolHeader->FvLength != Length) ||
+        (FwVolHeader->BlockMap[0].Length != BlockSize))
+    {
+      Status = EFI_VOLUME_CORRUPTED;
+    }
+  }
+  if (EFI_ERROR (Status)) {
+    UINTN  Offset;
+    UINTN  Start;
+
+    DEBUG ((
+      DEBUG_INFO,
+      "Variable FV header is not valid. It will be reinitialized.\n"
+      ));
+
+    //
+    // Get FvbInfo to provide in FwhInstance.
+    //
+    Status = GetFvbInfo (Length, &GoodFwVolHeader);
+    ASSERT (!EFI_ERROR (Status));
+
+    Start = (UINTN)(UINT8*) FwVolHeader - PcdGet64 (PcdFlashFdBase);
+    ASSERT (Start % BlockSize == 0 && Length % BlockSize == 0);
+    ASSERT (GoodFwVolHeader->HeaderLength <= BlockSize);
+
+    //
+    // Erase all the blocks
+    //
+    for (Offset = Start; Offset < Start + Length; Offset += BlockSize) {
+      Status = QemuFlashEraseBlock (Offset / BlockSize);
+      ASSERT_EFI_ERROR (Status);
+    }
+
+    //
+    // Write good FV header
+    //
+    WriteLength = GoodFwVolHeader->HeaderLength;
+    Status      = QemuFlashWrite (
+                    Start / BlockSize,
+                    0,
+                    &WriteLength,
+                    (UINT8 *)GoodFwVolHeader
+                    );
+    ASSERT_EFI_ERROR (Status);
+    ASSERT (WriteLength == GoodFwVolHeader->HeaderLength);
+  }
+
+  return Status;
+}
+
+EFI_STATUS
+EFIAPI
+FvbInitialize (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+/*++
+
+  Routine Description:
+    This function does common initialization for FVB services
+
+  Arguments:
+
+  Returns:
+
+--*/
+{
+  EFI_STATUS                  Status;
+  EFI_FW_VOL_INSTANCE         *FwhInstance;
+  EFI_FIRMWARE_VOLUME_HEADER  *FwVolHeader;
+  UINT32                      BufferSize;
+  EFI_FV_BLOCK_MAP_ENTRY      *PtrBlockMapEntry;
+  EFI_FW_VOL_BLOCK_DEVICE     *FvbDevice;
+  UINT32                      MaxLbaSize;
+  EFI_PHYSICAL_ADDRESS        BaseAddress;
+  UINTN                       Length;
+  UINTN                       NumOfBlocks;
+  RETURN_STATUS               PcdStatus;
+
+  if (EFI_ERROR (QemuFlashInitialize ())) {
+    //
+    // Return an error so image will be unloaded
+    //
+    DEBUG ((
+      DEBUG_INFO,
+      "QEMU flash was not detected. Writable FVB is not being installed.\n"
+      ));
+    return EFI_WRITE_PROTECTED;
+  }
+
+  //
+  // Allocate runtime services data for global variable, which contains
+  // the private data of all firmware volume block instances
+  //
+  mFvbModuleGlobal = AllocateRuntimePool (sizeof (ESAL_FWB_GLOBAL));
+  ASSERT (mFvbModuleGlobal != NULL);
+
+  BaseAddress = (UINTN) PcdGet64 (PcdOvmfFlashNvStorageVariableBase);
+  Length = PcdGet32 (PcdAllVarSize);
+
+  Status = InitializeVariableFvHeader ();
+  if (EFI_ERROR (Status)) {
+    DEBUG ((
+      DEBUG_INFO,
+      "QEMU Flash: Unable to initialize variable FV header\n"
+      ));
+    return EFI_WRITE_PROTECTED;
+  }
+
+  FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)BaseAddress;
+  Status      = ValidateFvHeader (FwVolHeader);
+  if (EFI_ERROR (Status)) {
+    //
+    // Get FvbInfo
+    //
+    Status = GetFvbInfo (Length, &FwVolHeader);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_INFO, "EFI_ERROR (GetFvbInfo (Length, &FwVolHeader))\n"));
+      return EFI_WRITE_PROTECTED;
+    }
+  }
+
+  BufferSize = (sizeof (EFI_FW_VOL_INSTANCE) +
+                FwVolHeader->HeaderLength -
+                sizeof (EFI_FIRMWARE_VOLUME_HEADER)
+                );
+  mFvbModuleGlobal->FvInstance = AllocateRuntimePool (BufferSize);
+  ASSERT (mFvbModuleGlobal->FvInstance != NULL);
+
+  FwhInstance = mFvbModuleGlobal->FvInstance;
+
+  mFvbModuleGlobal->NumFv = 0;
+  MaxLbaSize              = 0;
+
+  FwVolHeader =
+    (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)
+    PcdGet64 (PcdOvmfFlashNvStorageVariableBase);
+
+  FwhInstance->FvBase = (UINTN)BaseAddress;
+
+  CopyMem (
+    (UINTN *)&(FwhInstance->VolumeHeader),
+    (UINTN *)FwVolHeader,
+    FwVolHeader->HeaderLength
+    );
+  FwVolHeader = &(FwhInstance->VolumeHeader);
+
+  NumOfBlocks = 0;
+
+  for (PtrBlockMapEntry = FwVolHeader->BlockMap;
+       PtrBlockMapEntry->NumBlocks != 0;
+       PtrBlockMapEntry++)
+  {
+    //
+    // Get the maximum size of a block.
+    //
+    if (MaxLbaSize < PtrBlockMapEntry->Length) {
+      MaxLbaSize = PtrBlockMapEntry->Length;
+    }
+
+    NumOfBlocks = NumOfBlocks + PtrBlockMapEntry->NumBlocks;
+  }
+
+  //
+  // The total number of blocks in the FV.
+  //
+  FwhInstance->NumOfBlocks = NumOfBlocks;
+
+  //
+  // Add a FVB Protocol Instance
+  //
+  FvbDevice = AllocateRuntimePool (sizeof (EFI_FW_VOL_BLOCK_DEVICE));
+  ASSERT (FvbDevice != NULL);
+
+  CopyMem (FvbDevice, &mFvbDeviceTemplate, sizeof (EFI_FW_VOL_BLOCK_DEVICE));
+
+  FvbDevice->Instance = mFvbModuleGlobal->NumFv;
+  mFvbModuleGlobal->NumFv++;
+
+  //
+  // Set up the devicepath
+  //
+  if (FwVolHeader->ExtHeaderOffset == 0) {
+    FV_MEMMAP_DEVICE_PATH  *FvMemmapDevicePath;
+
+    //
+    // FV does not contains extension header, then produce MEMMAP_DEVICE_PATH
+    //
+    FvMemmapDevicePath = AllocateCopyPool (
+                           sizeof (FV_MEMMAP_DEVICE_PATH),
+                           &mFvMemmapDevicePathTemplate
+                           );
+    FvMemmapDevicePath->MemMapDevPath.StartingAddress = BaseAddress;
+    FvMemmapDevicePath->MemMapDevPath.EndingAddress   =
+      BaseAddress + FwVolHeader->FvLength - 1;
+    FvbDevice->DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)FvMemmapDevicePath;
+  } else {
+    FV_PIWG_DEVICE_PATH  *FvPiwgDevicePath;
+
+    FvPiwgDevicePath = AllocateCopyPool (
+                         sizeof (FV_PIWG_DEVICE_PATH),
+                         &mFvPIWGDevicePathTemplate
+                         );
+    CopyGuid (
+      &FvPiwgDevicePath->FvDevPath.FvName,
+      (GUID *)(UINTN)(BaseAddress + FwVolHeader->ExtHeaderOffset)
+      );
+    FvbDevice->DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)FvPiwgDevicePath;
+  }
+
+  //
+  // Module type specific hook.
+  //
+  InstallProtocolInterfaces (FvbDevice);
+
+  MarkMemoryRangeForRuntimeAccess (BaseAddress, Length);
+
+  //
+  // Set several PCD values to point to flash
+  //
+
+
+  PcdStatus = PcdSet64S (
+    PcdFlashNvStorageVariableBase64,
+    (UINTN) PcdGet64 (PcdOvmfFlashNvStorageVariableBase)
+    );
+  ASSERT_RETURN_ERROR (PcdStatus);
+  PcdStatus = PcdSet64S (
+    PcdFlashNvStorageFtwWorkingBase64,
+    PcdGet64 (PcdOvmfFlashNvStorageFtwWorkingBase)
+    );
+  ASSERT_RETURN_ERROR (PcdStatus);
+  PcdStatus = PcdSet64S (
+    PcdFlashNvStorageFtwSpareBase64,
+    PcdGet64 (PcdOvmfFlashNvStorageFtwSpareBase)
+    );
+  ASSERT_RETURN_ERROR (PcdStatus);
+
+  FwhInstance = (EFI_FW_VOL_INSTANCE *)
+                (
+                 (UINTN)((UINT8 *)FwhInstance) + FwVolHeader->HeaderLength +
+                 (sizeof (EFI_FW_VOL_INSTANCE) - sizeof (EFI_FIRMWARE_VOLUME_HEADER))
+                );
+
+  //
+  // Module type specific hook.
+  //
+  InstallVirtualAddressChangeHandler ();
+
+  PcdStatus = PcdSetBoolS (PcdOvmfFlashVariablesEnable, TRUE);
+  ASSERT_RETURN_ERROR (PcdStatus);
+  return EFI_SUCCESS;
+}
diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.h b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.h
new file mode 100644
index 0000000000..e7234a0c5e
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.h
@@ -0,0 +1,178 @@
+/** @file
+  Firmware volume block driver for Intel Firmware Hub (FWH) device
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+#ifndef _FW_BLOCK_SERVICE_H
+#define _FW_BLOCK_SERVICE_H
+
+typedef struct {
+  UINTN                         FvBase;
+  UINTN                         NumOfBlocks;
+  EFI_FIRMWARE_VOLUME_HEADER    VolumeHeader;
+} EFI_FW_VOL_INSTANCE;
+
+typedef struct {
+  UINT32                 NumFv;
+  EFI_FW_VOL_INSTANCE    *FvInstance;
+} ESAL_FWB_GLOBAL;
+
+extern ESAL_FWB_GLOBAL  *mFvbModuleGlobal;
+
+//
+// Fvb Protocol instance data
+//
+#define FVB_DEVICE_FROM_THIS(a)  CR (a, EFI_FW_VOL_BLOCK_DEVICE,\
+                                  FwVolBlockInstance, FVB_DEVICE_SIGNATURE)
+
+#define FVB_EXTEND_DEVICE_FROM_THIS(a)  CR (a, EFI_FW_VOL_BLOCK_DEVICE,\
+                                         FvbExtension, FVB_DEVICE_SIGNATURE)
+
+#define FVB_DEVICE_SIGNATURE  SIGNATURE_32 ('F', 'V', 'B', 'N')
+
+typedef struct {
+  MEDIA_FW_VOL_DEVICE_PATH    FvDevPath;
+  EFI_DEVICE_PATH_PROTOCOL    EndDevPath;
+} FV_PIWG_DEVICE_PATH;
+
+typedef struct {
+  MEMMAP_DEVICE_PATH          MemMapDevPath;
+  EFI_DEVICE_PATH_PROTOCOL    EndDevPath;
+} FV_MEMMAP_DEVICE_PATH;
+
+typedef struct {
+  UINTN                                 Signature;
+  EFI_DEVICE_PATH_PROTOCOL              *DevicePath;
+  UINTN                                 Instance;
+  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL    FwVolBlockInstance;
+} EFI_FW_VOL_BLOCK_DEVICE;
+
+EFI_STATUS
+GetFvbInfo (
+  IN  UINT64                      FvLength,
+  OUT EFI_FIRMWARE_VOLUME_HEADER  **FvbInfo
+  );
+
+EFI_STATUS
+FvbSetVolumeAttributes (
+  IN UINTN                     Instance,
+  IN OUT EFI_FVB_ATTRIBUTES_2  *Attributes,
+  IN ESAL_FWB_GLOBAL           *Global
+  );
+
+EFI_STATUS
+FvbGetVolumeAttributes (
+  IN UINTN                  Instance,
+  OUT EFI_FVB_ATTRIBUTES_2  *Attributes,
+  IN ESAL_FWB_GLOBAL        *Global
+  );
+
+EFI_STATUS
+FvbGetPhysicalAddress (
+  IN UINTN                  Instance,
+  OUT EFI_PHYSICAL_ADDRESS  *Address,
+  IN ESAL_FWB_GLOBAL        *Global
+  );
+
+EFI_STATUS
+EFIAPI
+FvbInitialize (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  );
+
+
+VOID
+EFIAPI
+FvbClassAddressChangeEvent (
+  IN EFI_EVENT  Event,
+  IN VOID       *Context
+  );
+
+EFI_STATUS
+FvbGetLbaAddress (
+  IN  UINTN            Instance,
+  IN  EFI_LBA          Lba,
+  OUT UINTN            *LbaAddress,
+  OUT UINTN            *LbaLength,
+  OUT UINTN            *NumOfBlocks,
+  IN  ESAL_FWB_GLOBAL  *Global
+  );
+
+//
+// Protocol APIs
+//
+EFI_STATUS
+EFIAPI
+FvbProtocolGetAttributes (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
+  OUT EFI_FVB_ATTRIBUTES_2                     *Attributes
+  );
+
+EFI_STATUS
+EFIAPI
+FvbProtocolSetAttributes (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
+  IN OUT EFI_FVB_ATTRIBUTES_2                  *Attributes
+  );
+
+EFI_STATUS
+EFIAPI
+FvbProtocolGetPhysicalAddress (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
+  OUT EFI_PHYSICAL_ADDRESS                     *Address
+  );
+
+EFI_STATUS
+EFIAPI
+FvbProtocolGetBlockSize (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
+  IN CONST EFI_LBA                             Lba,
+  OUT UINTN                                    *BlockSize,
+  OUT UINTN                                    *NumOfBlocks
+  );
+
+EFI_STATUS
+EFIAPI
+FvbProtocolRead (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
+  IN CONST EFI_LBA                             Lba,
+  IN CONST UINTN                               Offset,
+  IN OUT UINTN                                 *NumBytes,
+  IN UINT8                                     *Buffer
+  );
+
+EFI_STATUS
+EFIAPI
+FvbProtocolWrite (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
+  IN       EFI_LBA                             Lba,
+  IN       UINTN                               Offset,
+  IN OUT   UINTN                               *NumBytes,
+  IN       UINT8                               *Buffer
+  );
+
+EFI_STATUS
+EFIAPI
+FvbProtocolEraseBlocks (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
+  ...
+  );
+
+//
+// The following functions have different implementations dependent on the
+// module type chosen for building this driver.
+//
+VOID
+InstallProtocolInterfaces (
+  IN EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice
+  );
+
+VOID
+InstallVirtualAddressChangeHandler (
+  VOID
+  );
+#endif
diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockServiceDxe.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockServiceDxe.c
new file mode 100644
index 0000000000..cb85499539
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockServiceDxe.c
@@ -0,0 +1,152 @@
+/** @file
+  Functions related to the Firmware Volume Block service whose
+  implementation is specific to the runtime DXE driver build.
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Guid/EventGroup.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/PcdLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeLib.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/FirmwareVolumeBlock.h>
+
+#include "FwBlockService.h"
+#include "QemuFlash.h"
+
+VOID
+InstallProtocolInterfaces (
+  IN EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice
+  )
+{
+  EFI_STATUS                          Status;
+  EFI_HANDLE                          FwbHandle;
+  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *OldFwbInterface;
+
+  //
+  // Find a handle with a matching device path that has supports FW Block
+  // protocol
+  //
+  Status = gBS->LocateDevicePath (
+                  &gEfiFirmwareVolumeBlockProtocolGuid,
+                  &FvbDevice->DevicePath,
+                  &FwbHandle
+                  );
+  if (EFI_ERROR (Status)) {
+    //
+    // LocateDevicePath fails so install a new interface and device path
+    //
+    FwbHandle = NULL;
+    DEBUG ((DEBUG_INFO, "Installing QEMU flash FVB\n"));
+    Status = gBS->InstallMultipleProtocolInterfaces (
+                    &FwbHandle,
+                    &gEfiFirmwareVolumeBlockProtocolGuid,
+                    &FvbDevice->FwVolBlockInstance,
+                    &gEfiDevicePathProtocolGuid,
+                    FvbDevice->DevicePath,
+                    NULL
+                    );
+    ASSERT_EFI_ERROR (Status);
+  } else if (IsDevicePathEnd (FvbDevice->DevicePath)) {
+    //
+    // Device already exists, so reinstall the FVB protocol
+    //
+    Status = gBS->HandleProtocol (
+                    FwbHandle,
+                    &gEfiFirmwareVolumeBlockProtocolGuid,
+                    (VOID **)&OldFwbInterface
+                    );
+    ASSERT_EFI_ERROR (Status);
+
+    DEBUG ((DEBUG_INFO, "Reinstalling FVB for QEMU flash region\n"));
+    Status = gBS->ReinstallProtocolInterface (
+                    FwbHandle,
+                    &gEfiFirmwareVolumeBlockProtocolGuid,
+                    OldFwbInterface,
+                    &FvbDevice->FwVolBlockInstance
+                    );
+    ASSERT_EFI_ERROR (Status);
+  } else {
+    //
+    // There was a FVB protocol on an End Device Path node
+    //
+    ASSERT (FALSE);
+  }
+}
+
+
+STATIC
+VOID
+EFIAPI
+FvbVirtualAddressChangeEvent (
+  IN EFI_EVENT  Event,
+  IN VOID       *Context
+  )
+/*++
+
+  Routine Description:
+
+    Fixup internal data so that EFI and SAL can be call in virtual mode.
+    Call the passed in Child Notify event and convert the mFvbModuleGlobal
+    date items to there virtual address.
+
+  Arguments:
+
+    (Standard EFI notify event - EFI_EVENT_NOTIFY)
+
+  Returns:
+
+    None
+
+--*/
+{
+  EFI_FW_VOL_INSTANCE  *FwhInstance;
+  UINTN                Index;
+
+  FwhInstance = mFvbModuleGlobal->FvInstance;
+  EfiConvertPointer (0x0, (VOID **)&mFvbModuleGlobal->FvInstance);
+
+  //
+  // Convert the base address of all the instances
+  //
+  Index = 0;
+  while (Index < mFvbModuleGlobal->NumFv) {
+    EfiConvertPointer (0x0, (VOID **)&FwhInstance->FvBase);
+    FwhInstance = (EFI_FW_VOL_INSTANCE *)
+                  (
+                   (UINTN)((UINT8 *)FwhInstance) +
+                   FwhInstance->VolumeHeader.HeaderLength +
+                   (sizeof (EFI_FW_VOL_INSTANCE) - sizeof (EFI_FIRMWARE_VOLUME_HEADER))
+                  );
+    Index++;
+  }
+
+  EfiConvertPointer (0x0, (VOID **)&mFvbModuleGlobal);
+  QemuFlashConvertPointers ();
+}
+
+
+VOID
+InstallVirtualAddressChangeHandler (
+  VOID
+  )
+{
+  EFI_STATUS  Status;
+  EFI_EVENT   VirtualAddressChangeEvent;
+
+  Status = gBS->CreateEventEx (
+                  EVT_NOTIFY_SIGNAL,
+                  TPL_NOTIFY,
+                  FvbVirtualAddressChangeEvent,
+                  NULL,
+                  &gEfiEventVirtualAddressChangeGuid,
+                  &VirtualAddressChangeEvent
+                  );
+  ASSERT_EFI_ERROR (Status);
+}
diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c
new file mode 100644
index 0000000000..a85d736060
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c
@@ -0,0 +1,251 @@
+/** @file
+  LoongArch support for QEMU system firmware flash device
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/PcdLib.h>
+
+#include "QemuFlash.h"
+
+#define WRITE_BYTE_CMD           0x10
+#define BLOCK_ERASE_CMD          0x20
+#define CLEAR_STATUS_CMD         0x50
+#define READ_STATUS_CMD          0x70
+#define READ_DEVID_CMD           0x90
+#define BLOCK_ERASE_CONFIRM_CMD  0xd0
+#define READ_ARRAY_CMD           0xff
+
+#define CLEARED_ARRAY_STATUS  0x00
+
+UINT8  *mFlashBase;
+
+STATIC UINTN  mFdBlockSize  = 0;
+STATIC UINTN  mFdBlockCount = 0;
+
+STATIC
+volatile UINT8 *
+QemuFlashPtr (
+  IN        EFI_LBA  Lba,
+  IN        UINTN    Offset
+  )
+{
+  return mFlashBase + ((UINTN)Lba * mFdBlockSize) + Offset;
+}
+
+
+/**
+  Determines if the QEMU flash memory device is present.
+
+  @retval FALSE   The QEMU flash device is not present.
+  @retval TRUE    The QEMU flash device is present.
+
+**/
+STATIC
+BOOLEAN
+QemuFlashDetected (
+  VOID
+  )
+{
+  BOOLEAN         FlashDetected;
+  volatile UINT8  *Ptr;
+
+  UINTN  Offset;
+  UINT8  OriginalUint8;
+  UINT8  ProbeUint8;
+
+  FlashDetected = FALSE;
+  Ptr           = QemuFlashPtr (0, 0);
+
+  for (Offset = 0; Offset < mFdBlockSize; Offset++) {
+    Ptr        = QemuFlashPtr (0, Offset);
+    ProbeUint8 = *Ptr;
+    if ((ProbeUint8 != CLEAR_STATUS_CMD) &&
+        (ProbeUint8 != READ_STATUS_CMD) &&
+        (ProbeUint8 != CLEARED_ARRAY_STATUS))
+    {
+      break;
+    }
+  }
+
+  if (Offset >= mFdBlockSize) {
+    DEBUG ((DEBUG_INFO, "QEMU Flash: Failed to find probe location\n"));
+    return FALSE;
+  }
+
+  DEBUG ((DEBUG_INFO, "QEMU Flash: Attempting flash detection at %p\n", Ptr));
+
+  OriginalUint8 = *Ptr;
+  *Ptr          = CLEAR_STATUS_CMD;
+  ProbeUint8    = *Ptr;
+  if ((OriginalUint8 != CLEAR_STATUS_CMD) &&
+      (ProbeUint8 == CLEAR_STATUS_CMD))
+  {
+    DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as RAM\n"));
+    *Ptr = OriginalUint8;
+  } else {
+    *Ptr       = READ_STATUS_CMD;
+    ProbeUint8 = *Ptr;
+    if (ProbeUint8 == OriginalUint8) {
+      DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as ROM\n"));
+    } else if (ProbeUint8 == READ_STATUS_CMD) {
+      DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as RAM\n"));
+      *Ptr = OriginalUint8;
+    } else if (ProbeUint8 == CLEARED_ARRAY_STATUS) {
+      DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as FLASH\n"));
+      FlashDetected = TRUE;
+      *Ptr          = READ_ARRAY_CMD;
+    }
+  }
+
+  DEBUG ((
+    DEBUG_INFO,
+    "QemuFlashDetected => %a\n",
+    FlashDetected ? "Yes" : "No"
+    ));
+  return FlashDetected;
+}
+
+
+/**
+  Read from QEMU Flash
+
+  @param[in] Lba      The starting logical block index to read from.
+  @param[in] Offset   Offset into the block at which to begin reading.
+  @param[in] NumBytes On input, indicates the requested read size. On
+                      output, indicates the actual number of bytes read
+  @param[in] Buffer   Pointer to the buffer to read into.
+
+**/
+EFI_STATUS
+QemuFlashRead (
+  IN        EFI_LBA  Lba,
+  IN        UINTN    Offset,
+  IN        UINTN    *NumBytes,
+  IN        UINT8    *Buffer
+  )
+{
+  UINT8  *Ptr;
+
+  //
+  // Only write to the first 64k. We don't bother saving the FTW Spare
+  // block into the flash memory.
+  //
+  if (Lba >= mFdBlockCount) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Get flash address
+  //
+  Ptr = (UINT8 *)QemuFlashPtr (Lba, Offset);
+
+  CopyMem (Buffer, Ptr, *NumBytes);
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Write to QEMU Flash
+
+  @param[in] Lba      The starting logical block index to write to.
+  @param[in] Offset   Offset into the block at which to begin writing.
+  @param[in] NumBytes On input, indicates the requested write size. On
+                      output, indicates the actual number of bytes written
+  @param[in] Buffer   Pointer to the data to write.
+
+**/
+EFI_STATUS
+QemuFlashWrite (
+  IN        EFI_LBA  Lba,
+  IN        UINTN    Offset,
+  IN        UINTN    *NumBytes,
+  IN        UINT8    *Buffer
+  )
+{
+  volatile UINT8  *Ptr;
+  UINTN           Loop;
+
+  //
+  // Only write to the first 64k. We don't bother saving the FTW Spare
+  // block into the flash memory.
+  //
+  if (Lba >= mFdBlockCount) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Program flash
+  //
+  Ptr = QemuFlashPtr (Lba, Offset);
+  for (Loop = 0; Loop < *NumBytes; Loop++) {
+    *Ptr = WRITE_BYTE_CMD;
+    *Ptr = Buffer[Loop];
+    Ptr++;
+  }
+
+  //
+  // Restore flash to read mode
+  //
+  if (*NumBytes > 0) {
+    *(Ptr - 1) = READ_ARRAY_CMD;
+  }
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Erase a QEMU Flash block
+
+  @param Lba    The logical block index to erase.
+
+**/
+EFI_STATUS
+QemuFlashEraseBlock (
+  IN   EFI_LBA  Lba
+  )
+{
+  volatile UINT8  *Ptr;
+
+  if (Lba >= mFdBlockCount) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Ptr = QemuFlashPtr (Lba, 0);
+  *Ptr = BLOCK_ERASE_CMD;
+  *Ptr = BLOCK_ERASE_CONFIRM_CMD;
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Initializes QEMU flash memory support
+
+  @retval EFI_WRITE_PROTECTED   The QEMU flash device is not present.
+  @retval EFI_SUCCESS           The QEMU flash device is supported.
+
+**/
+EFI_STATUS
+QemuFlashInitialize (
+  VOID
+  )
+{
+  mFlashBase   = (UINT8 *)(UINTN)PcdGet64 (PcdOvmfFlashNvStorageVariableBase);
+  mFdBlockSize = PcdGet32 (PcdFlashBlockSize);
+  ASSERT(PcdGet32 (PcdAllVarSize) % mFdBlockSize == 0);
+  mFdBlockCount = PcdGet32 (PcdAllVarSize) / mFdBlockSize;
+
+  if (!QemuFlashDetected ()) {
+    return EFI_WRITE_PROTECTED;
+  }
+
+  return EFI_SUCCESS;
+}
+
diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h
new file mode 100644
index 0000000000..27caabc5f0
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h
@@ -0,0 +1,86 @@
+/** @file
+  LoongArch support for QEMU system firmware flash device
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __QEMU_FLASH_H__
+#define __QEMU_FLASH_H__
+
+#include <Protocol/FirmwareVolumeBlock.h>
+
+extern UINT8  *mFlashBase;
+
+/**
+  Read from QEMU Flash
+
+  @param[in] Lba      The starting logical block index to read from.
+  @param[in] Offset   Offset into the block at which to begin reading.
+  @param[in] NumBytes On input, indicates the requested read size. On
+                      output, indicates the actual number of bytes read
+  @param[in] Buffer   Pointer to the buffer to read into.
+
+**/
+EFI_STATUS
+QemuFlashRead (
+  IN        EFI_LBA  Lba,
+  IN        UINTN    Offset,
+  IN        UINTN    *NumBytes,
+  IN        UINT8    *Buffer
+  );
+
+
+/**
+  Write to QEMU Flash
+
+  @param[in] Lba      The starting logical block index to write to.
+  @param[in] Offset   Offset into the block at which to begin writing.
+  @param[in] NumBytes On input, indicates the requested write size. On
+                      output, indicates the actual number of bytes written
+  @param[in] Buffer   Pointer to the data to write.
+
+**/
+EFI_STATUS
+QemuFlashWrite (
+  IN        EFI_LBA  Lba,
+  IN        UINTN    Offset,
+  IN        UINTN    *NumBytes,
+  IN        UINT8    *Buffer
+  );
+
+
+/**
+  Erase a QEMU Flash block
+
+  @param Lba    The logical block index to erase.
+
+**/
+EFI_STATUS
+QemuFlashEraseBlock (
+  IN   EFI_LBA  Lba
+  );
+
+
+/**
+  Initializes QEMU flash memory support
+
+  @retval EFI_WRITE_PROTECTED   The QEMU flash device is not present.
+  @retval EFI_SUCCESS           The QEMU flash device is supported.
+
+**/
+EFI_STATUS
+QemuFlashInitialize (
+  VOID
+  );
+
+
+VOID
+QemuFlashConvertPointers (
+  VOID
+  );
+
+#endif
+
diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c
new file mode 100644
index 0000000000..b63314aac2
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c
@@ -0,0 +1,21 @@
+/** @file
+  LoongArch support for QEMU system firmware flash device: functions specific to the
+  runtime DXE driver build.
+
+  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/UefiRuntimeLib.h>
+
+#include "QemuFlash.h"
+
+VOID
+QemuFlashConvertPointers (
+  VOID
+  )
+{
+  EfiConvertPointer (0x0, (VOID **)&mFlashBase);
+}
diff --git a/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc b/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc
index 74c83720b7..59beafb34f 100644
--- a/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc
+++ b/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc
@@ -171,10 +171,10 @@
   VirtioLib                        | OvmfPkg/Library/VirtioLib/VirtioLib.inf
   FrameBufferBltLib                | MdeModulePkg/Library/FrameBufferBltLib/FrameBufferBltLib.inf
   QemuFwCfgLib                     | OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibMmio.inf
-
   DebugLib                         | MdePkg/Library/BaseDebugLibSerialPort/BaseDebugLibSerialPort.inf
-
   PeiServicesLib                   | MdePkg/Library/PeiServicesLib/PeiServicesLib.inf
+  VariableFlashInfoLib             | MdeModulePkg/Library/BaseVariableFlashInfoLib/BaseVariableFlashInfoLib.inf
+
 [LibraryClasses.common.SEC]
   ReportStatusCodeLib              | MdeModulePkg/Library/PeiReportStatusCodeLib/PeiReportStatusCodeLib.inf
   HobLib                           | MdePkg/Library/PeiHobLib/PeiHobLib.inf
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [edk2-platforms][PATCH V3 15/16] Platform/Loongson: Add QemuFlashFvbServicesRuntimeDxe driver.
  2022-10-14  4:01 ` [edk2-platforms][PATCH V3 15/16] Platform/Loongson: Add QemuFlashFvbServicesRuntimeDxe driver xianglai
@ 2022-10-17 11:00   ` Ard Biesheuvel
  2022-10-17 12:56     ` xianglai
  0 siblings, 1 reply; 21+ messages in thread
From: Ard Biesheuvel @ 2022-10-17 11:00 UTC (permalink / raw)
  To: xianglai li; +Cc: devel, quic_llindhol, michael.d.kinney, kraxel, maobibo

On Fri, 14 Oct 2022 at 06:01, xianglai li <lixianglai@loongson.cn> wrote:
>
> This library provides flash read and write functionality
> and supports writing variables to flash.
>
> REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4054
>
> Signed-off-by: xianglai li <lixianglai@loongson.cn>

Why do you need a new driver for this? We already have drivers to
support QEMU's NOR flash emulation, and the NorFlashDxe driver is
being migrated to OvmfPkg/ in a series that is on the list now for
RISC-V

> ---
>  .../QemuFlashFvbServicesRuntimeDxe/FvbInfo.c  |  115 ++
>  .../FvbServicesRuntimeDxe.inf                 |   73 ++
>  .../FwBlockService.c                          | 1158 +++++++++++++++++
>  .../FwBlockService.h                          |  178 +++
>  .../FwBlockServiceDxe.c                       |  152 +++
>  .../QemuFlash.c                               |  251 ++++
>  .../QemuFlash.h                               |   86 ++
>  .../QemuFlashDxe.c                            |   21 +
>  .../Loongson/LoongArchQemuPkg/Loongson.dsc    |    4 +-
>  9 files changed, 2036 insertions(+), 2 deletions(-)
>  create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbInfo.c
>  create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
>  create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
>  create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.h
>  create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockServiceDxe.c
>  create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c
>  create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h
>  create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c
>
> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbInfo.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbInfo.c
> new file mode 100644
> index 0000000000..df772f72be
> --- /dev/null
> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbInfo.c
> @@ -0,0 +1,115 @@
> +/** @file
> +  Defines data structure that is the volume header found.These data is intent
> +  to decouple FVB driver with FV header.
> +
> +  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +//
> +// The package level header files this module uses
> +//
> +#include <Pi/PiFirmwareVolume.h>
> +
> +//
> +// The protocols, PPI and GUID definitions for this module
> +//
> +#include <Guid/SystemNvDataGuid.h>
> +//
> +// The Library classes this module consumes
> +//
> +#include <Library/BaseLib.h>
> +#include <Library/PcdLib.h>
> +
> +typedef struct {
> +  UINT64                        FvLength;
> +  EFI_FIRMWARE_VOLUME_HEADER    FvbInfo;
> +  //
> +  // EFI_FV_BLOCK_MAP_ENTRY    ExtraBlockMap[n];//n=0
> +  //
> +  EFI_FV_BLOCK_MAP_ENTRY        End[1];
> +} EFI_FVB_MEDIA_INFO;
> +
> +EFI_FVB_MEDIA_INFO  mPlatformFvbMediaInfo[] = {
> +  //
> +  // System NvStorage FVB
> +  //
> +  {
> +    FixedPcdGet32 (PcdAllVarSize),
> +    {
> +      {
> +        0,
> +      },  // ZeroVector[16]
> +      EFI_SYSTEM_NV_DATA_FV_GUID,
> +      FixedPcdGet32 (PcdAllVarSize),
> +      EFI_FVH_SIGNATURE,
> +      EFI_FVB2_MEMORY_MAPPED |
> +      EFI_FVB2_READ_ENABLED_CAP |
> +      EFI_FVB2_READ_STATUS |
> +      EFI_FVB2_WRITE_ENABLED_CAP |
> +      EFI_FVB2_WRITE_STATUS |
> +      EFI_FVB2_ERASE_POLARITY |
> +      EFI_FVB2_ALIGNMENT_16,
> +      sizeof (EFI_FIRMWARE_VOLUME_HEADER) + sizeof (EFI_FV_BLOCK_MAP_ENTRY),
> +      0,  // CheckSum
> +      0,  // ExtHeaderOffset
> +      {
> +        0,
> +      },  // Reserved[1]
> +      2,  // Revision
> +      {
> +        {
> +          (FixedPcdGet32 (PcdAllVarSize))/
> +          FixedPcdGet32 (PcdFlashBlockSize),
> +          FixedPcdGet32 (PcdFlashBlockSize),
> +        }
> +      } // BlockMap[1]
> +    },
> +    {
> +      {
> +        0,
> +        0
> +      }
> +    }  // End[1]
> +  }
> +};
> +
> +EFI_STATUS
> +GetFvbInfo (
> +  IN  UINT64                      FvLength,
> +  OUT EFI_FIRMWARE_VOLUME_HEADER  **FvbInfo
> +  )
> +{
> +  STATIC BOOLEAN  Checksummed = FALSE;
> +  UINTN           Index;
> +
> +  if (!Checksummed) {
> +    for (Index = 0;
> +         Index < sizeof (mPlatformFvbMediaInfo) / sizeof (EFI_FVB_MEDIA_INFO);
> +         Index += 1)
> +    {
> +      UINT16  Checksum;
> +      mPlatformFvbMediaInfo[Index].FvbInfo.Checksum = 0;
> +      Checksum                                      = CalculateCheckSum16 (
> +                                                        (UINT16 *)&mPlatformFvbMediaInfo[Index].FvbInfo,
> +                                                        mPlatformFvbMediaInfo[Index].FvbInfo.HeaderLength
> +                                                        );
> +      mPlatformFvbMediaInfo[Index].FvbInfo.Checksum = Checksum;
> +    }
> +    Checksummed = TRUE;
> +  }
> +
> +  for (Index = 0;
> +       Index < sizeof (mPlatformFvbMediaInfo) / sizeof (EFI_FVB_MEDIA_INFO);
> +       Index += 1)
> +  {
> +    if (mPlatformFvbMediaInfo[Index].FvLength == FvLength) {
> +      *FvbInfo = &mPlatformFvbMediaInfo[Index].FvbInfo;
> +      return EFI_SUCCESS;
> +    }
> +  }
> +
> +  return EFI_NOT_FOUND;
> +}
> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
> new file mode 100644
> index 0000000000..4b0c42b075
> --- /dev/null
> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
> @@ -0,0 +1,73 @@
> +## @file
> +# Component description file for Emu Fimware Volume Block DXE driver module.
> +#
> +#  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +#
> +#  SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +##
> +
> +
> +[Defines]
> +  INF_VERSION                    = 0x00010005
> +  BASE_NAME                      = FvbServicesRuntimeDxe
> +  FILE_GUID                      = 733cbac2-b23f-4b92-bc8e-fb01ce5907b7
> +  MODULE_TYPE                    = DXE_RUNTIME_DRIVER
> +  VERSION_STRING                 = 1.0
> +  ENTRY_POINT                    = FvbInitialize
> +
> +#
> +# The following information is for reference only and not required by the build
> +# tools.
> +#
> +#  VALID_ARCHITECTURES           = IA32 X64
> +#
> +
> +[Sources]
> +  FvbInfo.c
> +  FwBlockService.c
> +  FwBlockServiceDxe.c
> +  QemuFlash.c
> +  QemuFlashDxe.c
> +
> +[Packages]
> +  MdePkg/MdePkg.dec
> +  MdeModulePkg/MdeModulePkg.dec
> +  OvmfPkg/OvmfPkg.dec
> +  Platform/Loongson/LoongArchQemuPkg/Loongson.dec
> +
> +[LibraryClasses]
> +  BaseLib
> +  BaseMemoryLib
> +  DebugLib
> +  DevicePathLib
> +  DxeServicesTableLib
> +  MemoryAllocationLib
> +  PcdLib
> +  UefiBootServicesTableLib
> +  UefiDriverEntryPoint
> +  UefiRuntimeLib
> +
> +[Guids]
> +  gEfiEventVirtualAddressChangeGuid   # ALWAYS_CONSUMED
> +  # gEfiEventVirtualAddressChangeGuid # Create Event: EVENT_GROUP_GUID
> +
> +[Protocols]
> +  gEfiFirmwareVolumeBlockProtocolGuid           # PROTOCOL SOMETIMES_PRODUCED
> +  gEfiDevicePathProtocolGuid                    # PROTOCOL SOMETIMES_PRODUCED
> +
> +[FixedPcd]
> +  gLoongArchQemuPkgTokenSpaceGuid.PcdOvmfFlashNvStorageVariableBase
> +  gLoongArchQemuPkgTokenSpaceGuid.PcdOvmfFlashNvStorageFtwWorkingBase
> +  gLoongArchQemuPkgTokenSpaceGuid.PcdOvmfFlashNvStorageFtwSpareBase
> +  gLoongArchQemuPkgTokenSpaceGuid.PcdFlashFdBase
> +  gLoongArchQemuPkgTokenSpaceGuid.PcdAllVarSize
> +  gLoongArchQemuPkgTokenSpaceGuid.PcdFlashBlockSize
> +[Pcd]
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase64
> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashVariablesEnable
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase64
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase64
> +
> +[Depex]
> +  TRUE
> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
> new file mode 100644
> index 0000000000..d44f2b460c
> --- /dev/null
> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
> @@ -0,0 +1,1158 @@
> +/** @file
> +  Implementations for Firmware Volume Block protocol.
> +
> +  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +//
> +// The protocols, PPI and GUID definitions for this module
> +//
> +#include <Protocol/DevicePath.h>
> +#include <Protocol/FirmwareVolumeBlock.h>
> +
> +//
> +// The Library classes this module consumes
> +//
> +#include <Library/BaseLib.h>
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/DevicePathLib.h>
> +#include <Library/DxeServicesTableLib.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +
> +#include "FwBlockService.h"
> +#include "QemuFlash.h"
> +
> +#define EFI_FVB2_STATUS \
> +          (EFI_FVB2_READ_STATUS | EFI_FVB2_WRITE_STATUS | EFI_FVB2_LOCK_STATUS)
> +
> +ESAL_FWB_GLOBAL  *mFvbModuleGlobal;
> +
> +FV_MEMMAP_DEVICE_PATH  mFvMemmapDevicePathTemplate = {
> +  {
> +    {
> +      HARDWARE_DEVICE_PATH,
> +      HW_MEMMAP_DP,
> +      {
> +        (UINT8)(sizeof (MEMMAP_DEVICE_PATH)),
> +        (UINT8)(sizeof (MEMMAP_DEVICE_PATH) >> 8)
> +      }
> +    },
> +    EfiMemoryMappedIO,
> +    (EFI_PHYSICAL_ADDRESS)0,
> +    (EFI_PHYSICAL_ADDRESS)0,
> +  },
> +  {
> +    END_DEVICE_PATH_TYPE,
> +    END_ENTIRE_DEVICE_PATH_SUBTYPE,
> +    {
> +      END_DEVICE_PATH_LENGTH,
> +      0
> +    }
> +  }
> +};
> +
> +FV_PIWG_DEVICE_PATH  mFvPIWGDevicePathTemplate = {
> +  {
> +    {
> +      MEDIA_DEVICE_PATH,
> +      MEDIA_PIWG_FW_VOL_DP,
> +      {
> +        (UINT8)(sizeof (MEDIA_FW_VOL_DEVICE_PATH)),
> +        (UINT8)(sizeof (MEDIA_FW_VOL_DEVICE_PATH) >> 8)
> +      }
> +    },
> +    { 0 }
> +  },
> +  {
> +    END_DEVICE_PATH_TYPE,
> +    END_ENTIRE_DEVICE_PATH_SUBTYPE,
> +    {
> +      END_DEVICE_PATH_LENGTH,
> +      0
> +    }
> +  }
> +};
> +
> +EFI_FW_VOL_BLOCK_DEVICE  mFvbDeviceTemplate = {
> +  FVB_DEVICE_SIGNATURE,
> +  NULL,
> +  0,
> +  {
> +    FvbProtocolGetAttributes,
> +    FvbProtocolSetAttributes,
> +    FvbProtocolGetPhysicalAddress,
> +    FvbProtocolGetBlockSize,
> +    FvbProtocolRead,
> +    FvbProtocolWrite,
> +    FvbProtocolEraseBlocks,
> +    NULL
> +  }
> +};
> +
> +
> +EFI_STATUS
> +GetFvbInstance (
> +  IN  UINTN                Instance,
> +  IN  ESAL_FWB_GLOBAL      *Global,
> +  OUT EFI_FW_VOL_INSTANCE  **FwhInstance
> +  )
> +/*++
> +
> +  Routine Description:
> +    Retrieves the physical address of a memory mapped FV
> +
> +  Arguments:
> +    Instance              - The FV instance whose base address is going to be
> +                            returned
> +    Global                - Pointer to ESAL_FWB_GLOBAL that contains all
> +                            instance data
> +    FwhInstance           - The EFI_FW_VOL_INSTANCE firmware instance structure
> +
> +  Returns:
> +    EFI_SUCCESS           - Successfully returns
> +    EFI_INVALID_PARAMETER - Instance not found
> +
> +--*/
> +{
> +  EFI_FW_VOL_INSTANCE  *FwhRecord;
> +
> +  *FwhInstance = NULL;
> +  if (Instance >= Global->NumFv) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +  //
> +  // Find the right instance of the FVB private data
> +  //
> +  FwhRecord = Global->FvInstance;
> +  while (Instance > 0) {
> +    FwhRecord = (EFI_FW_VOL_INSTANCE *)
> +                (
> +                 (UINTN)((UINT8 *)FwhRecord) + FwhRecord->VolumeHeader.HeaderLength +
> +                 (sizeof (EFI_FW_VOL_INSTANCE) - sizeof (EFI_FIRMWARE_VOLUME_HEADER))
> +                );
> +    Instance--;
> +  }
> +
> +  *FwhInstance = FwhRecord;
> +
> +  return EFI_SUCCESS;
> +}
> +
> +EFI_STATUS
> +FvbGetPhysicalAddress (
> +  IN UINTN                  Instance,
> +  OUT EFI_PHYSICAL_ADDRESS  *Address,
> +  IN ESAL_FWB_GLOBAL        *Global
> +  )
> +/*++
> +
> +  Routine Description:
> +    Retrieves the physical address of a memory mapped FV
> +
> +  Arguments:
> +    Instance              - The FV instance whose base address is going to be
> +                            returned
> +    Address               - Pointer to a caller allocated EFI_PHYSICAL_ADDRESS
> +                            that on successful return, contains the base
> +                            address of the firmware volume.
> +    Global                - Pointer to ESAL_FWB_GLOBAL that contains all
> +                            instance data
> +
> +  Returns:
> +    EFI_SUCCESS           - Successfully returns
> +    EFI_INVALID_PARAMETER - Instance not found
> +
> +--*/
> +{
> +  EFI_FW_VOL_INSTANCE  *FwhInstance;
> +  EFI_STATUS           Status;
> +
> +  //
> +  // Find the right instance of the FVB private data
> +  //
> +  Status = GetFvbInstance (Instance, Global, &FwhInstance);
> +  ASSERT_EFI_ERROR (Status);
> +  *Address = FwhInstance->FvBase;
> +
> +  return EFI_SUCCESS;
> +}
> +
> +EFI_STATUS
> +FvbGetVolumeAttributes (
> +  IN UINTN                  Instance,
> +  OUT EFI_FVB_ATTRIBUTES_2  *Attributes,
> +  IN ESAL_FWB_GLOBAL        *Global
> +  )
> +/*++
> +
> +  Routine Description:
> +    Retrieves attributes, insures positive polarity of attribute bits, returns
> +    resulting attributes in output parameter
> +
> +  Arguments:
> +    Instance              - The FV instance whose attributes is going to be
> +                            returned
> +    Attributes            - Output buffer which contains attributes
> +    Global                - Pointer to ESAL_FWB_GLOBAL that contains all
> +                            instance data
> +
> +  Returns:
> +    EFI_SUCCESS           - Successfully returns
> +    EFI_INVALID_PARAMETER - Instance not found
> +
> +--*/
> +{
> +  EFI_FW_VOL_INSTANCE  *FwhInstance;
> +  EFI_STATUS           Status;
> +
> +  //
> +  // Find the right instance of the FVB private data
> +  //
> +  Status = GetFvbInstance (Instance, Global, &FwhInstance);
> +  ASSERT_EFI_ERROR (Status);
> +  *Attributes = FwhInstance->VolumeHeader.Attributes;
> +
> +  return EFI_SUCCESS;
> +}
> +
> +EFI_STATUS
> +FvbGetLbaAddress (
> +  IN  UINTN            Instance,
> +  IN  EFI_LBA          Lba,
> +  OUT UINTN            *LbaAddress,
> +  OUT UINTN            *LbaLength,
> +  OUT UINTN            *NumOfBlocks,
> +  IN  ESAL_FWB_GLOBAL  *Global
> +  )
> +/*++
> +
> +  Routine Description:
> +    Retrieves the starting address of an LBA in an FV
> +
> +  Arguments:
> +    Instance              - The FV instance which the Lba belongs to
> +    Lba                   - The logical block address
> +    LbaAddress            - On output, contains the physical starting address
> +                            of the Lba
> +    LbaLength             - On output, contains the length of the block
> +    NumOfBlocks           - A pointer to a caller allocated UINTN in which the
> +                            number of consecutive blocks starting with Lba is
> +                            returned. All blocks in this range have a size of
> +                            BlockSize
> +    Global                - Pointer to ESAL_FWB_GLOBAL that contains all
> +                            instance data
> +
> +  Returns:
> +    EFI_SUCCESS           - Successfully returns
> +    EFI_INVALID_PARAMETER - Instance not found
> +
> +--*/
> +{
> +  UINT32                  NumBlocks;
> +  UINT32                  BlockLength;
> +  UINTN                   Offset;
> +  EFI_LBA                 StartLba;
> +  EFI_LBA                 NextLba;
> +  EFI_FW_VOL_INSTANCE     *FwhInstance;
> +  EFI_FV_BLOCK_MAP_ENTRY  *BlockMap;
> +  EFI_STATUS              Status;
> +
> +  //
> +  // Find the right instance of the FVB private data
> +  //
> +  Status = GetFvbInstance (Instance, Global, &FwhInstance);
> +  ASSERT_EFI_ERROR (Status);
> +
> +  StartLba = 0;
> +  Offset   = 0;
> +  BlockMap = &(FwhInstance->VolumeHeader.BlockMap[0]);
> +
> +  //
> +  // Parse the blockmap of the FV to find which map entry the Lba belongs to
> +  //
> +  while (TRUE) {
> +    NumBlocks   = BlockMap->NumBlocks;
> +    BlockLength = BlockMap->Length;
> +
> +    if ((NumBlocks == 0) || (BlockLength == 0)) {
> +      return EFI_INVALID_PARAMETER;
> +    }
> +
> +    NextLba = StartLba + NumBlocks;
> +
> +    //
> +    // The map entry found
> +    //
> +    if ((Lba >= StartLba) && (Lba < NextLba)) {
> +      Offset = Offset + (UINTN)MultU64x32 ((Lba - StartLba), BlockLength);
> +      if (LbaAddress != NULL) {
> +        *LbaAddress = FwhInstance->FvBase + Offset;
> +      }
> +
> +      if (LbaLength != NULL) {
> +        *LbaLength = BlockLength;
> +      }
> +
> +      if (NumOfBlocks != NULL) {
> +        *NumOfBlocks = (UINTN)(NextLba - Lba);
> +      }
> +
> +      return EFI_SUCCESS;
> +    }
> +
> +    StartLba = NextLba;
> +    Offset   = Offset + NumBlocks * BlockLength;
> +    BlockMap++;
> +  }
> +}
> +
> +EFI_STATUS
> +FvbSetVolumeAttributes (
> +  IN UINTN                     Instance,
> +  IN OUT EFI_FVB_ATTRIBUTES_2  *Attributes,
> +  IN ESAL_FWB_GLOBAL           *Global
> +  )
> +/*++
> +
> +  Routine Description:
> +    Modifies the current settings of the firmware volume according to the
> +    input parameter, and returns the new setting of the volume
> +
> +  Arguments:
> +    Instance              - The FV instance whose attributes is going to be
> +                            modified
> +    Attributes            - On input, it is a pointer to EFI_FVB_ATTRIBUTES_2
> +                            containing the desired firmware volume settings.
> +                            On successful return, it contains the new settings
> +                            of the firmware volume
> +    Global                - Pointer to ESAL_FWB_GLOBAL that contains all
> +                            instance data
> +
> +  Returns:
> +    EFI_SUCCESS           - Successfully returns
> +    EFI_ACCESS_DENIED     - The volume setting is locked and cannot be modified
> +    EFI_INVALID_PARAMETER - Instance not found, or The attributes requested are
> +                            in conflict with the capabilities as declared in
> +                            the firmware volume header
> +
> +--*/
> +{
> +  EFI_FW_VOL_INSTANCE   *FwhInstance;
> +  EFI_FVB_ATTRIBUTES_2  OldAttributes;
> +  EFI_FVB_ATTRIBUTES_2  *AttribPtr;
> +  UINT32                Capabilities;
> +  UINT32                OldStatus;
> +  UINT32                NewStatus;
> +  EFI_STATUS            Status;
> +  EFI_FVB_ATTRIBUTES_2  UnchangedAttributes;
> +
> +  //
> +  // Find the right instance of the FVB private data
> +  //
> +  Status = GetFvbInstance (Instance, Global, &FwhInstance);
> +  ASSERT_EFI_ERROR (Status);
> +
> +  AttribPtr =
> +    (EFI_FVB_ATTRIBUTES_2 *)&(FwhInstance->VolumeHeader.Attributes);
> +  OldAttributes = *AttribPtr;
> +  Capabilities  = OldAttributes & (EFI_FVB2_READ_DISABLED_CAP | \
> +                                   EFI_FVB2_READ_ENABLED_CAP | \
> +                                   EFI_FVB2_WRITE_DISABLED_CAP | \
> +                                   EFI_FVB2_WRITE_ENABLED_CAP | \
> +                                   EFI_FVB2_LOCK_CAP \
> +                                   );
> +  OldStatus = OldAttributes & EFI_FVB2_STATUS;
> +  NewStatus = *Attributes & EFI_FVB2_STATUS;
> +
> +  UnchangedAttributes = EFI_FVB2_READ_DISABLED_CAP  | \
> +                        EFI_FVB2_READ_ENABLED_CAP   | \
> +                        EFI_FVB2_WRITE_DISABLED_CAP | \
> +                        EFI_FVB2_WRITE_ENABLED_CAP  | \
> +                        EFI_FVB2_LOCK_CAP           | \
> +                        EFI_FVB2_STICKY_WRITE       | \
> +                        EFI_FVB2_MEMORY_MAPPED      | \
> +                        EFI_FVB2_ERASE_POLARITY     | \
> +                        EFI_FVB2_READ_LOCK_CAP      | \
> +                        EFI_FVB2_WRITE_LOCK_CAP     | \
> +                        EFI_FVB2_ALIGNMENT;
> +
> +  //
> +  // Some attributes of FV is read only can *not* be set
> +  //
> +  if ((OldAttributes & UnchangedAttributes) ^
> +      (*Attributes & UnchangedAttributes))
> +  {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +  //
> +  // If firmware volume is locked, no status bit can be updated
> +  //
> +  if (OldAttributes & EFI_FVB2_LOCK_STATUS) {
> +    if (OldStatus ^ NewStatus) {
> +      return EFI_ACCESS_DENIED;
> +    }
> +  }
> +  //
> +  // Test read disable
> +  //
> +  if ((Capabilities & EFI_FVB2_READ_DISABLED_CAP) == 0) {
> +    if ((NewStatus & EFI_FVB2_READ_STATUS) == 0) {
> +      return EFI_INVALID_PARAMETER;
> +    }
> +  }
> +  //
> +  // Test read enable
> +  //
> +  if ((Capabilities & EFI_FVB2_READ_ENABLED_CAP) == 0) {
> +    if (NewStatus & EFI_FVB2_READ_STATUS) {
> +      return EFI_INVALID_PARAMETER;
> +    }
> +  }
> +  //
> +  // Test write disable
> +  //
> +  if ((Capabilities & EFI_FVB2_WRITE_DISABLED_CAP) == 0) {
> +    if ((NewStatus & EFI_FVB2_WRITE_STATUS) == 0) {
> +      return EFI_INVALID_PARAMETER;
> +    }
> +  }
> +  //
> +  // Test write enable
> +  //
> +  if ((Capabilities & EFI_FVB2_WRITE_ENABLED_CAP) == 0) {
> +    if (NewStatus & EFI_FVB2_WRITE_STATUS) {
> +      return EFI_INVALID_PARAMETER;
> +    }
> +  }
> +  //
> +  // Test lock
> +  //
> +  if ((Capabilities & EFI_FVB2_LOCK_CAP) == 0) {
> +    if (NewStatus & EFI_FVB2_LOCK_STATUS) {
> +      return EFI_INVALID_PARAMETER;
> +    }
> +  }
> +
> +  *AttribPtr  = (*AttribPtr) & (0xFFFFFFFF & (~EFI_FVB2_STATUS));
> +  *AttribPtr  = (*AttribPtr) | NewStatus;
> +  *Attributes = *AttribPtr;
> +
> +  return EFI_SUCCESS;
> +}
> +
> +//
> +// FVB protocol APIs
> +//
> +EFI_STATUS
> +EFIAPI
> +FvbProtocolGetPhysicalAddress (
> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
> +  OUT EFI_PHYSICAL_ADDRESS                     *Address
> +  )
> +/*++
> +
> +  Routine Description:
> +
> +    Retrieves the physical address of the device.
> +
> +  Arguments:
> +
> +    This                  - Calling context
> +    Address               - Output buffer containing the address.
> +
> +  Returns:
> +    EFI_SUCCESS           - Successfully returns
> +
> +--*/
> +{
> +  EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice;
> +
> +  FvbDevice = FVB_DEVICE_FROM_THIS (This);
> +
> +  return FvbGetPhysicalAddress (
> +           FvbDevice->Instance,
> +           Address,
> +           mFvbModuleGlobal
> +           );
> +}
> +
> +EFI_STATUS
> +EFIAPI
> +FvbProtocolGetBlockSize (
> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
> +  IN CONST EFI_LBA                             Lba,
> +  OUT UINTN                                    *BlockSize,
> +  OUT UINTN                                    *NumOfBlocks
> +  )
> +/*++
> +
> +  Routine Description:
> +    Retrieve the size of a logical block
> +
> +  Arguments:
> +    This                  - Calling context
> +    Lba                   - Indicates which block to return the size for.
> +    BlockSize             - A pointer to a caller allocated UINTN in which
> +                            the size of the block is returned
> +    NumOfBlocks           - a pointer to a caller allocated UINTN in which the
> +                            number of consecutive blocks starting with Lba is
> +                            returned. All blocks in this range have a size of
> +                            BlockSize
> +
> +  Returns:
> +    EFI_SUCCESS           - The firmware volume was read successfully and
> +                            contents are in Buffer
> +
> +--*/
> +{
> +  EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice;
> +
> +  FvbDevice = FVB_DEVICE_FROM_THIS (This);
> +
> +  return FvbGetLbaAddress (
> +           FvbDevice->Instance,
> +           Lba,
> +           NULL,
> +           BlockSize,
> +           NumOfBlocks,
> +           mFvbModuleGlobal
> +           );
> +}
> +
> +EFI_STATUS
> +EFIAPI
> +FvbProtocolGetAttributes (
> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
> +  OUT EFI_FVB_ATTRIBUTES_2                     *Attributes
> +  )
> +/*++
> +
> +  Routine Description:
> +      Retrieves Volume attributes.  No polarity translations are done.
> +
> +  Arguments:
> +      This                - Calling context
> +      Attributes          - output buffer which contains attributes
> +
> +  Returns:
> +    EFI_SUCCESS           - Successfully returns
> +
> +--*/
> +{
> +  EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice;
> +
> +  FvbDevice = FVB_DEVICE_FROM_THIS (This);
> +
> +  return FvbGetVolumeAttributes (
> +           FvbDevice->Instance,
> +           Attributes,
> +           mFvbModuleGlobal
> +           );
> +}
> +
> +EFI_STATUS
> +EFIAPI
> +FvbProtocolSetAttributes (
> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
> +  IN OUT EFI_FVB_ATTRIBUTES_2                  *Attributes
> +  )
> +/*++
> +
> +  Routine Description:
> +    Sets Volume attributes. No polarity translations are done.
> +
> +  Arguments:
> +    This                  - Calling context
> +    Attributes            - output buffer which contains attributes
> +
> +  Returns:
> +    EFI_SUCCESS           - Successfully returns
> +
> +--*/
> +{
> +  EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice;
> +
> +  FvbDevice = FVB_DEVICE_FROM_THIS (This);
> +
> +  return FvbSetVolumeAttributes (
> +           FvbDevice->Instance,
> +           Attributes,
> +           mFvbModuleGlobal
> +           );
> +}
> +
> +EFI_STATUS
> +EFIAPI
> +FvbProtocolEraseBlocks (
> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
> +  ...
> +  )
> +/*++
> +
> +  Routine Description:
> +
> +    The EraseBlock() function erases one or more blocks as denoted by the
> +    variable argument list. The entire parameter list of blocks must be
> +    verified prior to erasing any blocks.  If a block is requested that does
> +    not exist within the associated firmware volume (it has a larger index than
> +    the last block of the firmware volume), the EraseBlock() function must
> +    return EFI_INVALID_PARAMETER without modifying the contents of the firmware
> +    volume.
> +
> +  Arguments:
> +    This                  - Calling context
> +    ...                   - Starting LBA followed by Number of Lba to erase.
> +                            a -1 to terminate the list.
> +
> +  Returns:
> +    EFI_SUCCESS           - The erase request was successfully completed
> +    EFI_ACCESS_DENIED     - The firmware volume is in the WriteDisabled state
> +    EFI_DEVICE_ERROR      - The block device is not functioning correctly and
> +                            could not be written. Firmware device may have been
> +                            partially erased
> +
> +--*/
> +{
> +  EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice;
> +  EFI_FW_VOL_INSTANCE      *FwhInstance;
> +  UINTN                    NumOfBlocks;
> +  VA_LIST                  args;
> +  EFI_LBA                  StartingLba;
> +  UINTN                    NumOfLba;
> +  EFI_STATUS               Status;
> +
> +  FvbDevice = FVB_DEVICE_FROM_THIS (This);
> +
> +  Status = GetFvbInstance (
> +             FvbDevice->Instance,
> +             mFvbModuleGlobal,
> +             &FwhInstance
> +             );
> +  ASSERT_EFI_ERROR (Status);
> +
> +  NumOfBlocks = FwhInstance->NumOfBlocks;
> +
> +  VA_START (args, This);
> +
> +  do {
> +    StartingLba = VA_ARG (args, EFI_LBA);
> +    if (StartingLba == EFI_LBA_LIST_TERMINATOR) {
> +      break;
> +    }
> +
> +    NumOfLba = VA_ARG (args, UINTN);
> +
> +    //
> +    // Check input parameters
> +    //
> +    if ((NumOfLba == 0) || ((StartingLba + NumOfLba) > NumOfBlocks)) {
> +      VA_END (args);
> +      return EFI_INVALID_PARAMETER;
> +    }
> +  } while (1);
> +
> +  VA_END (args);
> +
> +  VA_START (args, This);
> +  do {
> +    StartingLba = VA_ARG (args, EFI_LBA);
> +    if (StartingLba == EFI_LBA_LIST_TERMINATOR) {
> +      break;
> +    }
> +
> +    NumOfLba = VA_ARG (args, UINTN);
> +
> +    while (NumOfLba > 0) {
> +      Status = QemuFlashEraseBlock (StartingLba);
> +      if (EFI_ERROR (Status)) {
> +        VA_END (args);
> +        return Status;
> +      }
> +
> +      StartingLba++;
> +      NumOfLba--;
> +    }
> +
> +  } while (1);
> +
> +  VA_END (args);
> +
> +  return EFI_SUCCESS;
> +}
> +
> +EFI_STATUS
> +EFIAPI
> +FvbProtocolWrite (
> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
> +  IN       EFI_LBA                             Lba,
> +  IN       UINTN                               Offset,
> +  IN OUT   UINTN                               *NumBytes,
> +  IN       UINT8                               *Buffer
> +  )
> +/*++
> +
> +  Routine Description:
> +
> +    Writes data beginning at Lba:Offset from FV. The write terminates either
> +    when *NumBytes of data have been written, or when a block boundary is
> +    reached.  *NumBytes is updated to reflect the actual number of bytes
> +    written. The write operation does not include erase. This routine will
> +    attempt to write only the specified bytes. If the writes do not stick,
> +    it will return an error.
> +
> +  Arguments:
> +    This                  - Calling context
> +    Lba                   - Block in which to begin write
> +    Offset                - Offset in the block at which to begin write
> +    NumBytes              - On input, indicates the requested write size. On
> +                            output, indicates the actual number of bytes
> +                            written
> +    Buffer                - Buffer containing source data for the write.
> +
> +  Returns:
> +    EFI_SUCCESS           - The firmware volume was written successfully
> +    EFI_BAD_BUFFER_SIZE   - Write attempted across a LBA boundary. On output,
> +                            NumBytes contains the total number of bytes
> +                            actually written
> +    EFI_ACCESS_DENIED     - The firmware volume is in the WriteDisabled state
> +    EFI_DEVICE_ERROR      - The block device is not functioning correctly and
> +                            could not be written
> +    EFI_INVALID_PARAMETER - NumBytes or Buffer are NULL
> +
> +--*/
> +{
> +  return QemuFlashWrite (
> +           (EFI_LBA)Lba,
> +           (UINTN)Offset,
> +           NumBytes,
> +           (UINT8 *)Buffer
> +           );
> +}
> +
> +EFI_STATUS
> +EFIAPI
> +FvbProtocolRead (
> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
> +  IN CONST EFI_LBA                             Lba,
> +  IN CONST UINTN                               Offset,
> +  IN OUT UINTN                                 *NumBytes,
> +  IN UINT8                                     *Buffer
> +  )
> +/*++
> +
> +  Routine Description:
> +
> +    Reads data beginning at Lba:Offset from FV. The Read terminates either
> +    when *NumBytes of data have been read, or when a block boundary is
> +    reached.  *NumBytes is updated to reflect the actual number of bytes
> +    written. The write operation does not include erase. This routine will
> +    attempt to write only the specified bytes. If the writes do not stick,
> +    it will return an error.
> +
> +  Arguments:
> +    This                  - Calling context
> +    Lba                   - Block in which to begin Read
> +    Offset                - Offset in the block at which to begin Read
> +    NumBytes              - On input, indicates the requested write size. On
> +                            output, indicates the actual number of bytes Read
> +    Buffer                - Buffer containing source data for the Read.
> +
> +  Returns:
> +    EFI_SUCCESS           - The firmware volume was read successfully and
> +                            contents are in Buffer
> +    EFI_BAD_BUFFER_SIZE   - Read attempted across a LBA boundary. On output,
> +                            NumBytes contains the total number of bytes
> +                            returned in Buffer
> +    EFI_ACCESS_DENIED     - The firmware volume is in the ReadDisabled state
> +    EFI_DEVICE_ERROR      - The block device is not functioning correctly and
> +                            could not be read
> +    EFI_INVALID_PARAMETER - NumBytes or Buffer are NULL
> +
> +--*/
> +{
> +  return QemuFlashRead (
> +           (EFI_LBA)Lba,
> +           (UINTN)Offset,
> +           NumBytes,
> +           (UINT8 *)Buffer
> +           );
> +}
> +
> +EFI_STATUS
> +ValidateFvHeader (
> +  EFI_FIRMWARE_VOLUME_HEADER  *FwVolHeader
> +  )
> +/*++
> +
> +  Routine Description:
> +    Check the integrity of firmware volume header
> +
> +  Arguments:
> +    FwVolHeader           - A pointer to a firmware volume header
> +
> +  Returns:
> +    EFI_SUCCESS           - The firmware volume is consistent
> +    EFI_NOT_FOUND         - The firmware volume has corrupted. So it is not an
> +                            FV
> +
> +--*/
> +{
> +  UINT16  Checksum;
> +
> +  //
> +  // Verify the header revision, header signature, length
> +  // Length of FvBlock cannot be 2**64-1
> +  // HeaderLength cannot be an odd number
> +  //
> +  if ((FwVolHeader->Revision != EFI_FVH_REVISION) ||
> +      (FwVolHeader->Signature != EFI_FVH_SIGNATURE) ||
> +      (FwVolHeader->FvLength == ((UINTN)-1)) ||
> +      ((FwVolHeader->HeaderLength & 0x01) != 0)
> +      )
> +  {
> +    return EFI_NOT_FOUND;
> +  }
> +
> +  //
> +  // Verify the header checksum
> +  //
> +
> +  Checksum = CalculateSum16 (
> +               (UINT16 *)FwVolHeader,
> +               FwVolHeader->HeaderLength
> +               );
> +  if (Checksum != 0) {
> +    UINT16  Expected;
> +
> +    Expected =
> +      (UINT16)(((UINTN)FwVolHeader->Checksum + 0x10000 - Checksum) & 0xffff);
> +
> +    DEBUG ((
> +      DEBUG_INFO,
> +      "FV@%p Checksum is 0x%x, expected 0x%x\n",
> +      FwVolHeader,
> +      FwVolHeader->Checksum,
> +      Expected
> +      ));
> +    return EFI_NOT_FOUND;
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> +STATIC
> +EFI_STATUS
> +MarkMemoryRangeForRuntimeAccess (
> +  EFI_PHYSICAL_ADDRESS                BaseAddress,
> +  UINTN                               Length
> +  )
> +{
> +  EFI_STATUS                          Status;
> +
> +  //
> +  // Mark flash region as runtime memory
> +  //
> +  Status = gDS->RemoveMemorySpace (
> +                  BaseAddress,
> +                  Length
> +                  );
> +
> +  Status = gDS->AddMemorySpace (
> +                  EfiGcdMemoryTypeSystemMemory,
> +                  BaseAddress,
> +                  Length,
> +                  EFI_MEMORY_UC | EFI_MEMORY_RUNTIME
> +                  );
> +  ASSERT_EFI_ERROR (Status);
> +
> +  Status = gBS->AllocatePages (
> +                  AllocateAddress,
> +                  EfiRuntimeServicesData,
> +                  EFI_SIZE_TO_PAGES (Length),
> +                  &BaseAddress
> +                  );
> +  ASSERT_EFI_ERROR (Status);
> +
> +  return Status;
> +}
> +
> +STATIC
> +EFI_STATUS
> +InitializeVariableFvHeader (
> +  VOID
> +  )
> +{
> +  EFI_STATUS                  Status;
> +  EFI_FIRMWARE_VOLUME_HEADER  *GoodFwVolHeader;
> +  EFI_FIRMWARE_VOLUME_HEADER  *FwVolHeader;
> +  UINTN                       Length;
> +  UINTN                       WriteLength;
> +  UINTN                       BlockSize;
> +
> +  FwVolHeader =
> +    (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)
> +    PcdGet64 (PcdOvmfFlashNvStorageVariableBase);
> +
> +  Length =  FixedPcdGet32 (PcdAllVarSize);
> +  BlockSize = PcdGet32 (PcdFlashBlockSize);
> +
> +  Status = ValidateFvHeader (FwVolHeader);
> +  if (!EFI_ERROR (Status)) {
> +    if ((FwVolHeader->FvLength != Length) ||
> +        (FwVolHeader->BlockMap[0].Length != BlockSize))
> +    {
> +      Status = EFI_VOLUME_CORRUPTED;
> +    }
> +  }
> +  if (EFI_ERROR (Status)) {
> +    UINTN  Offset;
> +    UINTN  Start;
> +
> +    DEBUG ((
> +      DEBUG_INFO,
> +      "Variable FV header is not valid. It will be reinitialized.\n"
> +      ));
> +
> +    //
> +    // Get FvbInfo to provide in FwhInstance.
> +    //
> +    Status = GetFvbInfo (Length, &GoodFwVolHeader);
> +    ASSERT (!EFI_ERROR (Status));
> +
> +    Start = (UINTN)(UINT8*) FwVolHeader - PcdGet64 (PcdFlashFdBase);
> +    ASSERT (Start % BlockSize == 0 && Length % BlockSize == 0);
> +    ASSERT (GoodFwVolHeader->HeaderLength <= BlockSize);
> +
> +    //
> +    // Erase all the blocks
> +    //
> +    for (Offset = Start; Offset < Start + Length; Offset += BlockSize) {
> +      Status = QemuFlashEraseBlock (Offset / BlockSize);
> +      ASSERT_EFI_ERROR (Status);
> +    }
> +
> +    //
> +    // Write good FV header
> +    //
> +    WriteLength = GoodFwVolHeader->HeaderLength;
> +    Status      = QemuFlashWrite (
> +                    Start / BlockSize,
> +                    0,
> +                    &WriteLength,
> +                    (UINT8 *)GoodFwVolHeader
> +                    );
> +    ASSERT_EFI_ERROR (Status);
> +    ASSERT (WriteLength == GoodFwVolHeader->HeaderLength);
> +  }
> +
> +  return Status;
> +}
> +
> +EFI_STATUS
> +EFIAPI
> +FvbInitialize (
> +  IN EFI_HANDLE        ImageHandle,
> +  IN EFI_SYSTEM_TABLE  *SystemTable
> +  )
> +/*++
> +
> +  Routine Description:
> +    This function does common initialization for FVB services
> +
> +  Arguments:
> +
> +  Returns:
> +
> +--*/
> +{
> +  EFI_STATUS                  Status;
> +  EFI_FW_VOL_INSTANCE         *FwhInstance;
> +  EFI_FIRMWARE_VOLUME_HEADER  *FwVolHeader;
> +  UINT32                      BufferSize;
> +  EFI_FV_BLOCK_MAP_ENTRY      *PtrBlockMapEntry;
> +  EFI_FW_VOL_BLOCK_DEVICE     *FvbDevice;
> +  UINT32                      MaxLbaSize;
> +  EFI_PHYSICAL_ADDRESS        BaseAddress;
> +  UINTN                       Length;
> +  UINTN                       NumOfBlocks;
> +  RETURN_STATUS               PcdStatus;
> +
> +  if (EFI_ERROR (QemuFlashInitialize ())) {
> +    //
> +    // Return an error so image will be unloaded
> +    //
> +    DEBUG ((
> +      DEBUG_INFO,
> +      "QEMU flash was not detected. Writable FVB is not being installed.\n"
> +      ));
> +    return EFI_WRITE_PROTECTED;
> +  }
> +
> +  //
> +  // Allocate runtime services data for global variable, which contains
> +  // the private data of all firmware volume block instances
> +  //
> +  mFvbModuleGlobal = AllocateRuntimePool (sizeof (ESAL_FWB_GLOBAL));
> +  ASSERT (mFvbModuleGlobal != NULL);
> +
> +  BaseAddress = (UINTN) PcdGet64 (PcdOvmfFlashNvStorageVariableBase);
> +  Length = PcdGet32 (PcdAllVarSize);
> +
> +  Status = InitializeVariableFvHeader ();
> +  if (EFI_ERROR (Status)) {
> +    DEBUG ((
> +      DEBUG_INFO,
> +      "QEMU Flash: Unable to initialize variable FV header\n"
> +      ));
> +    return EFI_WRITE_PROTECTED;
> +  }
> +
> +  FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)BaseAddress;
> +  Status      = ValidateFvHeader (FwVolHeader);
> +  if (EFI_ERROR (Status)) {
> +    //
> +    // Get FvbInfo
> +    //
> +    Status = GetFvbInfo (Length, &FwVolHeader);
> +    if (EFI_ERROR (Status)) {
> +      DEBUG ((DEBUG_INFO, "EFI_ERROR (GetFvbInfo (Length, &FwVolHeader))\n"));
> +      return EFI_WRITE_PROTECTED;
> +    }
> +  }
> +
> +  BufferSize = (sizeof (EFI_FW_VOL_INSTANCE) +
> +                FwVolHeader->HeaderLength -
> +                sizeof (EFI_FIRMWARE_VOLUME_HEADER)
> +                );
> +  mFvbModuleGlobal->FvInstance = AllocateRuntimePool (BufferSize);
> +  ASSERT (mFvbModuleGlobal->FvInstance != NULL);
> +
> +  FwhInstance = mFvbModuleGlobal->FvInstance;
> +
> +  mFvbModuleGlobal->NumFv = 0;
> +  MaxLbaSize              = 0;
> +
> +  FwVolHeader =
> +    (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)
> +    PcdGet64 (PcdOvmfFlashNvStorageVariableBase);
> +
> +  FwhInstance->FvBase = (UINTN)BaseAddress;
> +
> +  CopyMem (
> +    (UINTN *)&(FwhInstance->VolumeHeader),
> +    (UINTN *)FwVolHeader,
> +    FwVolHeader->HeaderLength
> +    );
> +  FwVolHeader = &(FwhInstance->VolumeHeader);
> +
> +  NumOfBlocks = 0;
> +
> +  for (PtrBlockMapEntry = FwVolHeader->BlockMap;
> +       PtrBlockMapEntry->NumBlocks != 0;
> +       PtrBlockMapEntry++)
> +  {
> +    //
> +    // Get the maximum size of a block.
> +    //
> +    if (MaxLbaSize < PtrBlockMapEntry->Length) {
> +      MaxLbaSize = PtrBlockMapEntry->Length;
> +    }
> +
> +    NumOfBlocks = NumOfBlocks + PtrBlockMapEntry->NumBlocks;
> +  }
> +
> +  //
> +  // The total number of blocks in the FV.
> +  //
> +  FwhInstance->NumOfBlocks = NumOfBlocks;
> +
> +  //
> +  // Add a FVB Protocol Instance
> +  //
> +  FvbDevice = AllocateRuntimePool (sizeof (EFI_FW_VOL_BLOCK_DEVICE));
> +  ASSERT (FvbDevice != NULL);
> +
> +  CopyMem (FvbDevice, &mFvbDeviceTemplate, sizeof (EFI_FW_VOL_BLOCK_DEVICE));
> +
> +  FvbDevice->Instance = mFvbModuleGlobal->NumFv;
> +  mFvbModuleGlobal->NumFv++;
> +
> +  //
> +  // Set up the devicepath
> +  //
> +  if (FwVolHeader->ExtHeaderOffset == 0) {
> +    FV_MEMMAP_DEVICE_PATH  *FvMemmapDevicePath;
> +
> +    //
> +    // FV does not contains extension header, then produce MEMMAP_DEVICE_PATH
> +    //
> +    FvMemmapDevicePath = AllocateCopyPool (
> +                           sizeof (FV_MEMMAP_DEVICE_PATH),
> +                           &mFvMemmapDevicePathTemplate
> +                           );
> +    FvMemmapDevicePath->MemMapDevPath.StartingAddress = BaseAddress;
> +    FvMemmapDevicePath->MemMapDevPath.EndingAddress   =
> +      BaseAddress + FwVolHeader->FvLength - 1;
> +    FvbDevice->DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)FvMemmapDevicePath;
> +  } else {
> +    FV_PIWG_DEVICE_PATH  *FvPiwgDevicePath;
> +
> +    FvPiwgDevicePath = AllocateCopyPool (
> +                         sizeof (FV_PIWG_DEVICE_PATH),
> +                         &mFvPIWGDevicePathTemplate
> +                         );
> +    CopyGuid (
> +      &FvPiwgDevicePath->FvDevPath.FvName,
> +      (GUID *)(UINTN)(BaseAddress + FwVolHeader->ExtHeaderOffset)
> +      );
> +    FvbDevice->DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)FvPiwgDevicePath;
> +  }
> +
> +  //
> +  // Module type specific hook.
> +  //
> +  InstallProtocolInterfaces (FvbDevice);
> +
> +  MarkMemoryRangeForRuntimeAccess (BaseAddress, Length);
> +
> +  //
> +  // Set several PCD values to point to flash
> +  //
> +
> +
> +  PcdStatus = PcdSet64S (
> +    PcdFlashNvStorageVariableBase64,
> +    (UINTN) PcdGet64 (PcdOvmfFlashNvStorageVariableBase)
> +    );
> +  ASSERT_RETURN_ERROR (PcdStatus);
> +  PcdStatus = PcdSet64S (
> +    PcdFlashNvStorageFtwWorkingBase64,
> +    PcdGet64 (PcdOvmfFlashNvStorageFtwWorkingBase)
> +    );
> +  ASSERT_RETURN_ERROR (PcdStatus);
> +  PcdStatus = PcdSet64S (
> +    PcdFlashNvStorageFtwSpareBase64,
> +    PcdGet64 (PcdOvmfFlashNvStorageFtwSpareBase)
> +    );
> +  ASSERT_RETURN_ERROR (PcdStatus);
> +
> +  FwhInstance = (EFI_FW_VOL_INSTANCE *)
> +                (
> +                 (UINTN)((UINT8 *)FwhInstance) + FwVolHeader->HeaderLength +
> +                 (sizeof (EFI_FW_VOL_INSTANCE) - sizeof (EFI_FIRMWARE_VOLUME_HEADER))
> +                );
> +
> +  //
> +  // Module type specific hook.
> +  //
> +  InstallVirtualAddressChangeHandler ();
> +
> +  PcdStatus = PcdSetBoolS (PcdOvmfFlashVariablesEnable, TRUE);
> +  ASSERT_RETURN_ERROR (PcdStatus);
> +  return EFI_SUCCESS;
> +}
> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.h b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.h
> new file mode 100644
> index 0000000000..e7234a0c5e
> --- /dev/null
> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.h
> @@ -0,0 +1,178 @@
> +/** @file
> +  Firmware volume block driver for Intel Firmware Hub (FWH) device
> +
> +  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +#ifndef _FW_BLOCK_SERVICE_H
> +#define _FW_BLOCK_SERVICE_H
> +
> +typedef struct {
> +  UINTN                         FvBase;
> +  UINTN                         NumOfBlocks;
> +  EFI_FIRMWARE_VOLUME_HEADER    VolumeHeader;
> +} EFI_FW_VOL_INSTANCE;
> +
> +typedef struct {
> +  UINT32                 NumFv;
> +  EFI_FW_VOL_INSTANCE    *FvInstance;
> +} ESAL_FWB_GLOBAL;
> +
> +extern ESAL_FWB_GLOBAL  *mFvbModuleGlobal;
> +
> +//
> +// Fvb Protocol instance data
> +//
> +#define FVB_DEVICE_FROM_THIS(a)  CR (a, EFI_FW_VOL_BLOCK_DEVICE,\
> +                                  FwVolBlockInstance, FVB_DEVICE_SIGNATURE)
> +
> +#define FVB_EXTEND_DEVICE_FROM_THIS(a)  CR (a, EFI_FW_VOL_BLOCK_DEVICE,\
> +                                         FvbExtension, FVB_DEVICE_SIGNATURE)
> +
> +#define FVB_DEVICE_SIGNATURE  SIGNATURE_32 ('F', 'V', 'B', 'N')
> +
> +typedef struct {
> +  MEDIA_FW_VOL_DEVICE_PATH    FvDevPath;
> +  EFI_DEVICE_PATH_PROTOCOL    EndDevPath;
> +} FV_PIWG_DEVICE_PATH;
> +
> +typedef struct {
> +  MEMMAP_DEVICE_PATH          MemMapDevPath;
> +  EFI_DEVICE_PATH_PROTOCOL    EndDevPath;
> +} FV_MEMMAP_DEVICE_PATH;
> +
> +typedef struct {
> +  UINTN                                 Signature;
> +  EFI_DEVICE_PATH_PROTOCOL              *DevicePath;
> +  UINTN                                 Instance;
> +  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL    FwVolBlockInstance;
> +} EFI_FW_VOL_BLOCK_DEVICE;
> +
> +EFI_STATUS
> +GetFvbInfo (
> +  IN  UINT64                      FvLength,
> +  OUT EFI_FIRMWARE_VOLUME_HEADER  **FvbInfo
> +  );
> +
> +EFI_STATUS
> +FvbSetVolumeAttributes (
> +  IN UINTN                     Instance,
> +  IN OUT EFI_FVB_ATTRIBUTES_2  *Attributes,
> +  IN ESAL_FWB_GLOBAL           *Global
> +  );
> +
> +EFI_STATUS
> +FvbGetVolumeAttributes (
> +  IN UINTN                  Instance,
> +  OUT EFI_FVB_ATTRIBUTES_2  *Attributes,
> +  IN ESAL_FWB_GLOBAL        *Global
> +  );
> +
> +EFI_STATUS
> +FvbGetPhysicalAddress (
> +  IN UINTN                  Instance,
> +  OUT EFI_PHYSICAL_ADDRESS  *Address,
> +  IN ESAL_FWB_GLOBAL        *Global
> +  );
> +
> +EFI_STATUS
> +EFIAPI
> +FvbInitialize (
> +  IN EFI_HANDLE        ImageHandle,
> +  IN EFI_SYSTEM_TABLE  *SystemTable
> +  );
> +
> +
> +VOID
> +EFIAPI
> +FvbClassAddressChangeEvent (
> +  IN EFI_EVENT  Event,
> +  IN VOID       *Context
> +  );
> +
> +EFI_STATUS
> +FvbGetLbaAddress (
> +  IN  UINTN            Instance,
> +  IN  EFI_LBA          Lba,
> +  OUT UINTN            *LbaAddress,
> +  OUT UINTN            *LbaLength,
> +  OUT UINTN            *NumOfBlocks,
> +  IN  ESAL_FWB_GLOBAL  *Global
> +  );
> +
> +//
> +// Protocol APIs
> +//
> +EFI_STATUS
> +EFIAPI
> +FvbProtocolGetAttributes (
> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
> +  OUT EFI_FVB_ATTRIBUTES_2                     *Attributes
> +  );
> +
> +EFI_STATUS
> +EFIAPI
> +FvbProtocolSetAttributes (
> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
> +  IN OUT EFI_FVB_ATTRIBUTES_2                  *Attributes
> +  );
> +
> +EFI_STATUS
> +EFIAPI
> +FvbProtocolGetPhysicalAddress (
> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
> +  OUT EFI_PHYSICAL_ADDRESS                     *Address
> +  );
> +
> +EFI_STATUS
> +EFIAPI
> +FvbProtocolGetBlockSize (
> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
> +  IN CONST EFI_LBA                             Lba,
> +  OUT UINTN                                    *BlockSize,
> +  OUT UINTN                                    *NumOfBlocks
> +  );
> +
> +EFI_STATUS
> +EFIAPI
> +FvbProtocolRead (
> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
> +  IN CONST EFI_LBA                             Lba,
> +  IN CONST UINTN                               Offset,
> +  IN OUT UINTN                                 *NumBytes,
> +  IN UINT8                                     *Buffer
> +  );
> +
> +EFI_STATUS
> +EFIAPI
> +FvbProtocolWrite (
> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
> +  IN       EFI_LBA                             Lba,
> +  IN       UINTN                               Offset,
> +  IN OUT   UINTN                               *NumBytes,
> +  IN       UINT8                               *Buffer
> +  );
> +
> +EFI_STATUS
> +EFIAPI
> +FvbProtocolEraseBlocks (
> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
> +  ...
> +  );
> +
> +//
> +// The following functions have different implementations dependent on the
> +// module type chosen for building this driver.
> +//
> +VOID
> +InstallProtocolInterfaces (
> +  IN EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice
> +  );
> +
> +VOID
> +InstallVirtualAddressChangeHandler (
> +  VOID
> +  );
> +#endif
> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockServiceDxe.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockServiceDxe.c
> new file mode 100644
> index 0000000000..cb85499539
> --- /dev/null
> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockServiceDxe.c
> @@ -0,0 +1,152 @@
> +/** @file
> +  Functions related to the Firmware Volume Block service whose
> +  implementation is specific to the runtime DXE driver build.
> +
> +  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <Guid/EventGroup.h>
> +#include <Library/DebugLib.h>
> +#include <Library/DevicePathLib.h>
> +#include <Library/PcdLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +#include <Library/UefiRuntimeLib.h>
> +#include <Protocol/DevicePath.h>
> +#include <Protocol/FirmwareVolumeBlock.h>
> +
> +#include "FwBlockService.h"
> +#include "QemuFlash.h"
> +
> +VOID
> +InstallProtocolInterfaces (
> +  IN EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice
> +  )
> +{
> +  EFI_STATUS                          Status;
> +  EFI_HANDLE                          FwbHandle;
> +  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *OldFwbInterface;
> +
> +  //
> +  // Find a handle with a matching device path that has supports FW Block
> +  // protocol
> +  //
> +  Status = gBS->LocateDevicePath (
> +                  &gEfiFirmwareVolumeBlockProtocolGuid,
> +                  &FvbDevice->DevicePath,
> +                  &FwbHandle
> +                  );
> +  if (EFI_ERROR (Status)) {
> +    //
> +    // LocateDevicePath fails so install a new interface and device path
> +    //
> +    FwbHandle = NULL;
> +    DEBUG ((DEBUG_INFO, "Installing QEMU flash FVB\n"));
> +    Status = gBS->InstallMultipleProtocolInterfaces (
> +                    &FwbHandle,
> +                    &gEfiFirmwareVolumeBlockProtocolGuid,
> +                    &FvbDevice->FwVolBlockInstance,
> +                    &gEfiDevicePathProtocolGuid,
> +                    FvbDevice->DevicePath,
> +                    NULL
> +                    );
> +    ASSERT_EFI_ERROR (Status);
> +  } else if (IsDevicePathEnd (FvbDevice->DevicePath)) {
> +    //
> +    // Device already exists, so reinstall the FVB protocol
> +    //
> +    Status = gBS->HandleProtocol (
> +                    FwbHandle,
> +                    &gEfiFirmwareVolumeBlockProtocolGuid,
> +                    (VOID **)&OldFwbInterface
> +                    );
> +    ASSERT_EFI_ERROR (Status);
> +
> +    DEBUG ((DEBUG_INFO, "Reinstalling FVB for QEMU flash region\n"));
> +    Status = gBS->ReinstallProtocolInterface (
> +                    FwbHandle,
> +                    &gEfiFirmwareVolumeBlockProtocolGuid,
> +                    OldFwbInterface,
> +                    &FvbDevice->FwVolBlockInstance
> +                    );
> +    ASSERT_EFI_ERROR (Status);
> +  } else {
> +    //
> +    // There was a FVB protocol on an End Device Path node
> +    //
> +    ASSERT (FALSE);
> +  }
> +}
> +
> +
> +STATIC
> +VOID
> +EFIAPI
> +FvbVirtualAddressChangeEvent (
> +  IN EFI_EVENT  Event,
> +  IN VOID       *Context
> +  )
> +/*++
> +
> +  Routine Description:
> +
> +    Fixup internal data so that EFI and SAL can be call in virtual mode.
> +    Call the passed in Child Notify event and convert the mFvbModuleGlobal
> +    date items to there virtual address.
> +
> +  Arguments:
> +
> +    (Standard EFI notify event - EFI_EVENT_NOTIFY)
> +
> +  Returns:
> +
> +    None
> +
> +--*/
> +{
> +  EFI_FW_VOL_INSTANCE  *FwhInstance;
> +  UINTN                Index;
> +
> +  FwhInstance = mFvbModuleGlobal->FvInstance;
> +  EfiConvertPointer (0x0, (VOID **)&mFvbModuleGlobal->FvInstance);
> +
> +  //
> +  // Convert the base address of all the instances
> +  //
> +  Index = 0;
> +  while (Index < mFvbModuleGlobal->NumFv) {
> +    EfiConvertPointer (0x0, (VOID **)&FwhInstance->FvBase);
> +    FwhInstance = (EFI_FW_VOL_INSTANCE *)
> +                  (
> +                   (UINTN)((UINT8 *)FwhInstance) +
> +                   FwhInstance->VolumeHeader.HeaderLength +
> +                   (sizeof (EFI_FW_VOL_INSTANCE) - sizeof (EFI_FIRMWARE_VOLUME_HEADER))
> +                  );
> +    Index++;
> +  }
> +
> +  EfiConvertPointer (0x0, (VOID **)&mFvbModuleGlobal);
> +  QemuFlashConvertPointers ();
> +}
> +
> +
> +VOID
> +InstallVirtualAddressChangeHandler (
> +  VOID
> +  )
> +{
> +  EFI_STATUS  Status;
> +  EFI_EVENT   VirtualAddressChangeEvent;
> +
> +  Status = gBS->CreateEventEx (
> +                  EVT_NOTIFY_SIGNAL,
> +                  TPL_NOTIFY,
> +                  FvbVirtualAddressChangeEvent,
> +                  NULL,
> +                  &gEfiEventVirtualAddressChangeGuid,
> +                  &VirtualAddressChangeEvent
> +                  );
> +  ASSERT_EFI_ERROR (Status);
> +}
> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c
> new file mode 100644
> index 0000000000..a85d736060
> --- /dev/null
> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c
> @@ -0,0 +1,251 @@
> +/** @file
> +  LoongArch support for QEMU system firmware flash device
> +
> +  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/PcdLib.h>
> +
> +#include "QemuFlash.h"
> +
> +#define WRITE_BYTE_CMD           0x10
> +#define BLOCK_ERASE_CMD          0x20
> +#define CLEAR_STATUS_CMD         0x50
> +#define READ_STATUS_CMD          0x70
> +#define READ_DEVID_CMD           0x90
> +#define BLOCK_ERASE_CONFIRM_CMD  0xd0
> +#define READ_ARRAY_CMD           0xff
> +
> +#define CLEARED_ARRAY_STATUS  0x00
> +
> +UINT8  *mFlashBase;
> +
> +STATIC UINTN  mFdBlockSize  = 0;
> +STATIC UINTN  mFdBlockCount = 0;
> +
> +STATIC
> +volatile UINT8 *
> +QemuFlashPtr (
> +  IN        EFI_LBA  Lba,
> +  IN        UINTN    Offset
> +  )
> +{
> +  return mFlashBase + ((UINTN)Lba * mFdBlockSize) + Offset;
> +}
> +
> +
> +/**
> +  Determines if the QEMU flash memory device is present.
> +
> +  @retval FALSE   The QEMU flash device is not present.
> +  @retval TRUE    The QEMU flash device is present.
> +
> +**/
> +STATIC
> +BOOLEAN
> +QemuFlashDetected (
> +  VOID
> +  )
> +{
> +  BOOLEAN         FlashDetected;
> +  volatile UINT8  *Ptr;
> +
> +  UINTN  Offset;
> +  UINT8  OriginalUint8;
> +  UINT8  ProbeUint8;
> +
> +  FlashDetected = FALSE;
> +  Ptr           = QemuFlashPtr (0, 0);
> +
> +  for (Offset = 0; Offset < mFdBlockSize; Offset++) {
> +    Ptr        = QemuFlashPtr (0, Offset);
> +    ProbeUint8 = *Ptr;
> +    if ((ProbeUint8 != CLEAR_STATUS_CMD) &&
> +        (ProbeUint8 != READ_STATUS_CMD) &&
> +        (ProbeUint8 != CLEARED_ARRAY_STATUS))
> +    {
> +      break;
> +    }
> +  }
> +
> +  if (Offset >= mFdBlockSize) {
> +    DEBUG ((DEBUG_INFO, "QEMU Flash: Failed to find probe location\n"));
> +    return FALSE;
> +  }
> +
> +  DEBUG ((DEBUG_INFO, "QEMU Flash: Attempting flash detection at %p\n", Ptr));
> +
> +  OriginalUint8 = *Ptr;
> +  *Ptr          = CLEAR_STATUS_CMD;
> +  ProbeUint8    = *Ptr;
> +  if ((OriginalUint8 != CLEAR_STATUS_CMD) &&
> +      (ProbeUint8 == CLEAR_STATUS_CMD))
> +  {
> +    DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as RAM\n"));
> +    *Ptr = OriginalUint8;
> +  } else {
> +    *Ptr       = READ_STATUS_CMD;
> +    ProbeUint8 = *Ptr;
> +    if (ProbeUint8 == OriginalUint8) {
> +      DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as ROM\n"));
> +    } else if (ProbeUint8 == READ_STATUS_CMD) {
> +      DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as RAM\n"));
> +      *Ptr = OriginalUint8;
> +    } else if (ProbeUint8 == CLEARED_ARRAY_STATUS) {
> +      DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as FLASH\n"));
> +      FlashDetected = TRUE;
> +      *Ptr          = READ_ARRAY_CMD;
> +    }
> +  }
> +
> +  DEBUG ((
> +    DEBUG_INFO,
> +    "QemuFlashDetected => %a\n",
> +    FlashDetected ? "Yes" : "No"
> +    ));
> +  return FlashDetected;
> +}
> +
> +
> +/**
> +  Read from QEMU Flash
> +
> +  @param[in] Lba      The starting logical block index to read from.
> +  @param[in] Offset   Offset into the block at which to begin reading.
> +  @param[in] NumBytes On input, indicates the requested read size. On
> +                      output, indicates the actual number of bytes read
> +  @param[in] Buffer   Pointer to the buffer to read into.
> +
> +**/
> +EFI_STATUS
> +QemuFlashRead (
> +  IN        EFI_LBA  Lba,
> +  IN        UINTN    Offset,
> +  IN        UINTN    *NumBytes,
> +  IN        UINT8    *Buffer
> +  )
> +{
> +  UINT8  *Ptr;
> +
> +  //
> +  // Only write to the first 64k. We don't bother saving the FTW Spare
> +  // block into the flash memory.
> +  //
> +  if (Lba >= mFdBlockCount) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  //
> +  // Get flash address
> +  //
> +  Ptr = (UINT8 *)QemuFlashPtr (Lba, Offset);
> +
> +  CopyMem (Buffer, Ptr, *NumBytes);
> +
> +  return EFI_SUCCESS;
> +}
> +
> +
> +/**
> +  Write to QEMU Flash
> +
> +  @param[in] Lba      The starting logical block index to write to.
> +  @param[in] Offset   Offset into the block at which to begin writing.
> +  @param[in] NumBytes On input, indicates the requested write size. On
> +                      output, indicates the actual number of bytes written
> +  @param[in] Buffer   Pointer to the data to write.
> +
> +**/
> +EFI_STATUS
> +QemuFlashWrite (
> +  IN        EFI_LBA  Lba,
> +  IN        UINTN    Offset,
> +  IN        UINTN    *NumBytes,
> +  IN        UINT8    *Buffer
> +  )
> +{
> +  volatile UINT8  *Ptr;
> +  UINTN           Loop;
> +
> +  //
> +  // Only write to the first 64k. We don't bother saving the FTW Spare
> +  // block into the flash memory.
> +  //
> +  if (Lba >= mFdBlockCount) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  //
> +  // Program flash
> +  //
> +  Ptr = QemuFlashPtr (Lba, Offset);
> +  for (Loop = 0; Loop < *NumBytes; Loop++) {
> +    *Ptr = WRITE_BYTE_CMD;
> +    *Ptr = Buffer[Loop];
> +    Ptr++;
> +  }
> +
> +  //
> +  // Restore flash to read mode
> +  //
> +  if (*NumBytes > 0) {
> +    *(Ptr - 1) = READ_ARRAY_CMD;
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> +
> +/**
> +  Erase a QEMU Flash block
> +
> +  @param Lba    The logical block index to erase.
> +
> +**/
> +EFI_STATUS
> +QemuFlashEraseBlock (
> +  IN   EFI_LBA  Lba
> +  )
> +{
> +  volatile UINT8  *Ptr;
> +
> +  if (Lba >= mFdBlockCount) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  Ptr = QemuFlashPtr (Lba, 0);
> +  *Ptr = BLOCK_ERASE_CMD;
> +  *Ptr = BLOCK_ERASE_CONFIRM_CMD;
> +  return EFI_SUCCESS;
> +}
> +
> +
> +/**
> +  Initializes QEMU flash memory support
> +
> +  @retval EFI_WRITE_PROTECTED   The QEMU flash device is not present.
> +  @retval EFI_SUCCESS           The QEMU flash device is supported.
> +
> +**/
> +EFI_STATUS
> +QemuFlashInitialize (
> +  VOID
> +  )
> +{
> +  mFlashBase   = (UINT8 *)(UINTN)PcdGet64 (PcdOvmfFlashNvStorageVariableBase);
> +  mFdBlockSize = PcdGet32 (PcdFlashBlockSize);
> +  ASSERT(PcdGet32 (PcdAllVarSize) % mFdBlockSize == 0);
> +  mFdBlockCount = PcdGet32 (PcdAllVarSize) / mFdBlockSize;
> +
> +  if (!QemuFlashDetected ()) {
> +    return EFI_WRITE_PROTECTED;
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h
> new file mode 100644
> index 0000000000..27caabc5f0
> --- /dev/null
> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h
> @@ -0,0 +1,86 @@
> +/** @file
> +  LoongArch support for QEMU system firmware flash device
> +
> +  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#ifndef __QEMU_FLASH_H__
> +#define __QEMU_FLASH_H__
> +
> +#include <Protocol/FirmwareVolumeBlock.h>
> +
> +extern UINT8  *mFlashBase;
> +
> +/**
> +  Read from QEMU Flash
> +
> +  @param[in] Lba      The starting logical block index to read from.
> +  @param[in] Offset   Offset into the block at which to begin reading.
> +  @param[in] NumBytes On input, indicates the requested read size. On
> +                      output, indicates the actual number of bytes read
> +  @param[in] Buffer   Pointer to the buffer to read into.
> +
> +**/
> +EFI_STATUS
> +QemuFlashRead (
> +  IN        EFI_LBA  Lba,
> +  IN        UINTN    Offset,
> +  IN        UINTN    *NumBytes,
> +  IN        UINT8    *Buffer
> +  );
> +
> +
> +/**
> +  Write to QEMU Flash
> +
> +  @param[in] Lba      The starting logical block index to write to.
> +  @param[in] Offset   Offset into the block at which to begin writing.
> +  @param[in] NumBytes On input, indicates the requested write size. On
> +                      output, indicates the actual number of bytes written
> +  @param[in] Buffer   Pointer to the data to write.
> +
> +**/
> +EFI_STATUS
> +QemuFlashWrite (
> +  IN        EFI_LBA  Lba,
> +  IN        UINTN    Offset,
> +  IN        UINTN    *NumBytes,
> +  IN        UINT8    *Buffer
> +  );
> +
> +
> +/**
> +  Erase a QEMU Flash block
> +
> +  @param Lba    The logical block index to erase.
> +
> +**/
> +EFI_STATUS
> +QemuFlashEraseBlock (
> +  IN   EFI_LBA  Lba
> +  );
> +
> +
> +/**
> +  Initializes QEMU flash memory support
> +
> +  @retval EFI_WRITE_PROTECTED   The QEMU flash device is not present.
> +  @retval EFI_SUCCESS           The QEMU flash device is supported.
> +
> +**/
> +EFI_STATUS
> +QemuFlashInitialize (
> +  VOID
> +  );
> +
> +
> +VOID
> +QemuFlashConvertPointers (
> +  VOID
> +  );
> +
> +#endif
> +
> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c
> new file mode 100644
> index 0000000000..b63314aac2
> --- /dev/null
> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c
> @@ -0,0 +1,21 @@
> +/** @file
> +  LoongArch support for QEMU system firmware flash device: functions specific to the
> +  runtime DXE driver build.
> +
> +  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <Library/UefiRuntimeLib.h>
> +
> +#include "QemuFlash.h"
> +
> +VOID
> +QemuFlashConvertPointers (
> +  VOID
> +  )
> +{
> +  EfiConvertPointer (0x0, (VOID **)&mFlashBase);
> +}
> diff --git a/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc b/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc
> index 74c83720b7..59beafb34f 100644
> --- a/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc
> +++ b/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc
> @@ -171,10 +171,10 @@
>    VirtioLib                        | OvmfPkg/Library/VirtioLib/VirtioLib.inf
>    FrameBufferBltLib                | MdeModulePkg/Library/FrameBufferBltLib/FrameBufferBltLib.inf
>    QemuFwCfgLib                     | OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibMmio.inf
> -
>    DebugLib                         | MdePkg/Library/BaseDebugLibSerialPort/BaseDebugLibSerialPort.inf
> -
>    PeiServicesLib                   | MdePkg/Library/PeiServicesLib/PeiServicesLib.inf
> +  VariableFlashInfoLib             | MdeModulePkg/Library/BaseVariableFlashInfoLib/BaseVariableFlashInfoLib.inf
> +
>  [LibraryClasses.common.SEC]
>    ReportStatusCodeLib              | MdeModulePkg/Library/PeiReportStatusCodeLib/PeiReportStatusCodeLib.inf
>    HobLib                           | MdePkg/Library/PeiHobLib/PeiHobLib.inf
> --
> 2.31.1
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [edk2-platforms][PATCH V3 15/16] Platform/Loongson: Add QemuFlashFvbServicesRuntimeDxe driver.
  2022-10-17 11:00   ` Ard Biesheuvel
@ 2022-10-17 12:56     ` xianglai
  2022-10-18  8:47       ` xianglai
  0 siblings, 1 reply; 21+ messages in thread
From: xianglai @ 2022-10-17 12:56 UTC (permalink / raw)
  To: Ard Biesheuvel; +Cc: devel, quic_llindhol, michael.d.kinney, kraxel, maobibo

Hi Ard Biesheuvel :

In the function FvbInitialize, It validate the firmware volume 
header.The firmware volume header address is PcdOvmfFdBaseAddress,

in Ovmf PcdOvmfFdBaseAddress is equal to 
PcdOvmfFlashNvStorageVariableBase, so the checksum can pass.
When loongson is implemented, PcdOvmfFdBaseAddress and 
PcdOvmfFlashNvStorageVariableBase are not equal,

the firmware checksum will fail and enter the exception branch.
In the exception branch, due to the existence of reserved space in the 
layout of loongson's NORflash,

the exception branch will also fail to pass, which will eventually lead 
to function call failure.


++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

EFI_STATUS
EFIAPI
FvbInitialize (
   IN EFI_HANDLE        ImageHandle,
   IN EFI_SYSTEM_TABLE  *SystemTable
   )

{

....

BaseAddress = (UINTN)PcdGet32 (PcdOvmfFdBaseAddress);

....

FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)BaseAddress;

   Status      = ValidateFvHeader (FwVolHeader);
   if (EFI_ERROR (Status)) {
     //
     // Get FvbInfo
     //
     Status = GetFvbInfo (Length, &FwVolHeader);
     if (EFI_ERROR (Status)) {
       DEBUG ((DEBUG_INFO, "EFI_ERROR (GetFvbInfo (Length, 
&FwVolHeader))\n"));
       return EFI_WRITE_PROTECTED;
     }
   }

....

}

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


But I think the reserved space in NORflash is unnecessary,

maybe I can remove the reserved space in Norflash and go through the 
exception branch to pass the firmware volume  header checksum,

then I can just  Using the flash library implementation in Ovmf.

I can try it like this.


Thanks

xianglai



On 2022/10/17 下午7:00, Ard Biesheuvel wrote:
> On Fri, 14 Oct 2022 at 06:01, xianglai li <lixianglai@loongson.cn> wrote:
>> This library provides flash read and write functionality
>> and supports writing variables to flash.
>>
>> REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4054
>>
>> Signed-off-by: xianglai li <lixianglai@loongson.cn>
> Why do you need a new driver for this? We already have drivers to
> support QEMU's NOR flash emulation, and the NorFlashDxe driver is
> being migrated to OvmfPkg/ in a series that is on the list now for
> RISC-V
>
>> ---
>>   .../QemuFlashFvbServicesRuntimeDxe/FvbInfo.c  |  115 ++
>>   .../FvbServicesRuntimeDxe.inf                 |   73 ++
>>   .../FwBlockService.c                          | 1158 +++++++++++++++++
>>   .../FwBlockService.h                          |  178 +++
>>   .../FwBlockServiceDxe.c                       |  152 +++
>>   .../QemuFlash.c                               |  251 ++++
>>   .../QemuFlash.h                               |   86 ++
>>   .../QemuFlashDxe.c                            |   21 +
>>   .../Loongson/LoongArchQemuPkg/Loongson.dsc    |    4 +-
>>   9 files changed, 2036 insertions(+), 2 deletions(-)
>>   create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbInfo.c
>>   create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
>>   create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
>>   create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.h
>>   create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockServiceDxe.c
>>   create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c
>>   create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h
>>   create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c
>>
>> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbInfo.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbInfo.c
>> new file mode 100644
>> index 0000000000..df772f72be
>> --- /dev/null
>> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbInfo.c
>> @@ -0,0 +1,115 @@
>> +/** @file
>> +  Defines data structure that is the volume header found.These data is intent
>> +  to decouple FVB driver with FV header.
>> +
>> +  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +
>> +**/
>> +
>> +//
>> +// The package level header files this module uses
>> +//
>> +#include <Pi/PiFirmwareVolume.h>
>> +
>> +//
>> +// The protocols, PPI and GUID definitions for this module
>> +//
>> +#include <Guid/SystemNvDataGuid.h>
>> +//
>> +// The Library classes this module consumes
>> +//
>> +#include <Library/BaseLib.h>
>> +#include <Library/PcdLib.h>
>> +
>> +typedef struct {
>> +  UINT64                        FvLength;
>> +  EFI_FIRMWARE_VOLUME_HEADER    FvbInfo;
>> +  //
>> +  // EFI_FV_BLOCK_MAP_ENTRY    ExtraBlockMap[n];//n=0
>> +  //
>> +  EFI_FV_BLOCK_MAP_ENTRY        End[1];
>> +} EFI_FVB_MEDIA_INFO;
>> +
>> +EFI_FVB_MEDIA_INFO  mPlatformFvbMediaInfo[] = {
>> +  //
>> +  // System NvStorage FVB
>> +  //
>> +  {
>> +    FixedPcdGet32 (PcdAllVarSize),
>> +    {
>> +      {
>> +        0,
>> +      },  // ZeroVector[16]
>> +      EFI_SYSTEM_NV_DATA_FV_GUID,
>> +      FixedPcdGet32 (PcdAllVarSize),
>> +      EFI_FVH_SIGNATURE,
>> +      EFI_FVB2_MEMORY_MAPPED |
>> +      EFI_FVB2_READ_ENABLED_CAP |
>> +      EFI_FVB2_READ_STATUS |
>> +      EFI_FVB2_WRITE_ENABLED_CAP |
>> +      EFI_FVB2_WRITE_STATUS |
>> +      EFI_FVB2_ERASE_POLARITY |
>> +      EFI_FVB2_ALIGNMENT_16,
>> +      sizeof (EFI_FIRMWARE_VOLUME_HEADER) + sizeof (EFI_FV_BLOCK_MAP_ENTRY),
>> +      0,  // CheckSum
>> +      0,  // ExtHeaderOffset
>> +      {
>> +        0,
>> +      },  // Reserved[1]
>> +      2,  // Revision
>> +      {
>> +        {
>> +          (FixedPcdGet32 (PcdAllVarSize))/
>> +          FixedPcdGet32 (PcdFlashBlockSize),
>> +          FixedPcdGet32 (PcdFlashBlockSize),
>> +        }
>> +      } // BlockMap[1]
>> +    },
>> +    {
>> +      {
>> +        0,
>> +        0
>> +      }
>> +    }  // End[1]
>> +  }
>> +};
>> +
>> +EFI_STATUS
>> +GetFvbInfo (
>> +  IN  UINT64                      FvLength,
>> +  OUT EFI_FIRMWARE_VOLUME_HEADER  **FvbInfo
>> +  )
>> +{
>> +  STATIC BOOLEAN  Checksummed = FALSE;
>> +  UINTN           Index;
>> +
>> +  if (!Checksummed) {
>> +    for (Index = 0;
>> +         Index < sizeof (mPlatformFvbMediaInfo) / sizeof (EFI_FVB_MEDIA_INFO);
>> +         Index += 1)
>> +    {
>> +      UINT16  Checksum;
>> +      mPlatformFvbMediaInfo[Index].FvbInfo.Checksum = 0;
>> +      Checksum                                      = CalculateCheckSum16 (
>> +                                                        (UINT16 *)&mPlatformFvbMediaInfo[Index].FvbInfo,
>> +                                                        mPlatformFvbMediaInfo[Index].FvbInfo.HeaderLength
>> +                                                        );
>> +      mPlatformFvbMediaInfo[Index].FvbInfo.Checksum = Checksum;
>> +    }
>> +    Checksummed = TRUE;
>> +  }
>> +
>> +  for (Index = 0;
>> +       Index < sizeof (mPlatformFvbMediaInfo) / sizeof (EFI_FVB_MEDIA_INFO);
>> +       Index += 1)
>> +  {
>> +    if (mPlatformFvbMediaInfo[Index].FvLength == FvLength) {
>> +      *FvbInfo = &mPlatformFvbMediaInfo[Index].FvbInfo;
>> +      return EFI_SUCCESS;
>> +    }
>> +  }
>> +
>> +  return EFI_NOT_FOUND;
>> +}
>> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
>> new file mode 100644
>> index 0000000000..4b0c42b075
>> --- /dev/null
>> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
>> @@ -0,0 +1,73 @@
>> +## @file
>> +# Component description file for Emu Fimware Volume Block DXE driver module.
>> +#
>> +#  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +#
>> +#  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +#
>> +##
>> +
>> +
>> +[Defines]
>> +  INF_VERSION                    = 0x00010005
>> +  BASE_NAME                      = FvbServicesRuntimeDxe
>> +  FILE_GUID                      = 733cbac2-b23f-4b92-bc8e-fb01ce5907b7
>> +  MODULE_TYPE                    = DXE_RUNTIME_DRIVER
>> +  VERSION_STRING                 = 1.0
>> +  ENTRY_POINT                    = FvbInitialize
>> +
>> +#
>> +# The following information is for reference only and not required by the build
>> +# tools.
>> +#
>> +#  VALID_ARCHITECTURES           = IA32 X64
>> +#
>> +
>> +[Sources]
>> +  FvbInfo.c
>> +  FwBlockService.c
>> +  FwBlockServiceDxe.c
>> +  QemuFlash.c
>> +  QemuFlashDxe.c
>> +
>> +[Packages]
>> +  MdePkg/MdePkg.dec
>> +  MdeModulePkg/MdeModulePkg.dec
>> +  OvmfPkg/OvmfPkg.dec
>> +  Platform/Loongson/LoongArchQemuPkg/Loongson.dec
>> +
>> +[LibraryClasses]
>> +  BaseLib
>> +  BaseMemoryLib
>> +  DebugLib
>> +  DevicePathLib
>> +  DxeServicesTableLib
>> +  MemoryAllocationLib
>> +  PcdLib
>> +  UefiBootServicesTableLib
>> +  UefiDriverEntryPoint
>> +  UefiRuntimeLib
>> +
>> +[Guids]
>> +  gEfiEventVirtualAddressChangeGuid   # ALWAYS_CONSUMED
>> +  # gEfiEventVirtualAddressChangeGuid # Create Event: EVENT_GROUP_GUID
>> +
>> +[Protocols]
>> +  gEfiFirmwareVolumeBlockProtocolGuid           # PROTOCOL SOMETIMES_PRODUCED
>> +  gEfiDevicePathProtocolGuid                    # PROTOCOL SOMETIMES_PRODUCED
>> +
>> +[FixedPcd]
>> +  gLoongArchQemuPkgTokenSpaceGuid.PcdOvmfFlashNvStorageVariableBase
>> +  gLoongArchQemuPkgTokenSpaceGuid.PcdOvmfFlashNvStorageFtwWorkingBase
>> +  gLoongArchQemuPkgTokenSpaceGuid.PcdOvmfFlashNvStorageFtwSpareBase
>> +  gLoongArchQemuPkgTokenSpaceGuid.PcdFlashFdBase
>> +  gLoongArchQemuPkgTokenSpaceGuid.PcdAllVarSize
>> +  gLoongArchQemuPkgTokenSpaceGuid.PcdFlashBlockSize
>> +[Pcd]
>> +  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase64
>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashVariablesEnable
>> +  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase64
>> +  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase64
>> +
>> +[Depex]
>> +  TRUE
>> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
>> new file mode 100644
>> index 0000000000..d44f2b460c
>> --- /dev/null
>> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
>> @@ -0,0 +1,1158 @@
>> +/** @file
>> +  Implementations for Firmware Volume Block protocol.
>> +
>> +  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +
>> +**/
>> +
>> +//
>> +// The protocols, PPI and GUID definitions for this module
>> +//
>> +#include <Protocol/DevicePath.h>
>> +#include <Protocol/FirmwareVolumeBlock.h>
>> +
>> +//
>> +// The Library classes this module consumes
>> +//
>> +#include <Library/BaseLib.h>
>> +#include <Library/BaseMemoryLib.h>
>> +#include <Library/DebugLib.h>
>> +#include <Library/DevicePathLib.h>
>> +#include <Library/DxeServicesTableLib.h>
>> +#include <Library/MemoryAllocationLib.h>
>> +#include <Library/UefiBootServicesTableLib.h>
>> +
>> +#include "FwBlockService.h"
>> +#include "QemuFlash.h"
>> +
>> +#define EFI_FVB2_STATUS \
>> +          (EFI_FVB2_READ_STATUS | EFI_FVB2_WRITE_STATUS | EFI_FVB2_LOCK_STATUS)
>> +
>> +ESAL_FWB_GLOBAL  *mFvbModuleGlobal;
>> +
>> +FV_MEMMAP_DEVICE_PATH  mFvMemmapDevicePathTemplate = {
>> +  {
>> +    {
>> +      HARDWARE_DEVICE_PATH,
>> +      HW_MEMMAP_DP,
>> +      {
>> +        (UINT8)(sizeof (MEMMAP_DEVICE_PATH)),
>> +        (UINT8)(sizeof (MEMMAP_DEVICE_PATH) >> 8)
>> +      }
>> +    },
>> +    EfiMemoryMappedIO,
>> +    (EFI_PHYSICAL_ADDRESS)0,
>> +    (EFI_PHYSICAL_ADDRESS)0,
>> +  },
>> +  {
>> +    END_DEVICE_PATH_TYPE,
>> +    END_ENTIRE_DEVICE_PATH_SUBTYPE,
>> +    {
>> +      END_DEVICE_PATH_LENGTH,
>> +      0
>> +    }
>> +  }
>> +};
>> +
>> +FV_PIWG_DEVICE_PATH  mFvPIWGDevicePathTemplate = {
>> +  {
>> +    {
>> +      MEDIA_DEVICE_PATH,
>> +      MEDIA_PIWG_FW_VOL_DP,
>> +      {
>> +        (UINT8)(sizeof (MEDIA_FW_VOL_DEVICE_PATH)),
>> +        (UINT8)(sizeof (MEDIA_FW_VOL_DEVICE_PATH) >> 8)
>> +      }
>> +    },
>> +    { 0 }
>> +  },
>> +  {
>> +    END_DEVICE_PATH_TYPE,
>> +    END_ENTIRE_DEVICE_PATH_SUBTYPE,
>> +    {
>> +      END_DEVICE_PATH_LENGTH,
>> +      0
>> +    }
>> +  }
>> +};
>> +
>> +EFI_FW_VOL_BLOCK_DEVICE  mFvbDeviceTemplate = {
>> +  FVB_DEVICE_SIGNATURE,
>> +  NULL,
>> +  0,
>> +  {
>> +    FvbProtocolGetAttributes,
>> +    FvbProtocolSetAttributes,
>> +    FvbProtocolGetPhysicalAddress,
>> +    FvbProtocolGetBlockSize,
>> +    FvbProtocolRead,
>> +    FvbProtocolWrite,
>> +    FvbProtocolEraseBlocks,
>> +    NULL
>> +  }
>> +};
>> +
>> +
>> +EFI_STATUS
>> +GetFvbInstance (
>> +  IN  UINTN                Instance,
>> +  IN  ESAL_FWB_GLOBAL      *Global,
>> +  OUT EFI_FW_VOL_INSTANCE  **FwhInstance
>> +  )
>> +/*++
>> +
>> +  Routine Description:
>> +    Retrieves the physical address of a memory mapped FV
>> +
>> +  Arguments:
>> +    Instance              - The FV instance whose base address is going to be
>> +                            returned
>> +    Global                - Pointer to ESAL_FWB_GLOBAL that contains all
>> +                            instance data
>> +    FwhInstance           - The EFI_FW_VOL_INSTANCE firmware instance structure
>> +
>> +  Returns:
>> +    EFI_SUCCESS           - Successfully returns
>> +    EFI_INVALID_PARAMETER - Instance not found
>> +
>> +--*/
>> +{
>> +  EFI_FW_VOL_INSTANCE  *FwhRecord;
>> +
>> +  *FwhInstance = NULL;
>> +  if (Instance >= Global->NumFv) {
>> +    return EFI_INVALID_PARAMETER;
>> +  }
>> +  //
>> +  // Find the right instance of the FVB private data
>> +  //
>> +  FwhRecord = Global->FvInstance;
>> +  while (Instance > 0) {
>> +    FwhRecord = (EFI_FW_VOL_INSTANCE *)
>> +                (
>> +                 (UINTN)((UINT8 *)FwhRecord) + FwhRecord->VolumeHeader.HeaderLength +
>> +                 (sizeof (EFI_FW_VOL_INSTANCE) - sizeof (EFI_FIRMWARE_VOLUME_HEADER))
>> +                );
>> +    Instance--;
>> +  }
>> +
>> +  *FwhInstance = FwhRecord;
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +EFI_STATUS
>> +FvbGetPhysicalAddress (
>> +  IN UINTN                  Instance,
>> +  OUT EFI_PHYSICAL_ADDRESS  *Address,
>> +  IN ESAL_FWB_GLOBAL        *Global
>> +  )
>> +/*++
>> +
>> +  Routine Description:
>> +    Retrieves the physical address of a memory mapped FV
>> +
>> +  Arguments:
>> +    Instance              - The FV instance whose base address is going to be
>> +                            returned
>> +    Address               - Pointer to a caller allocated EFI_PHYSICAL_ADDRESS
>> +                            that on successful return, contains the base
>> +                            address of the firmware volume.
>> +    Global                - Pointer to ESAL_FWB_GLOBAL that contains all
>> +                            instance data
>> +
>> +  Returns:
>> +    EFI_SUCCESS           - Successfully returns
>> +    EFI_INVALID_PARAMETER - Instance not found
>> +
>> +--*/
>> +{
>> +  EFI_FW_VOL_INSTANCE  *FwhInstance;
>> +  EFI_STATUS           Status;
>> +
>> +  //
>> +  // Find the right instance of the FVB private data
>> +  //
>> +  Status = GetFvbInstance (Instance, Global, &FwhInstance);
>> +  ASSERT_EFI_ERROR (Status);
>> +  *Address = FwhInstance->FvBase;
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +EFI_STATUS
>> +FvbGetVolumeAttributes (
>> +  IN UINTN                  Instance,
>> +  OUT EFI_FVB_ATTRIBUTES_2  *Attributes,
>> +  IN ESAL_FWB_GLOBAL        *Global
>> +  )
>> +/*++
>> +
>> +  Routine Description:
>> +    Retrieves attributes, insures positive polarity of attribute bits, returns
>> +    resulting attributes in output parameter
>> +
>> +  Arguments:
>> +    Instance              - The FV instance whose attributes is going to be
>> +                            returned
>> +    Attributes            - Output buffer which contains attributes
>> +    Global                - Pointer to ESAL_FWB_GLOBAL that contains all
>> +                            instance data
>> +
>> +  Returns:
>> +    EFI_SUCCESS           - Successfully returns
>> +    EFI_INVALID_PARAMETER - Instance not found
>> +
>> +--*/
>> +{
>> +  EFI_FW_VOL_INSTANCE  *FwhInstance;
>> +  EFI_STATUS           Status;
>> +
>> +  //
>> +  // Find the right instance of the FVB private data
>> +  //
>> +  Status = GetFvbInstance (Instance, Global, &FwhInstance);
>> +  ASSERT_EFI_ERROR (Status);
>> +  *Attributes = FwhInstance->VolumeHeader.Attributes;
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +EFI_STATUS
>> +FvbGetLbaAddress (
>> +  IN  UINTN            Instance,
>> +  IN  EFI_LBA          Lba,
>> +  OUT UINTN            *LbaAddress,
>> +  OUT UINTN            *LbaLength,
>> +  OUT UINTN            *NumOfBlocks,
>> +  IN  ESAL_FWB_GLOBAL  *Global
>> +  )
>> +/*++
>> +
>> +  Routine Description:
>> +    Retrieves the starting address of an LBA in an FV
>> +
>> +  Arguments:
>> +    Instance              - The FV instance which the Lba belongs to
>> +    Lba                   - The logical block address
>> +    LbaAddress            - On output, contains the physical starting address
>> +                            of the Lba
>> +    LbaLength             - On output, contains the length of the block
>> +    NumOfBlocks           - A pointer to a caller allocated UINTN in which the
>> +                            number of consecutive blocks starting with Lba is
>> +                            returned. All blocks in this range have a size of
>> +                            BlockSize
>> +    Global                - Pointer to ESAL_FWB_GLOBAL that contains all
>> +                            instance data
>> +
>> +  Returns:
>> +    EFI_SUCCESS           - Successfully returns
>> +    EFI_INVALID_PARAMETER - Instance not found
>> +
>> +--*/
>> +{
>> +  UINT32                  NumBlocks;
>> +  UINT32                  BlockLength;
>> +  UINTN                   Offset;
>> +  EFI_LBA                 StartLba;
>> +  EFI_LBA                 NextLba;
>> +  EFI_FW_VOL_INSTANCE     *FwhInstance;
>> +  EFI_FV_BLOCK_MAP_ENTRY  *BlockMap;
>> +  EFI_STATUS              Status;
>> +
>> +  //
>> +  // Find the right instance of the FVB private data
>> +  //
>> +  Status = GetFvbInstance (Instance, Global, &FwhInstance);
>> +  ASSERT_EFI_ERROR (Status);
>> +
>> +  StartLba = 0;
>> +  Offset   = 0;
>> +  BlockMap = &(FwhInstance->VolumeHeader.BlockMap[0]);
>> +
>> +  //
>> +  // Parse the blockmap of the FV to find which map entry the Lba belongs to
>> +  //
>> +  while (TRUE) {
>> +    NumBlocks   = BlockMap->NumBlocks;
>> +    BlockLength = BlockMap->Length;
>> +
>> +    if ((NumBlocks == 0) || (BlockLength == 0)) {
>> +      return EFI_INVALID_PARAMETER;
>> +    }
>> +
>> +    NextLba = StartLba + NumBlocks;
>> +
>> +    //
>> +    // The map entry found
>> +    //
>> +    if ((Lba >= StartLba) && (Lba < NextLba)) {
>> +      Offset = Offset + (UINTN)MultU64x32 ((Lba - StartLba), BlockLength);
>> +      if (LbaAddress != NULL) {
>> +        *LbaAddress = FwhInstance->FvBase + Offset;
>> +      }
>> +
>> +      if (LbaLength != NULL) {
>> +        *LbaLength = BlockLength;
>> +      }
>> +
>> +      if (NumOfBlocks != NULL) {
>> +        *NumOfBlocks = (UINTN)(NextLba - Lba);
>> +      }
>> +
>> +      return EFI_SUCCESS;
>> +    }
>> +
>> +    StartLba = NextLba;
>> +    Offset   = Offset + NumBlocks * BlockLength;
>> +    BlockMap++;
>> +  }
>> +}
>> +
>> +EFI_STATUS
>> +FvbSetVolumeAttributes (
>> +  IN UINTN                     Instance,
>> +  IN OUT EFI_FVB_ATTRIBUTES_2  *Attributes,
>> +  IN ESAL_FWB_GLOBAL           *Global
>> +  )
>> +/*++
>> +
>> +  Routine Description:
>> +    Modifies the current settings of the firmware volume according to the
>> +    input parameter, and returns the new setting of the volume
>> +
>> +  Arguments:
>> +    Instance              - The FV instance whose attributes is going to be
>> +                            modified
>> +    Attributes            - On input, it is a pointer to EFI_FVB_ATTRIBUTES_2
>> +                            containing the desired firmware volume settings.
>> +                            On successful return, it contains the new settings
>> +                            of the firmware volume
>> +    Global                - Pointer to ESAL_FWB_GLOBAL that contains all
>> +                            instance data
>> +
>> +  Returns:
>> +    EFI_SUCCESS           - Successfully returns
>> +    EFI_ACCESS_DENIED     - The volume setting is locked and cannot be modified
>> +    EFI_INVALID_PARAMETER - Instance not found, or The attributes requested are
>> +                            in conflict with the capabilities as declared in
>> +                            the firmware volume header
>> +
>> +--*/
>> +{
>> +  EFI_FW_VOL_INSTANCE   *FwhInstance;
>> +  EFI_FVB_ATTRIBUTES_2  OldAttributes;
>> +  EFI_FVB_ATTRIBUTES_2  *AttribPtr;
>> +  UINT32                Capabilities;
>> +  UINT32                OldStatus;
>> +  UINT32                NewStatus;
>> +  EFI_STATUS            Status;
>> +  EFI_FVB_ATTRIBUTES_2  UnchangedAttributes;
>> +
>> +  //
>> +  // Find the right instance of the FVB private data
>> +  //
>> +  Status = GetFvbInstance (Instance, Global, &FwhInstance);
>> +  ASSERT_EFI_ERROR (Status);
>> +
>> +  AttribPtr =
>> +    (EFI_FVB_ATTRIBUTES_2 *)&(FwhInstance->VolumeHeader.Attributes);
>> +  OldAttributes = *AttribPtr;
>> +  Capabilities  = OldAttributes & (EFI_FVB2_READ_DISABLED_CAP | \
>> +                                   EFI_FVB2_READ_ENABLED_CAP | \
>> +                                   EFI_FVB2_WRITE_DISABLED_CAP | \
>> +                                   EFI_FVB2_WRITE_ENABLED_CAP | \
>> +                                   EFI_FVB2_LOCK_CAP \
>> +                                   );
>> +  OldStatus = OldAttributes & EFI_FVB2_STATUS;
>> +  NewStatus = *Attributes & EFI_FVB2_STATUS;
>> +
>> +  UnchangedAttributes = EFI_FVB2_READ_DISABLED_CAP  | \
>> +                        EFI_FVB2_READ_ENABLED_CAP   | \
>> +                        EFI_FVB2_WRITE_DISABLED_CAP | \
>> +                        EFI_FVB2_WRITE_ENABLED_CAP  | \
>> +                        EFI_FVB2_LOCK_CAP           | \
>> +                        EFI_FVB2_STICKY_WRITE       | \
>> +                        EFI_FVB2_MEMORY_MAPPED      | \
>> +                        EFI_FVB2_ERASE_POLARITY     | \
>> +                        EFI_FVB2_READ_LOCK_CAP      | \
>> +                        EFI_FVB2_WRITE_LOCK_CAP     | \
>> +                        EFI_FVB2_ALIGNMENT;
>> +
>> +  //
>> +  // Some attributes of FV is read only can *not* be set
>> +  //
>> +  if ((OldAttributes & UnchangedAttributes) ^
>> +      (*Attributes & UnchangedAttributes))
>> +  {
>> +    return EFI_INVALID_PARAMETER;
>> +  }
>> +  //
>> +  // If firmware volume is locked, no status bit can be updated
>> +  //
>> +  if (OldAttributes & EFI_FVB2_LOCK_STATUS) {
>> +    if (OldStatus ^ NewStatus) {
>> +      return EFI_ACCESS_DENIED;
>> +    }
>> +  }
>> +  //
>> +  // Test read disable
>> +  //
>> +  if ((Capabilities & EFI_FVB2_READ_DISABLED_CAP) == 0) {
>> +    if ((NewStatus & EFI_FVB2_READ_STATUS) == 0) {
>> +      return EFI_INVALID_PARAMETER;
>> +    }
>> +  }
>> +  //
>> +  // Test read enable
>> +  //
>> +  if ((Capabilities & EFI_FVB2_READ_ENABLED_CAP) == 0) {
>> +    if (NewStatus & EFI_FVB2_READ_STATUS) {
>> +      return EFI_INVALID_PARAMETER;
>> +    }
>> +  }
>> +  //
>> +  // Test write disable
>> +  //
>> +  if ((Capabilities & EFI_FVB2_WRITE_DISABLED_CAP) == 0) {
>> +    if ((NewStatus & EFI_FVB2_WRITE_STATUS) == 0) {
>> +      return EFI_INVALID_PARAMETER;
>> +    }
>> +  }
>> +  //
>> +  // Test write enable
>> +  //
>> +  if ((Capabilities & EFI_FVB2_WRITE_ENABLED_CAP) == 0) {
>> +    if (NewStatus & EFI_FVB2_WRITE_STATUS) {
>> +      return EFI_INVALID_PARAMETER;
>> +    }
>> +  }
>> +  //
>> +  // Test lock
>> +  //
>> +  if ((Capabilities & EFI_FVB2_LOCK_CAP) == 0) {
>> +    if (NewStatus & EFI_FVB2_LOCK_STATUS) {
>> +      return EFI_INVALID_PARAMETER;
>> +    }
>> +  }
>> +
>> +  *AttribPtr  = (*AttribPtr) & (0xFFFFFFFF & (~EFI_FVB2_STATUS));
>> +  *AttribPtr  = (*AttribPtr) | NewStatus;
>> +  *Attributes = *AttribPtr;
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +//
>> +// FVB protocol APIs
>> +//
>> +EFI_STATUS
>> +EFIAPI
>> +FvbProtocolGetPhysicalAddress (
>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>> +  OUT EFI_PHYSICAL_ADDRESS                     *Address
>> +  )
>> +/*++
>> +
>> +  Routine Description:
>> +
>> +    Retrieves the physical address of the device.
>> +
>> +  Arguments:
>> +
>> +    This                  - Calling context
>> +    Address               - Output buffer containing the address.
>> +
>> +  Returns:
>> +    EFI_SUCCESS           - Successfully returns
>> +
>> +--*/
>> +{
>> +  EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice;
>> +
>> +  FvbDevice = FVB_DEVICE_FROM_THIS (This);
>> +
>> +  return FvbGetPhysicalAddress (
>> +           FvbDevice->Instance,
>> +           Address,
>> +           mFvbModuleGlobal
>> +           );
>> +}
>> +
>> +EFI_STATUS
>> +EFIAPI
>> +FvbProtocolGetBlockSize (
>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>> +  IN CONST EFI_LBA                             Lba,
>> +  OUT UINTN                                    *BlockSize,
>> +  OUT UINTN                                    *NumOfBlocks
>> +  )
>> +/*++
>> +
>> +  Routine Description:
>> +    Retrieve the size of a logical block
>> +
>> +  Arguments:
>> +    This                  - Calling context
>> +    Lba                   - Indicates which block to return the size for.
>> +    BlockSize             - A pointer to a caller allocated UINTN in which
>> +                            the size of the block is returned
>> +    NumOfBlocks           - a pointer to a caller allocated UINTN in which the
>> +                            number of consecutive blocks starting with Lba is
>> +                            returned. All blocks in this range have a size of
>> +                            BlockSize
>> +
>> +  Returns:
>> +    EFI_SUCCESS           - The firmware volume was read successfully and
>> +                            contents are in Buffer
>> +
>> +--*/
>> +{
>> +  EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice;
>> +
>> +  FvbDevice = FVB_DEVICE_FROM_THIS (This);
>> +
>> +  return FvbGetLbaAddress (
>> +           FvbDevice->Instance,
>> +           Lba,
>> +           NULL,
>> +           BlockSize,
>> +           NumOfBlocks,
>> +           mFvbModuleGlobal
>> +           );
>> +}
>> +
>> +EFI_STATUS
>> +EFIAPI
>> +FvbProtocolGetAttributes (
>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>> +  OUT EFI_FVB_ATTRIBUTES_2                     *Attributes
>> +  )
>> +/*++
>> +
>> +  Routine Description:
>> +      Retrieves Volume attributes.  No polarity translations are done.
>> +
>> +  Arguments:
>> +      This                - Calling context
>> +      Attributes          - output buffer which contains attributes
>> +
>> +  Returns:
>> +    EFI_SUCCESS           - Successfully returns
>> +
>> +--*/
>> +{
>> +  EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice;
>> +
>> +  FvbDevice = FVB_DEVICE_FROM_THIS (This);
>> +
>> +  return FvbGetVolumeAttributes (
>> +           FvbDevice->Instance,
>> +           Attributes,
>> +           mFvbModuleGlobal
>> +           );
>> +}
>> +
>> +EFI_STATUS
>> +EFIAPI
>> +FvbProtocolSetAttributes (
>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>> +  IN OUT EFI_FVB_ATTRIBUTES_2                  *Attributes
>> +  )
>> +/*++
>> +
>> +  Routine Description:
>> +    Sets Volume attributes. No polarity translations are done.
>> +
>> +  Arguments:
>> +    This                  - Calling context
>> +    Attributes            - output buffer which contains attributes
>> +
>> +  Returns:
>> +    EFI_SUCCESS           - Successfully returns
>> +
>> +--*/
>> +{
>> +  EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice;
>> +
>> +  FvbDevice = FVB_DEVICE_FROM_THIS (This);
>> +
>> +  return FvbSetVolumeAttributes (
>> +           FvbDevice->Instance,
>> +           Attributes,
>> +           mFvbModuleGlobal
>> +           );
>> +}
>> +
>> +EFI_STATUS
>> +EFIAPI
>> +FvbProtocolEraseBlocks (
>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>> +  ...
>> +  )
>> +/*++
>> +
>> +  Routine Description:
>> +
>> +    The EraseBlock() function erases one or more blocks as denoted by the
>> +    variable argument list. The entire parameter list of blocks must be
>> +    verified prior to erasing any blocks.  If a block is requested that does
>> +    not exist within the associated firmware volume (it has a larger index than
>> +    the last block of the firmware volume), the EraseBlock() function must
>> +    return EFI_INVALID_PARAMETER without modifying the contents of the firmware
>> +    volume.
>> +
>> +  Arguments:
>> +    This                  - Calling context
>> +    ...                   - Starting LBA followed by Number of Lba to erase.
>> +                            a -1 to terminate the list.
>> +
>> +  Returns:
>> +    EFI_SUCCESS           - The erase request was successfully completed
>> +    EFI_ACCESS_DENIED     - The firmware volume is in the WriteDisabled state
>> +    EFI_DEVICE_ERROR      - The block device is not functioning correctly and
>> +                            could not be written. Firmware device may have been
>> +                            partially erased
>> +
>> +--*/
>> +{
>> +  EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice;
>> +  EFI_FW_VOL_INSTANCE      *FwhInstance;
>> +  UINTN                    NumOfBlocks;
>> +  VA_LIST                  args;
>> +  EFI_LBA                  StartingLba;
>> +  UINTN                    NumOfLba;
>> +  EFI_STATUS               Status;
>> +
>> +  FvbDevice = FVB_DEVICE_FROM_THIS (This);
>> +
>> +  Status = GetFvbInstance (
>> +             FvbDevice->Instance,
>> +             mFvbModuleGlobal,
>> +             &FwhInstance
>> +             );
>> +  ASSERT_EFI_ERROR (Status);
>> +
>> +  NumOfBlocks = FwhInstance->NumOfBlocks;
>> +
>> +  VA_START (args, This);
>> +
>> +  do {
>> +    StartingLba = VA_ARG (args, EFI_LBA);
>> +    if (StartingLba == EFI_LBA_LIST_TERMINATOR) {
>> +      break;
>> +    }
>> +
>> +    NumOfLba = VA_ARG (args, UINTN);
>> +
>> +    //
>> +    // Check input parameters
>> +    //
>> +    if ((NumOfLba == 0) || ((StartingLba + NumOfLba) > NumOfBlocks)) {
>> +      VA_END (args);
>> +      return EFI_INVALID_PARAMETER;
>> +    }
>> +  } while (1);
>> +
>> +  VA_END (args);
>> +
>> +  VA_START (args, This);
>> +  do {
>> +    StartingLba = VA_ARG (args, EFI_LBA);
>> +    if (StartingLba == EFI_LBA_LIST_TERMINATOR) {
>> +      break;
>> +    }
>> +
>> +    NumOfLba = VA_ARG (args, UINTN);
>> +
>> +    while (NumOfLba > 0) {
>> +      Status = QemuFlashEraseBlock (StartingLba);
>> +      if (EFI_ERROR (Status)) {
>> +        VA_END (args);
>> +        return Status;
>> +      }
>> +
>> +      StartingLba++;
>> +      NumOfLba--;
>> +    }
>> +
>> +  } while (1);
>> +
>> +  VA_END (args);
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +EFI_STATUS
>> +EFIAPI
>> +FvbProtocolWrite (
>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>> +  IN       EFI_LBA                             Lba,
>> +  IN       UINTN                               Offset,
>> +  IN OUT   UINTN                               *NumBytes,
>> +  IN       UINT8                               *Buffer
>> +  )
>> +/*++
>> +
>> +  Routine Description:
>> +
>> +    Writes data beginning at Lba:Offset from FV. The write terminates either
>> +    when *NumBytes of data have been written, or when a block boundary is
>> +    reached.  *NumBytes is updated to reflect the actual number of bytes
>> +    written. The write operation does not include erase. This routine will
>> +    attempt to write only the specified bytes. If the writes do not stick,
>> +    it will return an error.
>> +
>> +  Arguments:
>> +    This                  - Calling context
>> +    Lba                   - Block in which to begin write
>> +    Offset                - Offset in the block at which to begin write
>> +    NumBytes              - On input, indicates the requested write size. On
>> +                            output, indicates the actual number of bytes
>> +                            written
>> +    Buffer                - Buffer containing source data for the write.
>> +
>> +  Returns:
>> +    EFI_SUCCESS           - The firmware volume was written successfully
>> +    EFI_BAD_BUFFER_SIZE   - Write attempted across a LBA boundary. On output,
>> +                            NumBytes contains the total number of bytes
>> +                            actually written
>> +    EFI_ACCESS_DENIED     - The firmware volume is in the WriteDisabled state
>> +    EFI_DEVICE_ERROR      - The block device is not functioning correctly and
>> +                            could not be written
>> +    EFI_INVALID_PARAMETER - NumBytes or Buffer are NULL
>> +
>> +--*/
>> +{
>> +  return QemuFlashWrite (
>> +           (EFI_LBA)Lba,
>> +           (UINTN)Offset,
>> +           NumBytes,
>> +           (UINT8 *)Buffer
>> +           );
>> +}
>> +
>> +EFI_STATUS
>> +EFIAPI
>> +FvbProtocolRead (
>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>> +  IN CONST EFI_LBA                             Lba,
>> +  IN CONST UINTN                               Offset,
>> +  IN OUT UINTN                                 *NumBytes,
>> +  IN UINT8                                     *Buffer
>> +  )
>> +/*++
>> +
>> +  Routine Description:
>> +
>> +    Reads data beginning at Lba:Offset from FV. The Read terminates either
>> +    when *NumBytes of data have been read, or when a block boundary is
>> +    reached.  *NumBytes is updated to reflect the actual number of bytes
>> +    written. The write operation does not include erase. This routine will
>> +    attempt to write only the specified bytes. If the writes do not stick,
>> +    it will return an error.
>> +
>> +  Arguments:
>> +    This                  - Calling context
>> +    Lba                   - Block in which to begin Read
>> +    Offset                - Offset in the block at which to begin Read
>> +    NumBytes              - On input, indicates the requested write size. On
>> +                            output, indicates the actual number of bytes Read
>> +    Buffer                - Buffer containing source data for the Read.
>> +
>> +  Returns:
>> +    EFI_SUCCESS           - The firmware volume was read successfully and
>> +                            contents are in Buffer
>> +    EFI_BAD_BUFFER_SIZE   - Read attempted across a LBA boundary. On output,
>> +                            NumBytes contains the total number of bytes
>> +                            returned in Buffer
>> +    EFI_ACCESS_DENIED     - The firmware volume is in the ReadDisabled state
>> +    EFI_DEVICE_ERROR      - The block device is not functioning correctly and
>> +                            could not be read
>> +    EFI_INVALID_PARAMETER - NumBytes or Buffer are NULL
>> +
>> +--*/
>> +{
>> +  return QemuFlashRead (
>> +           (EFI_LBA)Lba,
>> +           (UINTN)Offset,
>> +           NumBytes,
>> +           (UINT8 *)Buffer
>> +           );
>> +}
>> +
>> +EFI_STATUS
>> +ValidateFvHeader (
>> +  EFI_FIRMWARE_VOLUME_HEADER  *FwVolHeader
>> +  )
>> +/*++
>> +
>> +  Routine Description:
>> +    Check the integrity of firmware volume header
>> +
>> +  Arguments:
>> +    FwVolHeader           - A pointer to a firmware volume header
>> +
>> +  Returns:
>> +    EFI_SUCCESS           - The firmware volume is consistent
>> +    EFI_NOT_FOUND         - The firmware volume has corrupted. So it is not an
>> +                            FV
>> +
>> +--*/
>> +{
>> +  UINT16  Checksum;
>> +
>> +  //
>> +  // Verify the header revision, header signature, length
>> +  // Length of FvBlock cannot be 2**64-1
>> +  // HeaderLength cannot be an odd number
>> +  //
>> +  if ((FwVolHeader->Revision != EFI_FVH_REVISION) ||
>> +      (FwVolHeader->Signature != EFI_FVH_SIGNATURE) ||
>> +      (FwVolHeader->FvLength == ((UINTN)-1)) ||
>> +      ((FwVolHeader->HeaderLength & 0x01) != 0)
>> +      )
>> +  {
>> +    return EFI_NOT_FOUND;
>> +  }
>> +
>> +  //
>> +  // Verify the header checksum
>> +  //
>> +
>> +  Checksum = CalculateSum16 (
>> +               (UINT16 *)FwVolHeader,
>> +               FwVolHeader->HeaderLength
>> +               );
>> +  if (Checksum != 0) {
>> +    UINT16  Expected;
>> +
>> +    Expected =
>> +      (UINT16)(((UINTN)FwVolHeader->Checksum + 0x10000 - Checksum) & 0xffff);
>> +
>> +    DEBUG ((
>> +      DEBUG_INFO,
>> +      "FV@%p Checksum is 0x%x, expected 0x%x\n",
>> +      FwVolHeader,
>> +      FwVolHeader->Checksum,
>> +      Expected
>> +      ));
>> +    return EFI_NOT_FOUND;
>> +  }
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +STATIC
>> +EFI_STATUS
>> +MarkMemoryRangeForRuntimeAccess (
>> +  EFI_PHYSICAL_ADDRESS                BaseAddress,
>> +  UINTN                               Length
>> +  )
>> +{
>> +  EFI_STATUS                          Status;
>> +
>> +  //
>> +  // Mark flash region as runtime memory
>> +  //
>> +  Status = gDS->RemoveMemorySpace (
>> +                  BaseAddress,
>> +                  Length
>> +                  );
>> +
>> +  Status = gDS->AddMemorySpace (
>> +                  EfiGcdMemoryTypeSystemMemory,
>> +                  BaseAddress,
>> +                  Length,
>> +                  EFI_MEMORY_UC | EFI_MEMORY_RUNTIME
>> +                  );
>> +  ASSERT_EFI_ERROR (Status);
>> +
>> +  Status = gBS->AllocatePages (
>> +                  AllocateAddress,
>> +                  EfiRuntimeServicesData,
>> +                  EFI_SIZE_TO_PAGES (Length),
>> +                  &BaseAddress
>> +                  );
>> +  ASSERT_EFI_ERROR (Status);
>> +
>> +  return Status;
>> +}
>> +
>> +STATIC
>> +EFI_STATUS
>> +InitializeVariableFvHeader (
>> +  VOID
>> +  )
>> +{
>> +  EFI_STATUS                  Status;
>> +  EFI_FIRMWARE_VOLUME_HEADER  *GoodFwVolHeader;
>> +  EFI_FIRMWARE_VOLUME_HEADER  *FwVolHeader;
>> +  UINTN                       Length;
>> +  UINTN                       WriteLength;
>> +  UINTN                       BlockSize;
>> +
>> +  FwVolHeader =
>> +    (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)
>> +    PcdGet64 (PcdOvmfFlashNvStorageVariableBase);
>> +
>> +  Length =  FixedPcdGet32 (PcdAllVarSize);
>> +  BlockSize = PcdGet32 (PcdFlashBlockSize);
>> +
>> +  Status = ValidateFvHeader (FwVolHeader);
>> +  if (!EFI_ERROR (Status)) {
>> +    if ((FwVolHeader->FvLength != Length) ||
>> +        (FwVolHeader->BlockMap[0].Length != BlockSize))
>> +    {
>> +      Status = EFI_VOLUME_CORRUPTED;
>> +    }
>> +  }
>> +  if (EFI_ERROR (Status)) {
>> +    UINTN  Offset;
>> +    UINTN  Start;
>> +
>> +    DEBUG ((
>> +      DEBUG_INFO,
>> +      "Variable FV header is not valid. It will be reinitialized.\n"
>> +      ));
>> +
>> +    //
>> +    // Get FvbInfo to provide in FwhInstance.
>> +    //
>> +    Status = GetFvbInfo (Length, &GoodFwVolHeader);
>> +    ASSERT (!EFI_ERROR (Status));
>> +
>> +    Start = (UINTN)(UINT8*) FwVolHeader - PcdGet64 (PcdFlashFdBase);
>> +    ASSERT (Start % BlockSize == 0 && Length % BlockSize == 0);
>> +    ASSERT (GoodFwVolHeader->HeaderLength <= BlockSize);
>> +
>> +    //
>> +    // Erase all the blocks
>> +    //
>> +    for (Offset = Start; Offset < Start + Length; Offset += BlockSize) {
>> +      Status = QemuFlashEraseBlock (Offset / BlockSize);
>> +      ASSERT_EFI_ERROR (Status);
>> +    }
>> +
>> +    //
>> +    // Write good FV header
>> +    //
>> +    WriteLength = GoodFwVolHeader->HeaderLength;
>> +    Status      = QemuFlashWrite (
>> +                    Start / BlockSize,
>> +                    0,
>> +                    &WriteLength,
>> +                    (UINT8 *)GoodFwVolHeader
>> +                    );
>> +    ASSERT_EFI_ERROR (Status);
>> +    ASSERT (WriteLength == GoodFwVolHeader->HeaderLength);
>> +  }
>> +
>> +  return Status;
>> +}
>> +
>> +EFI_STATUS
>> +EFIAPI
>> +FvbInitialize (
>> +  IN EFI_HANDLE        ImageHandle,
>> +  IN EFI_SYSTEM_TABLE  *SystemTable
>> +  )
>> +/*++
>> +
>> +  Routine Description:
>> +    This function does common initialization for FVB services
>> +
>> +  Arguments:
>> +
>> +  Returns:
>> +
>> +--*/
>> +{
>> +  EFI_STATUS                  Status;
>> +  EFI_FW_VOL_INSTANCE         *FwhInstance;
>> +  EFI_FIRMWARE_VOLUME_HEADER  *FwVolHeader;
>> +  UINT32                      BufferSize;
>> +  EFI_FV_BLOCK_MAP_ENTRY      *PtrBlockMapEntry;
>> +  EFI_FW_VOL_BLOCK_DEVICE     *FvbDevice;
>> +  UINT32                      MaxLbaSize;
>> +  EFI_PHYSICAL_ADDRESS        BaseAddress;
>> +  UINTN                       Length;
>> +  UINTN                       NumOfBlocks;
>> +  RETURN_STATUS               PcdStatus;
>> +
>> +  if (EFI_ERROR (QemuFlashInitialize ())) {
>> +    //
>> +    // Return an error so image will be unloaded
>> +    //
>> +    DEBUG ((
>> +      DEBUG_INFO,
>> +      "QEMU flash was not detected. Writable FVB is not being installed.\n"
>> +      ));
>> +    return EFI_WRITE_PROTECTED;
>> +  }
>> +
>> +  //
>> +  // Allocate runtime services data for global variable, which contains
>> +  // the private data of all firmware volume block instances
>> +  //
>> +  mFvbModuleGlobal = AllocateRuntimePool (sizeof (ESAL_FWB_GLOBAL));
>> +  ASSERT (mFvbModuleGlobal != NULL);
>> +
>> +  BaseAddress = (UINTN) PcdGet64 (PcdOvmfFlashNvStorageVariableBase);
>> +  Length = PcdGet32 (PcdAllVarSize);
>> +
>> +  Status = InitializeVariableFvHeader ();
>> +  if (EFI_ERROR (Status)) {
>> +    DEBUG ((
>> +      DEBUG_INFO,
>> +      "QEMU Flash: Unable to initialize variable FV header\n"
>> +      ));
>> +    return EFI_WRITE_PROTECTED;
>> +  }
>> +
>> +  FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)BaseAddress;
>> +  Status      = ValidateFvHeader (FwVolHeader);
>> +  if (EFI_ERROR (Status)) {
>> +    //
>> +    // Get FvbInfo
>> +    //
>> +    Status = GetFvbInfo (Length, &FwVolHeader);
>> +    if (EFI_ERROR (Status)) {
>> +      DEBUG ((DEBUG_INFO, "EFI_ERROR (GetFvbInfo (Length, &FwVolHeader))\n"));
>> +      return EFI_WRITE_PROTECTED;
>> +    }
>> +  }
>> +
>> +  BufferSize = (sizeof (EFI_FW_VOL_INSTANCE) +
>> +                FwVolHeader->HeaderLength -
>> +                sizeof (EFI_FIRMWARE_VOLUME_HEADER)
>> +                );
>> +  mFvbModuleGlobal->FvInstance = AllocateRuntimePool (BufferSize);
>> +  ASSERT (mFvbModuleGlobal->FvInstance != NULL);
>> +
>> +  FwhInstance = mFvbModuleGlobal->FvInstance;
>> +
>> +  mFvbModuleGlobal->NumFv = 0;
>> +  MaxLbaSize              = 0;
>> +
>> +  FwVolHeader =
>> +    (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)
>> +    PcdGet64 (PcdOvmfFlashNvStorageVariableBase);
>> +
>> +  FwhInstance->FvBase = (UINTN)BaseAddress;
>> +
>> +  CopyMem (
>> +    (UINTN *)&(FwhInstance->VolumeHeader),
>> +    (UINTN *)FwVolHeader,
>> +    FwVolHeader->HeaderLength
>> +    );
>> +  FwVolHeader = &(FwhInstance->VolumeHeader);
>> +
>> +  NumOfBlocks = 0;
>> +
>> +  for (PtrBlockMapEntry = FwVolHeader->BlockMap;
>> +       PtrBlockMapEntry->NumBlocks != 0;
>> +       PtrBlockMapEntry++)
>> +  {
>> +    //
>> +    // Get the maximum size of a block.
>> +    //
>> +    if (MaxLbaSize < PtrBlockMapEntry->Length) {
>> +      MaxLbaSize = PtrBlockMapEntry->Length;
>> +    }
>> +
>> +    NumOfBlocks = NumOfBlocks + PtrBlockMapEntry->NumBlocks;
>> +  }
>> +
>> +  //
>> +  // The total number of blocks in the FV.
>> +  //
>> +  FwhInstance->NumOfBlocks = NumOfBlocks;
>> +
>> +  //
>> +  // Add a FVB Protocol Instance
>> +  //
>> +  FvbDevice = AllocateRuntimePool (sizeof (EFI_FW_VOL_BLOCK_DEVICE));
>> +  ASSERT (FvbDevice != NULL);
>> +
>> +  CopyMem (FvbDevice, &mFvbDeviceTemplate, sizeof (EFI_FW_VOL_BLOCK_DEVICE));
>> +
>> +  FvbDevice->Instance = mFvbModuleGlobal->NumFv;
>> +  mFvbModuleGlobal->NumFv++;
>> +
>> +  //
>> +  // Set up the devicepath
>> +  //
>> +  if (FwVolHeader->ExtHeaderOffset == 0) {
>> +    FV_MEMMAP_DEVICE_PATH  *FvMemmapDevicePath;
>> +
>> +    //
>> +    // FV does not contains extension header, then produce MEMMAP_DEVICE_PATH
>> +    //
>> +    FvMemmapDevicePath = AllocateCopyPool (
>> +                           sizeof (FV_MEMMAP_DEVICE_PATH),
>> +                           &mFvMemmapDevicePathTemplate
>> +                           );
>> +    FvMemmapDevicePath->MemMapDevPath.StartingAddress = BaseAddress;
>> +    FvMemmapDevicePath->MemMapDevPath.EndingAddress   =
>> +      BaseAddress + FwVolHeader->FvLength - 1;
>> +    FvbDevice->DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)FvMemmapDevicePath;
>> +  } else {
>> +    FV_PIWG_DEVICE_PATH  *FvPiwgDevicePath;
>> +
>> +    FvPiwgDevicePath = AllocateCopyPool (
>> +                         sizeof (FV_PIWG_DEVICE_PATH),
>> +                         &mFvPIWGDevicePathTemplate
>> +                         );
>> +    CopyGuid (
>> +      &FvPiwgDevicePath->FvDevPath.FvName,
>> +      (GUID *)(UINTN)(BaseAddress + FwVolHeader->ExtHeaderOffset)
>> +      );
>> +    FvbDevice->DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)FvPiwgDevicePath;
>> +  }
>> +
>> +  //
>> +  // Module type specific hook.
>> +  //
>> +  InstallProtocolInterfaces (FvbDevice);
>> +
>> +  MarkMemoryRangeForRuntimeAccess (BaseAddress, Length);
>> +
>> +  //
>> +  // Set several PCD values to point to flash
>> +  //
>> +
>> +
>> +  PcdStatus = PcdSet64S (
>> +    PcdFlashNvStorageVariableBase64,
>> +    (UINTN) PcdGet64 (PcdOvmfFlashNvStorageVariableBase)
>> +    );
>> +  ASSERT_RETURN_ERROR (PcdStatus);
>> +  PcdStatus = PcdSet64S (
>> +    PcdFlashNvStorageFtwWorkingBase64,
>> +    PcdGet64 (PcdOvmfFlashNvStorageFtwWorkingBase)
>> +    );
>> +  ASSERT_RETURN_ERROR (PcdStatus);
>> +  PcdStatus = PcdSet64S (
>> +    PcdFlashNvStorageFtwSpareBase64,
>> +    PcdGet64 (PcdOvmfFlashNvStorageFtwSpareBase)
>> +    );
>> +  ASSERT_RETURN_ERROR (PcdStatus);
>> +
>> +  FwhInstance = (EFI_FW_VOL_INSTANCE *)
>> +                (
>> +                 (UINTN)((UINT8 *)FwhInstance) + FwVolHeader->HeaderLength +
>> +                 (sizeof (EFI_FW_VOL_INSTANCE) - sizeof (EFI_FIRMWARE_VOLUME_HEADER))
>> +                );
>> +
>> +  //
>> +  // Module type specific hook.
>> +  //
>> +  InstallVirtualAddressChangeHandler ();
>> +
>> +  PcdStatus = PcdSetBoolS (PcdOvmfFlashVariablesEnable, TRUE);
>> +  ASSERT_RETURN_ERROR (PcdStatus);
>> +  return EFI_SUCCESS;
>> +}
>> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.h b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.h
>> new file mode 100644
>> index 0000000000..e7234a0c5e
>> --- /dev/null
>> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.h
>> @@ -0,0 +1,178 @@
>> +/** @file
>> +  Firmware volume block driver for Intel Firmware Hub (FWH) device
>> +
>> +  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +
>> +**/
>> +#ifndef _FW_BLOCK_SERVICE_H
>> +#define _FW_BLOCK_SERVICE_H
>> +
>> +typedef struct {
>> +  UINTN                         FvBase;
>> +  UINTN                         NumOfBlocks;
>> +  EFI_FIRMWARE_VOLUME_HEADER    VolumeHeader;
>> +} EFI_FW_VOL_INSTANCE;
>> +
>> +typedef struct {
>> +  UINT32                 NumFv;
>> +  EFI_FW_VOL_INSTANCE    *FvInstance;
>> +} ESAL_FWB_GLOBAL;
>> +
>> +extern ESAL_FWB_GLOBAL  *mFvbModuleGlobal;
>> +
>> +//
>> +// Fvb Protocol instance data
>> +//
>> +#define FVB_DEVICE_FROM_THIS(a)  CR (a, EFI_FW_VOL_BLOCK_DEVICE,\
>> +                                  FwVolBlockInstance, FVB_DEVICE_SIGNATURE)
>> +
>> +#define FVB_EXTEND_DEVICE_FROM_THIS(a)  CR (a, EFI_FW_VOL_BLOCK_DEVICE,\
>> +                                         FvbExtension, FVB_DEVICE_SIGNATURE)
>> +
>> +#define FVB_DEVICE_SIGNATURE  SIGNATURE_32 ('F', 'V', 'B', 'N')
>> +
>> +typedef struct {
>> +  MEDIA_FW_VOL_DEVICE_PATH    FvDevPath;
>> +  EFI_DEVICE_PATH_PROTOCOL    EndDevPath;
>> +} FV_PIWG_DEVICE_PATH;
>> +
>> +typedef struct {
>> +  MEMMAP_DEVICE_PATH          MemMapDevPath;
>> +  EFI_DEVICE_PATH_PROTOCOL    EndDevPath;
>> +} FV_MEMMAP_DEVICE_PATH;
>> +
>> +typedef struct {
>> +  UINTN                                 Signature;
>> +  EFI_DEVICE_PATH_PROTOCOL              *DevicePath;
>> +  UINTN                                 Instance;
>> +  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL    FwVolBlockInstance;
>> +} EFI_FW_VOL_BLOCK_DEVICE;
>> +
>> +EFI_STATUS
>> +GetFvbInfo (
>> +  IN  UINT64                      FvLength,
>> +  OUT EFI_FIRMWARE_VOLUME_HEADER  **FvbInfo
>> +  );
>> +
>> +EFI_STATUS
>> +FvbSetVolumeAttributes (
>> +  IN UINTN                     Instance,
>> +  IN OUT EFI_FVB_ATTRIBUTES_2  *Attributes,
>> +  IN ESAL_FWB_GLOBAL           *Global
>> +  );
>> +
>> +EFI_STATUS
>> +FvbGetVolumeAttributes (
>> +  IN UINTN                  Instance,
>> +  OUT EFI_FVB_ATTRIBUTES_2  *Attributes,
>> +  IN ESAL_FWB_GLOBAL        *Global
>> +  );
>> +
>> +EFI_STATUS
>> +FvbGetPhysicalAddress (
>> +  IN UINTN                  Instance,
>> +  OUT EFI_PHYSICAL_ADDRESS  *Address,
>> +  IN ESAL_FWB_GLOBAL        *Global
>> +  );
>> +
>> +EFI_STATUS
>> +EFIAPI
>> +FvbInitialize (
>> +  IN EFI_HANDLE        ImageHandle,
>> +  IN EFI_SYSTEM_TABLE  *SystemTable
>> +  );
>> +
>> +
>> +VOID
>> +EFIAPI
>> +FvbClassAddressChangeEvent (
>> +  IN EFI_EVENT  Event,
>> +  IN VOID       *Context
>> +  );
>> +
>> +EFI_STATUS
>> +FvbGetLbaAddress (
>> +  IN  UINTN            Instance,
>> +  IN  EFI_LBA          Lba,
>> +  OUT UINTN            *LbaAddress,
>> +  OUT UINTN            *LbaLength,
>> +  OUT UINTN            *NumOfBlocks,
>> +  IN  ESAL_FWB_GLOBAL  *Global
>> +  );
>> +
>> +//
>> +// Protocol APIs
>> +//
>> +EFI_STATUS
>> +EFIAPI
>> +FvbProtocolGetAttributes (
>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>> +  OUT EFI_FVB_ATTRIBUTES_2                     *Attributes
>> +  );
>> +
>> +EFI_STATUS
>> +EFIAPI
>> +FvbProtocolSetAttributes (
>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>> +  IN OUT EFI_FVB_ATTRIBUTES_2                  *Attributes
>> +  );
>> +
>> +EFI_STATUS
>> +EFIAPI
>> +FvbProtocolGetPhysicalAddress (
>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>> +  OUT EFI_PHYSICAL_ADDRESS                     *Address
>> +  );
>> +
>> +EFI_STATUS
>> +EFIAPI
>> +FvbProtocolGetBlockSize (
>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>> +  IN CONST EFI_LBA                             Lba,
>> +  OUT UINTN                                    *BlockSize,
>> +  OUT UINTN                                    *NumOfBlocks
>> +  );
>> +
>> +EFI_STATUS
>> +EFIAPI
>> +FvbProtocolRead (
>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>> +  IN CONST EFI_LBA                             Lba,
>> +  IN CONST UINTN                               Offset,
>> +  IN OUT UINTN                                 *NumBytes,
>> +  IN UINT8                                     *Buffer
>> +  );
>> +
>> +EFI_STATUS
>> +EFIAPI
>> +FvbProtocolWrite (
>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>> +  IN       EFI_LBA                             Lba,
>> +  IN       UINTN                               Offset,
>> +  IN OUT   UINTN                               *NumBytes,
>> +  IN       UINT8                               *Buffer
>> +  );
>> +
>> +EFI_STATUS
>> +EFIAPI
>> +FvbProtocolEraseBlocks (
>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>> +  ...
>> +  );
>> +
>> +//
>> +// The following functions have different implementations dependent on the
>> +// module type chosen for building this driver.
>> +//
>> +VOID
>> +InstallProtocolInterfaces (
>> +  IN EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice
>> +  );
>> +
>> +VOID
>> +InstallVirtualAddressChangeHandler (
>> +  VOID
>> +  );
>> +#endif
>> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockServiceDxe.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockServiceDxe.c
>> new file mode 100644
>> index 0000000000..cb85499539
>> --- /dev/null
>> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockServiceDxe.c
>> @@ -0,0 +1,152 @@
>> +/** @file
>> +  Functions related to the Firmware Volume Block service whose
>> +  implementation is specific to the runtime DXE driver build.
>> +
>> +  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +
>> +**/
>> +
>> +#include <Guid/EventGroup.h>
>> +#include <Library/DebugLib.h>
>> +#include <Library/DevicePathLib.h>
>> +#include <Library/PcdLib.h>
>> +#include <Library/UefiBootServicesTableLib.h>
>> +#include <Library/UefiRuntimeLib.h>
>> +#include <Protocol/DevicePath.h>
>> +#include <Protocol/FirmwareVolumeBlock.h>
>> +
>> +#include "FwBlockService.h"
>> +#include "QemuFlash.h"
>> +
>> +VOID
>> +InstallProtocolInterfaces (
>> +  IN EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice
>> +  )
>> +{
>> +  EFI_STATUS                          Status;
>> +  EFI_HANDLE                          FwbHandle;
>> +  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *OldFwbInterface;
>> +
>> +  //
>> +  // Find a handle with a matching device path that has supports FW Block
>> +  // protocol
>> +  //
>> +  Status = gBS->LocateDevicePath (
>> +                  &gEfiFirmwareVolumeBlockProtocolGuid,
>> +                  &FvbDevice->DevicePath,
>> +                  &FwbHandle
>> +                  );
>> +  if (EFI_ERROR (Status)) {
>> +    //
>> +    // LocateDevicePath fails so install a new interface and device path
>> +    //
>> +    FwbHandle = NULL;
>> +    DEBUG ((DEBUG_INFO, "Installing QEMU flash FVB\n"));
>> +    Status = gBS->InstallMultipleProtocolInterfaces (
>> +                    &FwbHandle,
>> +                    &gEfiFirmwareVolumeBlockProtocolGuid,
>> +                    &FvbDevice->FwVolBlockInstance,
>> +                    &gEfiDevicePathProtocolGuid,
>> +                    FvbDevice->DevicePath,
>> +                    NULL
>> +                    );
>> +    ASSERT_EFI_ERROR (Status);
>> +  } else if (IsDevicePathEnd (FvbDevice->DevicePath)) {
>> +    //
>> +    // Device already exists, so reinstall the FVB protocol
>> +    //
>> +    Status = gBS->HandleProtocol (
>> +                    FwbHandle,
>> +                    &gEfiFirmwareVolumeBlockProtocolGuid,
>> +                    (VOID **)&OldFwbInterface
>> +                    );
>> +    ASSERT_EFI_ERROR (Status);
>> +
>> +    DEBUG ((DEBUG_INFO, "Reinstalling FVB for QEMU flash region\n"));
>> +    Status = gBS->ReinstallProtocolInterface (
>> +                    FwbHandle,
>> +                    &gEfiFirmwareVolumeBlockProtocolGuid,
>> +                    OldFwbInterface,
>> +                    &FvbDevice->FwVolBlockInstance
>> +                    );
>> +    ASSERT_EFI_ERROR (Status);
>> +  } else {
>> +    //
>> +    // There was a FVB protocol on an End Device Path node
>> +    //
>> +    ASSERT (FALSE);
>> +  }
>> +}
>> +
>> +
>> +STATIC
>> +VOID
>> +EFIAPI
>> +FvbVirtualAddressChangeEvent (
>> +  IN EFI_EVENT  Event,
>> +  IN VOID       *Context
>> +  )
>> +/*++
>> +
>> +  Routine Description:
>> +
>> +    Fixup internal data so that EFI and SAL can be call in virtual mode.
>> +    Call the passed in Child Notify event and convert the mFvbModuleGlobal
>> +    date items to there virtual address.
>> +
>> +  Arguments:
>> +
>> +    (Standard EFI notify event - EFI_EVENT_NOTIFY)
>> +
>> +  Returns:
>> +
>> +    None
>> +
>> +--*/
>> +{
>> +  EFI_FW_VOL_INSTANCE  *FwhInstance;
>> +  UINTN                Index;
>> +
>> +  FwhInstance = mFvbModuleGlobal->FvInstance;
>> +  EfiConvertPointer (0x0, (VOID **)&mFvbModuleGlobal->FvInstance);
>> +
>> +  //
>> +  // Convert the base address of all the instances
>> +  //
>> +  Index = 0;
>> +  while (Index < mFvbModuleGlobal->NumFv) {
>> +    EfiConvertPointer (0x0, (VOID **)&FwhInstance->FvBase);
>> +    FwhInstance = (EFI_FW_VOL_INSTANCE *)
>> +                  (
>> +                   (UINTN)((UINT8 *)FwhInstance) +
>> +                   FwhInstance->VolumeHeader.HeaderLength +
>> +                   (sizeof (EFI_FW_VOL_INSTANCE) - sizeof (EFI_FIRMWARE_VOLUME_HEADER))
>> +                  );
>> +    Index++;
>> +  }
>> +
>> +  EfiConvertPointer (0x0, (VOID **)&mFvbModuleGlobal);
>> +  QemuFlashConvertPointers ();
>> +}
>> +
>> +
>> +VOID
>> +InstallVirtualAddressChangeHandler (
>> +  VOID
>> +  )
>> +{
>> +  EFI_STATUS  Status;
>> +  EFI_EVENT   VirtualAddressChangeEvent;
>> +
>> +  Status = gBS->CreateEventEx (
>> +                  EVT_NOTIFY_SIGNAL,
>> +                  TPL_NOTIFY,
>> +                  FvbVirtualAddressChangeEvent,
>> +                  NULL,
>> +                  &gEfiEventVirtualAddressChangeGuid,
>> +                  &VirtualAddressChangeEvent
>> +                  );
>> +  ASSERT_EFI_ERROR (Status);
>> +}
>> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c
>> new file mode 100644
>> index 0000000000..a85d736060
>> --- /dev/null
>> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c
>> @@ -0,0 +1,251 @@
>> +/** @file
>> +  LoongArch support for QEMU system firmware flash device
>> +
>> +  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +
>> +**/
>> +
>> +#include <Library/BaseMemoryLib.h>
>> +#include <Library/DebugLib.h>
>> +#include <Library/PcdLib.h>
>> +
>> +#include "QemuFlash.h"
>> +
>> +#define WRITE_BYTE_CMD           0x10
>> +#define BLOCK_ERASE_CMD          0x20
>> +#define CLEAR_STATUS_CMD         0x50
>> +#define READ_STATUS_CMD          0x70
>> +#define READ_DEVID_CMD           0x90
>> +#define BLOCK_ERASE_CONFIRM_CMD  0xd0
>> +#define READ_ARRAY_CMD           0xff
>> +
>> +#define CLEARED_ARRAY_STATUS  0x00
>> +
>> +UINT8  *mFlashBase;
>> +
>> +STATIC UINTN  mFdBlockSize  = 0;
>> +STATIC UINTN  mFdBlockCount = 0;
>> +
>> +STATIC
>> +volatile UINT8 *
>> +QemuFlashPtr (
>> +  IN        EFI_LBA  Lba,
>> +  IN        UINTN    Offset
>> +  )
>> +{
>> +  return mFlashBase + ((UINTN)Lba * mFdBlockSize) + Offset;
>> +}
>> +
>> +
>> +/**
>> +  Determines if the QEMU flash memory device is present.
>> +
>> +  @retval FALSE   The QEMU flash device is not present.
>> +  @retval TRUE    The QEMU flash device is present.
>> +
>> +**/
>> +STATIC
>> +BOOLEAN
>> +QemuFlashDetected (
>> +  VOID
>> +  )
>> +{
>> +  BOOLEAN         FlashDetected;
>> +  volatile UINT8  *Ptr;
>> +
>> +  UINTN  Offset;
>> +  UINT8  OriginalUint8;
>> +  UINT8  ProbeUint8;
>> +
>> +  FlashDetected = FALSE;
>> +  Ptr           = QemuFlashPtr (0, 0);
>> +
>> +  for (Offset = 0; Offset < mFdBlockSize; Offset++) {
>> +    Ptr        = QemuFlashPtr (0, Offset);
>> +    ProbeUint8 = *Ptr;
>> +    if ((ProbeUint8 != CLEAR_STATUS_CMD) &&
>> +        (ProbeUint8 != READ_STATUS_CMD) &&
>> +        (ProbeUint8 != CLEARED_ARRAY_STATUS))
>> +    {
>> +      break;
>> +    }
>> +  }
>> +
>> +  if (Offset >= mFdBlockSize) {
>> +    DEBUG ((DEBUG_INFO, "QEMU Flash: Failed to find probe location\n"));
>> +    return FALSE;
>> +  }
>> +
>> +  DEBUG ((DEBUG_INFO, "QEMU Flash: Attempting flash detection at %p\n", Ptr));
>> +
>> +  OriginalUint8 = *Ptr;
>> +  *Ptr          = CLEAR_STATUS_CMD;
>> +  ProbeUint8    = *Ptr;
>> +  if ((OriginalUint8 != CLEAR_STATUS_CMD) &&
>> +      (ProbeUint8 == CLEAR_STATUS_CMD))
>> +  {
>> +    DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as RAM\n"));
>> +    *Ptr = OriginalUint8;
>> +  } else {
>> +    *Ptr       = READ_STATUS_CMD;
>> +    ProbeUint8 = *Ptr;
>> +    if (ProbeUint8 == OriginalUint8) {
>> +      DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as ROM\n"));
>> +    } else if (ProbeUint8 == READ_STATUS_CMD) {
>> +      DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as RAM\n"));
>> +      *Ptr = OriginalUint8;
>> +    } else if (ProbeUint8 == CLEARED_ARRAY_STATUS) {
>> +      DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as FLASH\n"));
>> +      FlashDetected = TRUE;
>> +      *Ptr          = READ_ARRAY_CMD;
>> +    }
>> +  }
>> +
>> +  DEBUG ((
>> +    DEBUG_INFO,
>> +    "QemuFlashDetected => %a\n",
>> +    FlashDetected ? "Yes" : "No"
>> +    ));
>> +  return FlashDetected;
>> +}
>> +
>> +
>> +/**
>> +  Read from QEMU Flash
>> +
>> +  @param[in] Lba      The starting logical block index to read from.
>> +  @param[in] Offset   Offset into the block at which to begin reading.
>> +  @param[in] NumBytes On input, indicates the requested read size. On
>> +                      output, indicates the actual number of bytes read
>> +  @param[in] Buffer   Pointer to the buffer to read into.
>> +
>> +**/
>> +EFI_STATUS
>> +QemuFlashRead (
>> +  IN        EFI_LBA  Lba,
>> +  IN        UINTN    Offset,
>> +  IN        UINTN    *NumBytes,
>> +  IN        UINT8    *Buffer
>> +  )
>> +{
>> +  UINT8  *Ptr;
>> +
>> +  //
>> +  // Only write to the first 64k. We don't bother saving the FTW Spare
>> +  // block into the flash memory.
>> +  //
>> +  if (Lba >= mFdBlockCount) {
>> +    return EFI_INVALID_PARAMETER;
>> +  }
>> +
>> +  //
>> +  // Get flash address
>> +  //
>> +  Ptr = (UINT8 *)QemuFlashPtr (Lba, Offset);
>> +
>> +  CopyMem (Buffer, Ptr, *NumBytes);
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +
>> +/**
>> +  Write to QEMU Flash
>> +
>> +  @param[in] Lba      The starting logical block index to write to.
>> +  @param[in] Offset   Offset into the block at which to begin writing.
>> +  @param[in] NumBytes On input, indicates the requested write size. On
>> +                      output, indicates the actual number of bytes written
>> +  @param[in] Buffer   Pointer to the data to write.
>> +
>> +**/
>> +EFI_STATUS
>> +QemuFlashWrite (
>> +  IN        EFI_LBA  Lba,
>> +  IN        UINTN    Offset,
>> +  IN        UINTN    *NumBytes,
>> +  IN        UINT8    *Buffer
>> +  )
>> +{
>> +  volatile UINT8  *Ptr;
>> +  UINTN           Loop;
>> +
>> +  //
>> +  // Only write to the first 64k. We don't bother saving the FTW Spare
>> +  // block into the flash memory.
>> +  //
>> +  if (Lba >= mFdBlockCount) {
>> +    return EFI_INVALID_PARAMETER;
>> +  }
>> +
>> +  //
>> +  // Program flash
>> +  //
>> +  Ptr = QemuFlashPtr (Lba, Offset);
>> +  for (Loop = 0; Loop < *NumBytes; Loop++) {
>> +    *Ptr = WRITE_BYTE_CMD;
>> +    *Ptr = Buffer[Loop];
>> +    Ptr++;
>> +  }
>> +
>> +  //
>> +  // Restore flash to read mode
>> +  //
>> +  if (*NumBytes > 0) {
>> +    *(Ptr - 1) = READ_ARRAY_CMD;
>> +  }
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +
>> +/**
>> +  Erase a QEMU Flash block
>> +
>> +  @param Lba    The logical block index to erase.
>> +
>> +**/
>> +EFI_STATUS
>> +QemuFlashEraseBlock (
>> +  IN   EFI_LBA  Lba
>> +  )
>> +{
>> +  volatile UINT8  *Ptr;
>> +
>> +  if (Lba >= mFdBlockCount) {
>> +    return EFI_INVALID_PARAMETER;
>> +  }
>> +
>> +  Ptr = QemuFlashPtr (Lba, 0);
>> +  *Ptr = BLOCK_ERASE_CMD;
>> +  *Ptr = BLOCK_ERASE_CONFIRM_CMD;
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +
>> +/**
>> +  Initializes QEMU flash memory support
>> +
>> +  @retval EFI_WRITE_PROTECTED   The QEMU flash device is not present.
>> +  @retval EFI_SUCCESS           The QEMU flash device is supported.
>> +
>> +**/
>> +EFI_STATUS
>> +QemuFlashInitialize (
>> +  VOID
>> +  )
>> +{
>> +  mFlashBase   = (UINT8 *)(UINTN)PcdGet64 (PcdOvmfFlashNvStorageVariableBase);
>> +  mFdBlockSize = PcdGet32 (PcdFlashBlockSize);
>> +  ASSERT(PcdGet32 (PcdAllVarSize) % mFdBlockSize == 0);
>> +  mFdBlockCount = PcdGet32 (PcdAllVarSize) / mFdBlockSize;
>> +
>> +  if (!QemuFlashDetected ()) {
>> +    return EFI_WRITE_PROTECTED;
>> +  }
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h
>> new file mode 100644
>> index 0000000000..27caabc5f0
>> --- /dev/null
>> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h
>> @@ -0,0 +1,86 @@
>> +/** @file
>> +  LoongArch support for QEMU system firmware flash device
>> +
>> +  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +
>> +**/
>> +
>> +#ifndef __QEMU_FLASH_H__
>> +#define __QEMU_FLASH_H__
>> +
>> +#include <Protocol/FirmwareVolumeBlock.h>
>> +
>> +extern UINT8  *mFlashBase;
>> +
>> +/**
>> +  Read from QEMU Flash
>> +
>> +  @param[in] Lba      The starting logical block index to read from.
>> +  @param[in] Offset   Offset into the block at which to begin reading.
>> +  @param[in] NumBytes On input, indicates the requested read size. On
>> +                      output, indicates the actual number of bytes read
>> +  @param[in] Buffer   Pointer to the buffer to read into.
>> +
>> +**/
>> +EFI_STATUS
>> +QemuFlashRead (
>> +  IN        EFI_LBA  Lba,
>> +  IN        UINTN    Offset,
>> +  IN        UINTN    *NumBytes,
>> +  IN        UINT8    *Buffer
>> +  );
>> +
>> +
>> +/**
>> +  Write to QEMU Flash
>> +
>> +  @param[in] Lba      The starting logical block index to write to.
>> +  @param[in] Offset   Offset into the block at which to begin writing.
>> +  @param[in] NumBytes On input, indicates the requested write size. On
>> +                      output, indicates the actual number of bytes written
>> +  @param[in] Buffer   Pointer to the data to write.
>> +
>> +**/
>> +EFI_STATUS
>> +QemuFlashWrite (
>> +  IN        EFI_LBA  Lba,
>> +  IN        UINTN    Offset,
>> +  IN        UINTN    *NumBytes,
>> +  IN        UINT8    *Buffer
>> +  );
>> +
>> +
>> +/**
>> +  Erase a QEMU Flash block
>> +
>> +  @param Lba    The logical block index to erase.
>> +
>> +**/
>> +EFI_STATUS
>> +QemuFlashEraseBlock (
>> +  IN   EFI_LBA  Lba
>> +  );
>> +
>> +
>> +/**
>> +  Initializes QEMU flash memory support
>> +
>> +  @retval EFI_WRITE_PROTECTED   The QEMU flash device is not present.
>> +  @retval EFI_SUCCESS           The QEMU flash device is supported.
>> +
>> +**/
>> +EFI_STATUS
>> +QemuFlashInitialize (
>> +  VOID
>> +  );
>> +
>> +
>> +VOID
>> +QemuFlashConvertPointers (
>> +  VOID
>> +  );
>> +
>> +#endif
>> +
>> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c
>> new file mode 100644
>> index 0000000000..b63314aac2
>> --- /dev/null
>> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c
>> @@ -0,0 +1,21 @@
>> +/** @file
>> +  LoongArch support for QEMU system firmware flash device: functions specific to the
>> +  runtime DXE driver build.
>> +
>> +  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +
>> +**/
>> +
>> +#include <Library/UefiRuntimeLib.h>
>> +
>> +#include "QemuFlash.h"
>> +
>> +VOID
>> +QemuFlashConvertPointers (
>> +  VOID
>> +  )
>> +{
>> +  EfiConvertPointer (0x0, (VOID **)&mFlashBase);
>> +}
>> diff --git a/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc b/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc
>> index 74c83720b7..59beafb34f 100644
>> --- a/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc
>> +++ b/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc
>> @@ -171,10 +171,10 @@
>>     VirtioLib                        | OvmfPkg/Library/VirtioLib/VirtioLib.inf
>>     FrameBufferBltLib                | MdeModulePkg/Library/FrameBufferBltLib/FrameBufferBltLib.inf
>>     QemuFwCfgLib                     | OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibMmio.inf
>> -
>>     DebugLib                         | MdePkg/Library/BaseDebugLibSerialPort/BaseDebugLibSerialPort.inf
>> -
>>     PeiServicesLib                   | MdePkg/Library/PeiServicesLib/PeiServicesLib.inf
>> +  VariableFlashInfoLib             | MdeModulePkg/Library/BaseVariableFlashInfoLib/BaseVariableFlashInfoLib.inf
>> +
>>   [LibraryClasses.common.SEC]
>>     ReportStatusCodeLib              | MdeModulePkg/Library/PeiReportStatusCodeLib/PeiReportStatusCodeLib.inf
>>     HobLib                           | MdePkg/Library/PeiHobLib/PeiHobLib.inf
>> --
>> 2.31.1
>>


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [edk2-platforms][PATCH V3 15/16] Platform/Loongson: Add QemuFlashFvbServicesRuntimeDxe driver.
  2022-10-17 12:56     ` xianglai
@ 2022-10-18  8:47       ` xianglai
  0 siblings, 0 replies; 21+ messages in thread
From: xianglai @ 2022-10-18  8:47 UTC (permalink / raw)
  To: Ard Biesheuvel
  Cc: devel, quic_llindhol, michael.d.kinney, kraxel, maobibo,
	michael.d.kinney, kraxel, maobibo

Hi Ard Biesheuvel :

   We have researched a bit, the flash implementation of loongson may be 
a bit unreasonable,

the corresponding flash implementation of loongson in qemu has not been 
submitted to the community,

its final solution has not been determined,

we want to wait for the solution to be determined before re-implementing 
the flash-related drivers,

I believe that by then NorFlashDxe has been ported into the OvmfPkg/.

So I will remove the flash-related patch in the next version.


Thanks

xianglai




On 2022/10/17 下午8:56, xianglaili wrote:
> Hi Ard Biesheuvel :
>
> In the function FvbInitialize, It validate the firmware volume 
> header.The firmware volume header address is PcdOvmfFdBaseAddress,
>
> in Ovmf PcdOvmfFdBaseAddress is equal to 
> PcdOvmfFlashNvStorageVariableBase, so the checksum can pass.
> When loongson is implemented, PcdOvmfFdBaseAddress and 
> PcdOvmfFlashNvStorageVariableBase are not equal,
>
> the firmware checksum will fail and enter the exception branch.
> In the exception branch, due to the existence of reserved space in the 
> layout of loongson's NORflash,
>
> the exception branch will also fail to pass, which will eventually 
> lead to function call failure.
>
>
> ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
>
>
> EFI_STATUS
> EFIAPI
> FvbInitialize (
>   IN EFI_HANDLE        ImageHandle,
>   IN EFI_SYSTEM_TABLE  *SystemTable
>   )
>
> {
>
> ....
>
> BaseAddress = (UINTN)PcdGet32 (PcdOvmfFdBaseAddress);
>
> ....
>
> FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)BaseAddress;
>
>   Status      = ValidateFvHeader (FwVolHeader);
>   if (EFI_ERROR (Status)) {
>     //
>     // Get FvbInfo
>     //
>     Status = GetFvbInfo (Length, &FwVolHeader);
>     if (EFI_ERROR (Status)) {
>       DEBUG ((DEBUG_INFO, "EFI_ERROR (GetFvbInfo (Length, 
> &FwVolHeader))\n"));
>       return EFI_WRITE_PROTECTED;
>     }
>   }
>
> ....
>
> }
>
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
>
>
>
> But I think the reserved space in NORflash is unnecessary,
>
> maybe I can remove the reserved space in Norflash and go through the 
> exception branch to pass the firmware volume  header checksum,
>
> then I can just  Using the flash library implementation in Ovmf.
>
> I can try it like this.
>
>
> Thanks
>
> xianglai
>
>
>
> On 2022/10/17 下午7:00, Ard Biesheuvel wrote:
>> On Fri, 14 Oct 2022 at 06:01, xianglai li <lixianglai@loongson.cn> 
>> wrote:
>>> This library provides flash read and write functionality
>>> and supports writing variables to flash.
>>>
>>> REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4054
>>>
>>> Signed-off-by: xianglai li <lixianglai@loongson.cn>
>> Why do you need a new driver for this? We already have drivers to
>> support QEMU's NOR flash emulation, and the NorFlashDxe driver is
>> being migrated to OvmfPkg/ in a series that is on the list now for
>> RISC-V
>>
>>> ---
>>>   .../QemuFlashFvbServicesRuntimeDxe/FvbInfo.c  |  115 ++
>>>   .../FvbServicesRuntimeDxe.inf                 |   73 ++
>>>   .../FwBlockService.c                          | 1158 
>>> +++++++++++++++++
>>>   .../FwBlockService.h                          |  178 +++
>>>   .../FwBlockServiceDxe.c                       |  152 +++
>>>   .../QemuFlash.c                               |  251 ++++
>>>   .../QemuFlash.h                               |   86 ++
>>>   .../QemuFlashDxe.c                            |   21 +
>>>   .../Loongson/LoongArchQemuPkg/Loongson.dsc    |    4 +-
>>>   9 files changed, 2036 insertions(+), 2 deletions(-)
>>>   create mode 100644 
>>> Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbInfo.c
>>>   create mode 100644 
>>> Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
>>>   create mode 100644 
>>> Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
>>>   create mode 100644 
>>> Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.h
>>>   create mode 100644 
>>> Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockServiceDxe.c
>>>   create mode 100644 
>>> Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c
>>>   create mode 100644 
>>> Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h
>>>   create mode 100644 
>>> Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c
>>>
>>> diff --git 
>>> a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbInfo.c 
>>> b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbInfo.c 
>>>
>>> new file mode 100644
>>> index 0000000000..df772f72be
>>> --- /dev/null
>>> +++ 
>>> b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbInfo.c
>>> @@ -0,0 +1,115 @@
>>> +/** @file
>>> +  Defines data structure that is the volume header found.These data 
>>> is intent
>>> +  to decouple FVB driver with FV header.
>>> +
>>> +  Copyright (c) 2021 Loongson Technology Corporation Limited. All 
>>> rights reserved.<BR>
>>> +
>>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>>> +
>>> +**/
>>> +
>>> +//
>>> +// The package level header files this module uses
>>> +//
>>> +#include <Pi/PiFirmwareVolume.h>
>>> +
>>> +//
>>> +// The protocols, PPI and GUID definitions for this module
>>> +//
>>> +#include <Guid/SystemNvDataGuid.h>
>>> +//
>>> +// The Library classes this module consumes
>>> +//
>>> +#include <Library/BaseLib.h>
>>> +#include <Library/PcdLib.h>
>>> +
>>> +typedef struct {
>>> +  UINT64                        FvLength;
>>> +  EFI_FIRMWARE_VOLUME_HEADER    FvbInfo;
>>> +  //
>>> +  // EFI_FV_BLOCK_MAP_ENTRY    ExtraBlockMap[n];//n=0
>>> +  //
>>> +  EFI_FV_BLOCK_MAP_ENTRY        End[1];
>>> +} EFI_FVB_MEDIA_INFO;
>>> +
>>> +EFI_FVB_MEDIA_INFO  mPlatformFvbMediaInfo[] = {
>>> +  //
>>> +  // System NvStorage FVB
>>> +  //
>>> +  {
>>> +    FixedPcdGet32 (PcdAllVarSize),
>>> +    {
>>> +      {
>>> +        0,
>>> +      },  // ZeroVector[16]
>>> +      EFI_SYSTEM_NV_DATA_FV_GUID,
>>> +      FixedPcdGet32 (PcdAllVarSize),
>>> +      EFI_FVH_SIGNATURE,
>>> +      EFI_FVB2_MEMORY_MAPPED |
>>> +      EFI_FVB2_READ_ENABLED_CAP |
>>> +      EFI_FVB2_READ_STATUS |
>>> +      EFI_FVB2_WRITE_ENABLED_CAP |
>>> +      EFI_FVB2_WRITE_STATUS |
>>> +      EFI_FVB2_ERASE_POLARITY |
>>> +      EFI_FVB2_ALIGNMENT_16,
>>> +      sizeof (EFI_FIRMWARE_VOLUME_HEADER) + sizeof 
>>> (EFI_FV_BLOCK_MAP_ENTRY),
>>> +      0,  // CheckSum
>>> +      0,  // ExtHeaderOffset
>>> +      {
>>> +        0,
>>> +      },  // Reserved[1]
>>> +      2,  // Revision
>>> +      {
>>> +        {
>>> +          (FixedPcdGet32 (PcdAllVarSize))/
>>> +          FixedPcdGet32 (PcdFlashBlockSize),
>>> +          FixedPcdGet32 (PcdFlashBlockSize),
>>> +        }
>>> +      } // BlockMap[1]
>>> +    },
>>> +    {
>>> +      {
>>> +        0,
>>> +        0
>>> +      }
>>> +    }  // End[1]
>>> +  }
>>> +};
>>> +
>>> +EFI_STATUS
>>> +GetFvbInfo (
>>> +  IN  UINT64                      FvLength,
>>> +  OUT EFI_FIRMWARE_VOLUME_HEADER  **FvbInfo
>>> +  )
>>> +{
>>> +  STATIC BOOLEAN  Checksummed = FALSE;
>>> +  UINTN           Index;
>>> +
>>> +  if (!Checksummed) {
>>> +    for (Index = 0;
>>> +         Index < sizeof (mPlatformFvbMediaInfo) / sizeof 
>>> (EFI_FVB_MEDIA_INFO);
>>> +         Index += 1)
>>> +    {
>>> +      UINT16  Checksum;
>>> +      mPlatformFvbMediaInfo[Index].FvbInfo.Checksum = 0;
>>> +      Checksum                                      = 
>>> CalculateCheckSum16 (
>>> + (UINT16 *)&mPlatformFvbMediaInfo[Index].FvbInfo,
>>> + mPlatformFvbMediaInfo[Index].FvbInfo.HeaderLength
>>> +                                                        );
>>> +      mPlatformFvbMediaInfo[Index].FvbInfo.Checksum = Checksum;
>>> +    }
>>> +    Checksummed = TRUE;
>>> +  }
>>> +
>>> +  for (Index = 0;
>>> +       Index < sizeof (mPlatformFvbMediaInfo) / sizeof 
>>> (EFI_FVB_MEDIA_INFO);
>>> +       Index += 1)
>>> +  {
>>> +    if (mPlatformFvbMediaInfo[Index].FvLength == FvLength) {
>>> +      *FvbInfo = &mPlatformFvbMediaInfo[Index].FvbInfo;
>>> +      return EFI_SUCCESS;
>>> +    }
>>> +  }
>>> +
>>> +  return EFI_NOT_FOUND;
>>> +}
>>> diff --git 
>>> a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf 
>>> b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf 
>>>
>>> new file mode 100644
>>> index 0000000000..4b0c42b075
>>> --- /dev/null
>>> +++ 
>>> b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
>>> @@ -0,0 +1,73 @@
>>> +## @file
>>> +# Component description file for Emu Fimware Volume Block DXE 
>>> driver module.
>>> +#
>>> +#  Copyright (c) 2021 Loongson Technology Corporation Limited. All 
>>> rights reserved.<BR>
>>> +#
>>> +#  SPDX-License-Identifier: BSD-2-Clause-Patent
>>> +#
>>> +##
>>> +
>>> +
>>> +[Defines]
>>> +  INF_VERSION                    = 0x00010005
>>> +  BASE_NAME                      = FvbServicesRuntimeDxe
>>> +  FILE_GUID                      = 
>>> 733cbac2-b23f-4b92-bc8e-fb01ce5907b7
>>> +  MODULE_TYPE                    = DXE_RUNTIME_DRIVER
>>> +  VERSION_STRING                 = 1.0
>>> +  ENTRY_POINT                    = FvbInitialize
>>> +
>>> +#
>>> +# The following information is for reference only and not required 
>>> by the build
>>> +# tools.
>>> +#
>>> +#  VALID_ARCHITECTURES           = IA32 X64
>>> +#
>>> +
>>> +[Sources]
>>> +  FvbInfo.c
>>> +  FwBlockService.c
>>> +  FwBlockServiceDxe.c
>>> +  QemuFlash.c
>>> +  QemuFlashDxe.c
>>> +
>>> +[Packages]
>>> +  MdePkg/MdePkg.dec
>>> +  MdeModulePkg/MdeModulePkg.dec
>>> +  OvmfPkg/OvmfPkg.dec
>>> +  Platform/Loongson/LoongArchQemuPkg/Loongson.dec
>>> +
>>> +[LibraryClasses]
>>> +  BaseLib
>>> +  BaseMemoryLib
>>> +  DebugLib
>>> +  DevicePathLib
>>> +  DxeServicesTableLib
>>> +  MemoryAllocationLib
>>> +  PcdLib
>>> +  UefiBootServicesTableLib
>>> +  UefiDriverEntryPoint
>>> +  UefiRuntimeLib
>>> +
>>> +[Guids]
>>> +  gEfiEventVirtualAddressChangeGuid   # ALWAYS_CONSUMED
>>> +  # gEfiEventVirtualAddressChangeGuid # Create Event: EVENT_GROUP_GUID
>>> +
>>> +[Protocols]
>>> +  gEfiFirmwareVolumeBlockProtocolGuid           # PROTOCOL 
>>> SOMETIMES_PRODUCED
>>> +  gEfiDevicePathProtocolGuid                    # PROTOCOL 
>>> SOMETIMES_PRODUCED
>>> +
>>> +[FixedPcd]
>>> + gLoongArchQemuPkgTokenSpaceGuid.PcdOvmfFlashNvStorageVariableBase
>>> + gLoongArchQemuPkgTokenSpaceGuid.PcdOvmfFlashNvStorageFtwWorkingBase
>>> + gLoongArchQemuPkgTokenSpaceGuid.PcdOvmfFlashNvStorageFtwSpareBase
>>> +  gLoongArchQemuPkgTokenSpaceGuid.PcdFlashFdBase
>>> +  gLoongArchQemuPkgTokenSpaceGuid.PcdAllVarSize
>>> +  gLoongArchQemuPkgTokenSpaceGuid.PcdFlashBlockSize
>>> +[Pcd]
>>> + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase64
>>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashVariablesEnable
>>> + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase64
>>> + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase64
>>> +
>>> +[Depex]
>>> +  TRUE
>>> diff --git 
>>> a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c 
>>> b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c 
>>>
>>> new file mode 100644
>>> index 0000000000..d44f2b460c
>>> --- /dev/null
>>> +++ 
>>> b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
>>> @@ -0,0 +1,1158 @@
>>> +/** @file
>>> +  Implementations for Firmware Volume Block protocol.
>>> +
>>> +  Copyright (c) 2021 Loongson Technology Corporation Limited. All 
>>> rights reserved.<BR>
>>> +
>>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>>> +
>>> +**/
>>> +
>>> +//
>>> +// The protocols, PPI and GUID definitions for this module
>>> +//
>>> +#include <Protocol/DevicePath.h>
>>> +#include <Protocol/FirmwareVolumeBlock.h>
>>> +
>>> +//
>>> +// The Library classes this module consumes
>>> +//
>>> +#include <Library/BaseLib.h>
>>> +#include <Library/BaseMemoryLib.h>
>>> +#include <Library/DebugLib.h>
>>> +#include <Library/DevicePathLib.h>
>>> +#include <Library/DxeServicesTableLib.h>
>>> +#include <Library/MemoryAllocationLib.h>
>>> +#include <Library/UefiBootServicesTableLib.h>
>>> +
>>> +#include "FwBlockService.h"
>>> +#include "QemuFlash.h"
>>> +
>>> +#define EFI_FVB2_STATUS \
>>> +          (EFI_FVB2_READ_STATUS | EFI_FVB2_WRITE_STATUS | 
>>> EFI_FVB2_LOCK_STATUS)
>>> +
>>> +ESAL_FWB_GLOBAL  *mFvbModuleGlobal;
>>> +
>>> +FV_MEMMAP_DEVICE_PATH  mFvMemmapDevicePathTemplate = {
>>> +  {
>>> +    {
>>> +      HARDWARE_DEVICE_PATH,
>>> +      HW_MEMMAP_DP,
>>> +      {
>>> +        (UINT8)(sizeof (MEMMAP_DEVICE_PATH)),
>>> +        (UINT8)(sizeof (MEMMAP_DEVICE_PATH) >> 8)
>>> +      }
>>> +    },
>>> +    EfiMemoryMappedIO,
>>> +    (EFI_PHYSICAL_ADDRESS)0,
>>> +    (EFI_PHYSICAL_ADDRESS)0,
>>> +  },
>>> +  {
>>> +    END_DEVICE_PATH_TYPE,
>>> +    END_ENTIRE_DEVICE_PATH_SUBTYPE,
>>> +    {
>>> +      END_DEVICE_PATH_LENGTH,
>>> +      0
>>> +    }
>>> +  }
>>> +};
>>> +
>>> +FV_PIWG_DEVICE_PATH  mFvPIWGDevicePathTemplate = {
>>> +  {
>>> +    {
>>> +      MEDIA_DEVICE_PATH,
>>> +      MEDIA_PIWG_FW_VOL_DP,
>>> +      {
>>> +        (UINT8)(sizeof (MEDIA_FW_VOL_DEVICE_PATH)),
>>> +        (UINT8)(sizeof (MEDIA_FW_VOL_DEVICE_PATH) >> 8)
>>> +      }
>>> +    },
>>> +    { 0 }
>>> +  },
>>> +  {
>>> +    END_DEVICE_PATH_TYPE,
>>> +    END_ENTIRE_DEVICE_PATH_SUBTYPE,
>>> +    {
>>> +      END_DEVICE_PATH_LENGTH,
>>> +      0
>>> +    }
>>> +  }
>>> +};
>>> +
>>> +EFI_FW_VOL_BLOCK_DEVICE  mFvbDeviceTemplate = {
>>> +  FVB_DEVICE_SIGNATURE,
>>> +  NULL,
>>> +  0,
>>> +  {
>>> +    FvbProtocolGetAttributes,
>>> +    FvbProtocolSetAttributes,
>>> +    FvbProtocolGetPhysicalAddress,
>>> +    FvbProtocolGetBlockSize,
>>> +    FvbProtocolRead,
>>> +    FvbProtocolWrite,
>>> +    FvbProtocolEraseBlocks,
>>> +    NULL
>>> +  }
>>> +};
>>> +
>>> +
>>> +EFI_STATUS
>>> +GetFvbInstance (
>>> +  IN  UINTN                Instance,
>>> +  IN  ESAL_FWB_GLOBAL      *Global,
>>> +  OUT EFI_FW_VOL_INSTANCE  **FwhInstance
>>> +  )
>>> +/*++
>>> +
>>> +  Routine Description:
>>> +    Retrieves the physical address of a memory mapped FV
>>> +
>>> +  Arguments:
>>> +    Instance              - The FV instance whose base address is 
>>> going to be
>>> +                            returned
>>> +    Global                - Pointer to ESAL_FWB_GLOBAL that 
>>> contains all
>>> +                            instance data
>>> +    FwhInstance           - The EFI_FW_VOL_INSTANCE firmware 
>>> instance structure
>>> +
>>> +  Returns:
>>> +    EFI_SUCCESS           - Successfully returns
>>> +    EFI_INVALID_PARAMETER - Instance not found
>>> +
>>> +--*/
>>> +{
>>> +  EFI_FW_VOL_INSTANCE  *FwhRecord;
>>> +
>>> +  *FwhInstance = NULL;
>>> +  if (Instance >= Global->NumFv) {
>>> +    return EFI_INVALID_PARAMETER;
>>> +  }
>>> +  //
>>> +  // Find the right instance of the FVB private data
>>> +  //
>>> +  FwhRecord = Global->FvInstance;
>>> +  while (Instance > 0) {
>>> +    FwhRecord = (EFI_FW_VOL_INSTANCE *)
>>> +                (
>>> +                 (UINTN)((UINT8 *)FwhRecord) + 
>>> FwhRecord->VolumeHeader.HeaderLength +
>>> +                 (sizeof (EFI_FW_VOL_INSTANCE) - sizeof 
>>> (EFI_FIRMWARE_VOLUME_HEADER))
>>> +                );
>>> +    Instance--;
>>> +  }
>>> +
>>> +  *FwhInstance = FwhRecord;
>>> +
>>> +  return EFI_SUCCESS;
>>> +}
>>> +
>>> +EFI_STATUS
>>> +FvbGetPhysicalAddress (
>>> +  IN UINTN                  Instance,
>>> +  OUT EFI_PHYSICAL_ADDRESS  *Address,
>>> +  IN ESAL_FWB_GLOBAL        *Global
>>> +  )
>>> +/*++
>>> +
>>> +  Routine Description:
>>> +    Retrieves the physical address of a memory mapped FV
>>> +
>>> +  Arguments:
>>> +    Instance              - The FV instance whose base address is 
>>> going to be
>>> +                            returned
>>> +    Address               - Pointer to a caller allocated 
>>> EFI_PHYSICAL_ADDRESS
>>> +                            that on successful return, contains the 
>>> base
>>> +                            address of the firmware volume.
>>> +    Global                - Pointer to ESAL_FWB_GLOBAL that 
>>> contains all
>>> +                            instance data
>>> +
>>> +  Returns:
>>> +    EFI_SUCCESS           - Successfully returns
>>> +    EFI_INVALID_PARAMETER - Instance not found
>>> +
>>> +--*/
>>> +{
>>> +  EFI_FW_VOL_INSTANCE  *FwhInstance;
>>> +  EFI_STATUS           Status;
>>> +
>>> +  //
>>> +  // Find the right instance of the FVB private data
>>> +  //
>>> +  Status = GetFvbInstance (Instance, Global, &FwhInstance);
>>> +  ASSERT_EFI_ERROR (Status);
>>> +  *Address = FwhInstance->FvBase;
>>> +
>>> +  return EFI_SUCCESS;
>>> +}
>>> +
>>> +EFI_STATUS
>>> +FvbGetVolumeAttributes (
>>> +  IN UINTN                  Instance,
>>> +  OUT EFI_FVB_ATTRIBUTES_2  *Attributes,
>>> +  IN ESAL_FWB_GLOBAL        *Global
>>> +  )
>>> +/*++
>>> +
>>> +  Routine Description:
>>> +    Retrieves attributes, insures positive polarity of attribute 
>>> bits, returns
>>> +    resulting attributes in output parameter
>>> +
>>> +  Arguments:
>>> +    Instance              - The FV instance whose attributes is 
>>> going to be
>>> +                            returned
>>> +    Attributes            - Output buffer which contains attributes
>>> +    Global                - Pointer to ESAL_FWB_GLOBAL that 
>>> contains all
>>> +                            instance data
>>> +
>>> +  Returns:
>>> +    EFI_SUCCESS           - Successfully returns
>>> +    EFI_INVALID_PARAMETER - Instance not found
>>> +
>>> +--*/
>>> +{
>>> +  EFI_FW_VOL_INSTANCE  *FwhInstance;
>>> +  EFI_STATUS           Status;
>>> +
>>> +  //
>>> +  // Find the right instance of the FVB private data
>>> +  //
>>> +  Status = GetFvbInstance (Instance, Global, &FwhInstance);
>>> +  ASSERT_EFI_ERROR (Status);
>>> +  *Attributes = FwhInstance->VolumeHeader.Attributes;
>>> +
>>> +  return EFI_SUCCESS;
>>> +}
>>> +
>>> +EFI_STATUS
>>> +FvbGetLbaAddress (
>>> +  IN  UINTN            Instance,
>>> +  IN  EFI_LBA          Lba,
>>> +  OUT UINTN            *LbaAddress,
>>> +  OUT UINTN            *LbaLength,
>>> +  OUT UINTN            *NumOfBlocks,
>>> +  IN  ESAL_FWB_GLOBAL  *Global
>>> +  )
>>> +/*++
>>> +
>>> +  Routine Description:
>>> +    Retrieves the starting address of an LBA in an FV
>>> +
>>> +  Arguments:
>>> +    Instance              - The FV instance which the Lba belongs to
>>> +    Lba                   - The logical block address
>>> +    LbaAddress            - On output, contains the physical 
>>> starting address
>>> +                            of the Lba
>>> +    LbaLength             - On output, contains the length of the 
>>> block
>>> +    NumOfBlocks           - A pointer to a caller allocated UINTN 
>>> in which the
>>> +                            number of consecutive blocks starting 
>>> with Lba is
>>> +                            returned. All blocks in this range have 
>>> a size of
>>> +                            BlockSize
>>> +    Global                - Pointer to ESAL_FWB_GLOBAL that 
>>> contains all
>>> +                            instance data
>>> +
>>> +  Returns:
>>> +    EFI_SUCCESS           - Successfully returns
>>> +    EFI_INVALID_PARAMETER - Instance not found
>>> +
>>> +--*/
>>> +{
>>> +  UINT32                  NumBlocks;
>>> +  UINT32                  BlockLength;
>>> +  UINTN                   Offset;
>>> +  EFI_LBA                 StartLba;
>>> +  EFI_LBA                 NextLba;
>>> +  EFI_FW_VOL_INSTANCE     *FwhInstance;
>>> +  EFI_FV_BLOCK_MAP_ENTRY  *BlockMap;
>>> +  EFI_STATUS              Status;
>>> +
>>> +  //
>>> +  // Find the right instance of the FVB private data
>>> +  //
>>> +  Status = GetFvbInstance (Instance, Global, &FwhInstance);
>>> +  ASSERT_EFI_ERROR (Status);
>>> +
>>> +  StartLba = 0;
>>> +  Offset   = 0;
>>> +  BlockMap = &(FwhInstance->VolumeHeader.BlockMap[0]);
>>> +
>>> +  //
>>> +  // Parse the blockmap of the FV to find which map entry the Lba 
>>> belongs to
>>> +  //
>>> +  while (TRUE) {
>>> +    NumBlocks   = BlockMap->NumBlocks;
>>> +    BlockLength = BlockMap->Length;
>>> +
>>> +    if ((NumBlocks == 0) || (BlockLength == 0)) {
>>> +      return EFI_INVALID_PARAMETER;
>>> +    }
>>> +
>>> +    NextLba = StartLba + NumBlocks;
>>> +
>>> +    //
>>> +    // The map entry found
>>> +    //
>>> +    if ((Lba >= StartLba) && (Lba < NextLba)) {
>>> +      Offset = Offset + (UINTN)MultU64x32 ((Lba - StartLba), 
>>> BlockLength);
>>> +      if (LbaAddress != NULL) {
>>> +        *LbaAddress = FwhInstance->FvBase + Offset;
>>> +      }
>>> +
>>> +      if (LbaLength != NULL) {
>>> +        *LbaLength = BlockLength;
>>> +      }
>>> +
>>> +      if (NumOfBlocks != NULL) {
>>> +        *NumOfBlocks = (UINTN)(NextLba - Lba);
>>> +      }
>>> +
>>> +      return EFI_SUCCESS;
>>> +    }
>>> +
>>> +    StartLba = NextLba;
>>> +    Offset   = Offset + NumBlocks * BlockLength;
>>> +    BlockMap++;
>>> +  }
>>> +}
>>> +
>>> +EFI_STATUS
>>> +FvbSetVolumeAttributes (
>>> +  IN UINTN                     Instance,
>>> +  IN OUT EFI_FVB_ATTRIBUTES_2  *Attributes,
>>> +  IN ESAL_FWB_GLOBAL           *Global
>>> +  )
>>> +/*++
>>> +
>>> +  Routine Description:
>>> +    Modifies the current settings of the firmware volume according 
>>> to the
>>> +    input parameter, and returns the new setting of the volume
>>> +
>>> +  Arguments:
>>> +    Instance              - The FV instance whose attributes is 
>>> going to be
>>> +                            modified
>>> +    Attributes            - On input, it is a pointer to 
>>> EFI_FVB_ATTRIBUTES_2
>>> +                            containing the desired firmware volume 
>>> settings.
>>> +                            On successful return, it contains the 
>>> new settings
>>> +                            of the firmware volume
>>> +    Global                - Pointer to ESAL_FWB_GLOBAL that 
>>> contains all
>>> +                            instance data
>>> +
>>> +  Returns:
>>> +    EFI_SUCCESS           - Successfully returns
>>> +    EFI_ACCESS_DENIED     - The volume setting is locked and cannot 
>>> be modified
>>> +    EFI_INVALID_PARAMETER - Instance not found, or The attributes 
>>> requested are
>>> +                            in conflict with the capabilities as 
>>> declared in
>>> +                            the firmware volume header
>>> +
>>> +--*/
>>> +{
>>> +  EFI_FW_VOL_INSTANCE   *FwhInstance;
>>> +  EFI_FVB_ATTRIBUTES_2  OldAttributes;
>>> +  EFI_FVB_ATTRIBUTES_2  *AttribPtr;
>>> +  UINT32                Capabilities;
>>> +  UINT32                OldStatus;
>>> +  UINT32                NewStatus;
>>> +  EFI_STATUS            Status;
>>> +  EFI_FVB_ATTRIBUTES_2  UnchangedAttributes;
>>> +
>>> +  //
>>> +  // Find the right instance of the FVB private data
>>> +  //
>>> +  Status = GetFvbInstance (Instance, Global, &FwhInstance);
>>> +  ASSERT_EFI_ERROR (Status);
>>> +
>>> +  AttribPtr =
>>> +    (EFI_FVB_ATTRIBUTES_2 *)&(FwhInstance->VolumeHeader.Attributes);
>>> +  OldAttributes = *AttribPtr;
>>> +  Capabilities  = OldAttributes & (EFI_FVB2_READ_DISABLED_CAP | \
>>> +                                   EFI_FVB2_READ_ENABLED_CAP | \
>>> + EFI_FVB2_WRITE_DISABLED_CAP | \
>>> +                                   EFI_FVB2_WRITE_ENABLED_CAP | \
>>> +                                   EFI_FVB2_LOCK_CAP \
>>> +                                   );
>>> +  OldStatus = OldAttributes & EFI_FVB2_STATUS;
>>> +  NewStatus = *Attributes & EFI_FVB2_STATUS;
>>> +
>>> +  UnchangedAttributes = EFI_FVB2_READ_DISABLED_CAP  | \
>>> +                        EFI_FVB2_READ_ENABLED_CAP   | \
>>> +                        EFI_FVB2_WRITE_DISABLED_CAP | \
>>> +                        EFI_FVB2_WRITE_ENABLED_CAP  | \
>>> +                        EFI_FVB2_LOCK_CAP           | \
>>> +                        EFI_FVB2_STICKY_WRITE       | \
>>> +                        EFI_FVB2_MEMORY_MAPPED      | \
>>> +                        EFI_FVB2_ERASE_POLARITY     | \
>>> +                        EFI_FVB2_READ_LOCK_CAP      | \
>>> +                        EFI_FVB2_WRITE_LOCK_CAP     | \
>>> +                        EFI_FVB2_ALIGNMENT;
>>> +
>>> +  //
>>> +  // Some attributes of FV is read only can *not* be set
>>> +  //
>>> +  if ((OldAttributes & UnchangedAttributes) ^
>>> +      (*Attributes & UnchangedAttributes))
>>> +  {
>>> +    return EFI_INVALID_PARAMETER;
>>> +  }
>>> +  //
>>> +  // If firmware volume is locked, no status bit can be updated
>>> +  //
>>> +  if (OldAttributes & EFI_FVB2_LOCK_STATUS) {
>>> +    if (OldStatus ^ NewStatus) {
>>> +      return EFI_ACCESS_DENIED;
>>> +    }
>>> +  }
>>> +  //
>>> +  // Test read disable
>>> +  //
>>> +  if ((Capabilities & EFI_FVB2_READ_DISABLED_CAP) == 0) {
>>> +    if ((NewStatus & EFI_FVB2_READ_STATUS) == 0) {
>>> +      return EFI_INVALID_PARAMETER;
>>> +    }
>>> +  }
>>> +  //
>>> +  // Test read enable
>>> +  //
>>> +  if ((Capabilities & EFI_FVB2_READ_ENABLED_CAP) == 0) {
>>> +    if (NewStatus & EFI_FVB2_READ_STATUS) {
>>> +      return EFI_INVALID_PARAMETER;
>>> +    }
>>> +  }
>>> +  //
>>> +  // Test write disable
>>> +  //
>>> +  if ((Capabilities & EFI_FVB2_WRITE_DISABLED_CAP) == 0) {
>>> +    if ((NewStatus & EFI_FVB2_WRITE_STATUS) == 0) {
>>> +      return EFI_INVALID_PARAMETER;
>>> +    }
>>> +  }
>>> +  //
>>> +  // Test write enable
>>> +  //
>>> +  if ((Capabilities & EFI_FVB2_WRITE_ENABLED_CAP) == 0) {
>>> +    if (NewStatus & EFI_FVB2_WRITE_STATUS) {
>>> +      return EFI_INVALID_PARAMETER;
>>> +    }
>>> +  }
>>> +  //
>>> +  // Test lock
>>> +  //
>>> +  if ((Capabilities & EFI_FVB2_LOCK_CAP) == 0) {
>>> +    if (NewStatus & EFI_FVB2_LOCK_STATUS) {
>>> +      return EFI_INVALID_PARAMETER;
>>> +    }
>>> +  }
>>> +
>>> +  *AttribPtr  = (*AttribPtr) & (0xFFFFFFFF & (~EFI_FVB2_STATUS));
>>> +  *AttribPtr  = (*AttribPtr) | NewStatus;
>>> +  *Attributes = *AttribPtr;
>>> +
>>> +  return EFI_SUCCESS;
>>> +}
>>> +
>>> +//
>>> +// FVB protocol APIs
>>> +//
>>> +EFI_STATUS
>>> +EFIAPI
>>> +FvbProtocolGetPhysicalAddress (
>>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>>> +  OUT EFI_PHYSICAL_ADDRESS                     *Address
>>> +  )
>>> +/*++
>>> +
>>> +  Routine Description:
>>> +
>>> +    Retrieves the physical address of the device.
>>> +
>>> +  Arguments:
>>> +
>>> +    This                  - Calling context
>>> +    Address               - Output buffer containing the address.
>>> +
>>> +  Returns:
>>> +    EFI_SUCCESS           - Successfully returns
>>> +
>>> +--*/
>>> +{
>>> +  EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice;
>>> +
>>> +  FvbDevice = FVB_DEVICE_FROM_THIS (This);
>>> +
>>> +  return FvbGetPhysicalAddress (
>>> +           FvbDevice->Instance,
>>> +           Address,
>>> +           mFvbModuleGlobal
>>> +           );
>>> +}
>>> +
>>> +EFI_STATUS
>>> +EFIAPI
>>> +FvbProtocolGetBlockSize (
>>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>>> +  IN CONST EFI_LBA                             Lba,
>>> +  OUT UINTN                                    *BlockSize,
>>> +  OUT UINTN                                    *NumOfBlocks
>>> +  )
>>> +/*++
>>> +
>>> +  Routine Description:
>>> +    Retrieve the size of a logical block
>>> +
>>> +  Arguments:
>>> +    This                  - Calling context
>>> +    Lba                   - Indicates which block to return the 
>>> size for.
>>> +    BlockSize             - A pointer to a caller allocated UINTN 
>>> in which
>>> +                            the size of the block is returned
>>> +    NumOfBlocks           - a pointer to a caller allocated UINTN 
>>> in which the
>>> +                            number of consecutive blocks starting 
>>> with Lba is
>>> +                            returned. All blocks in this range have 
>>> a size of
>>> +                            BlockSize
>>> +
>>> +  Returns:
>>> +    EFI_SUCCESS           - The firmware volume was read 
>>> successfully and
>>> +                            contents are in Buffer
>>> +
>>> +--*/
>>> +{
>>> +  EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice;
>>> +
>>> +  FvbDevice = FVB_DEVICE_FROM_THIS (This);
>>> +
>>> +  return FvbGetLbaAddress (
>>> +           FvbDevice->Instance,
>>> +           Lba,
>>> +           NULL,
>>> +           BlockSize,
>>> +           NumOfBlocks,
>>> +           mFvbModuleGlobal
>>> +           );
>>> +}
>>> +
>>> +EFI_STATUS
>>> +EFIAPI
>>> +FvbProtocolGetAttributes (
>>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>>> +  OUT EFI_FVB_ATTRIBUTES_2                     *Attributes
>>> +  )
>>> +/*++
>>> +
>>> +  Routine Description:
>>> +      Retrieves Volume attributes.  No polarity translations are done.
>>> +
>>> +  Arguments:
>>> +      This                - Calling context
>>> +      Attributes          - output buffer which contains attributes
>>> +
>>> +  Returns:
>>> +    EFI_SUCCESS           - Successfully returns
>>> +
>>> +--*/
>>> +{
>>> +  EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice;
>>> +
>>> +  FvbDevice = FVB_DEVICE_FROM_THIS (This);
>>> +
>>> +  return FvbGetVolumeAttributes (
>>> +           FvbDevice->Instance,
>>> +           Attributes,
>>> +           mFvbModuleGlobal
>>> +           );
>>> +}
>>> +
>>> +EFI_STATUS
>>> +EFIAPI
>>> +FvbProtocolSetAttributes (
>>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>>> +  IN OUT EFI_FVB_ATTRIBUTES_2                  *Attributes
>>> +  )
>>> +/*++
>>> +
>>> +  Routine Description:
>>> +    Sets Volume attributes. No polarity translations are done.
>>> +
>>> +  Arguments:
>>> +    This                  - Calling context
>>> +    Attributes            - output buffer which contains attributes
>>> +
>>> +  Returns:
>>> +    EFI_SUCCESS           - Successfully returns
>>> +
>>> +--*/
>>> +{
>>> +  EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice;
>>> +
>>> +  FvbDevice = FVB_DEVICE_FROM_THIS (This);
>>> +
>>> +  return FvbSetVolumeAttributes (
>>> +           FvbDevice->Instance,
>>> +           Attributes,
>>> +           mFvbModuleGlobal
>>> +           );
>>> +}
>>> +
>>> +EFI_STATUS
>>> +EFIAPI
>>> +FvbProtocolEraseBlocks (
>>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>>> +  ...
>>> +  )
>>> +/*++
>>> +
>>> +  Routine Description:
>>> +
>>> +    The EraseBlock() function erases one or more blocks as denoted 
>>> by the
>>> +    variable argument list. The entire parameter list of blocks 
>>> must be
>>> +    verified prior to erasing any blocks.  If a block is requested 
>>> that does
>>> +    not exist within the associated firmware volume (it has a 
>>> larger index than
>>> +    the last block of the firmware volume), the EraseBlock() 
>>> function must
>>> +    return EFI_INVALID_PARAMETER without modifying the contents of 
>>> the firmware
>>> +    volume.
>>> +
>>> +  Arguments:
>>> +    This                  - Calling context
>>> +    ...                   - Starting LBA followed by Number of Lba 
>>> to erase.
>>> +                            a -1 to terminate the list.
>>> +
>>> +  Returns:
>>> +    EFI_SUCCESS           - The erase request was successfully 
>>> completed
>>> +    EFI_ACCESS_DENIED     - The firmware volume is in the 
>>> WriteDisabled state
>>> +    EFI_DEVICE_ERROR      - The block device is not functioning 
>>> correctly and
>>> +                            could not be written. Firmware device 
>>> may have been
>>> +                            partially erased
>>> +
>>> +--*/
>>> +{
>>> +  EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice;
>>> +  EFI_FW_VOL_INSTANCE      *FwhInstance;
>>> +  UINTN                    NumOfBlocks;
>>> +  VA_LIST                  args;
>>> +  EFI_LBA                  StartingLba;
>>> +  UINTN                    NumOfLba;
>>> +  EFI_STATUS               Status;
>>> +
>>> +  FvbDevice = FVB_DEVICE_FROM_THIS (This);
>>> +
>>> +  Status = GetFvbInstance (
>>> +             FvbDevice->Instance,
>>> +             mFvbModuleGlobal,
>>> +             &FwhInstance
>>> +             );
>>> +  ASSERT_EFI_ERROR (Status);
>>> +
>>> +  NumOfBlocks = FwhInstance->NumOfBlocks;
>>> +
>>> +  VA_START (args, This);
>>> +
>>> +  do {
>>> +    StartingLba = VA_ARG (args, EFI_LBA);
>>> +    if (StartingLba == EFI_LBA_LIST_TERMINATOR) {
>>> +      break;
>>> +    }
>>> +
>>> +    NumOfLba = VA_ARG (args, UINTN);
>>> +
>>> +    //
>>> +    // Check input parameters
>>> +    //
>>> +    if ((NumOfLba == 0) || ((StartingLba + NumOfLba) > NumOfBlocks)) {
>>> +      VA_END (args);
>>> +      return EFI_INVALID_PARAMETER;
>>> +    }
>>> +  } while (1);
>>> +
>>> +  VA_END (args);
>>> +
>>> +  VA_START (args, This);
>>> +  do {
>>> +    StartingLba = VA_ARG (args, EFI_LBA);
>>> +    if (StartingLba == EFI_LBA_LIST_TERMINATOR) {
>>> +      break;
>>> +    }
>>> +
>>> +    NumOfLba = VA_ARG (args, UINTN);
>>> +
>>> +    while (NumOfLba > 0) {
>>> +      Status = QemuFlashEraseBlock (StartingLba);
>>> +      if (EFI_ERROR (Status)) {
>>> +        VA_END (args);
>>> +        return Status;
>>> +      }
>>> +
>>> +      StartingLba++;
>>> +      NumOfLba--;
>>> +    }
>>> +
>>> +  } while (1);
>>> +
>>> +  VA_END (args);
>>> +
>>> +  return EFI_SUCCESS;
>>> +}
>>> +
>>> +EFI_STATUS
>>> +EFIAPI
>>> +FvbProtocolWrite (
>>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>>> +  IN       EFI_LBA                             Lba,
>>> +  IN       UINTN                               Offset,
>>> +  IN OUT   UINTN                               *NumBytes,
>>> +  IN       UINT8                               *Buffer
>>> +  )
>>> +/*++
>>> +
>>> +  Routine Description:
>>> +
>>> +    Writes data beginning at Lba:Offset from FV. The write 
>>> terminates either
>>> +    when *NumBytes of data have been written, or when a block 
>>> boundary is
>>> +    reached.  *NumBytes is updated to reflect the actual number of 
>>> bytes
>>> +    written. The write operation does not include erase. This 
>>> routine will
>>> +    attempt to write only the specified bytes. If the writes do not 
>>> stick,
>>> +    it will return an error.
>>> +
>>> +  Arguments:
>>> +    This                  - Calling context
>>> +    Lba                   - Block in which to begin write
>>> +    Offset                - Offset in the block at which to begin 
>>> write
>>> +    NumBytes              - On input, indicates the requested write 
>>> size. On
>>> +                            output, indicates the actual number of 
>>> bytes
>>> +                            written
>>> +    Buffer                - Buffer containing source data for the 
>>> write.
>>> +
>>> +  Returns:
>>> +    EFI_SUCCESS           - The firmware volume was written 
>>> successfully
>>> +    EFI_BAD_BUFFER_SIZE   - Write attempted across a LBA boundary. 
>>> On output,
>>> +                            NumBytes contains the total number of 
>>> bytes
>>> +                            actually written
>>> +    EFI_ACCESS_DENIED     - The firmware volume is in the 
>>> WriteDisabled state
>>> +    EFI_DEVICE_ERROR      - The block device is not functioning 
>>> correctly and
>>> +                            could not be written
>>> +    EFI_INVALID_PARAMETER - NumBytes or Buffer are NULL
>>> +
>>> +--*/
>>> +{
>>> +  return QemuFlashWrite (
>>> +           (EFI_LBA)Lba,
>>> +           (UINTN)Offset,
>>> +           NumBytes,
>>> +           (UINT8 *)Buffer
>>> +           );
>>> +}
>>> +
>>> +EFI_STATUS
>>> +EFIAPI
>>> +FvbProtocolRead (
>>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>>> +  IN CONST EFI_LBA                             Lba,
>>> +  IN CONST UINTN                               Offset,
>>> +  IN OUT UINTN                                 *NumBytes,
>>> +  IN UINT8                                     *Buffer
>>> +  )
>>> +/*++
>>> +
>>> +  Routine Description:
>>> +
>>> +    Reads data beginning at Lba:Offset from FV. The Read terminates 
>>> either
>>> +    when *NumBytes of data have been read, or when a block boundary is
>>> +    reached.  *NumBytes is updated to reflect the actual number of 
>>> bytes
>>> +    written. The write operation does not include erase. This 
>>> routine will
>>> +    attempt to write only the specified bytes. If the writes do not 
>>> stick,
>>> +    it will return an error.
>>> +
>>> +  Arguments:
>>> +    This                  - Calling context
>>> +    Lba                   - Block in which to begin Read
>>> +    Offset                - Offset in the block at which to begin Read
>>> +    NumBytes              - On input, indicates the requested write 
>>> size. On
>>> +                            output, indicates the actual number of 
>>> bytes Read
>>> +    Buffer                - Buffer containing source data for the 
>>> Read.
>>> +
>>> +  Returns:
>>> +    EFI_SUCCESS           - The firmware volume was read 
>>> successfully and
>>> +                            contents are in Buffer
>>> +    EFI_BAD_BUFFER_SIZE   - Read attempted across a LBA boundary. 
>>> On output,
>>> +                            NumBytes contains the total number of 
>>> bytes
>>> +                            returned in Buffer
>>> +    EFI_ACCESS_DENIED     - The firmware volume is in the 
>>> ReadDisabled state
>>> +    EFI_DEVICE_ERROR      - The block device is not functioning 
>>> correctly and
>>> +                            could not be read
>>> +    EFI_INVALID_PARAMETER - NumBytes or Buffer are NULL
>>> +
>>> +--*/
>>> +{
>>> +  return QemuFlashRead (
>>> +           (EFI_LBA)Lba,
>>> +           (UINTN)Offset,
>>> +           NumBytes,
>>> +           (UINT8 *)Buffer
>>> +           );
>>> +}
>>> +
>>> +EFI_STATUS
>>> +ValidateFvHeader (
>>> +  EFI_FIRMWARE_VOLUME_HEADER  *FwVolHeader
>>> +  )
>>> +/*++
>>> +
>>> +  Routine Description:
>>> +    Check the integrity of firmware volume header
>>> +
>>> +  Arguments:
>>> +    FwVolHeader           - A pointer to a firmware volume header
>>> +
>>> +  Returns:
>>> +    EFI_SUCCESS           - The firmware volume is consistent
>>> +    EFI_NOT_FOUND         - The firmware volume has corrupted. So 
>>> it is not an
>>> +                            FV
>>> +
>>> +--*/
>>> +{
>>> +  UINT16  Checksum;
>>> +
>>> +  //
>>> +  // Verify the header revision, header signature, length
>>> +  // Length of FvBlock cannot be 2**64-1
>>> +  // HeaderLength cannot be an odd number
>>> +  //
>>> +  if ((FwVolHeader->Revision != EFI_FVH_REVISION) ||
>>> +      (FwVolHeader->Signature != EFI_FVH_SIGNATURE) ||
>>> +      (FwVolHeader->FvLength == ((UINTN)-1)) ||
>>> +      ((FwVolHeader->HeaderLength & 0x01) != 0)
>>> +      )
>>> +  {
>>> +    return EFI_NOT_FOUND;
>>> +  }
>>> +
>>> +  //
>>> +  // Verify the header checksum
>>> +  //
>>> +
>>> +  Checksum = CalculateSum16 (
>>> +               (UINT16 *)FwVolHeader,
>>> +               FwVolHeader->HeaderLength
>>> +               );
>>> +  if (Checksum != 0) {
>>> +    UINT16  Expected;
>>> +
>>> +    Expected =
>>> +      (UINT16)(((UINTN)FwVolHeader->Checksum + 0x10000 - Checksum) 
>>> & 0xffff);
>>> +
>>> +    DEBUG ((
>>> +      DEBUG_INFO,
>>> +      "FV@%p Checksum is 0x%x, expected 0x%x\n",
>>> +      FwVolHeader,
>>> +      FwVolHeader->Checksum,
>>> +      Expected
>>> +      ));
>>> +    return EFI_NOT_FOUND;
>>> +  }
>>> +
>>> +  return EFI_SUCCESS;
>>> +}
>>> +
>>> +STATIC
>>> +EFI_STATUS
>>> +MarkMemoryRangeForRuntimeAccess (
>>> +  EFI_PHYSICAL_ADDRESS                BaseAddress,
>>> +  UINTN                               Length
>>> +  )
>>> +{
>>> +  EFI_STATUS                          Status;
>>> +
>>> +  //
>>> +  // Mark flash region as runtime memory
>>> +  //
>>> +  Status = gDS->RemoveMemorySpace (
>>> +                  BaseAddress,
>>> +                  Length
>>> +                  );
>>> +
>>> +  Status = gDS->AddMemorySpace (
>>> +                  EfiGcdMemoryTypeSystemMemory,
>>> +                  BaseAddress,
>>> +                  Length,
>>> +                  EFI_MEMORY_UC | EFI_MEMORY_RUNTIME
>>> +                  );
>>> +  ASSERT_EFI_ERROR (Status);
>>> +
>>> +  Status = gBS->AllocatePages (
>>> +                  AllocateAddress,
>>> +                  EfiRuntimeServicesData,
>>> +                  EFI_SIZE_TO_PAGES (Length),
>>> +                  &BaseAddress
>>> +                  );
>>> +  ASSERT_EFI_ERROR (Status);
>>> +
>>> +  return Status;
>>> +}
>>> +
>>> +STATIC
>>> +EFI_STATUS
>>> +InitializeVariableFvHeader (
>>> +  VOID
>>> +  )
>>> +{
>>> +  EFI_STATUS                  Status;
>>> +  EFI_FIRMWARE_VOLUME_HEADER  *GoodFwVolHeader;
>>> +  EFI_FIRMWARE_VOLUME_HEADER  *FwVolHeader;
>>> +  UINTN                       Length;
>>> +  UINTN                       WriteLength;
>>> +  UINTN                       BlockSize;
>>> +
>>> +  FwVolHeader =
>>> +    (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)
>>> +    PcdGet64 (PcdOvmfFlashNvStorageVariableBase);
>>> +
>>> +  Length =  FixedPcdGet32 (PcdAllVarSize);
>>> +  BlockSize = PcdGet32 (PcdFlashBlockSize);
>>> +
>>> +  Status = ValidateFvHeader (FwVolHeader);
>>> +  if (!EFI_ERROR (Status)) {
>>> +    if ((FwVolHeader->FvLength != Length) ||
>>> +        (FwVolHeader->BlockMap[0].Length != BlockSize))
>>> +    {
>>> +      Status = EFI_VOLUME_CORRUPTED;
>>> +    }
>>> +  }
>>> +  if (EFI_ERROR (Status)) {
>>> +    UINTN  Offset;
>>> +    UINTN  Start;
>>> +
>>> +    DEBUG ((
>>> +      DEBUG_INFO,
>>> +      "Variable FV header is not valid. It will be reinitialized.\n"
>>> +      ));
>>> +
>>> +    //
>>> +    // Get FvbInfo to provide in FwhInstance.
>>> +    //
>>> +    Status = GetFvbInfo (Length, &GoodFwVolHeader);
>>> +    ASSERT (!EFI_ERROR (Status));
>>> +
>>> +    Start = (UINTN)(UINT8*) FwVolHeader - PcdGet64 (PcdFlashFdBase);
>>> +    ASSERT (Start % BlockSize == 0 && Length % BlockSize == 0);
>>> +    ASSERT (GoodFwVolHeader->HeaderLength <= BlockSize);
>>> +
>>> +    //
>>> +    // Erase all the blocks
>>> +    //
>>> +    for (Offset = Start; Offset < Start + Length; Offset += 
>>> BlockSize) {
>>> +      Status = QemuFlashEraseBlock (Offset / BlockSize);
>>> +      ASSERT_EFI_ERROR (Status);
>>> +    }
>>> +
>>> +    //
>>> +    // Write good FV header
>>> +    //
>>> +    WriteLength = GoodFwVolHeader->HeaderLength;
>>> +    Status      = QemuFlashWrite (
>>> +                    Start / BlockSize,
>>> +                    0,
>>> +                    &WriteLength,
>>> +                    (UINT8 *)GoodFwVolHeader
>>> +                    );
>>> +    ASSERT_EFI_ERROR (Status);
>>> +    ASSERT (WriteLength == GoodFwVolHeader->HeaderLength);
>>> +  }
>>> +
>>> +  return Status;
>>> +}
>>> +
>>> +EFI_STATUS
>>> +EFIAPI
>>> +FvbInitialize (
>>> +  IN EFI_HANDLE        ImageHandle,
>>> +  IN EFI_SYSTEM_TABLE  *SystemTable
>>> +  )
>>> +/*++
>>> +
>>> +  Routine Description:
>>> +    This function does common initialization for FVB services
>>> +
>>> +  Arguments:
>>> +
>>> +  Returns:
>>> +
>>> +--*/
>>> +{
>>> +  EFI_STATUS                  Status;
>>> +  EFI_FW_VOL_INSTANCE         *FwhInstance;
>>> +  EFI_FIRMWARE_VOLUME_HEADER  *FwVolHeader;
>>> +  UINT32                      BufferSize;
>>> +  EFI_FV_BLOCK_MAP_ENTRY      *PtrBlockMapEntry;
>>> +  EFI_FW_VOL_BLOCK_DEVICE     *FvbDevice;
>>> +  UINT32                      MaxLbaSize;
>>> +  EFI_PHYSICAL_ADDRESS        BaseAddress;
>>> +  UINTN                       Length;
>>> +  UINTN                       NumOfBlocks;
>>> +  RETURN_STATUS               PcdStatus;
>>> +
>>> +  if (EFI_ERROR (QemuFlashInitialize ())) {
>>> +    //
>>> +    // Return an error so image will be unloaded
>>> +    //
>>> +    DEBUG ((
>>> +      DEBUG_INFO,
>>> +      "QEMU flash was not detected. Writable FVB is not being 
>>> installed.\n"
>>> +      ));
>>> +    return EFI_WRITE_PROTECTED;
>>> +  }
>>> +
>>> +  //
>>> +  // Allocate runtime services data for global variable, which 
>>> contains
>>> +  // the private data of all firmware volume block instances
>>> +  //
>>> +  mFvbModuleGlobal = AllocateRuntimePool (sizeof (ESAL_FWB_GLOBAL));
>>> +  ASSERT (mFvbModuleGlobal != NULL);
>>> +
>>> +  BaseAddress = (UINTN) PcdGet64 (PcdOvmfFlashNvStorageVariableBase);
>>> +  Length = PcdGet32 (PcdAllVarSize);
>>> +
>>> +  Status = InitializeVariableFvHeader ();
>>> +  if (EFI_ERROR (Status)) {
>>> +    DEBUG ((
>>> +      DEBUG_INFO,
>>> +      "QEMU Flash: Unable to initialize variable FV header\n"
>>> +      ));
>>> +    return EFI_WRITE_PROTECTED;
>>> +  }
>>> +
>>> +  FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)BaseAddress;
>>> +  Status      = ValidateFvHeader (FwVolHeader);
>>> +  if (EFI_ERROR (Status)) {
>>> +    //
>>> +    // Get FvbInfo
>>> +    //
>>> +    Status = GetFvbInfo (Length, &FwVolHeader);
>>> +    if (EFI_ERROR (Status)) {
>>> +      DEBUG ((DEBUG_INFO, "EFI_ERROR (GetFvbInfo (Length, 
>>> &FwVolHeader))\n"));
>>> +      return EFI_WRITE_PROTECTED;
>>> +    }
>>> +  }
>>> +
>>> +  BufferSize = (sizeof (EFI_FW_VOL_INSTANCE) +
>>> +                FwVolHeader->HeaderLength -
>>> +                sizeof (EFI_FIRMWARE_VOLUME_HEADER)
>>> +                );
>>> +  mFvbModuleGlobal->FvInstance = AllocateRuntimePool (BufferSize);
>>> +  ASSERT (mFvbModuleGlobal->FvInstance != NULL);
>>> +
>>> +  FwhInstance = mFvbModuleGlobal->FvInstance;
>>> +
>>> +  mFvbModuleGlobal->NumFv = 0;
>>> +  MaxLbaSize              = 0;
>>> +
>>> +  FwVolHeader =
>>> +    (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)
>>> +    PcdGet64 (PcdOvmfFlashNvStorageVariableBase);
>>> +
>>> +  FwhInstance->FvBase = (UINTN)BaseAddress;
>>> +
>>> +  CopyMem (
>>> +    (UINTN *)&(FwhInstance->VolumeHeader),
>>> +    (UINTN *)FwVolHeader,
>>> +    FwVolHeader->HeaderLength
>>> +    );
>>> +  FwVolHeader = &(FwhInstance->VolumeHeader);
>>> +
>>> +  NumOfBlocks = 0;
>>> +
>>> +  for (PtrBlockMapEntry = FwVolHeader->BlockMap;
>>> +       PtrBlockMapEntry->NumBlocks != 0;
>>> +       PtrBlockMapEntry++)
>>> +  {
>>> +    //
>>> +    // Get the maximum size of a block.
>>> +    //
>>> +    if (MaxLbaSize < PtrBlockMapEntry->Length) {
>>> +      MaxLbaSize = PtrBlockMapEntry->Length;
>>> +    }
>>> +
>>> +    NumOfBlocks = NumOfBlocks + PtrBlockMapEntry->NumBlocks;
>>> +  }
>>> +
>>> +  //
>>> +  // The total number of blocks in the FV.
>>> +  //
>>> +  FwhInstance->NumOfBlocks = NumOfBlocks;
>>> +
>>> +  //
>>> +  // Add a FVB Protocol Instance
>>> +  //
>>> +  FvbDevice = AllocateRuntimePool (sizeof (EFI_FW_VOL_BLOCK_DEVICE));
>>> +  ASSERT (FvbDevice != NULL);
>>> +
>>> +  CopyMem (FvbDevice, &mFvbDeviceTemplate, sizeof 
>>> (EFI_FW_VOL_BLOCK_DEVICE));
>>> +
>>> +  FvbDevice->Instance = mFvbModuleGlobal->NumFv;
>>> +  mFvbModuleGlobal->NumFv++;
>>> +
>>> +  //
>>> +  // Set up the devicepath
>>> +  //
>>> +  if (FwVolHeader->ExtHeaderOffset == 0) {
>>> +    FV_MEMMAP_DEVICE_PATH  *FvMemmapDevicePath;
>>> +
>>> +    //
>>> +    // FV does not contains extension header, then produce 
>>> MEMMAP_DEVICE_PATH
>>> +    //
>>> +    FvMemmapDevicePath = AllocateCopyPool (
>>> +                           sizeof (FV_MEMMAP_DEVICE_PATH),
>>> +                           &mFvMemmapDevicePathTemplate
>>> +                           );
>>> +    FvMemmapDevicePath->MemMapDevPath.StartingAddress = BaseAddress;
>>> +    FvMemmapDevicePath->MemMapDevPath.EndingAddress   =
>>> +      BaseAddress + FwVolHeader->FvLength - 1;
>>> +    FvbDevice->DevicePath = (EFI_DEVICE_PATH_PROTOCOL 
>>> *)FvMemmapDevicePath;
>>> +  } else {
>>> +    FV_PIWG_DEVICE_PATH  *FvPiwgDevicePath;
>>> +
>>> +    FvPiwgDevicePath = AllocateCopyPool (
>>> +                         sizeof (FV_PIWG_DEVICE_PATH),
>>> +                         &mFvPIWGDevicePathTemplate
>>> +                         );
>>> +    CopyGuid (
>>> +      &FvPiwgDevicePath->FvDevPath.FvName,
>>> +      (GUID *)(UINTN)(BaseAddress + FwVolHeader->ExtHeaderOffset)
>>> +      );
>>> +    FvbDevice->DevicePath = (EFI_DEVICE_PATH_PROTOCOL 
>>> *)FvPiwgDevicePath;
>>> +  }
>>> +
>>> +  //
>>> +  // Module type specific hook.
>>> +  //
>>> +  InstallProtocolInterfaces (FvbDevice);
>>> +
>>> +  MarkMemoryRangeForRuntimeAccess (BaseAddress, Length);
>>> +
>>> +  //
>>> +  // Set several PCD values to point to flash
>>> +  //
>>> +
>>> +
>>> +  PcdStatus = PcdSet64S (
>>> +    PcdFlashNvStorageVariableBase64,
>>> +    (UINTN) PcdGet64 (PcdOvmfFlashNvStorageVariableBase)
>>> +    );
>>> +  ASSERT_RETURN_ERROR (PcdStatus);
>>> +  PcdStatus = PcdSet64S (
>>> +    PcdFlashNvStorageFtwWorkingBase64,
>>> +    PcdGet64 (PcdOvmfFlashNvStorageFtwWorkingBase)
>>> +    );
>>> +  ASSERT_RETURN_ERROR (PcdStatus);
>>> +  PcdStatus = PcdSet64S (
>>> +    PcdFlashNvStorageFtwSpareBase64,
>>> +    PcdGet64 (PcdOvmfFlashNvStorageFtwSpareBase)
>>> +    );
>>> +  ASSERT_RETURN_ERROR (PcdStatus);
>>> +
>>> +  FwhInstance = (EFI_FW_VOL_INSTANCE *)
>>> +                (
>>> +                 (UINTN)((UINT8 *)FwhInstance) + 
>>> FwVolHeader->HeaderLength +
>>> +                 (sizeof (EFI_FW_VOL_INSTANCE) - sizeof 
>>> (EFI_FIRMWARE_VOLUME_HEADER))
>>> +                );
>>> +
>>> +  //
>>> +  // Module type specific hook.
>>> +  //
>>> +  InstallVirtualAddressChangeHandler ();
>>> +
>>> +  PcdStatus = PcdSetBoolS (PcdOvmfFlashVariablesEnable, TRUE);
>>> +  ASSERT_RETURN_ERROR (PcdStatus);
>>> +  return EFI_SUCCESS;
>>> +}
>>> diff --git 
>>> a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.h 
>>> b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.h 
>>>
>>> new file mode 100644
>>> index 0000000000..e7234a0c5e
>>> --- /dev/null
>>> +++ 
>>> b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.h
>>> @@ -0,0 +1,178 @@
>>> +/** @file
>>> +  Firmware volume block driver for Intel Firmware Hub (FWH) device
>>> +
>>> +  Copyright (c) 2021 Loongson Technology Corporation Limited. All 
>>> rights reserved.<BR>
>>> +
>>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>>> +
>>> +**/
>>> +#ifndef _FW_BLOCK_SERVICE_H
>>> +#define _FW_BLOCK_SERVICE_H
>>> +
>>> +typedef struct {
>>> +  UINTN                         FvBase;
>>> +  UINTN                         NumOfBlocks;
>>> +  EFI_FIRMWARE_VOLUME_HEADER    VolumeHeader;
>>> +} EFI_FW_VOL_INSTANCE;
>>> +
>>> +typedef struct {
>>> +  UINT32                 NumFv;
>>> +  EFI_FW_VOL_INSTANCE    *FvInstance;
>>> +} ESAL_FWB_GLOBAL;
>>> +
>>> +extern ESAL_FWB_GLOBAL  *mFvbModuleGlobal;
>>> +
>>> +//
>>> +// Fvb Protocol instance data
>>> +//
>>> +#define FVB_DEVICE_FROM_THIS(a)  CR (a, EFI_FW_VOL_BLOCK_DEVICE,\
>>> +                                  FwVolBlockInstance, 
>>> FVB_DEVICE_SIGNATURE)
>>> +
>>> +#define FVB_EXTEND_DEVICE_FROM_THIS(a)  CR (a, 
>>> EFI_FW_VOL_BLOCK_DEVICE,\
>>> +                                         FvbExtension, 
>>> FVB_DEVICE_SIGNATURE)
>>> +
>>> +#define FVB_DEVICE_SIGNATURE  SIGNATURE_32 ('F', 'V', 'B', 'N')
>>> +
>>> +typedef struct {
>>> +  MEDIA_FW_VOL_DEVICE_PATH    FvDevPath;
>>> +  EFI_DEVICE_PATH_PROTOCOL    EndDevPath;
>>> +} FV_PIWG_DEVICE_PATH;
>>> +
>>> +typedef struct {
>>> +  MEMMAP_DEVICE_PATH          MemMapDevPath;
>>> +  EFI_DEVICE_PATH_PROTOCOL    EndDevPath;
>>> +} FV_MEMMAP_DEVICE_PATH;
>>> +
>>> +typedef struct {
>>> +  UINTN                                 Signature;
>>> +  EFI_DEVICE_PATH_PROTOCOL              *DevicePath;
>>> +  UINTN                                 Instance;
>>> +  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL    FwVolBlockInstance;
>>> +} EFI_FW_VOL_BLOCK_DEVICE;
>>> +
>>> +EFI_STATUS
>>> +GetFvbInfo (
>>> +  IN  UINT64                      FvLength,
>>> +  OUT EFI_FIRMWARE_VOLUME_HEADER  **FvbInfo
>>> +  );
>>> +
>>> +EFI_STATUS
>>> +FvbSetVolumeAttributes (
>>> +  IN UINTN                     Instance,
>>> +  IN OUT EFI_FVB_ATTRIBUTES_2  *Attributes,
>>> +  IN ESAL_FWB_GLOBAL           *Global
>>> +  );
>>> +
>>> +EFI_STATUS
>>> +FvbGetVolumeAttributes (
>>> +  IN UINTN                  Instance,
>>> +  OUT EFI_FVB_ATTRIBUTES_2  *Attributes,
>>> +  IN ESAL_FWB_GLOBAL        *Global
>>> +  );
>>> +
>>> +EFI_STATUS
>>> +FvbGetPhysicalAddress (
>>> +  IN UINTN                  Instance,
>>> +  OUT EFI_PHYSICAL_ADDRESS  *Address,
>>> +  IN ESAL_FWB_GLOBAL        *Global
>>> +  );
>>> +
>>> +EFI_STATUS
>>> +EFIAPI
>>> +FvbInitialize (
>>> +  IN EFI_HANDLE        ImageHandle,
>>> +  IN EFI_SYSTEM_TABLE  *SystemTable
>>> +  );
>>> +
>>> +
>>> +VOID
>>> +EFIAPI
>>> +FvbClassAddressChangeEvent (
>>> +  IN EFI_EVENT  Event,
>>> +  IN VOID       *Context
>>> +  );
>>> +
>>> +EFI_STATUS
>>> +FvbGetLbaAddress (
>>> +  IN  UINTN            Instance,
>>> +  IN  EFI_LBA          Lba,
>>> +  OUT UINTN            *LbaAddress,
>>> +  OUT UINTN            *LbaLength,
>>> +  OUT UINTN            *NumOfBlocks,
>>> +  IN  ESAL_FWB_GLOBAL  *Global
>>> +  );
>>> +
>>> +//
>>> +// Protocol APIs
>>> +//
>>> +EFI_STATUS
>>> +EFIAPI
>>> +FvbProtocolGetAttributes (
>>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>>> +  OUT EFI_FVB_ATTRIBUTES_2                     *Attributes
>>> +  );
>>> +
>>> +EFI_STATUS
>>> +EFIAPI
>>> +FvbProtocolSetAttributes (
>>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>>> +  IN OUT EFI_FVB_ATTRIBUTES_2                  *Attributes
>>> +  );
>>> +
>>> +EFI_STATUS
>>> +EFIAPI
>>> +FvbProtocolGetPhysicalAddress (
>>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>>> +  OUT EFI_PHYSICAL_ADDRESS                     *Address
>>> +  );
>>> +
>>> +EFI_STATUS
>>> +EFIAPI
>>> +FvbProtocolGetBlockSize (
>>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>>> +  IN CONST EFI_LBA                             Lba,
>>> +  OUT UINTN                                    *BlockSize,
>>> +  OUT UINTN                                    *NumOfBlocks
>>> +  );
>>> +
>>> +EFI_STATUS
>>> +EFIAPI
>>> +FvbProtocolRead (
>>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>>> +  IN CONST EFI_LBA                             Lba,
>>> +  IN CONST UINTN                               Offset,
>>> +  IN OUT UINTN                                 *NumBytes,
>>> +  IN UINT8                                     *Buffer
>>> +  );
>>> +
>>> +EFI_STATUS
>>> +EFIAPI
>>> +FvbProtocolWrite (
>>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>>> +  IN       EFI_LBA                             Lba,
>>> +  IN       UINTN                               Offset,
>>> +  IN OUT   UINTN                               *NumBytes,
>>> +  IN       UINT8                               *Buffer
>>> +  );
>>> +
>>> +EFI_STATUS
>>> +EFIAPI
>>> +FvbProtocolEraseBlocks (
>>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>>> +  ...
>>> +  );
>>> +
>>> +//
>>> +// The following functions have different implementations dependent 
>>> on the
>>> +// module type chosen for building this driver.
>>> +//
>>> +VOID
>>> +InstallProtocolInterfaces (
>>> +  IN EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice
>>> +  );
>>> +
>>> +VOID
>>> +InstallVirtualAddressChangeHandler (
>>> +  VOID
>>> +  );
>>> +#endif
>>> diff --git 
>>> a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockServiceDxe.c 
>>> b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockServiceDxe.c 
>>>
>>> new file mode 100644
>>> index 0000000000..cb85499539
>>> --- /dev/null
>>> +++ 
>>> b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockServiceDxe.c
>>> @@ -0,0 +1,152 @@
>>> +/** @file
>>> +  Functions related to the Firmware Volume Block service whose
>>> +  implementation is specific to the runtime DXE driver build.
>>> +
>>> +  Copyright (c) 2021 Loongson Technology Corporation Limited. All 
>>> rights reserved.<BR>
>>> +
>>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>>> +
>>> +**/
>>> +
>>> +#include <Guid/EventGroup.h>
>>> +#include <Library/DebugLib.h>
>>> +#include <Library/DevicePathLib.h>
>>> +#include <Library/PcdLib.h>
>>> +#include <Library/UefiBootServicesTableLib.h>
>>> +#include <Library/UefiRuntimeLib.h>
>>> +#include <Protocol/DevicePath.h>
>>> +#include <Protocol/FirmwareVolumeBlock.h>
>>> +
>>> +#include "FwBlockService.h"
>>> +#include "QemuFlash.h"
>>> +
>>> +VOID
>>> +InstallProtocolInterfaces (
>>> +  IN EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice
>>> +  )
>>> +{
>>> +  EFI_STATUS                          Status;
>>> +  EFI_HANDLE                          FwbHandle;
>>> +  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *OldFwbInterface;
>>> +
>>> +  //
>>> +  // Find a handle with a matching device path that has supports FW 
>>> Block
>>> +  // protocol
>>> +  //
>>> +  Status = gBS->LocateDevicePath (
>>> +                  &gEfiFirmwareVolumeBlockProtocolGuid,
>>> +                  &FvbDevice->DevicePath,
>>> +                  &FwbHandle
>>> +                  );
>>> +  if (EFI_ERROR (Status)) {
>>> +    //
>>> +    // LocateDevicePath fails so install a new interface and device 
>>> path
>>> +    //
>>> +    FwbHandle = NULL;
>>> +    DEBUG ((DEBUG_INFO, "Installing QEMU flash FVB\n"));
>>> +    Status = gBS->InstallMultipleProtocolInterfaces (
>>> +                    &FwbHandle,
>>> +                    &gEfiFirmwareVolumeBlockProtocolGuid,
>>> +                    &FvbDevice->FwVolBlockInstance,
>>> +                    &gEfiDevicePathProtocolGuid,
>>> +                    FvbDevice->DevicePath,
>>> +                    NULL
>>> +                    );
>>> +    ASSERT_EFI_ERROR (Status);
>>> +  } else if (IsDevicePathEnd (FvbDevice->DevicePath)) {
>>> +    //
>>> +    // Device already exists, so reinstall the FVB protocol
>>> +    //
>>> +    Status = gBS->HandleProtocol (
>>> +                    FwbHandle,
>>> +                    &gEfiFirmwareVolumeBlockProtocolGuid,
>>> +                    (VOID **)&OldFwbInterface
>>> +                    );
>>> +    ASSERT_EFI_ERROR (Status);
>>> +
>>> +    DEBUG ((DEBUG_INFO, "Reinstalling FVB for QEMU flash region\n"));
>>> +    Status = gBS->ReinstallProtocolInterface (
>>> +                    FwbHandle,
>>> +                    &gEfiFirmwareVolumeBlockProtocolGuid,
>>> +                    OldFwbInterface,
>>> +                    &FvbDevice->FwVolBlockInstance
>>> +                    );
>>> +    ASSERT_EFI_ERROR (Status);
>>> +  } else {
>>> +    //
>>> +    // There was a FVB protocol on an End Device Path node
>>> +    //
>>> +    ASSERT (FALSE);
>>> +  }
>>> +}
>>> +
>>> +
>>> +STATIC
>>> +VOID
>>> +EFIAPI
>>> +FvbVirtualAddressChangeEvent (
>>> +  IN EFI_EVENT  Event,
>>> +  IN VOID       *Context
>>> +  )
>>> +/*++
>>> +
>>> +  Routine Description:
>>> +
>>> +    Fixup internal data so that EFI and SAL can be call in virtual 
>>> mode.
>>> +    Call the passed in Child Notify event and convert the 
>>> mFvbModuleGlobal
>>> +    date items to there virtual address.
>>> +
>>> +  Arguments:
>>> +
>>> +    (Standard EFI notify event - EFI_EVENT_NOTIFY)
>>> +
>>> +  Returns:
>>> +
>>> +    None
>>> +
>>> +--*/
>>> +{
>>> +  EFI_FW_VOL_INSTANCE  *FwhInstance;
>>> +  UINTN                Index;
>>> +
>>> +  FwhInstance = mFvbModuleGlobal->FvInstance;
>>> +  EfiConvertPointer (0x0, (VOID **)&mFvbModuleGlobal->FvInstance);
>>> +
>>> +  //
>>> +  // Convert the base address of all the instances
>>> +  //
>>> +  Index = 0;
>>> +  while (Index < mFvbModuleGlobal->NumFv) {
>>> +    EfiConvertPointer (0x0, (VOID **)&FwhInstance->FvBase);
>>> +    FwhInstance = (EFI_FW_VOL_INSTANCE *)
>>> +                  (
>>> +                   (UINTN)((UINT8 *)FwhInstance) +
>>> +                   FwhInstance->VolumeHeader.HeaderLength +
>>> +                   (sizeof (EFI_FW_VOL_INSTANCE) - sizeof 
>>> (EFI_FIRMWARE_VOLUME_HEADER))
>>> +                  );
>>> +    Index++;
>>> +  }
>>> +
>>> +  EfiConvertPointer (0x0, (VOID **)&mFvbModuleGlobal);
>>> +  QemuFlashConvertPointers ();
>>> +}
>>> +
>>> +
>>> +VOID
>>> +InstallVirtualAddressChangeHandler (
>>> +  VOID
>>> +  )
>>> +{
>>> +  EFI_STATUS  Status;
>>> +  EFI_EVENT   VirtualAddressChangeEvent;
>>> +
>>> +  Status = gBS->CreateEventEx (
>>> +                  EVT_NOTIFY_SIGNAL,
>>> +                  TPL_NOTIFY,
>>> +                  FvbVirtualAddressChangeEvent,
>>> +                  NULL,
>>> +                  &gEfiEventVirtualAddressChangeGuid,
>>> +                  &VirtualAddressChangeEvent
>>> +                  );
>>> +  ASSERT_EFI_ERROR (Status);
>>> +}
>>> diff --git 
>>> a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c 
>>> b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c 
>>>
>>> new file mode 100644
>>> index 0000000000..a85d736060
>>> --- /dev/null
>>> +++ 
>>> b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c
>>> @@ -0,0 +1,251 @@
>>> +/** @file
>>> +  LoongArch support for QEMU system firmware flash device
>>> +
>>> +  Copyright (c) 2021 Loongson Technology Corporation Limited. All 
>>> rights reserved.<BR>
>>> +
>>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>>> +
>>> +**/
>>> +
>>> +#include <Library/BaseMemoryLib.h>
>>> +#include <Library/DebugLib.h>
>>> +#include <Library/PcdLib.h>
>>> +
>>> +#include "QemuFlash.h"
>>> +
>>> +#define WRITE_BYTE_CMD           0x10
>>> +#define BLOCK_ERASE_CMD          0x20
>>> +#define CLEAR_STATUS_CMD         0x50
>>> +#define READ_STATUS_CMD          0x70
>>> +#define READ_DEVID_CMD           0x90
>>> +#define BLOCK_ERASE_CONFIRM_CMD  0xd0
>>> +#define READ_ARRAY_CMD           0xff
>>> +
>>> +#define CLEARED_ARRAY_STATUS  0x00
>>> +
>>> +UINT8  *mFlashBase;
>>> +
>>> +STATIC UINTN  mFdBlockSize  = 0;
>>> +STATIC UINTN  mFdBlockCount = 0;
>>> +
>>> +STATIC
>>> +volatile UINT8 *
>>> +QemuFlashPtr (
>>> +  IN        EFI_LBA  Lba,
>>> +  IN        UINTN    Offset
>>> +  )
>>> +{
>>> +  return mFlashBase + ((UINTN)Lba * mFdBlockSize) + Offset;
>>> +}
>>> +
>>> +
>>> +/**
>>> +  Determines if the QEMU flash memory device is present.
>>> +
>>> +  @retval FALSE   The QEMU flash device is not present.
>>> +  @retval TRUE    The QEMU flash device is present.
>>> +
>>> +**/
>>> +STATIC
>>> +BOOLEAN
>>> +QemuFlashDetected (
>>> +  VOID
>>> +  )
>>> +{
>>> +  BOOLEAN         FlashDetected;
>>> +  volatile UINT8  *Ptr;
>>> +
>>> +  UINTN  Offset;
>>> +  UINT8  OriginalUint8;
>>> +  UINT8  ProbeUint8;
>>> +
>>> +  FlashDetected = FALSE;
>>> +  Ptr           = QemuFlashPtr (0, 0);
>>> +
>>> +  for (Offset = 0; Offset < mFdBlockSize; Offset++) {
>>> +    Ptr        = QemuFlashPtr (0, Offset);
>>> +    ProbeUint8 = *Ptr;
>>> +    if ((ProbeUint8 != CLEAR_STATUS_CMD) &&
>>> +        (ProbeUint8 != READ_STATUS_CMD) &&
>>> +        (ProbeUint8 != CLEARED_ARRAY_STATUS))
>>> +    {
>>> +      break;
>>> +    }
>>> +  }
>>> +
>>> +  if (Offset >= mFdBlockSize) {
>>> +    DEBUG ((DEBUG_INFO, "QEMU Flash: Failed to find probe 
>>> location\n"));
>>> +    return FALSE;
>>> +  }
>>> +
>>> +  DEBUG ((DEBUG_INFO, "QEMU Flash: Attempting flash detection at 
>>> %p\n", Ptr));
>>> +
>>> +  OriginalUint8 = *Ptr;
>>> +  *Ptr          = CLEAR_STATUS_CMD;
>>> +  ProbeUint8    = *Ptr;
>>> +  if ((OriginalUint8 != CLEAR_STATUS_CMD) &&
>>> +      (ProbeUint8 == CLEAR_STATUS_CMD))
>>> +  {
>>> +    DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as RAM\n"));
>>> +    *Ptr = OriginalUint8;
>>> +  } else {
>>> +    *Ptr       = READ_STATUS_CMD;
>>> +    ProbeUint8 = *Ptr;
>>> +    if (ProbeUint8 == OriginalUint8) {
>>> +      DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as 
>>> ROM\n"));
>>> +    } else if (ProbeUint8 == READ_STATUS_CMD) {
>>> +      DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as 
>>> RAM\n"));
>>> +      *Ptr = OriginalUint8;
>>> +    } else if (ProbeUint8 == CLEARED_ARRAY_STATUS) {
>>> +      DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as 
>>> FLASH\n"));
>>> +      FlashDetected = TRUE;
>>> +      *Ptr          = READ_ARRAY_CMD;
>>> +    }
>>> +  }
>>> +
>>> +  DEBUG ((
>>> +    DEBUG_INFO,
>>> +    "QemuFlashDetected => %a\n",
>>> +    FlashDetected ? "Yes" : "No"
>>> +    ));
>>> +  return FlashDetected;
>>> +}
>>> +
>>> +
>>> +/**
>>> +  Read from QEMU Flash
>>> +
>>> +  @param[in] Lba      The starting logical block index to read from.
>>> +  @param[in] Offset   Offset into the block at which to begin reading.
>>> +  @param[in] NumBytes On input, indicates the requested read size. On
>>> +                      output, indicates the actual number of bytes 
>>> read
>>> +  @param[in] Buffer   Pointer to the buffer to read into.
>>> +
>>> +**/
>>> +EFI_STATUS
>>> +QemuFlashRead (
>>> +  IN        EFI_LBA  Lba,
>>> +  IN        UINTN    Offset,
>>> +  IN        UINTN    *NumBytes,
>>> +  IN        UINT8    *Buffer
>>> +  )
>>> +{
>>> +  UINT8  *Ptr;
>>> +
>>> +  //
>>> +  // Only write to the first 64k. We don't bother saving the FTW Spare
>>> +  // block into the flash memory.
>>> +  //
>>> +  if (Lba >= mFdBlockCount) {
>>> +    return EFI_INVALID_PARAMETER;
>>> +  }
>>> +
>>> +  //
>>> +  // Get flash address
>>> +  //
>>> +  Ptr = (UINT8 *)QemuFlashPtr (Lba, Offset);
>>> +
>>> +  CopyMem (Buffer, Ptr, *NumBytes);
>>> +
>>> +  return EFI_SUCCESS;
>>> +}
>>> +
>>> +
>>> +/**
>>> +  Write to QEMU Flash
>>> +
>>> +  @param[in] Lba      The starting logical block index to write to.
>>> +  @param[in] Offset   Offset into the block at which to begin writing.
>>> +  @param[in] NumBytes On input, indicates the requested write size. On
>>> +                      output, indicates the actual number of bytes 
>>> written
>>> +  @param[in] Buffer   Pointer to the data to write.
>>> +
>>> +**/
>>> +EFI_STATUS
>>> +QemuFlashWrite (
>>> +  IN        EFI_LBA  Lba,
>>> +  IN        UINTN    Offset,
>>> +  IN        UINTN    *NumBytes,
>>> +  IN        UINT8    *Buffer
>>> +  )
>>> +{
>>> +  volatile UINT8  *Ptr;
>>> +  UINTN           Loop;
>>> +
>>> +  //
>>> +  // Only write to the first 64k. We don't bother saving the FTW Spare
>>> +  // block into the flash memory.
>>> +  //
>>> +  if (Lba >= mFdBlockCount) {
>>> +    return EFI_INVALID_PARAMETER;
>>> +  }
>>> +
>>> +  //
>>> +  // Program flash
>>> +  //
>>> +  Ptr = QemuFlashPtr (Lba, Offset);
>>> +  for (Loop = 0; Loop < *NumBytes; Loop++) {
>>> +    *Ptr = WRITE_BYTE_CMD;
>>> +    *Ptr = Buffer[Loop];
>>> +    Ptr++;
>>> +  }
>>> +
>>> +  //
>>> +  // Restore flash to read mode
>>> +  //
>>> +  if (*NumBytes > 0) {
>>> +    *(Ptr - 1) = READ_ARRAY_CMD;
>>> +  }
>>> +
>>> +  return EFI_SUCCESS;
>>> +}
>>> +
>>> +
>>> +/**
>>> +  Erase a QEMU Flash block
>>> +
>>> +  @param Lba    The logical block index to erase.
>>> +
>>> +**/
>>> +EFI_STATUS
>>> +QemuFlashEraseBlock (
>>> +  IN   EFI_LBA  Lba
>>> +  )
>>> +{
>>> +  volatile UINT8  *Ptr;
>>> +
>>> +  if (Lba >= mFdBlockCount) {
>>> +    return EFI_INVALID_PARAMETER;
>>> +  }
>>> +
>>> +  Ptr = QemuFlashPtr (Lba, 0);
>>> +  *Ptr = BLOCK_ERASE_CMD;
>>> +  *Ptr = BLOCK_ERASE_CONFIRM_CMD;
>>> +  return EFI_SUCCESS;
>>> +}
>>> +
>>> +
>>> +/**
>>> +  Initializes QEMU flash memory support
>>> +
>>> +  @retval EFI_WRITE_PROTECTED   The QEMU flash device is not present.
>>> +  @retval EFI_SUCCESS           The QEMU flash device is supported.
>>> +
>>> +**/
>>> +EFI_STATUS
>>> +QemuFlashInitialize (
>>> +  VOID
>>> +  )
>>> +{
>>> +  mFlashBase   = (UINT8 *)(UINTN)PcdGet64 
>>> (PcdOvmfFlashNvStorageVariableBase);
>>> +  mFdBlockSize = PcdGet32 (PcdFlashBlockSize);
>>> +  ASSERT(PcdGet32 (PcdAllVarSize) % mFdBlockSize == 0);
>>> +  mFdBlockCount = PcdGet32 (PcdAllVarSize) / mFdBlockSize;
>>> +
>>> +  if (!QemuFlashDetected ()) {
>>> +    return EFI_WRITE_PROTECTED;
>>> +  }
>>> +
>>> +  return EFI_SUCCESS;
>>> +}
>>> +
>>> diff --git 
>>> a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h 
>>> b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h 
>>>
>>> new file mode 100644
>>> index 0000000000..27caabc5f0
>>> --- /dev/null
>>> +++ 
>>> b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h
>>> @@ -0,0 +1,86 @@
>>> +/** @file
>>> +  LoongArch support for QEMU system firmware flash device
>>> +
>>> +  Copyright (c) 2021 Loongson Technology Corporation Limited. All 
>>> rights reserved.<BR>
>>> +
>>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>>> +
>>> +**/
>>> +
>>> +#ifndef __QEMU_FLASH_H__
>>> +#define __QEMU_FLASH_H__
>>> +
>>> +#include <Protocol/FirmwareVolumeBlock.h>
>>> +
>>> +extern UINT8  *mFlashBase;
>>> +
>>> +/**
>>> +  Read from QEMU Flash
>>> +
>>> +  @param[in] Lba      The starting logical block index to read from.
>>> +  @param[in] Offset   Offset into the block at which to begin reading.
>>> +  @param[in] NumBytes On input, indicates the requested read size. On
>>> +                      output, indicates the actual number of bytes 
>>> read
>>> +  @param[in] Buffer   Pointer to the buffer to read into.
>>> +
>>> +**/
>>> +EFI_STATUS
>>> +QemuFlashRead (
>>> +  IN        EFI_LBA  Lba,
>>> +  IN        UINTN    Offset,
>>> +  IN        UINTN    *NumBytes,
>>> +  IN        UINT8    *Buffer
>>> +  );
>>> +
>>> +
>>> +/**
>>> +  Write to QEMU Flash
>>> +
>>> +  @param[in] Lba      The starting logical block index to write to.
>>> +  @param[in] Offset   Offset into the block at which to begin writing.
>>> +  @param[in] NumBytes On input, indicates the requested write size. On
>>> +                      output, indicates the actual number of bytes 
>>> written
>>> +  @param[in] Buffer   Pointer to the data to write.
>>> +
>>> +**/
>>> +EFI_STATUS
>>> +QemuFlashWrite (
>>> +  IN        EFI_LBA  Lba,
>>> +  IN        UINTN    Offset,
>>> +  IN        UINTN    *NumBytes,
>>> +  IN        UINT8    *Buffer
>>> +  );
>>> +
>>> +
>>> +/**
>>> +  Erase a QEMU Flash block
>>> +
>>> +  @param Lba    The logical block index to erase.
>>> +
>>> +**/
>>> +EFI_STATUS
>>> +QemuFlashEraseBlock (
>>> +  IN   EFI_LBA  Lba
>>> +  );
>>> +
>>> +
>>> +/**
>>> +  Initializes QEMU flash memory support
>>> +
>>> +  @retval EFI_WRITE_PROTECTED   The QEMU flash device is not present.
>>> +  @retval EFI_SUCCESS           The QEMU flash device is supported.
>>> +
>>> +**/
>>> +EFI_STATUS
>>> +QemuFlashInitialize (
>>> +  VOID
>>> +  );
>>> +
>>> +
>>> +VOID
>>> +QemuFlashConvertPointers (
>>> +  VOID
>>> +  );
>>> +
>>> +#endif
>>> +
>>> diff --git 
>>> a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c 
>>> b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c 
>>>
>>> new file mode 100644
>>> index 0000000000..b63314aac2
>>> --- /dev/null
>>> +++ 
>>> b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c
>>> @@ -0,0 +1,21 @@
>>> +/** @file
>>> +  LoongArch support for QEMU system firmware flash device: 
>>> functions specific to the
>>> +  runtime DXE driver build.
>>> +
>>> +  Copyright (c) 2021 Loongson Technology Corporation Limited. All 
>>> rights reserved.<BR>
>>> +
>>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>>> +
>>> +**/
>>> +
>>> +#include <Library/UefiRuntimeLib.h>
>>> +
>>> +#include "QemuFlash.h"
>>> +
>>> +VOID
>>> +QemuFlashConvertPointers (
>>> +  VOID
>>> +  )
>>> +{
>>> +  EfiConvertPointer (0x0, (VOID **)&mFlashBase);
>>> +}
>>> diff --git a/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc 
>>> b/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc
>>> index 74c83720b7..59beafb34f 100644
>>> --- a/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc
>>> +++ b/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc
>>> @@ -171,10 +171,10 @@
>>>     VirtioLib                        | 
>>> OvmfPkg/Library/VirtioLib/VirtioLib.inf
>>>     FrameBufferBltLib                | 
>>> MdeModulePkg/Library/FrameBufferBltLib/FrameBufferBltLib.inf
>>>     QemuFwCfgLib                     | 
>>> OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibMmio.inf
>>> -
>>>     DebugLib                         | 
>>> MdePkg/Library/BaseDebugLibSerialPort/BaseDebugLibSerialPort.inf
>>> -
>>>     PeiServicesLib                   | 
>>> MdePkg/Library/PeiServicesLib/PeiServicesLib.inf
>>> +  VariableFlashInfoLib             | 
>>> MdeModulePkg/Library/BaseVariableFlashInfoLib/BaseVariableFlashInfoLib.inf
>>> +
>>>   [LibraryClasses.common.SEC]
>>>     ReportStatusCodeLib              | 
>>> MdeModulePkg/Library/PeiReportStatusCodeLib/PeiReportStatusCodeLib.inf
>>>     HobLib                           | 
>>> MdePkg/Library/PeiHobLib/PeiHobLib.inf
>>> -- 
>>> 2.31.1
>>>


^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2022-10-18  8:47 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-09-29  7:07 [edk2-platforms][PATCH V3 00/16] Platform: Add Loongson support xianglai
2022-09-29  7:07 ` [edk2-platforms][PATCH V3 01/16] Platform/Loongson: Add Serial Port library xianglai
2022-09-29  7:07 ` [edk2-platforms][PATCH V3 02/16] Platform/Loongson: Support SEC And Add Readme.md xianglai
2022-09-29  7:07 ` [edk2-platforms][PATCH V3 03/16] Platform/Loongson: Add PeiServicesTablePointerLib xianglai
2022-09-29  7:07 ` [edk2-platforms][PATCH V3 04/16] Platform/Loongson: Add QemuFwCfgLib xianglai
2022-09-29  7:07 ` [edk2-platforms][PATCH V3 05/16] Platform/Loongson: Add MmuLib xianglai
2022-09-29  7:07 ` [edk2-platforms][PATCH V3 06/16] Platform/Loongson: Add StableTimerLib xianglai
2022-09-29  7:07 ` [edk2-platforms][PATCH V3 07/16] Platform/Loongson: Support PEI phase xianglai
2022-09-29  7:07 ` [edk2-platforms][PATCH V3 08/16] Platform/Loongson: Add CPU DXE driver xianglai
2022-09-29  7:07 ` [edk2-platforms][PATCH V3 09/16] Platform/Loongson: Add PciCpuIoDxe driver xianglai
2022-09-29  7:07 ` [edk2-platforms][PATCH V3 10/16] Platform/Loongson: Add timer Dxe driver xianglai
2022-09-29  7:07 ` [edk2-platforms][PATCH V3 11/16] Platform/Loongson: Add RealTime Clock lib xianglai
2022-09-29  7:08 ` [edk2-platforms][PATCH V3 12/16] Platform/Loongson: Add Platform Boot Manager Lib xianglai
2022-09-29  7:08 ` [edk2-platforms][PATCH V3 13/16] Platform/Loongson: Add Reset System Lib xianglai
2022-09-29  7:08 ` [edk2-platforms][PATCH V3 14/16] Platform/Loongson: Support Dxe xianglai
2022-09-29  7:08 ` [edk2-platforms][PATCH V3 15/16] Platform/Loongson: Add QemuFlashFvbServicesRuntimeDxe driver xianglai
2022-09-29  7:08 ` [edk2-platforms][PATCH V3 16/16] Platform/Loongson: Support for saving variables to flash xianglai
  -- strict thread matches above, loose matches on Subject: below --
2022-10-14  4:01 [edk2-platforms][PATCH V3 00/16] Platform: Add Loongson support xianglai
2022-10-14  4:01 ` [edk2-platforms][PATCH V3 15/16] Platform/Loongson: Add QemuFlashFvbServicesRuntimeDxe driver xianglai
2022-10-17 11:00   ` Ard Biesheuvel
2022-10-17 12:56     ` xianglai
2022-10-18  8:47       ` xianglai

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox