From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mga18.intel.com (mga18.intel.com [134.134.136.126]) by mx.groups.io with SMTP id smtpd.web10.32086.1590996532196279321 for ; Mon, 01 Jun 2020 00:28:52 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@intel.onmicrosoft.com header.s=selector2-intel-onmicrosoft-com header.b=nT3HdsD1; spf=pass (domain: intel.com, ip: 134.134.136.126, mailfrom: eric.dong@intel.com) IronPort-SDR: 93RyNcMHSIE9RS5IYiUjcgQds2Y/XZyXh8Qqr32Zrg68VizUdf/eiJLftg+ZS7oXNrc0ONenM3 +7Qj3qEvJR7w== X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga002.jf.intel.com ([10.7.209.21]) by orsmga106.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 01 Jun 2020 00:28:51 -0700 IronPort-SDR: fjsslhwAEwTqpLMg5r6QMY0FpVpwFbNCH3EH8eEdJggx+GtEQkZ/0j7PPtHB1QK/LsHMb14czG sadFry/Zb2hQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.73,459,1583222400"; d="scan'208";a="286192611" Received: from orsmsx105.amr.corp.intel.com ([10.22.225.132]) by orsmga002.jf.intel.com with ESMTP; 01 Jun 2020 00:28:51 -0700 Received: from orsmsx601.amr.corp.intel.com (10.22.229.14) by ORSMSX105.amr.corp.intel.com (10.22.225.132) with Microsoft SMTP Server (TLS) id 14.3.439.0; Mon, 1 Jun 2020 00:28:51 -0700 Received: from orsmsx607.amr.corp.intel.com (10.22.229.20) by ORSMSX601.amr.corp.intel.com (10.22.229.14) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.1713.5; Mon, 1 Jun 2020 00:28:50 -0700 Received: from ORSEDG002.ED.cps.intel.com (10.7.248.5) by orsmsx607.amr.corp.intel.com (10.22.229.20) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) id 15.1.1713.5 via Frontend Transport; Mon, 1 Jun 2020 00:28:50 -0700 Received: from NAM11-DM6-obe.outbound.protection.outlook.com (104.47.57.172) by edgegateway.intel.com (134.134.137.101) with Microsoft SMTP Server (TLS) id 14.3.439.0; Mon, 1 Jun 2020 00:28:50 -0700 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=nLzzRg0fRmpOJKTAwhkowPQDOIYAilXqBYhezM4F2J5A68XSvMre0QLSghlmL4OdE1TxAfFYF8CetCEQKZawfqhKYU4/2GXLtqG4L7+euSCN9VawvHfXOim1Ag+HUiDVqpkCcpG4K9bw6wD8b5nvfr4RF9rDVJyywWYheXNhfuRL42TwzOtHgniAiWFrI3kuRFxudFNVSQmh2DRErVFPmF0Hyt20iykWP+G9+dRFow75+tj0LHVSrcnj1zyo4Afadg0WXnO66w7d9pVm9wSS9O6otgncLZdNxAXWjSCpWPhAWCBb3FRnJx6aNJd/8cQJ+9bvGU3fB598uMSuv0gX+w== 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=s6peVBc1XmjH62cTEsD0WV+Yig29g1zFfpHkZDXa5FU=; b=c9Ooz+NHfrQ9A/eLTW01VZzIj35yEFRwt4IQO7JQlcTkF0CLWzinRG+HqCneXvtFKS2no7x46jRKeg9aHVNB2jDVqIdQbSMW6vUuPR9f4KCcDCDfcv0rRwbJR1d2vnYbBc5lFp2JCEv3OJjaRn6Cus2PBK7nBc3pdi8LzaG+B+n40khz51JDqTMt43UKyKUp3xzizll3wxT1uN9N4Rr4q5r1NDxPfKIhrDqb+gHRUEtWn1E4tzJFQZ7xquCxORNFUk2WLAuMu3k8EUXZe40pdyPww+QFf/z6LSMt2xszGsVP3opThKqfdjwOumxrQaipq149146zZT+h+K/mOhynZQ== 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=s6peVBc1XmjH62cTEsD0WV+Yig29g1zFfpHkZDXa5FU=; b=nT3HdsD1vdkNDnBSqgjt9kzWz2Xfrcx1UJee3K6qCUBBmuuOu6TC15+fJwOHyPtcbLUQ3uz0r4FchRKo31Qx2tqJijE5H9UHKJpegI5q4/VRknT9S/rTk+f03k4Xle5HjyKojLT1O2ShxkyT1NsR6WLEHwJHfsSSkPa2FScwpfk= Received: from DM6PR11MB3274.namprd11.prod.outlook.com (2603:10b6:5:b::26) by DM6PR11MB2762.namprd11.prod.outlook.com (2603:10b6:5:c8::13) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3045.21; Mon, 1 Jun 2020 07:28:47 +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.3045.018; Mon, 1 Jun 2020 07:28:47 +0000 From: "Dong, Eric" To: Tom Lendacky , "devel@edk2.groups.io" CC: "Justen, Jordan L" , Laszlo Ersek , Ard Biesheuvel , "Kinney, Michael D" , "Gao, Liming" , "Ni, Ray" , Brijesh Singh Subject: Re: [PATCH v8 42/46] UefiCpuPkg: Allow AP booting under SEV-ES Thread-Topic: [PATCH v8 42/46] UefiCpuPkg: Allow AP booting under SEV-ES Thread-Index: AQHWLifoCyLsunqt2E2d1R057j+uUajDY64A Date: Mon, 1 Jun 2020 07:28:47 +0000 Message-ID: References: <9405aed8ea6894167ee83bb1a7c95049bec1dea0.1589925074.git.thomas.lendacky@amd.com> In-Reply-To: <9405aed8ea6894167ee83bb1a7c95049bec1dea0.1589925074.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.36] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 71610c2a-bd2f-42ec-bcc8-08d805fd6c82 x-ms-traffictypediagnostic: DM6PR11MB2762: 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:10000; x-forefront-prvs: 0421BF7135 x-ms-exchange-senderadcheck: 1 x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: nUwgNIZxwiRWXE71M/WGn3TRabiV2TdXIIk7R4I3hQhvoA8ecomNBn4SW845WtnApi8jl+Crifd71hNqMhFslzXOc2lPqZlVz8yw7UkSbswznHismJimllKrTQly6l1Xa59lvWfcCTh2NoeycC2+SZoC2Q+UjqUgxo93XW+CtR3dfSPLBE4/awoCdH6eRl27KEuItZlu8mDWNNUrw6OPEYOhnvgwFW9u9O4QKZ+9rPXSRMuTwM6PJ3DBZ4ml3eF4z0+pY24dKjlPiCQC1WdRQ/CguoaVUsTkJGWpZVxomFqAPDnYrRLEh1zd7TW4xgZV2ZaC7Uo5N9zABbKCsAkKg/c2+Q9M8n2a6dApaiCMl7B7nRKLOQRlOuGWntke4T7ssm5InQTDAeOlQwyNK9DDow== 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:(39860400002)(366004)(136003)(396003)(376002)(346002)(5660300002)(966005)(9686003)(55016002)(83380400001)(30864003)(186003)(7696005)(53546011)(6506007)(33656002)(478600001)(19627235002)(71200400001)(26005)(52536014)(316002)(110136005)(54906003)(66946007)(76116006)(8936002)(66556008)(8676002)(2906002)(64756008)(4326008)(86362001)(66476007)(66446008)(559001)(579004);DIR:OUT;SFP:1102; x-ms-exchange-antispam-messagedata: YiLj2oFTy98RvzT8diC5IRejX/j+x9CvUPQgPXTdMrHs3f2v33GNjfD1SXVUmhCE3A0YU7syEKZYMLLLOHWMOC804ByUiXFbrj5DqsA1xjYCTzll1AprbnlWBpOxLmKeu8IrhGchKihEO7+GpgFlnYTXgvVBbNGCn+RJOALosBm20H73jLxtH4UfbF1AfJ+mMQwQLLoHEVCxiqDKavH3e4qcAXLE3OcbHctGQpJGGZpk8v87cHwWl7urkzwcMqYt+kuVj5/szI/4idR/J8acGRPoBYlg4ki5w0ndrwwUhAkuuRP3y+srZQohFsrkNCZhel8U1LQKUlYCmE0lYJpfGktHClhUurKOV9qHl0o8QB1zWZiIR7aN7Z/RWdWHQHgev1fNt2xzo5APdEeoT3SmQl947Ej3HOKxGwUpxw0KfUF7rtj7FdtWbCt3MluSA7XV363zsRSkfrxOxVdMoryjBQU0J2N/Cyjp0GPLC9o0MrJ2nWMyBPruMPsEh3ZJp3GN MIME-Version: 1.0 X-MS-Exchange-CrossTenant-Network-Message-Id: 71610c2a-bd2f-42ec-bcc8-08d805fd6c82 X-MS-Exchange-CrossTenant-originalarrivaltime: 01 Jun 2020 07:28:47.6958 (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: edD+X5c9HW/RaGngM6RD4DzaiO1JOL0OWe/tnHklDPAzG5IAheBvK28e/opqSOVXc6U34KIjeQQRdTe84p5k0A== X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM6PR11MB2762 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 Hi Tom, > -----Original Message----- > From: Tom Lendacky > Sent: Wednesday, May 20, 2020 5:51 AM > To: devel@edk2.groups.io > Cc: Justen, Jordan L ; Laszlo Ersek > ; Ard Biesheuvel ; Kinney, > Michael D ; Gao, Liming > ; Dong, Eric ; Ni, Ray > ; Brijesh Singh > Subject: [PATCH v8 42/46] UefiCpuPkg: Allow AP booting under SEV-ES >=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 | 312 +++++++++++++++++- > 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, 714 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..f0cbb3763b5d 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. > + > + @retval other 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..19527300ff3a 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. > + > + @retval other 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 a8b605f569bf..aeab575bb525 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 > @@ -314,6 +317,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) { > @@ -610,6 +621,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 > @@ -671,7 +788,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 @@ -778,7 +902,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); 1. Why needs to postpose the CpuMpData->MpCpuExchangeInfo->NumApsExecuting = update here? > + } > + > + Status =3D VmgExit (Ghcb, SVM_EXIT_AP_RESET_HOLD, 0, 0); 2. The AP will enter Halt mode after this call? Not continue the execute of= the below code? I'm not clear how the AP works for this case. > + 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) { > + CpuMpData =3D CpuMpData->NewCpuMpData; > + } > + > + MpInitLibSevEsAPReset (Ghcb, CpuMpData); 3. With this function, this AP will be wake up and execute the new procedur= e?=20 Thanks, Eric > + } else { > + CpuSleep (); > + } > CpuPause (); > } > } > @@ -891,6 +1060,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 > // > @@ -917,8 +1089,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 + @@ -971,7 > +1144,8 @@ BackupAndPrepareWakeupBuffer( > CopyMem ( > (VOID *) CpuMpData->WakeupBuffer, > (VOID *) CpuMpData->AddressMap.RendezvousFunnelAddress, > - CpuMpData->AddressMap.RendezvousFunnelSize > + CpuMpData->AddressMap.RendezvousFunnelSize + > + CpuMpData->AddressMap.SwitchToRealSize > ); > } >=20 > @@ -992,6 +1166,44 @@ RestoreWakeupBuffer( > ); > } >=20 > +/** > + Calculate the size of the reset stack. > + > + @retval 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. > + > + @retval 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 > @@ -1005,16 +1217,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); } @@ -1029,7 +1247,31 > @@ 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; > + } > } >=20 > /** > @@ -1066,6 +1308,7 @@ WakeUpAP ( > CpuMpData->InitFlag !=3D ApInitDone) { > ResetVectorRequired =3D TRUE; > AllocateResetVector (CpuMpData); > + AllocateSevEsAPMemory (CpuMpData); > FillExchangeInfoData (CpuMpData); > SaveLocalApicTimerSetting (CpuMpData); > } > @@ -1102,6 +1345,50 @@ 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) { > + 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 workare= a. > + // 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) w= ith > + // 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 l= east > + // 16-byte aligned, so Rip is set to 0). > + // > + JmpFar->Rip =3D 0; > + JmpFar->Segment =3D (UINT16) (ExchangeInfo->BufferStart >> 4); > + } > // > // Wakeup all APs > // > @@ -1669,7 +1956,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 > @@ -1728,6 +2015,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. > @@ -1786,6 +2075,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..e17a351e5cfd 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. > + > + @retval other 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.17.1