From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mga11.intel.com (mga11.intel.com [192.55.52.93]) by mx.groups.io with SMTP id smtpd.web11.904.1594790598963368853 for ; Tue, 14 Jul 2020 22:23:19 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@intel.onmicrosoft.com header.s=selector2-intel-onmicrosoft-com header.b=b3R5gQKF; spf=pass (domain: intel.com, ip: 192.55.52.93, mailfrom: eric.dong@intel.com) IronPort-SDR: 7kl8YpAt4PPScUTcXPh6FBsRj08xc4KCqHsvoh0XpSrF0sTTAvPW+3hMHfZpq0L/BfgpRgSVRG 6Ml+r/R4JMNw== X-IronPort-AV: E=McAfee;i="6000,8403,9682"; a="147091944" X-IronPort-AV: E=Sophos;i="5.75,354,1589266800"; d="scan'208";a="147091944" X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga005.jf.intel.com ([10.7.209.41]) by fmsmga102.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 14 Jul 2020 22:23:17 -0700 IronPort-SDR: vwMEv1GktinCj4dWxNSUaddet4wZuCvWiFVXskksEVxGwvXq6k4PDUe62IGjoVAkgcr3FA8NS4 uuJoKvbRE6Yg== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.75,354,1589266800"; d="scan'208";a="459945899" Received: from fmsmsx107.amr.corp.intel.com ([10.18.124.205]) by orsmga005.jf.intel.com with ESMTP; 14 Jul 2020 22:23:17 -0700 Received: from fmsmsx102.amr.corp.intel.com (10.18.124.200) by fmsmsx107.amr.corp.intel.com (10.18.124.205) with Microsoft SMTP Server (TLS) id 14.3.439.0; Tue, 14 Jul 2020 22:23:16 -0700 Received: from FMSEDG001.ED.cps.intel.com (10.1.192.133) by FMSMSX102.amr.corp.intel.com (10.18.124.200) with Microsoft SMTP Server (TLS) id 14.3.439.0; Tue, 14 Jul 2020 22:23:15 -0700 Received: from NAM02-SN1-obe.outbound.protection.outlook.com (104.47.36.54) by edgegateway.intel.com (192.55.55.68) with Microsoft SMTP Server (TLS) id 14.3.439.0; Tue, 14 Jul 2020 22:23:15 -0700 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=Ei2h8SKh1XAoGcq2AfVlunsXxAU88QcoJwdOceSd+hP+W5w+rLv4Gk9rA1LKkBWrmeQjRTlRXz8tyJoUa+UW/yP0WLVh1XpittkgD8zGzho/i3sRTE2CekavVgh8ljtJtqMe7SEueAj7VtwbbjQe4La/94EL7ZSByyokMQxTu/UI6dxUQ49msETeoIKX2SWMJaPDst/FpmngVd8v0mkge/1mO1kz1vxpUG4ZkO4yMRUF6IfscSsF5rAACHjAj1de9GY8BjvP4eNgPvVHgm3BdhSfDznu1AdyGQU2sG2jS0aM/CIGJVVahziIARPWYkaod3qZCtnXgI3+HOYQEN9XEA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=f2NXiwVfgUYen0CbGDSJs2tdIacVXRgGaCWEKOdUWYM=; b=bCd0Ae0cx5s/5dEIK+jYO7NYILxMnj/PJCSeurl8JDR/DdpcUWMqjCDp1jEiu3zET/Bc2DeNh41IlMj8v47OVbY0okeOWH7i+i6IsbWpc3Csbi7T7omjKoiE5R2judELaaD1xUij0TSzat7ditSYyU6Pw7mJT0OO7eLsWuvRh+ePaqXIll1tdVpARcSf/MSxhNRqmHU25e7/7I04wjQF9jm0nm5HAXG0+4OjTBXuqr+30DChZSdG26O73SAxU+RMRdPH5ZlROl9j65UprUtPHShvYcIUNH6mWKMJzBqii1KwsboSLTfvcgBTXLw7e8GUWiaEu4zqiK07e1cdYpN6Gw== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=intel.com; dmarc=pass action=none header.from=intel.com; dkim=pass header.d=intel.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=intel.onmicrosoft.com; s=selector2-intel-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=f2NXiwVfgUYen0CbGDSJs2tdIacVXRgGaCWEKOdUWYM=; b=b3R5gQKFYH8WHhjcv60v3mr+LeK7s4WoI8t8wPQaSHGPG9MKrHPtj/FDVDm3BfPOwt8LXgBLBCkDn9s9WbiO00r2LPsdibtZEclC5ZoXDN8+f8qmQH8tONrV+9tdGUHSbIR9yA8yKTtVmeJ889+RsESLC7Zg2WKs7BroA5+w+b4= Received: from DM6PR11MB3274.namprd11.prod.outlook.com (2603:10b6:5:b::26) by DM6PR11MB3130.namprd11.prod.outlook.com (2603:10b6:5:67::20) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3174.20; Wed, 15 Jul 2020 05:23:13 +0000 Received: from DM6PR11MB3274.namprd11.prod.outlook.com ([fe80::cc01:6f05:1402:e7d7]) by DM6PR11MB3274.namprd11.prod.outlook.com ([fe80::cc01:6f05:1402:e7d7%6]) with mapi id 15.20.3174.025; Wed, 15 Jul 2020 05:23:13 +0000 From: "Dong, Eric" To: Tom Lendacky , "devel@edk2.groups.io" CC: Brijesh Singh , Ard Biesheuvel , "Justen, Jordan L" , Laszlo Ersek , "Gao, Liming" , "Kinney, Michael D" , "Ni, Ray" Subject: Re: [PATCH v10 42/46] UefiCpuPkg: Allow AP booting under SEV-ES Thread-Topic: [PATCH v10 42/46] UefiCpuPkg: Allow AP booting under SEV-ES Thread-Index: AQHWWex+e/h2IdS0kkiNXGimFfDWs6kIGM/A Date: Wed, 15 Jul 2020 05:23:13 +0000 Message-ID: References: <1dab98378f132f176345756ed09a602c86dab6d6.1594736896.git.thomas.lendacky@amd.com> In-Reply-To: <1dab98378f132f176345756ed09a602c86dab6d6.1594736896.git.thomas.lendacky@amd.com> Accept-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: amd.com; dkim=none (message not signed) header.d=none;amd.com; dmarc=none action=none header.from=intel.com; x-originating-ip: [192.102.204.38] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 88ceef44-ca87-4a98-4315-08d8287f2c04 x-ms-traffictypediagnostic: DM6PR11MB3130: x-ld-processed: 46c98d88-e344-4ed4-8496-4ed7712e255d,ExtAddr x-ms-exchange-transport-forked: True x-microsoft-antispam-prvs: x-ms-oob-tlc-oobclassifiers: OLM:8882; x-ms-exchange-senderadcheck: 1 x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: HZAy29VcehGkDkdFlPa8bHNJfEDJE2t9JbDkIj2bAmv0djw5IZ/X8kttZoo5gyBd7asERL/MwDr/KMTyYTMtOly+YWwOZ5d4nIThziQ02/Ys0XmIGjBbzjoBRxaMguUpfSI4YuR7RfkO0Qw4kTaeN8+l3yk/qgt31R6khDYXwZ0s0YD3S6XvpMd5YJ/Lxwl3RvwVwUt9DEKJ1LNyMuPZy/YBCOgLjUKoE3tLUeQoqGCoIAFp/r8PdIyu5tqAzJAVj0AFEt+i/xXZGA8oFyXRQiVhz6auXr4auVJPPwWM9yH5fzYftg+Cky8f+BqNbRVkYzhE9zMsyEJyjMSff4OfL4JCdxJu/6J+rzY0hlo/9NwHovZ3MN5GM+mKw6tEQjB89LqpF3P8BRfIfufNsOkq6Q== x-forefront-antispam-report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:DM6PR11MB3274.namprd11.prod.outlook.com;PTR:;CAT:NONE;SFTY:;SFS:(4636009)(136003)(396003)(346002)(39860400002)(366004)(376002)(2906002)(55016002)(66446008)(30864003)(19627235002)(33656002)(64756008)(4326008)(66476007)(107886003)(7696005)(66946007)(66556008)(53546011)(6506007)(26005)(186003)(83380400001)(76116006)(8936002)(110136005)(5660300002)(54906003)(86362001)(52536014)(966005)(316002)(71200400001)(478600001)(9686003)(8676002)(559001)(579004);DIR:OUT;SFP:1102; x-ms-exchange-antispam-messagedata: p+aNh1RoCZlXHtWHmoFPq3X9YrXGzqkGEMq7jY+2k8x5Uks6HSSC0HMoJfoGDzhNIJO3GqSjHWrH1jEyOvtAwdauUC8/yMCFZU0ZdEXu+HaIBinXnjQhk3fzPrtUHp+5KBDNH9KhbklrQC3VH7s0eTbM4E4J6dXl0upldmSqRW/PVY0LlmrRWI0aN/bUiLuZwGgzE8vDKIzucokuGOcUPsv7MZd9k77XsQ0iidL0Gjw0RT6v3ZFZiJf0FWNcz0u2yUOfQMp3m6BZ2RMNo0mfL0s5jqdJSAfSsKAIUiWd5kL0MsJSlCGZ1TavxKObmHt+cwuO1bzzg+ywoxoaitZbD+P8zghaTKyLDq+Emlcsce3k5H0Oh53tm7J93UDmKsqkaF2HAGXaFxOMsy1Oyc+pUgomOiiDnS+ap4qB/X/qJeqHyCarbzEK5PpPLA/kSkJqWKxxyR9QJAN3YKuKie1dptQ002FtLHjlC/f1A/lnZ/HTg5NLZLdYIW46N6j+hdxW MIME-Version: 1.0 X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: DM6PR11MB3274.namprd11.prod.outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: 88ceef44-ca87-4a98-4315-08d8287f2c04 X-MS-Exchange-CrossTenant-originalarrivaltime: 15 Jul 2020 05:23:13.5690 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 46c98d88-e344-4ed4-8496-4ed7712e255d X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: RLIF6l5vLYLjDIZAiZPOq3pmEX3mO/3510qh8/Fi16Px9JIMrlzjZ597yf1e9uPEBiufe9LMnMZVh0U0BQU0uw== X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM6PR11MB3130 Return-Path: eric.dong@intel.com X-OriginatorOrg: intel.com Content-Language: en-US Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable Reviewed-by: Eric Dong > -----Original Message----- > From: Tom Lendacky > Sent: Tuesday, July 14, 2020 10:38 PM > To: devel@edk2.groups.io > Cc: Brijesh Singh ; Ard Biesheuvel > ; Dong, Eric ; Justen, > Jordan L ; Laszlo Ersek ; > Gao, Liming ; Kinney, Michael D > ; Ni, Ray > Subject: [PATCH v10 42/46] UefiCpuPkg: Allow AP booting under SEV-ES >=20 > From: Tom Lendacky >=20 > BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3D2198 >=20 > Typically, an AP is booted using the INIT-SIPI-SIPI sequence. This sequen= ce is > intercepted by the hypervisor, which sets the AP's registers to the value= s > requested by the sequence. At that point, the hypervisor can start the AP= , > which will then begin execution at the appropriate location. >=20 > Under SEV-ES, AP booting presents some challenges since the hypervisor is > not allowed to alter the AP's register state. In this situation, we have = to > distinguish between the AP's first boot and AP's subsequent boots. >=20 > First boot: > Once the AP's register state has been defined (which is before the guest= is > first booted) it cannot be altered. Should the hypervisor attempt to alt= er the > register state, the change would be detected by the hardware and the > VMRUN instruction would fail. Given this, the first boot for the AP is > required to begin execution with this initial register state, which is t= ypically > the reset vector. This prevents the BSP from directing the AP startup > location through the INIT-SIPI-SIPI sequence. >=20 > To work around this, the firmware will provide a build time reserved are= a > that can be used as the initial IP value. The hypervisor can extract this > location value by checking for the SEV-ES reset block GUID that must be > located 48-bytes from the end of the firmware. The format of the SEV-ES > reset block area is: >=20 > 0x00 - 0x01 - SEV-ES Reset IP > 0x02 - 0x03 - SEV-ES Reset CS Segment Base[31:16] > 0x04 - 0x05 - Size of the SEV-ES reset block > 0x06 - 0x15 - SEV-ES Reset Block GUID > (00f771de-1a7e-4fcb-890e-68c77e2fb44e) >=20 > The total size is 22 bytes. Any expansion to this block must be done > by adding new values before existing values. >=20 > The hypervisor will use the IP and CS values obtained from the SEV-ES r= eset > block to set as the AP's initial values. The CS Segment Base represents = the > upper 16 bits of the CS segment base and must be left shifted by 16 bits= to > form the complete CS segment base value. >=20 > Before booting the AP for the first time, the BSP must initialize the S= EV-ES > reset area. This consists of programming a FAR JMP instruction to the > contents of a memory location that is also located in the SEV-ES reset a= rea. > The BSP must program the IP and CS values for the FAR JMP based on value= s > drived from the INIT-SIPI-SIPI sequence. >=20 > Subsequent boots: > Again, the hypervisor cannot alter the AP register state, so a method is > required to take the AP out of halt state and redirect it to the desired = IP > location. If it is determined that the AP is running in an SEV-ES guest,= then > instead of calling CpuSleep(), a VMGEXIT is issued with the AP Reset Hol= d > exit code (0x80000004). The hypervisor will put the AP in a halt state, = waiting > for an INIT-SIPI-SIPI sequence. Once the sequence is recognized, the > hypervisor will resume the AP. At this point the AP must transition from= the > current 64-bit long mode down to 16-bit real mode and begin executing at > the derived location from the INIT-SIPI-SIPI sequence. >=20 > Another change is around the area of obtaining the (x2)APIC ID during AP > startup. During AP startup, the AP can't take a #VC exception before the = AP > has established a stack. However, the AP stack is set by using the (x2)A= PIC ID, > which is obtained through CPUID instructions. A CPUID instruction will c= ause > a #VC, so a different method must be used. The GHCB protocol supports a > method to obtain CPUID information from the hypervisor through the GHCB > MSR. This method does not require a stack, so it is used to obtain the > necessary CPUID information to determine the (x2)APIC ID. >=20 > The new 16-bit protected mode GDT entry is used in order to transition fr= om > 64-bit long mode down to 16-bit real mode. >=20 > A new assembler routine is created that takes the AP from 64-bit long mod= e > to 16-bit real mode. This is located under 1MB in memory and transitions > from 64-bit long mode to 32-bit compatibility mode to 16-bit protected mo= de > and finally 16-bit real mode. >=20 > Cc: Eric Dong > Cc: Ray Ni > Cc: Laszlo Ersek > Signed-off-by: Tom Lendacky > --- > UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf | 3 + > UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf | 3 + > UefiCpuPkg/Library/MpInitLib/MpLib.h | 60 ++++ > UefiCpuPkg/Library/MpInitLib/DxeMpLib.c | 70 +++- > UefiCpuPkg/Library/MpInitLib/MpLib.c | 336 +++++++++++++++++- > UefiCpuPkg/Library/MpInitLib/PeiMpLib.c | 19 + > UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c | 2 +- > UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc | 2 +- > .../Library/MpInitLib/Ia32/MpFuncs.nasm | 15 + > UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc | 4 +- > UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm | 239 +++++++++++++ > 11 files changed, 738 insertions(+), 15 deletions(-) >=20 > diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf > b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf > index 583276595619..1771575c69c1 100644 > --- a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf > +++ b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf > @@ -52,6 +52,7 @@ [LibraryClasses] > DebugAgentLib > SynchronizationLib > PcdLib > + VmgExitLib >=20 > [Protocols] > gEfiTimerArchProtocolGuid ## SOMETIMES_CONSUMES > @@ -72,4 +73,6 @@ [Pcd] > gUefiCpuPkgTokenSpaceGuid.PcdCpuApTargetCstate #= # > SOMETIMES_CONSUMES >=20 > gUefiCpuPkgTokenSpaceGuid.PcdCpuApStatusCheckIntervalInMicroSeconds > ## CONSUMES > gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled #= # > CONSUMES > + gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase #= # > SOMETIMES_CONSUMES > gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard #= # > CONSUMES > + gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase #= # > CONSUMES > diff --git a/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf > b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf > index 4b3d39fbf36c..34abf25d43cd 100644 > --- a/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf > +++ b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf > @@ -51,6 +51,7 @@ [LibraryClasses] > SynchronizationLib > PeiServicesLib > PcdLib > + VmgExitLib >=20 > [Pcd] > gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber ## > CONSUMES > @@ -62,6 +63,8 @@ [Pcd] > gUefiCpuPkgTokenSpaceGuid.PcdCpuApLoopMode ## > CONSUMES > gUefiCpuPkgTokenSpaceGuid.PcdCpuApTargetCstate ## > SOMETIMES_CONSUMES > gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled ## CO= NSUMES > + gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase ## > SOMETIMES_CONSUMES > + gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase ## > CONSUMES >=20 > [Ppis] > gEdkiiPeiShadowMicrocodePpiGuid ## SOMETIMES_CONSUMES > diff --git a/UefiCpuPkg/Library/MpInitLib/MpLib.h > b/UefiCpuPkg/Library/MpInitLib/MpLib.h > index 5b46c295b6b2..b1a9d99cb3eb 100644 > --- a/UefiCpuPkg/Library/MpInitLib/MpLib.h > +++ b/UefiCpuPkg/Library/MpInitLib/MpLib.h > @@ -173,6 +173,11 @@ typedef struct { > UINT8 *RelocateApLoopFuncAddress; > UINTN RelocateApLoopFuncSize; > UINTN ModeTransitionOffset; > + UINTN SwitchToRealSize; > + UINTN SwitchToRealOffset; > + UINTN SwitchToRealNoNxOffset; > + UINTN SwitchToRealPM16ModeOffset; > + UINTN SwitchToRealPM16ModeSize; > } MP_ASSEMBLY_ADDRESS_MAP; >=20 > typedef struct _CPU_MP_DATA CPU_MP_DATA; @@ -211,6 +216,8 @@ > typedef struct { > // Enable5LevelPaging indicates whether 5-level paging is enabled in l= ong > mode. > // > BOOLEAN Enable5LevelPaging; > + BOOLEAN SevEsIsEnabled; > + UINTN GhcbBase; > } MP_CPU_EXCHANGE_INFO; >=20 > #pragma pack() > @@ -257,6 +264,7 @@ struct _CPU_MP_DATA { > UINT8 ApLoopMode; > UINT8 ApTargetCState; > UINT16 PmCodeSegment; > + UINT16 Pm16CodeSegment; > CPU_AP_DATA *CpuData; > volatile MP_CPU_EXCHANGE_INFO *MpCpuExchangeInfo; >=20 > @@ -278,8 +286,47 @@ struct _CPU_MP_DATA { > BOOLEAN WakeUpByInitSipiSipi; >=20 > BOOLEAN SevEsIsEnabled; > + UINTN SevEsAPBuffer; > + UINTN SevEsAPResetStackStart; > + CPU_MP_DATA *NewCpuMpData; > + > + UINT64 GhcbBase; > }; >=20 > +#define AP_RESET_STACK_SIZE 64 > + > +#pragma pack(1) > + > +typedef struct { > + UINT8 InsnBuffer[8]; > + UINT16 Rip; > + UINT16 Segment; > +} SEV_ES_AP_JMP_FAR; > + > +#pragma pack() > + > +/** > + Assembly code to move an AP from long mode to real mode. > + > + Move an AP from long mode to real mode in preparation to invoking > + the reset vector. This is used for SEV-ES guests where a hypervisor > + is not allowed to set the CS and RIP to point to the reset vector. > + > + @param[in] BufferStart The reset vector target. > + @param[in] Code16 16-bit protected mode code segment value. > + @param[in] Code32 32-bit protected mode code segment value. > + @param[in] StackStart The start of a stack to be used for transitio= ning > + from long mode to real mode. > +**/ > +typedef > +VOID > +(EFIAPI AP_RESET) ( > + IN UINTN BufferStart, > + IN UINT16 Code16, > + IN UINT16 Code32, > + IN UINTN StackStart > + ); > + > extern EFI_GUID mCpuInitMpLibHobGuid; >=20 > /** > @@ -385,6 +432,19 @@ GetModeTransitionBuffer ( > IN UINTN BufferSize > ); >=20 > +/** > + Return the address of the SEV-ES AP jump table. > + > + This buffer is required in order for an SEV-ES guest to transition > + from UEFI into an OS. > + > + @return Return SEV-ES AP jump table buffer > +**/ > +UINTN > +GetSevEsAPMemory ( > + VOID > + ); > + > /** > This function will be called by BSP to wakeup AP. >=20 > diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c > b/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c > index 8ccddf8e9f9c..9115ff9e3e30 100644 > --- a/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c > +++ b/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c > @@ -12,6 +12,8 @@ > #include > #include > #include > +#include > +#include >=20 > #include >=20 > @@ -144,6 +146,39 @@ GetModeTransitionBuffer ( > return (UINTN)StartAddress; > } >=20 > +/** > + Return the address of the SEV-ES AP jump table. > + > + This buffer is required in order for an SEV-ES guest to transition > + from UEFI into an OS. > + > + @return Return SEV-ES AP jump table buffer > +**/ > +UINTN > +GetSevEsAPMemory ( > + VOID > + ) > +{ > + EFI_STATUS Status; > + EFI_PHYSICAL_ADDRESS StartAddress; > + > + // > + // Allocate 1 page for AP jump table page // StartAddress =3D > + BASE_4GB - 1; Status =3D gBS->AllocatePages ( > + AllocateMaxAddress, > + EfiReservedMemoryType, > + 1, > + &StartAddress > + ); > + ASSERT_EFI_ERROR (Status); > + > + DEBUG ((DEBUG_INFO, "Dxe: SevEsAPMemory =3D %lx\n", (UINTN) > + StartAddress)); > + > + return (UINTN) StartAddress; > +} > + > /** > Checks APs status and updates APs status if needed. >=20 > @@ -218,6 +253,38 @@ CheckApsStatus ( > } > } >=20 > +/** > + Get Protected mode code segment with 16-bit default addressing > + from current GDT table. > + > + @return Protected mode 16-bit code segment value. > +**/ > +UINT16 > +GetProtectedMode16CS ( > + VOID > + ) > +{ > + IA32_DESCRIPTOR GdtrDesc; > + IA32_SEGMENT_DESCRIPTOR *GdtEntry; > + UINTN GdtEntryCount; > + UINT16 Index; > + > + Index =3D (UINT16) -1; > + AsmReadGdtr (&GdtrDesc); > + GdtEntryCount =3D (GdtrDesc.Limit + 1) / sizeof > +(IA32_SEGMENT_DESCRIPTOR); > + GdtEntry =3D (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base; > + for (Index =3D 0; Index < GdtEntryCount; Index++) { > + if (GdtEntry->Bits.L =3D=3D 0) { > + if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.DB =3D=3D 0) { > + break; > + } > + } > + GdtEntry++; > + } > + ASSERT (Index !=3D GdtEntryCount); > + return Index * 8; > +} > + > /** > Get Protected mode code segment from current GDT table. >=20 > @@ -238,7 +305,7 @@ GetProtectedModeCS ( > GdtEntry =3D (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base; > for (Index =3D 0; Index < GdtEntryCount; Index++) { > if (GdtEntry->Bits.L =3D=3D 0) { > - if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.L =3D=3D 0) { > + if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.DB =3D=3D 1) { > break; > } > } > @@ -300,6 +367,7 @@ MpInitChangeApLoopCallback ( >=20 > CpuMpData =3D GetCpuMpData (); > CpuMpData->PmCodeSegment =3D GetProtectedModeCS (); > + CpuMpData->Pm16CodeSegment =3D GetProtectedMode16CS (); > CpuMpData->ApLoopMode =3D PcdGet8 (PcdCpuApLoopMode); > mNumberToFinish =3D CpuMpData->CpuCount - 1; > WakeUpAP (CpuMpData, TRUE, 0, RelocateApLoop, NULL, TRUE); diff --git > a/UefiCpuPkg/Library/MpInitLib/MpLib.c > b/UefiCpuPkg/Library/MpInitLib/MpLib.c > index 2a3fbeef35f7..90416c81b616 100644 > --- a/UefiCpuPkg/Library/MpInitLib/MpLib.c > +++ b/UefiCpuPkg/Library/MpInitLib/MpLib.c > @@ -9,6 +9,9 @@ > **/ >=20 > #include "MpLib.h" > +#include > +#include > +#include >=20 > EFI_GUID mCpuInitMpLibHobGuid =3D CPU_INIT_MP_LIB_HOB_GUID; >=20 > @@ -291,6 +294,14 @@ GetApLoopMode ( > // > ApLoopMode =3D ApInHltLoop; > } > + > + if (PcdGetBool (PcdSevEsIsEnabled)) { > + // > + // For SEV-ES, force AP in Hlt-loop mode in order to use the GHCB > + // protocol for starting APs > + // > + ApLoopMode =3D ApInHltLoop; > + } > } >=20 > if (ApLoopMode !=3D ApInMwaitLoop) { > @@ -587,6 +598,112 @@ InitializeApData ( > SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateIdle); } >=20 > +/** > + Get Protected mode code segment with 16-bit default addressing > + from current GDT table. > + > + @return Protected mode 16-bit code segment value. > +**/ > +STATIC > +UINT16 > +GetProtectedMode16CS ( > + VOID > + ) > +{ > + IA32_DESCRIPTOR GdtrDesc; > + IA32_SEGMENT_DESCRIPTOR *GdtEntry; > + UINTN GdtEntryCount; > + UINT16 Index; > + > + Index =3D (UINT16) -1; > + AsmReadGdtr (&GdtrDesc); > + GdtEntryCount =3D (GdtrDesc.Limit + 1) / sizeof > +(IA32_SEGMENT_DESCRIPTOR); > + GdtEntry =3D (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base; > + for (Index =3D 0; Index < GdtEntryCount; Index++) { > + if (GdtEntry->Bits.L =3D=3D 0 && > + GdtEntry->Bits.DB =3D=3D 0 && > + GdtEntry->Bits.Type > 8) { > + break; > + } > + GdtEntry++; > + } > + ASSERT (Index !=3D GdtEntryCount); > + return Index * 8; > +} > + > +/** > + Get Protected mode code segment with 32-bit default addressing > + from current GDT table. > + > + @return Protected mode 32-bit code segment value. > +**/ > +STATIC > +UINT16 > +GetProtectedMode32CS ( > + VOID > + ) > +{ > + IA32_DESCRIPTOR GdtrDesc; > + IA32_SEGMENT_DESCRIPTOR *GdtEntry; > + UINTN GdtEntryCount; > + UINT16 Index; > + > + Index =3D (UINT16) -1; > + AsmReadGdtr (&GdtrDesc); > + GdtEntryCount =3D (GdtrDesc.Limit + 1) / sizeof > +(IA32_SEGMENT_DESCRIPTOR); > + GdtEntry =3D (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base; > + for (Index =3D 0; Index < GdtEntryCount; Index++) { > + if (GdtEntry->Bits.L =3D=3D 0 && > + GdtEntry->Bits.DB =3D=3D 1 && > + GdtEntry->Bits.Type > 8) { > + break; > + } > + GdtEntry++; > + } > + ASSERT (Index !=3D GdtEntryCount); > + return Index * 8; > +} > + > +/** > + Reset an AP when in SEV-ES mode. > + > + If successful, this function never returns. > + > + @param[in] Ghcb Pointer to the GHCB > + @param[in] CpuMpData Pointer to CPU MP Data > + > +**/ > +STATIC > +VOID > +MpInitLibSevEsAPReset ( > + IN GHCB *Ghcb, > + IN CPU_MP_DATA *CpuMpData > + ) > +{ > + UINT16 Code16, Code32; > + AP_RESET *APResetFn; > + UINTN BufferStart; > + UINTN StackStart; > + > + Code16 =3D GetProtectedMode16CS (); > + Code32 =3D GetProtectedMode32CS (); > + > + if (CpuMpData->WakeupBufferHigh !=3D 0) { > + APResetFn =3D (AP_RESET *) (CpuMpData->WakeupBufferHigh + > + CpuMpData->AddressMap.SwitchToRealNoNxOffset); > + } else { > + APResetFn =3D (AP_RESET *) (CpuMpData->MpCpuExchangeInfo- > >BufferStart > + + CpuMpData->AddressMap.SwitchToRealOffset); > + } > + > + BufferStart =3D CpuMpData->MpCpuExchangeInfo->BufferStart; > + StackStart =3D CpuMpData->SevEsAPResetStackStart - > + (AP_RESET_STACK_SIZE * GetApicId ()); > + > + // > + // This call never returns. > + // > + APResetFn (BufferStart, Code16, Code32, StackStart); } > + > /** > This function will be called from AP reset code if BSP uses WakeUpAP. >=20 > @@ -648,7 +765,14 @@ ApWakeupFunction ( > InitializeApData (CpuMpData, ProcessorNumber, BistData, > ApTopOfStack); > ApStartupSignalBuffer =3D CpuMpData- > >CpuData[ProcessorNumber].StartupApSignal; >=20 > - InterlockedDecrement ((UINT32 *) &CpuMpData->MpCpuExchangeInfo- > >NumApsExecuting); > + // > + // Delay decrementing the APs executing count when SEV-ES is enabl= ed > + // to allow the APs to issue an AP_RESET_HOLD before the BSP possi= bly > + // performs another INIT-SIPI-SIPI sequence. > + // > + if (!CpuMpData->SevEsIsEnabled) { > + InterlockedDecrement ((UINT32 *) &CpuMpData- > >MpCpuExchangeInfo->NumApsExecuting); > + } > } else { > // > // Execute AP function if AP is ready @@ -755,7 +879,52 @@ > ApWakeupFunction ( > // > while (TRUE) { > DisableInterrupts (); > - CpuSleep (); > + if (CpuMpData->SevEsIsEnabled) { > + MSR_SEV_ES_GHCB_REGISTER Msr; > + GHCB *Ghcb; > + UINT64 Status; > + BOOLEAN DoDecrement; > + > + if (CpuMpData->InitFlag =3D=3D ApInitConfig) { > + DoDecrement =3D TRUE; > + } > + > + while (TRUE) { > + Msr.GhcbPhysicalAddress =3D AsmReadMsr64 (MSR_SEV_ES_GHCB); > + Ghcb =3D Msr.Ghcb; > + > + VmgInit (Ghcb); > + > + if (DoDecrement) { > + DoDecrement =3D FALSE; > + > + // > + // Perform the delayed decrement just before issuing the f= irst > + // VMGEXIT with AP_RESET_HOLD. > + // > + InterlockedDecrement ((UINT32 *) &CpuMpData- > >MpCpuExchangeInfo->NumApsExecuting); > + } > + > + Status =3D VmgExit (Ghcb, SVM_EXIT_AP_RESET_HOLD, 0, 0); > + if ((Status =3D=3D 0) && (Ghcb->SaveArea.SwExitInfo2 !=3D 0)= ) { > + VmgDone (Ghcb); > + break; > + } > + > + VmgDone (Ghcb); > + } > + > + // > + // Awakened in a new phase? Use the new CpuMpData > + // > + if (CpuMpData->NewCpuMpData !=3D NULL) { > + CpuMpData =3D CpuMpData->NewCpuMpData; > + } > + > + MpInitLibSevEsAPReset (Ghcb, CpuMpData); > + } else { > + CpuSleep (); > + } > CpuPause (); > } > } > @@ -868,6 +1037,9 @@ FillExchangeInfoData ( > ExchangeInfo->Enable5LevelPaging =3D (BOOLEAN) (Cr4.Bits.LA57 =3D=3D 1= ); > DEBUG ((DEBUG_INFO, "%a: 5-Level Paging =3D %d\n", gEfiCallerBaseName, > ExchangeInfo->Enable5LevelPaging)); >=20 > + ExchangeInfo->SevEsIsEnabled =3D CpuMpData->SevEsIsEnabled; > + ExchangeInfo->GhcbBase =3D (UINTN) CpuMpData->GhcbBase; > + > // > // Get the BSP's data of GDT and IDT > // > @@ -894,8 +1066,9 @@ FillExchangeInfoData ( > // EfiBootServicesCode to avoid page fault if NX memory protection is > enabled. > // > if (CpuMpData->WakeupBufferHigh !=3D 0) { > - Size =3D CpuMpData->AddressMap.RendezvousFunnelSize - > - CpuMpData->AddressMap.ModeTransitionOffset; > + Size =3D CpuMpData->AddressMap.RendezvousFunnelSize + > + CpuMpData->AddressMap.SwitchToRealSize - > + CpuMpData->AddressMap.ModeTransitionOffset; > CopyMem ( > (VOID *)CpuMpData->WakeupBufferHigh, > CpuMpData->AddressMap.RendezvousFunnelAddress + @@ -948,7 > +1121,8 @@ BackupAndPrepareWakeupBuffer( > CopyMem ( > (VOID *) CpuMpData->WakeupBuffer, > (VOID *) CpuMpData->AddressMap.RendezvousFunnelAddress, > - CpuMpData->AddressMap.RendezvousFunnelSize > + CpuMpData->AddressMap.RendezvousFunnelSize + > + CpuMpData->AddressMap.SwitchToRealSize > ); > } >=20 > @@ -969,6 +1143,44 @@ RestoreWakeupBuffer( > ); > } >=20 > +/** > + Calculate the size of the reset stack. > + > + @return Total amount of memory required for stacks > +**/ > +STATIC > +UINTN > +GetApResetStackSize ( > + VOID > + ) > +{ > + return AP_RESET_STACK_SIZE * > +PcdGet32(PcdCpuMaxLogicalProcessorNumber); > +} > + > +/** > + Calculate the size of the reset vector. > + > + @param[in] AddressMap The pointer to Address Map structure. > + > + @return Total amount of memory required for the AP res= et area > +**/ > +STATIC > +UINTN > +GetApResetVectorSize ( > + IN MP_ASSEMBLY_ADDRESS_MAP *AddressMap > + ) > +{ > + UINTN Size; > + > + Size =3D ALIGN_VALUE (AddressMap->RendezvousFunnelSize + > + AddressMap->SwitchToRealSize + > + sizeof (MP_CPU_EXCHANGE_INFO), > + CPU_STACK_ALIGNMENT); Size +=3D > + GetApResetStackSize (); > + > + return Size; > +} > + > /** > Allocate reset vector buffer. >=20 > @@ -982,16 +1194,22 @@ AllocateResetVector ( > UINTN ApResetVectorSize; >=20 > if (CpuMpData->WakeupBuffer =3D=3D (UINTN) -1) { > - ApResetVectorSize =3D CpuMpData->AddressMap.RendezvousFunnelSize + > - sizeof (MP_CPU_EXCHANGE_INFO); > + ApResetVectorSize =3D GetApResetVectorSize (&CpuMpData- > >AddressMap); >=20 > CpuMpData->WakeupBuffer =3D GetWakeupBuffer (ApResetVectorSize)= ; > CpuMpData->MpCpuExchangeInfo =3D (MP_CPU_EXCHANGE_INFO *) > (UINTN) > - (CpuMpData->WakeupBuffer + CpuMpData- > >AddressMap.RendezvousFunnelSize); > + (CpuMpData->WakeupBuffer + > + CpuMpData->AddressMap.RendezvousFunnelSize + > + CpuMpData->AddressMap.SwitchToRealSize); > CpuMpData->WakeupBufferHigh =3D GetModeTransitionBuffer ( > - CpuMpData->AddressMap.RendezvousFunn= elSize - > + CpuMpData->AddressMap.RendezvousFunn= elSize + > + > + CpuMpData->AddressMap.SwitchToRealSize - > CpuMpData->AddressMap.ModeTransition= Offset > ); > + // > + // The reset stack starts at the end of the buffer. > + // > + CpuMpData->SevEsAPResetStackStart =3D CpuMpData->WakeupBuffer + > + ApResetVectorSize; > } > BackupAndPrepareWakeupBuffer (CpuMpData); } @@ -1006,7 +1224,80 > @@ FreeResetVector ( > IN CPU_MP_DATA *CpuMpData > ) > { > - RestoreWakeupBuffer (CpuMpData); > + // > + // If SEV-ES is enabled, the reset area is needed for AP parking and > + // and AP startup in the OS, so the reset area is reserved. Do not > + // perform the restore as this will overwrite memory which has data > + // needed by SEV-ES. > + // > + if (!CpuMpData->SevEsIsEnabled) { > + RestoreWakeupBuffer (CpuMpData); > + } > +} > + > +/** > + Allocate the SEV-ES AP jump table buffer. > + > + @param[in, out] CpuMpData The pointer to CPU MP Data structure. > +**/ > +VOID > +AllocateSevEsAPMemory ( > + IN OUT CPU_MP_DATA *CpuMpData > + ) > +{ > + if (CpuMpData->SevEsAPBuffer =3D=3D (UINTN) -1) { > + CpuMpData->SevEsAPBuffer =3D > + CpuMpData->SevEsIsEnabled ? GetSevEsAPMemory () : 0; > + } > +} > + > +/** > + Program the SEV-ES AP jump table buffer. > + > + @param[in] SipiVector The SIPI vector used for the AP Reset **/ > +VOID SetSevEsJumpTable ( > + IN UINTN SipiVector > + ) > +{ > + SEV_ES_AP_JMP_FAR *JmpFar; > + UINT32 Offset, InsnByte; > + UINT8 LoNib, HiNib; > + > + JmpFar =3D (SEV_ES_AP_JMP_FAR *) FixedPcdGet32 > (PcdSevEsWorkAreaBase); > + ASSERT (JmpFar !=3D NULL); > + > + // > + // Obtain the address of the Segment/Rip location in the workarea. > + // This will be set to a value derived from the SIPI vector and will > + // be the memory address used for the far jump below. > + // > + Offset =3D FixedPcdGet32 (PcdSevEsWorkAreaBase); Offset +=3D sizeof > + (JmpFar->InsnBuffer); LoNib =3D (UINT8) Offset; HiNib =3D (UINT8) > + (Offset >> 8); > + > + // > + // Program the workarea (which is the initial AP boot address) with > + // far jump to the SIPI vector (where XX and YY represent the // > + address of where the SIPI vector is stored. > + // > + // JMP FAR [CS:XXYY] =3D> 2E FF 2E YY XX > + // > + InsnByte =3D 0; > + JmpFar->InsnBuffer[InsnByte++] =3D 0x2E; // CS override prefix > + JmpFar->InsnBuffer[InsnByte++] =3D 0xFF; // JMP (FAR) > + JmpFar->InsnBuffer[InsnByte++] =3D 0x2E; // ModRM (JMP memory location= ) > + JmpFar->InsnBuffer[InsnByte++] =3D LoNib; // YY offset ... > + JmpFar->InsnBuffer[InsnByte++] =3D HiNib; // XX offset ... > + > + // > + // Program the Segment/Rip based on the SIPI vector (always at least > + // 16-byte aligned, so Rip is set to 0). > + // > + JmpFar->Rip =3D 0; > + JmpFar->Segment =3D (UINT16) (SipiVector >> 4); > } >=20 > /** > @@ -1043,6 +1334,7 @@ WakeUpAP ( > CpuMpData->InitFlag !=3D ApInitDone) { > ResetVectorRequired =3D TRUE; > AllocateResetVector (CpuMpData); > + AllocateSevEsAPMemory (CpuMpData); > FillExchangeInfoData (CpuMpData); > SaveLocalApicTimerSetting (CpuMpData); > } > @@ -1079,6 +1371,15 @@ WakeUpAP ( > } > } > if (ResetVectorRequired) { > + // > + // For SEV-ES, the initial AP boot address will be defined by > + // PcdSevEsWorkAreaBase. The Segment/Rip must be the jump address > + // from the original INIT-SIPI-SIPI. > + // > + if (CpuMpData->SevEsIsEnabled) { > + SetSevEsJumpTable (ExchangeInfo->BufferStart); > + } > + > // > // Wakeup all APs > // > @@ -1170,6 +1471,16 @@ WakeUpAP ( > *(UINT32 *) CpuData->StartupApSignal =3D WAKEUP_AP_SIGNAL; > if (ResetVectorRequired) { > CpuInfoInHob =3D (CPU_INFO_IN_HOB *) (UINTN) CpuMpData- > >CpuInfoInHob; > + > + // > + // For SEV-ES, the initial AP boot address will be defined by > + // PcdSevEsWorkAreaBase. The Segment/Rip must be the jump address > + // from the original INIT-SIPI-SIPI. > + // > + if (CpuMpData->SevEsIsEnabled) { > + SetSevEsJumpTable (ExchangeInfo->BufferStart); > + } > + > SendInitSipiSipi ( > CpuInfoInHob[ProcessorNumber].ApicId, > (UINT32) ExchangeInfo->BufferStart @@ -1646,7 +1957,7 @@ > MpInitLibInitialize ( > ASSERT (MaxLogicalProcessorNumber !=3D 0); >=20 > AsmGetAddressMap (&AddressMap); > - ApResetVectorSize =3D AddressMap.RendezvousFunnelSize + sizeof > (MP_CPU_EXCHANGE_INFO); > + ApResetVectorSize =3D GetApResetVectorSize (&AddressMap); > ApStackSize =3D PcdGet32(PcdCpuApStackSize); > ApLoopMode =3D GetApLoopMode (&MonitorFilterSize); >=20 > @@ -1705,6 +2016,8 @@ MpInitLibInitialize ( > CpuMpData->CpuInfoInHob =3D (UINT64) (UINTN) (CpuMpData->CpuData > + MaxLogicalProcessorNumber); > InitializeSpinLock(&CpuMpData->MpLock); > CpuMpData->SevEsIsEnabled =3D PcdGetBool (PcdSevEsIsEnabled); > + CpuMpData->SevEsAPBuffer =3D (UINTN) -1; > + CpuMpData->GhcbBase =3D PcdGet64 (PcdGhcbBase); >=20 > // > // Make sure no memory usage outside of the allocated buffer. > @@ -1763,6 +2076,7 @@ MpInitLibInitialize ( > // APs have been wakeup before, just get the CPU Information > // from HOB > // > + OldCpuMpData->NewCpuMpData =3D CpuMpData; > CpuMpData->CpuCount =3D OldCpuMpData->CpuCount; > CpuMpData->BspNumber =3D OldCpuMpData->BspNumber; > CpuMpData->CpuInfoInHob =3D OldCpuMpData->CpuInfoInHob; diff --git > a/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c > b/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c > index a548fed23fa7..3989bd6a7a9f 100644 > --- a/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c > +++ b/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c > @@ -280,6 +280,25 @@ GetModeTransitionBuffer ( > return 0; > } >=20 > +/** > + Return the address of the SEV-ES AP jump table. > + > + This buffer is required in order for an SEV-ES guest to transition > + from UEFI into an OS. > + > + @return Return SEV-ES AP jump table buffer > +**/ > +UINTN > +GetSevEsAPMemory ( > + VOID > + ) > +{ > + // > + // PEI phase doesn't need to do such transition. So simply return 0. > + // > + return 0; > +} > + > /** > Checks APs status and updates APs status if needed. >=20 > diff --git a/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c > b/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c > index 6298571e29b2..28f8e8e133e5 100644 > --- a/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c > +++ b/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c > @@ -121,7 +121,7 @@ GetProtectedModeCS ( > GdtEntry =3D (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base; > for (Index =3D 0; Index < GdtEntryCount; Index++) { > if (GdtEntry->Bits.L =3D=3D 0) { > - if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.L =3D=3D 0) { > + if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.DB =3D=3D 1) { > break; > } > } > diff --git a/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc > b/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc > index efb1bc2bf7cb..4f5a7c859a56 100644 > --- a/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc > +++ b/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc > @@ -19,7 +19,7 @@ CPU_SWITCH_STATE_IDLE equ 0 > CPU_SWITCH_STATE_STORED equ 1 > CPU_SWITCH_STATE_LOADED equ 2 >=20 > -LockLocation equ (RendezvousFunnelProcEnd - > RendezvousFunnelProcStart) > +LockLocation equ (SwitchToRealProcEnd - > RendezvousFunnelProcStart) > StackStartAddressLocation equ LockLocation + 04h > StackSizeLocation equ LockLocation + 08h > ApProcedureLocation equ LockLocation + 0Ch > diff --git a/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm > b/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm > index b74046b76af3..309d53bf3b37 100644 > --- a/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm > +++ b/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm > @@ -215,6 +215,16 @@ CProcedureInvoke: > jmp $ ; Never reach here > RendezvousFunnelProcEnd: >=20 > +;---------------------------------------------------------------------- > +--------------- > +;SwitchToRealProc procedure follows. > +;NOT USED IN 32 BIT MODE. > +;---------------------------------------------------------------------- > +--------------- > +global ASM_PFX(SwitchToRealProc) > +ASM_PFX(SwitchToRealProc): > +SwitchToRealProcStart: > + jmp $ ; Never reach here > +SwitchToRealProcEnd: > + > ;-----------------------------------------------------------------------= -------------- > ; AsmRelocateApLoop (MwaitSupport, ApTargetCState, PmCodeSegment, > TopOfApStack, CountTofinish); > ;-----------------------------------------------------------------------= -------------- > @@ -263,6 +273,11 @@ ASM_PFX(AsmGetAddressMap): > mov dword [ebx + 0Ch], AsmRelocateApLoopStart > mov dword [ebx + 10h], AsmRelocateApLoopEnd - > AsmRelocateApLoopStart > mov dword [ebx + 14h], Flat32Start - RendezvousFunnelProcStar= t > + mov dword [ebx + 18h], SwitchToRealProcEnd - > SwitchToRealProcStart ; SwitchToRealSize > + mov dword [ebx + 1Ch], SwitchToRealProcStart - > RendezvousFunnelProcStart ; SwitchToRealOffset > + mov dword [ebx + 20h], SwitchToRealProcStart - Flat32Start = ; > SwitchToRealNoNxOffset > + mov dword [ebx + 24h], 0 = ; > SwitchToRealPM16ModeOffset > + mov dword [ebx + 28h], 0 = ; > SwitchToRealPM16ModeSize >=20 > popad > ret > diff --git a/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc > b/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc > index 58ef369342a7..c92daaaffd6b 100644 > --- a/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc > +++ b/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc > @@ -19,7 +19,7 @@ CPU_SWITCH_STATE_IDLE equ 0 > CPU_SWITCH_STATE_STORED equ 1 > CPU_SWITCH_STATE_LOADED equ 2 >=20 > -LockLocation equ (RendezvousFunnelProcEnd - > RendezvousFunnelProcStart) > +LockLocation equ (SwitchToRealProcEnd - > RendezvousFunnelProcStart) > StackStartAddressLocation equ LockLocation + 08h > StackSizeLocation equ LockLocation + 10h > ApProcedureLocation equ LockLocation + 18h > @@ -41,3 +41,5 @@ ModeTransitionSegmentLocation equ LockLocation + > 98h > ModeHighMemoryLocation equ LockLocation + 9Ah > ModeHighSegmentLocation equ LockLocation + 9Eh > Enable5LevelPagingLocation equ LockLocation + 0A0h > +SevEsIsEnabledLocation equ LockLocation + 0A1h > +GhcbBaseLocation equ LockLocation + 0A2h > diff --git a/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm > b/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm > index 87f2523e856f..6956b408d004 100644 > --- a/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm > +++ b/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm > @@ -184,9 +184,97 @@ Releaselock: > add edi, StackStartAddressLocation > add rax, qword [edi] > mov rsp, rax > + > + lea edi, [esi + SevEsIsEnabledLocation] > + cmp byte [edi], 1 ; SevEsIsEnabled > + jne CProcedureInvoke > + > + ; > + ; program GHCB > + ; Each page after the GHCB is a per-CPU page, so the calculation > programs > + ; a GHCB to be every 8KB. > + ; > + mov eax, SIZE_4KB > + shl eax, 1 ; EAX =3D SIZE_4K * 2 > + mov ecx, ebx > + mul ecx ; EAX =3D SIZE_4K * 2 *= CpuNumber > + mov edi, esi > + add edi, GhcbBaseLocation > + add rax, qword [edi] > + mov rdx, rax > + shr rdx, 32 > + mov rcx, 0xc0010130 > + wrmsr > jmp CProcedureInvoke >=20 > GetApicId: > + lea edi, [esi + SevEsIsEnabledLocation] > + cmp byte [edi], 1 ; SevEsIsEnabled > + jne DoCpuid > + > + ; > + ; Since we don't have a stack yet, we can't take a #VC > + ; exception. Use the GHCB protocol to perform the CPUID > + ; calls. > + ; > + mov rcx, 0xc0010130 > + rdmsr > + shl rdx, 32 > + or rax, rdx > + mov rdi, rax ; RDI now holds the original GHCB GP= A > + > + mov rdx, 0 ; CPUID function 0 > + mov rax, 0 ; RAX register requested > + or rax, 4 > + wrmsr > + rep vmmcall > + rdmsr > + cmp edx, 0bh > + jb NoX2ApicSevEs ; CPUID level below > CPUID_EXTENDED_TOPOLOGY > + > + mov rdx, 0bh ; CPUID function 0x0b > + mov rax, 040000000h ; RBX register requested > + or rax, 4 > + wrmsr > + rep vmmcall > + rdmsr > + test edx, 0ffffh > + jz NoX2ApicSevEs ; CPUID.0BH:EBX[15:0] is zero > + > + mov rdx, 0bh ; CPUID function 0x0b > + mov rax, 0c0000000h ; RDX register requested > + or rax, 4 > + wrmsr > + rep vmmcall > + rdmsr > + > + ; Processor is x2APIC capable; 32-bit x2APIC ID is now in EDX > + jmp RestoreGhcb > + > +NoX2ApicSevEs: > + ; Processor is not x2APIC capable, so get 8-bit APIC ID > + mov rdx, 1 ; CPUID function 1 > + mov rax, 040000000h ; RBX register requested > + or rax, 4 > + wrmsr > + rep vmmcall > + rdmsr > + shr edx, 24 > + > +RestoreGhcb: > + mov rbx, rdx ; Save x2APIC/APIC ID > + > + mov rdx, rdi ; RDI holds the saved GHCB GPA > + shr rdx, 32 > + mov eax, edi > + wrmsr > + > + mov rdx, rbx > + > + ; x2APIC ID or APIC ID is in EDX > + jmp GetProcessorNumber > + > +DoCpuid: > mov eax, 0 > cpuid > cmp eax, 0bh > @@ -253,12 +341,158 @@ CProcedureInvoke: >=20 > RendezvousFunnelProcEnd: >=20 > +;---------------------------------------------------------------------- > +--------------- > +;SwitchToRealProc procedure follows. > +;ALSO THIS PROCEDURE IS EXECUTED BY APs TRANSITIONING TO 16 BIT > MODE. > +HENCE THIS PROC ;IS IN MACHINE CODE. > +; SwitchToRealProc (UINTN BufferStart, UINT16 Code16, UINT16 Code32, > +UINTN StackStart) ; rcx - Buffer Start ; rdx - Code16 Selector Offset > +; r8 - Code32 Selector Offset ; r9 - Stack Start > +;---------------------------------------------------------------------- > +--------------- > +global ASM_PFX(SwitchToRealProc) > +ASM_PFX(SwitchToRealProc): > +SwitchToRealProcStart: > +BITS 64 > + cli > + > + ; > + ; Get RDX reset value before changing stacks since the > + ; new stack won't be able to accomodate a #VC exception. > + ; > + push rax > + push rbx > + push rcx > + push rdx > + > + mov rax, 1 > + cpuid > + mov rsi, rax ; Save off the reset value fo= r RDX > + > + pop rdx > + pop rcx > + pop rbx > + pop rax > + > + ; > + ; Establish stack below 1MB > + ; > + mov rsp, r9 > + > + ; > + ; Push ultimate Reset Vector onto the stack > + ; > + mov rax, rcx > + shr rax, 4 > + push word 0x0002 ; RFLAGS > + push ax ; CS > + push word 0x0000 ; RIP > + push word 0x0000 ; For alignment, will be disc= arded > + > + ; > + ; Get address of "16-bit operand size" label > + ; > + lea rbx, [PM16Mode] > + > + ; > + ; Push addresses used to change to compatibility mode > + ; > + lea rax, [CompatMode] > + push r8 > + push rax > + > + ; > + ; Clear R8 - R15, for reset, before going into 32-bit mode > + ; > + xor r8, r8 > + xor r9, r9 > + xor r10, r10 > + xor r11, r11 > + xor r12, r12 > + xor r13, r13 > + xor r14, r14 > + xor r15, r15 > + > + ; > + ; Far return into 32-bit mode > + ; > +o64 retf > + > +BITS 32 > +CompatMode: > + ; > + ; Set up stack to prepare for exiting protected mode > + ; > + push edx ; Code16 CS > + push ebx ; PM16Mode label address > + > + ; > + ; Disable paging > + ; > + mov eax, cr0 ; Read CR0 > + btr eax, 31 ; Set PG=3D0 > + mov cr0, eax ; Write CR0 > + > + ; > + ; Disable long mode > + ; > + mov ecx, 0c0000080h ; EFER MSR number > + rdmsr ; Read EFER > + btr eax, 8 ; Set LME=3D0 > + wrmsr ; Write EFER > + > + ; > + ; Disable PAE > + ; > + mov eax, cr4 ; Read CR4 > + btr eax, 5 ; Set PAE=3D0 > + mov cr4, eax ; Write CR4 > + > + mov edx, esi ; Restore RDX reset value > + > + ; > + ; Switch to 16-bit operand size > + ; > + retf > + > +BITS 16 > + ; > + ; At entry to this label > + ; - RDX will have its reset value > + ; - On the top of the stack > + ; - Alignment data (two bytes) to be discarded > + ; - IP for Real Mode (two bytes) > + ; - CS for Real Mode (two bytes) > + ; > +PM16Mode: > + mov eax, cr0 ; Read CR0 > + btr eax, 0 ; Set PE=3D0 > + mov cr0, eax ; Write CR0 > + > + pop ax ; Discard alignment data > + > + ; > + ; Clear registers (except RDX and RSP) before going into 16-bit mode > + ; > + xor eax, eax > + xor ebx, ebx > + xor ecx, ecx > + xor esi, esi > + xor edi, edi > + xor ebp, ebp > + > + iret > + > +SwitchToRealProcEnd: > + > ;-----------------------------------------------------------------------= -------------- > ; AsmRelocateApLoop (MwaitSupport, ApTargetCState, PmCodeSegment, > TopOfApStack, CountTofinish); > ;-----------------------------------------------------------------------= -------------- > global ASM_PFX(AsmRelocateApLoop) > ASM_PFX(AsmRelocateApLoop): > AsmRelocateApLoopStart: > +BITS 64 > cli ; Disable interrupt before switching to= 32-bit mode > mov rax, [rsp + 40] ; CountTofinish > lock dec dword [rax] ; (*CountTofinish)-- > @@ -324,6 +558,11 @@ ASM_PFX(AsmGetAddressMap): > mov qword [rcx + 18h], rax > mov qword [rcx + 20h], AsmRelocateApLoopEnd - > AsmRelocateApLoopStart > mov qword [rcx + 28h], Flat32Start - RendezvousFunnelProcStar= t > + mov qword [rcx + 30h], SwitchToRealProcEnd - > SwitchToRealProcStart ; SwitchToRealSize > + mov qword [rcx + 38h], SwitchToRealProcStart - > RendezvousFunnelProcStart ; SwitchToRealOffset > + mov qword [rcx + 40h], SwitchToRealProcStart - Flat32Start = ; > SwitchToRealNoNxOffset > + mov qword [rcx + 48h], PM16Mode - > RendezvousFunnelProcStart ; SwitchToRealPM16ModeOffset > + mov qword [rcx + 50h], SwitchToRealProcEnd - PM16Mode = ; > SwitchToRealPM16ModeSize > ret >=20 > ;-----------------------------------------------------------------------= -------------- > -- > 2.27.0