From: "Laszlo Ersek" <lersek@redhat.com>
To: devel@edk2.groups.io, kraxel@redhat.com
Cc: Liming Gao <gaoliming@byosoft.com.cn>,
Michael Roth <michael.roth@amd.com>,
Oliver Steffen <osteffen@redhat.com>,
Erdem Aktas <erdemaktas@google.com>,
Tom Lendacky <thomas.lendacky@amd.com>,
Min Xu <min.m.xu@intel.com>,
Ard Biesheuvel <ardb+tianocore@kernel.org>,
Jiewen Yao <jiewen.yao@intel.com>
Subject: Re: [edk2-devel] [PATCH v3 5/6] OvmfPkg/ResetVector: add 5-level paging support
Date: Tue, 20 Feb 2024 18:45:44 +0100 [thread overview]
Message-ID: <63a3bb77-2ca1-6f6b-6132-492ad5427652@redhat.com> (raw)
In-Reply-To: <20240220090639.472222-6-kraxel@redhat.com>
On 2/20/24 10:06, Gerd Hoffmann wrote:
> Compile the OVMF ResetVector with 5-level paging support in case
> PcdUse5LevelPageTable is TRUE.
>
> When enabled the ResetVector will check at runtime whenever support for
> 5-level paging and gigabyte pages is available. In case both features
> are supported it will run OVMF in 5-level paging mode, otherwise
> fallback to 4-level paging.
>
> Gigabyte pages are required to make sure we can fit the page tables into
> the available space. We have six pages available, four of them are
> used. The first gibabyte is mapped with 2M pages, the 1GB -> 4GB range
> uses gigabyte pages. See the source code comment for the exact layout.
>
> In case TDX is used the TDX_WORK_AREA_PGTBL_READY will carry the
> information whenever 5-level paging is used (2) or not (1), so the
> APs can pick the correct paging mode.
>
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
> ---
> OvmfPkg/ResetVector/ResetVector.inf | 1 +
> OvmfPkg/ResetVector/Ia32/IntelTdx.asm | 17 ++-
> OvmfPkg/ResetVector/Ia32/PageTables64.asm | 131 +++++++++++++++++++++-
> OvmfPkg/ResetVector/ResetVector.nasmb | 1 +
> 4 files changed, 145 insertions(+), 5 deletions(-)
I'm sorry, but this is awful. The stuff in "PageTables64.asm" is now the
definition of spaghetti code. I find it nearly impossible to follow the
code through the forest of jumps.
For example, we have a label called "PageTablesReady", but nothing jumps
to it.
At this point I'd much prefer if we *didn't* try to reuse common page
table building code in this fashion, between TDX, SEV, and no-CC. IMO it
would be better to factor the common code out to a new "subroutine", in
a separate file, and then create a minimal and succinct high-level file
that deals with nothing but control flow / feature detection between
TDX, SEV, and neither. The current code mixes that kind of feature
checking, 5-level paging macro checking, and actual page table building
/ CR configuration.
Basically I'm proposing to implement a high-level assembly file that
reads like the following C pseudo-code:
Val = CheckTdx ();
if (!Val) {
goto Sev;
}
SetupTdx ();
goto Done;
Sev:
Val = CheckSev ();
if (!Val) {
goto NoCC;
}
SetupSev ();
goto Done;
NoCC:
SetupNoCc ();
Done:
...
And then CheckTdx() and CheckSev() would be standalone, separate
"subroutines", and SetupTdx(), SetupSev(), SetupNoCc() *too* would be
standalone, separate "subroutines". If the latter three would like to do
some stuff commonly, then they should "call" common "sub-subroutines",
or if that's not possible, then -- for all I care -- even *triplicate*
the common code, using NASM macros!
(I'm using quotes around "subroutines" and "call" because we don't have
a stack at this point yet, IIUC, so all our "one time calls" are
actually just normal jumps, with some NASM macro magic. That's fine,
we're only talking a handful of assembly instructions here, so
readability definitely trumps code path reuse. SECFV only contains
SecMain and ResetVector, and it's only 26% full -- 56K used, 152K free,
out of 208K total.)
The prime candidates for those "sub-subroutines" (or macros) are
"ClearOvmfPageTables", the 5-level page tree population, and the 4-level
page tree population.
Because, again, this level of code reuse, while I'm sure is brilliant,
functionally correct, and frugal with reset vector footprint, is also
super hard to read and maintain.
That's my opinion anyway.
Laszlo
>
> diff --git a/OvmfPkg/ResetVector/ResetVector.inf b/OvmfPkg/ResetVector/ResetVector.inf
> index a4154ca90c28..65f71b05a02e 100644
> --- a/OvmfPkg/ResetVector/ResetVector.inf
> +++ b/OvmfPkg/ResetVector/ResetVector.inf
> @@ -64,3 +64,4 @@ [FixedPcd]
> gUefiOvmfPkgTokenSpaceGuid.PcdQemuHashTableSize
> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsBase
> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSnpSecretsSize
> + gEfiMdeModulePkgTokenSpaceGuid.PcdUse5LevelPageTable
> diff --git a/OvmfPkg/ResetVector/Ia32/IntelTdx.asm b/OvmfPkg/ResetVector/Ia32/IntelTdx.asm
> index 06794baef81d..3e50ca76aacf 100644
> --- a/OvmfPkg/ResetVector/Ia32/IntelTdx.asm
> +++ b/OvmfPkg/ResetVector/Ia32/IntelTdx.asm
> @@ -179,7 +179,7 @@ InitTdx:
> ;
> ; Modified: EAX, EDX
> ;
> -; 0-NonTdx, 1-TdxBsp, 2-TdxAps
> +; 0-NonTdx, 1-TdxBsp, 2-TdxAps, 3-TdxApsLa57
> ;
> CheckTdxFeaturesBeforeBuildPagetables:
> xor eax, eax
> @@ -204,6 +204,21 @@ TdxPostBuildPageTables:
> ExitTdxPostBuildPageTables:
> OneTimeCallRet TdxPostBuildPageTables
>
> +%if PG_5_LEVEL
> +
> +;
> +; Set byte[TDX_WORK_AREA_PGTBL_READY] to 2
> +;
> +TdxPostBuildPageTablesLa57:
> + cmp byte[WORK_AREA_GUEST_TYPE], VM_GUEST_TDX
> + jne ExitTdxPostBuildPageTablesLa57
> + mov byte[TDX_WORK_AREA_PGTBL_READY], 2
> +
> +ExitTdxPostBuildPageTablesLa57:
> + OneTimeCallRet TdxPostBuildPageTablesLa57
> +
> +%endif
> +
> ;
> ; Check if TDX is enabled
> ;
> diff --git a/OvmfPkg/ResetVector/Ia32/PageTables64.asm b/OvmfPkg/ResetVector/Ia32/PageTables64.asm
> index 6fec6f2beeea..21de75a40097 100644
> --- a/OvmfPkg/ResetVector/Ia32/PageTables64.asm
> +++ b/OvmfPkg/ResetVector/Ia32/PageTables64.asm
> @@ -42,8 +42,10 @@ BITS 32
> PAGE_READ_WRITE + \
> PAGE_PRESENT)
>
> +%define NOT_TDX 0
> %define TDX_BSP 1
> %define TDX_AP 2
> +%define TDX_AP_LA57 3
>
> ;
> ; Modified: EAX, EBX, ECX, EDX
> @@ -55,11 +57,21 @@ SetCr3ForPageTables64:
> ; the page tables. APs will spin on until byte[TDX_WORK_AREA_PGTBL_READY]
> ; is set.
> OneTimeCall CheckTdxFeaturesBeforeBuildPagetables
> + cmp eax, NOT_TDX
> + je CheckSev
> cmp eax, TDX_BSP
> je ClearOvmfPageTables
> +%if PG_5_LEVEL
> cmp eax, TDX_AP
> je SetCr3
> + ; TDX_AP_LA57 -> set cr4.la57
> + mov eax, cr4
> + bts eax, 12
> + mov cr4, eax
> +%endif
> + jmp SetCr3
>
> +CheckSev:
> ; Check whether the SEV is active and populate the SevEsWorkArea
> OneTimeCall CheckSevFeatures
>
> @@ -86,6 +98,105 @@ clearPageTablesMemoryLoop:
> mov dword[ecx * 4 + PT_ADDR (0) - 4], eax
> loop clearPageTablesMemoryLoop
>
> +%if PG_5_LEVEL
> +
> + ; save GetSevCBitMaskAbove31 result (cpuid changes edx)
> + mov edi, edx
> +
> + ; check for cpuid leaf 0x07
> + mov eax, 0x00
> + cpuid
> + cmp eax, 0x07
> + jb Paging4Lvl
> +
> + ; check for la57 (aka 5-level paging)
> + mov eax, 0x07
> + mov ecx, 0x00
> + cpuid
> + bt ecx, 16
> + jnc Paging4Lvl
> +
> + ; check for cpuid leaf 0x80000001
> + mov eax, 0x80000000
> + cpuid
> + cmp eax, 0x80000001
> + jb Paging4Lvl
> +
> + ; check for 1g pages
> + mov eax, 0x80000001
> + cpuid
> + bt edx, 26
> + jnc Paging4Lvl
> +
> + ;
> + ; Use 5-level paging with gigabyte pages.
> + ;
> + ; We have 6 pages available for the early page tables,
> + ; we use four of them:
> + ; PT_ADDR(0) - level 5 directory
> + ; PT_ADDR(0x1000) - level 4 directory
> + ; PT_ADDR(0x2000) - level 2 directory (0 -> 1GB)
> + ; PT_ADDR(0x3000) - level 3 directory
> + ;
> + ; The level 2 directory for the first gigabyte has the same
> + ; physical address in both 4-level and 5-level paging mode,
> + ; SevClearPageEncMaskForGhcbPage depends on this.
> + ;
> + ; The 1 GB -> 4 GB range is mapped using 1G pages in the
> + ; level 3 directory.
> + ;
> + debugShowPostCode 0x51 ; 5-level paging
> +
> + ; restore GetSevCBitMaskAbove31 result
> + mov edx, edi
> +
> + ; level 5
> + mov dword[PT_ADDR (0)], PT_ADDR (0x1000) + PAGE_PDE_DIRECTORY_ATTR
> + mov dword[PT_ADDR (4)], edx
> +
> + ; level 4
> + mov dword[PT_ADDR (0x1000)], PT_ADDR (0x3000) + PAGE_PDE_DIRECTORY_ATTR
> + mov dword[PT_ADDR (0x1004)], edx
> +
> + ; level 3 (1x -> level 2, 3x 1GB)
> + mov dword[PT_ADDR (0x3000)], PT_ADDR (0x2000) + PAGE_PDE_DIRECTORY_ATTR
> + mov dword[PT_ADDR (0x3004)], edx
> + mov dword[PT_ADDR (0x3008)], (1 << 30) + PAGE_PDE_LARGEPAGE_ATTR
> + mov dword[PT_ADDR (0x300c)], edx
> + mov dword[PT_ADDR (0x3010)], (2 << 30) + PAGE_PDE_LARGEPAGE_ATTR
> + mov dword[PT_ADDR (0x3014)], edx
> + mov dword[PT_ADDR (0x3018)], (3 << 30) + PAGE_PDE_LARGEPAGE_ATTR
> + mov dword[PT_ADDR (0x301c)], edx
> +
> + ;
> + ; level 2 (512 * 2MB entries => 1GB)
> + ;
> + mov ecx, 0x200
> +pageTableEntriesLoopLa57:
> + mov eax, ecx
> + dec eax
> + shl eax, 21
> + add eax, PAGE_PDE_LARGEPAGE_ATTR
> + mov [ecx * 8 + PT_ADDR (0x2000 - 8)], eax
> + mov [(ecx * 8 + PT_ADDR (0x2000 - 8)) + 4], edx
> + loop pageTableEntriesLoopLa57
> +
> + ; set la57 bit in cr4
> + mov eax, cr4
> + bts eax, 12
> + mov cr4, eax
> +
> + ; done
> + jmp PageTablesReadyLa57
> +
> +Paging4Lvl:
> + debugShowPostCode 0x41 ; 4-level paging
> +
> + ; restore GetSevCBitMaskAbove31 result
> + mov edx, edi
> +
> +%endif ; PG_5_LEVEL
> +
> ;
> ; Top level Page Directory Pointers (1 * 512GB entry)
> ;
> @@ -117,13 +228,25 @@ pageTableEntriesLoop:
> mov [(ecx * 8 + PT_ADDR (0x2000 - 8)) + 4], edx
> loop pageTableEntriesLoop
>
> +%if PG_5_LEVEL
> +
> +PageTablesReadyLa57:
> + ; TDX will do some PostBuildPages task, such as setting
> + ; byte[TDX_WORK_AREA_PGTBL_READY].
> + OneTimeCall TdxPostBuildPageTablesLa57
> + jmp SevPostBuildPageTables
> +
> +%endif
> +
> +PageTablesReady:
> + ; TDX will do some PostBuildPages task, such as setting
> + ; byte[TDX_WORK_AREA_PGTBL_READY].
> + OneTimeCall TdxPostBuildPageTables
> +
> +SevPostBuildPageTables:
> ; Clear the C-bit from the GHCB page if the SEV-ES is enabled.
> OneTimeCall SevClearPageEncMaskForGhcbPage
>
> - ; TDX will do some PostBuildPages task, such as setting
> - ; byte[TDX_WORK_AREA_PGTBL_READY].
> - OneTimeCall TdxPostBuildPageTables
> -
> SetCr3:
> ;
> ; Set CR3 now that the paging structures are available
> diff --git a/OvmfPkg/ResetVector/ResetVector.nasmb b/OvmfPkg/ResetVector/ResetVector.nasmb
> index 366a70fb9992..2bd80149e58b 100644
> --- a/OvmfPkg/ResetVector/ResetVector.nasmb
> +++ b/OvmfPkg/ResetVector/ResetVector.nasmb
> @@ -53,6 +53,7 @@
>
> %define WORK_AREA_GUEST_TYPE (FixedPcdGet32 (PcdOvmfWorkAreaBase))
> %define PT_ADDR(Offset) (FixedPcdGet32 (PcdOvmfSecPageTablesBase) + (Offset))
> +%define PG_5_LEVEL (FixedPcdGetBool (PcdUse5LevelPageTable))
>
> %define GHCB_PT_ADDR (FixedPcdGet32 (PcdOvmfSecGhcbPageTableBase))
> %define GHCB_BASE (FixedPcdGet32 (PcdOvmfSecGhcbBase))
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#115664): https://edk2.groups.io/g/devel/message/115664
Mute This Topic: https://groups.io/mt/104464309/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-
next prev parent reply other threads:[~2024-02-20 17:45 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-02-20 9:06 [edk2-devel] [PATCH v3 0/6] OvmfPkg: Add support for 5-level paging Gerd Hoffmann
2024-02-20 9:06 ` [edk2-devel] [PATCH v3 1/6] MdeModulePkg/DxeIplPeim: fix PcdUse5LevelPageTable assert Gerd Hoffmann
2024-02-20 9:06 ` [edk2-devel] [PATCH v3 2/6] MdeModulePkg/DxeIplPeim: rename variable Gerd Hoffmann
2024-02-20 9:06 ` [edk2-devel] [PATCH v3 3/6] OvmfPkg/ResetVector: improve page table flag names Gerd Hoffmann
2024-02-20 9:06 ` [edk2-devel] [PATCH v3 4/6] OvmfPkg/ResetVector: SEV: keep #vc handler installed longer Gerd Hoffmann
2024-02-20 16:48 ` Laszlo Ersek
2024-02-20 19:56 ` Lendacky, Thomas via groups.io
2024-02-20 9:06 ` [edk2-devel] [PATCH v3 5/6] OvmfPkg/ResetVector: add 5-level paging support Gerd Hoffmann
2024-02-20 17:45 ` Laszlo Ersek [this message]
2024-02-20 18:32 ` Laszlo Ersek
2024-02-20 22:18 ` Lendacky, Thomas via groups.io
2024-02-20 9:06 ` [edk2-devel] [PATCH v3 6/6] OvmfPkg/PlatformInitLib: " Gerd Hoffmann
2024-02-21 6:42 ` [edk2-devel] [PATCH v3 0/6] OvmfPkg: Add support for 5-level paging Min Xu
2024-02-21 13:31 ` Ard Biesheuvel
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-list from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=63a3bb77-2ca1-6f6b-6132-492ad5427652@redhat.com \
--to=devel@edk2.groups.io \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox