From 05de7b260762af1f1afc4b881b3853540d802bba Mon Sep 17 00:00:00 2001 From: Dov Murik Date: Mon, 8 May 2023 10:22:35 +0300 Subject: [PATCH 1/2] runtime: Add sev package The sev package provides utilities for launching AMD SEV and SEV-ES confidential guests. Fixes: #6795 Signed-off-by: Dov Murik --- src/runtime/pkg/README.md | 1 + src/runtime/pkg/sev/README.md | 14 ++ src/runtime/pkg/sev/ovmf.go | 101 +++++++++ src/runtime/pkg/sev/sev.go | 203 +++++++++++++++++++ src/runtime/pkg/sev/sev_test.go | 54 +++++ src/runtime/pkg/sev/testdata/README.md | 9 + src/runtime/pkg/sev/testdata/ovmf_suffix.bin | Bin 0 -> 4096 bytes src/runtime/pkg/sev/vcpu_sigs.go | 76 +++++++ src/runtime/pkg/sev/vcpu_sigs_test.go | 21 ++ src/runtime/pkg/sev/vmsa.go | 172 ++++++++++++++++ 10 files changed, 651 insertions(+) create mode 100644 src/runtime/pkg/sev/README.md create mode 100644 src/runtime/pkg/sev/ovmf.go create mode 100644 src/runtime/pkg/sev/sev.go create mode 100644 src/runtime/pkg/sev/sev_test.go create mode 100644 src/runtime/pkg/sev/testdata/README.md create mode 100644 src/runtime/pkg/sev/testdata/ovmf_suffix.bin create mode 100644 src/runtime/pkg/sev/vcpu_sigs.go create mode 100644 src/runtime/pkg/sev/vcpu_sigs_test.go create mode 100644 src/runtime/pkg/sev/vmsa.go diff --git a/src/runtime/pkg/README.md b/src/runtime/pkg/README.md index b5b0d07d32..72bf3248bb 100644 --- a/src/runtime/pkg/README.md +++ b/src/runtime/pkg/README.md @@ -7,4 +7,5 @@ This repository contains a number of packages in addition to the |-|-| | [`katatestutils`](katatestutils) | Unit test utilities. | | [`katautils`](katautils) | Utilities. | +| [`sev`](sev) | AMD SEV confidential guest utilities. | | [`signals`](signals) | Signal handling functions. | diff --git a/src/runtime/pkg/sev/README.md b/src/runtime/pkg/sev/README.md new file mode 100644 index 0000000000..8e864f46fa --- /dev/null +++ b/src/runtime/pkg/sev/README.md @@ -0,0 +1,14 @@ +# AMD SEV confidential guest utilities + +This package provides utilities for launching AMD SEV confidential guests. + +## Calculating expected launch digests + +The `CalculateLaunchDigest` function can be used to calculate the expected +SHA-256 of an SEV confidential guest given its firmware, kernel, initrd, and +kernel command-line. + +### Unit test data + +The [`testdata`](testdata) directory contains file used for testing +`CalculateLaunchDigest`. diff --git a/src/runtime/pkg/sev/ovmf.go b/src/runtime/pkg/sev/ovmf.go new file mode 100644 index 0000000000..9c6947abef --- /dev/null +++ b/src/runtime/pkg/sev/ovmf.go @@ -0,0 +1,101 @@ +// Copyright contributors to AMD SEV/-ES in Go +// +// SPDX-License-Identifier: Apache-2.0 + +package sev + +import ( + "bytes" + "encoding/binary" + "errors" + "os" +) + +// GUID 96b582de-1fb2-45f7-baea-a366c55a082d +var ovmfTableFooterGuid = guidLE{0xde, 0x82, 0xb5, 0x96, 0xb2, 0x1f, 0xf7, 0x45, 0xba, 0xea, 0xa3, 0x66, 0xc5, 0x5a, 0x08, 0x2d} + +// GUID 00f771de-1a7e-4fcb-890e-68c77e2fb44e +var sevEsResetBlockGuid = guidLE{0xde, 0x71, 0xf7, 0x00, 0x7e, 0x1a, 0xcb, 0x4f, 0x89, 0x0e, 0x68, 0xc7, 0x7e, 0x2f, 0xb4, 0x4e} + +type ovmfFooterTableEntry struct { + Size uint16 + Guid guidLE +} + +type ovmf struct { + table map[guidLE][]byte +} + +func NewOvmf(filename string) (ovmf, error) { + buf, err := os.ReadFile(filename) + if err != nil { + return ovmf{}, err + } + table, err := parseFooterTable(buf) + if err != nil { + return ovmf{}, err + } + return ovmf{table}, nil +} + +// Parse the OVMF footer table and return a map from GUID to entry value +func parseFooterTable(data []byte) (map[guidLE][]byte, error) { + table := make(map[guidLE][]byte) + + buf := new(bytes.Buffer) + err := binary.Write(buf, binary.LittleEndian, ovmfFooterTableEntry{}) + if err != nil { + return table, err + } + entryHeaderSize := buf.Len() + + // The OVMF table ends 32 bytes before the end of the firmware binary + startOfFooterTable := len(data) - 32 - entryHeaderSize + footerBytes := bytes.NewReader(data[startOfFooterTable:]) + var footer ovmfFooterTableEntry + err = binary.Read(footerBytes, binary.LittleEndian, &footer) + if err != nil { + return table, err + } + if footer.Guid != ovmfTableFooterGuid { + // No OVMF footer table + return table, nil + } + tableSize := int(footer.Size) - entryHeaderSize + if tableSize < 0 { + return table, nil + } + tableBytes := data[(startOfFooterTable - tableSize):startOfFooterTable] + for len(tableBytes) >= entryHeaderSize { + tsize := len(tableBytes) + entryBytes := bytes.NewReader(tableBytes[tsize-entryHeaderSize:]) + var entry ovmfFooterTableEntry + err := binary.Read(entryBytes, binary.LittleEndian, &entry) + if err != nil { + return table, err + } + if int(entry.Size) < entryHeaderSize { + return table, errors.New("Invalid entry size") + } + entryData := tableBytes[tsize-int(entry.Size) : tsize-entryHeaderSize] + table[entry.Guid] = entryData + tableBytes = tableBytes[:tsize-int(entry.Size)] + } + return table, nil +} + +func (o *ovmf) tableItem(guid guidLE) ([]byte, error) { + value, ok := o.table[guid] + if !ok { + return []byte{}, errors.New("OVMF footer table entry not found") + } + return value, nil +} + +func (o *ovmf) sevEsResetEip() (uint32, error) { + value, err := o.tableItem(sevEsResetBlockGuid) + if err != nil { + return 0, err + } + return binary.LittleEndian.Uint32(value), nil +} diff --git a/src/runtime/pkg/sev/sev.go b/src/runtime/pkg/sev/sev.go new file mode 100644 index 0000000000..bdf73cf603 --- /dev/null +++ b/src/runtime/pkg/sev/sev.go @@ -0,0 +1,203 @@ +// Copyright contributors to AMD SEV/-ES in Go +// +// SPDX-License-Identifier: Apache-2.0 +// + +// Package sev can be used to compute the expected hash values for +// SEV/-ES pre-launch attestation +package sev + +import ( + "bytes" + "crypto/sha256" + "encoding/binary" + "io" + "os" +) + +type guidLE [16]byte + +// The following definitions must be identical to those in QEMU target/i386/sev.c + +// GUID: 9438d606-4f22-4cc9-b479-a793d411fd21 +var sevHashTableHeaderGuid = guidLE{0x06, 0xd6, 0x38, 0x94, 0x22, 0x4f, 0xc9, 0x4c, 0xb4, 0x79, 0xa7, 0x93, 0xd4, 0x11, 0xfd, 0x21} + +// GUID: 4de79437-abd2-427f-b835-d5b172d2045b +var sevKernelEntryGuid = guidLE{0x37, 0x94, 0xe7, 0x4d, 0xd2, 0xab, 0x7f, 0x42, 0xb8, 0x35, 0xd5, 0xb1, 0x72, 0xd2, 0x04, 0x5b} + +// GUID: 44baf731-3a2f-4bd7-9af1-41e29169781d +var sevInitrdEntryGuid = guidLE{0x31, 0xf7, 0xba, 0x44, 0x2f, 0x3a, 0xd7, 0x4b, 0x9a, 0xf1, 0x41, 0xe2, 0x91, 0x69, 0x78, 0x1d} + +// GUID: 97d02dd8-bd20-4c94-aa78-e7714d36ab2a +var sevCmdlineEntryGuid = guidLE{0xd8, 0x2d, 0xd0, 0x97, 0x20, 0xbd, 0x94, 0x4c, 0xaa, 0x78, 0xe7, 0x71, 0x4d, 0x36, 0xab, 0x2a} + +type sevHashTableEntry struct { + entryGuid guidLE + length uint16 + hash [sha256.Size]byte +} + +type sevHashTable struct { + tableGuid guidLE + length uint16 + cmdline sevHashTableEntry + initrd sevHashTableEntry + kernel sevHashTableEntry +} + +type paddedSevHashTable struct { + table sevHashTable + padding [8]byte +} + +func fileSha256(filename string) (res [sha256.Size]byte, err error) { + f, err := os.Open(filename) + if err != nil { + return res, err + } + defer f.Close() + + digest := sha256.New() + if _, err := io.Copy(digest, f); err != nil { + return res, err + } + + copy(res[:], digest.Sum(nil)) + return res, nil +} + +func constructSevHashesTable(kernelPath, initrdPath, cmdline string) ([]byte, error) { + kernelHash, err := fileSha256(kernelPath) + if err != nil { + return []byte{}, err + } + + initrdHash, err := fileSha256(initrdPath) + if err != nil { + return []byte{}, err + } + + cmdlineHash := sha256.Sum256(append([]byte(cmdline), 0)) + + buf := new(bytes.Buffer) + err = binary.Write(buf, binary.LittleEndian, sevHashTableEntry{}) + if err != nil { + return []byte{}, err + } + entrySize := uint16(buf.Len()) + + buf = new(bytes.Buffer) + err = binary.Write(buf, binary.LittleEndian, sevHashTable{}) + if err != nil { + return []byte{}, err + } + tableSize := uint16(buf.Len()) + + ht := paddedSevHashTable{ + table: sevHashTable{ + tableGuid: sevHashTableHeaderGuid, + length: tableSize, + cmdline: sevHashTableEntry{ + entryGuid: sevCmdlineEntryGuid, + length: entrySize, + hash: cmdlineHash, + }, + initrd: sevHashTableEntry{ + entryGuid: sevInitrdEntryGuid, + length: entrySize, + hash: initrdHash, + }, + kernel: sevHashTableEntry{ + entryGuid: sevKernelEntryGuid, + length: entrySize, + hash: kernelHash, + }, + }, + padding: [8]byte{0, 0, 0, 0, 0, 0, 0, 0}, + } + + htBuf := new(bytes.Buffer) + err = binary.Write(htBuf, binary.LittleEndian, ht) + if err != nil { + return []byte{}, err + } + return htBuf.Bytes(), nil +} + +// CalculateLaunchDigest returns the sha256 encoded SEV launch digest based off +// the current firmware, kernel, initrd, and the kernel cmdline +func CalculateLaunchDigest(firmwarePath, kernelPath, initrdPath, cmdline string) (res [sha256.Size]byte, err error) { + f, err := os.Open(firmwarePath) + if err != nil { + return res, err + } + defer f.Close() + + digest := sha256.New() + if _, err := io.Copy(digest, f); err != nil { + return res, err + } + + // When used for confidential containers in kata-containers, kernelPath + // is always set (direct boot). However, this current package can also + // be used by other programs which may calculate launch digests of + // arbitrary SEV guests without SEV kernel hashes table. + if kernelPath != "" { + ht, err := constructSevHashesTable(kernelPath, initrdPath, cmdline) + if err != nil { + return res, err + } + digest.Write(ht) + } + + copy(res[:], digest.Sum(nil)) + return res, nil +} + +// CalculateSEVESLaunchDigest returns the sha256 encoded SEV-ES launch digest +// based off the current firmware, kernel, initrd, and the kernel cmdline, and +// the number of vcpus and their type +func CalculateSEVESLaunchDigest(vcpus int, vcpuSig VCPUSig, firmwarePath, kernelPath, initrdPath, cmdline string) (res [sha256.Size]byte, err error) { + f, err := os.Open(firmwarePath) + if err != nil { + return res, err + } + defer f.Close() + + digest := sha256.New() + if _, err := io.Copy(digest, f); err != nil { + return res, err + } + + // When used for confidential containers in kata-containers, kernelPath + // is always set (direct boot). However, this current package can also + // be used by other programs which may calculate launch digests of + // arbitrary SEV guests without SEV kernel hashes table. + if kernelPath != "" { + ht, err := constructSevHashesTable(kernelPath, initrdPath, cmdline) + if err != nil { + return res, err + } + digest.Write(ht) + } + + o, err := NewOvmf(firmwarePath) + if err != nil { + return res, err + } + resetEip, err := o.sevEsResetEip() + if err != nil { + return res, err + } + v := vmsaBuilder{uint64(resetEip), vcpuSig} + for i := 0; i < vcpus; i++ { + vmsaPage, err := v.buildPage(i) + if err != nil { + return res, err + } + digest.Write(vmsaPage) + } + + copy(res[:], digest.Sum(nil)) + return res, nil +} diff --git a/src/runtime/pkg/sev/sev_test.go b/src/runtime/pkg/sev/sev_test.go new file mode 100644 index 0000000000..68a82ea90d --- /dev/null +++ b/src/runtime/pkg/sev/sev_test.go @@ -0,0 +1,54 @@ +// Copyright contributors to AMD SEV/-ES in Go +// +// SPDX-License-Identifier: Apache-2.0 + +package sev + +import ( + "encoding/hex" + "testing" +) + +func TestCalculateLaunchDigestWithoutKernelHashes(t *testing.T) { + ld, err := CalculateLaunchDigest("testdata/ovmf_suffix.bin", "", "", "") + if err != nil { + t.Fatalf("unexpected err value: %s", err) + } + hexld := hex.EncodeToString(ld[:]) + if hexld != "b184e06e012366fd7b33ebfb361a515d05f00d354dca07b36abbc1e1e177ced5" { + t.Fatalf("wrong measurement: %s", hexld) + } +} + +func TestCalculateLaunchDigestWithKernelHashes(t *testing.T) { + ld, err := CalculateLaunchDigest("testdata/ovmf_suffix.bin", "/dev/null", "/dev/null", "") + if err != nil { + t.Fatalf("unexpected err value: %s", err) + } + hexld := hex.EncodeToString(ld[:]) + if hexld != "d59d7696efd7facfaa653758586e6120c4b6eaec3e327771d278cc6a44786ba5" { + t.Fatalf("wrong measurement: %s", hexld) + } +} + +func TestCalculateLaunchDigestWithKernelHashesSevEs(t *testing.T) { + ld, err := CalculateSEVESLaunchDigest(1, SigEpycV4, "testdata/ovmf_suffix.bin", "/dev/null", "/dev/null", "") + if err != nil { + t.Fatalf("unexpected err value: %s", err) + } + hexld := hex.EncodeToString(ld[:]) + if hexld != "7e5c26fb454621eb466978b4d0242b3c04b44a034de7fc0a2d8dac60ea2b6403" { + t.Fatalf("wrong measurement: %s", hexld) + } +} + +func TestCalculateLaunchDigestWithKernelHashesSevEsAndSmp(t *testing.T) { + ld, err := CalculateSEVESLaunchDigest(4, SigEpycV4, "testdata/ovmf_suffix.bin", "/dev/null", "/dev/null", "") + if err != nil { + t.Fatalf("unexpected err value: %s", err) + } + hexld := hex.EncodeToString(ld[:]) + if hexld != "b2111b0051fc3a06ec216899b2c78da99fb9d56c6ff2e8261dd3fe6cff79ecbc" { + t.Fatalf("wrong measurement: %s", hexld) + } +} diff --git a/src/runtime/pkg/sev/testdata/README.md b/src/runtime/pkg/sev/testdata/README.md new file mode 100644 index 0000000000..34554dc8e2 --- /dev/null +++ b/src/runtime/pkg/sev/testdata/README.md @@ -0,0 +1,9 @@ +# sev/testdata + +The `ovmf_suffix.bin` contains the last 4KB of the `OVMF.fd` binary from edk2's +`OvmfPkg/AmdSev/AmdSevX64.dsc` build. To save space, we committed only the +last 4KB instead of the the full 4MB binary. + +The end of the file contains a GUIDed footer table with entries that hold the +SEV-ES AP reset vector address, which is needed in order to compute VMSAs for +SEV-ES guests. diff --git a/src/runtime/pkg/sev/testdata/ovmf_suffix.bin b/src/runtime/pkg/sev/testdata/ovmf_suffix.bin new file mode 100644 index 0000000000000000000000000000000000000000..cc6d7ca7f0873de1305bfef33a7c1814934d8cf4 GIT binary patch literal 4096 zcmeGee{2(F^j&)`9Tu+64hk!*gAx%lTp?}bV<;`>U?XDi* z)8t^ggXLEKB1Q`tU$8$=F;?gO&hN|Ab4F!+K;n#It8&sMi7kO1PR9<(qw!okkb@ z3#KbMC={DKd|}mUZmfQRNsy*9M;|lZ)2f~34=dZ3zBI4r z{YrOL*%?%$@Mk3Y#ps(f7h$VPNDbGJl#YfH9gtFT4ZlT})&<~iv(n$-r;!i+Da4Ti z|B#1om+Mk$x9n;86DiiPy_`)0FpdOstjoYmNw_-Xw+tlN+`K3nNX#r0n3#N5N0da& z92`TVBWyMk|GjoV9Aid;iT{CUmIcw{nvO$4n;d3})P1Q>Rc#RHU^5HM(}*sYRbZV) zG&wV0)Psp8J4+|8DTDG1OlO3+9ZOioz@1o0ZP9#+l9>Ff40Z0Ba@}79Xi0G^Z*`^C z9mN13GS%+~kv;_uKSTjf5nIb+2{VS`2SIugC4Jjb!Fp;CL1$+)18w5;DH^TFl8<6O zTy4WQ7=u|hyh^qsH%i(iLZWWLNA6613a|qry9ZQtCPUds$@rOuq8tQu&qN4d8kl1V zs&-w}MV!a{C7^aii$U}KAUQ|OGU0wxS6@5krY=IsbkOaz2PZ)S6K26JPCJQV-0h^( zkQ6PUaby4p1HkN*5GkNIqp*br*0@-Rk7Dl<`gF|BeV7PY9#mEN zYuut>!?uw``sG1&J9PHOwSJpwAvI0|)|3GdpX$(P#Nq}Mq4NoC)@Tc%iwSMjXd9s` z2yI6!3Rr_$CubQh;mxFtb5MwFjG*(fEaQvGKcWQt5GT=;cG-m-ZH_Yy{J_GR%^X+7 zCpT=aS;Q^l`!};So47KqEtrPh7~1*SxoK^oFSqXANG|fC_yc0T2=%$~d~#EzyP9M8 zeg`gYlxD8zT#}+wUc;}FLl^?~4u278ZD4y9ORCp(q>U$$PFI}CNc)hgGL)tJPpnQ0 zcp@eUwTGloBGeT+gXdgOj>y@VXqS{4K|0RlYO5zF7R%ewbeKD;#Xy*pY}OiPm~GH} zlFO2*`Ym~+v}y^)fCmD;2rfUCbc%+0#Piem8N~6q$pnTkUYN4uxH5#6#tq!_0*emDLWkvn`hBv2uYx2Bfn*{i2Pg9nD;2WfgQ zG(J?JjBYHcPz)aN1ZJdP38eA}ia|b)N69ODaeI?&<^A}^m;kjrx1~I{xjgqoxtLIY z)|z(sz6z|IaWJRm`JW!%zklJ2>&gO$8bhDIhbb8co|;|f3HQ*tRmgqnD;V?x&;S!5 zXYML*;e2}WD=$oX`?tg2RKgXM;QX5i>pz~kJ_< 0xf { + family_low = 0xf + family_high = (family - 0x0f) & 0xff + } else { + family_low = family + family_high = 0 + } + + model_low := model & 0xf + model_high := (model >> 4) & 0xf + + stepping_low := stepping & 0xf + + return VCPUSig((family_high << 20) | + (model_high << 16) | + (family_low << 8) | + (model_low << 4) | + stepping_low) +} diff --git a/src/runtime/pkg/sev/vcpu_sigs_test.go b/src/runtime/pkg/sev/vcpu_sigs_test.go new file mode 100644 index 0000000000..70f8487509 --- /dev/null +++ b/src/runtime/pkg/sev/vcpu_sigs_test.go @@ -0,0 +1,21 @@ +// Copyright contributors to AMD SEV/-ES in Go +// +// SPDX-License-Identifier: Apache-2.0 + +package sev + +import ( + "testing" +) + +func TestNewVCPUSig(t *testing.T) { + if NewVCPUSig(23, 1, 2) != SigEpyc { + t.Errorf("wrong EPYC CPU signature") + } + if NewVCPUSig(23, 49, 0) != SigEpycRome { + t.Errorf("wrong EPYC-Rome CPU signature") + } + if NewVCPUSig(25, 1, 1) != SigEpycMilan { + t.Errorf("wrong EPYC-Milan CPU signature") + } +} diff --git a/src/runtime/pkg/sev/vmsa.go b/src/runtime/pkg/sev/vmsa.go new file mode 100644 index 0000000000..c2bbc4122b --- /dev/null +++ b/src/runtime/pkg/sev/vmsa.go @@ -0,0 +1,172 @@ +// Copyright contributors to AMD SEV/-ES in Go +// +// SPDX-License-Identifier: Apache-2.0 + +package sev + +import ( + "bytes" + "encoding/binary" +) + +// VMCB Segment (struct vmcb_seg in the linux kernel) +type vmcbSeg struct { + selector uint16 + attrib uint16 + limit uint32 + base uint64 +} + +// VMSA page +// +// The names of the fields are taken from struct sev_es_work_area in the linux kernel: +// https://github.com/AMDESE/linux/blob/sev-snp-v12/arch/x86/include/asm/svm.h#L318 +// (following the definitions in AMD APM Vol 2 Table B-4) +type sevEsSaveArea struct { + es vmcbSeg + cs vmcbSeg + ss vmcbSeg + ds vmcbSeg + fs vmcbSeg + gs vmcbSeg + gdtr vmcbSeg + ldtr vmcbSeg + idtr vmcbSeg + tr vmcbSeg + vmpl0_ssp uint64 // nolint: unused + vmpl1_ssp uint64 // nolint: unused + vmpl2_ssp uint64 // nolint: unused + vmpl3_ssp uint64 // nolint: unused + u_cet uint64 // nolint: unused + reserved_1 [2]uint8 // nolint: unused + vmpl uint8 // nolint: unused + cpl uint8 // nolint: unused + reserved_2 [4]uint8 // nolint: unused + efer uint64 + reserved_3 [104]uint8 // nolint: unused + xss uint64 // nolint: unused + cr4 uint64 + cr3 uint64 // nolint: unused + cr0 uint64 + dr7 uint64 + dr6 uint64 + rflags uint64 + rip uint64 + dr0 uint64 // nolint: unused + dr1 uint64 // nolint: unused + dr2 uint64 // nolint: unused + dr3 uint64 // nolint: unused + dr0_addr_mask uint64 // nolint: unused + dr1_addr_mask uint64 // nolint: unused + dr2_addr_mask uint64 // nolint: unused + dr3_addr_mask uint64 // nolint: unused + reserved_4 [24]uint8 // nolint: unused + rsp uint64 // nolint: unused + s_cet uint64 // nolint: unused + ssp uint64 // nolint: unused + isst_addr uint64 // nolint: unused + rax uint64 // nolint: unused + star uint64 // nolint: unused + lstar uint64 // nolint: unused + cstar uint64 // nolint: unused + sfmask uint64 // nolint: unused + kernel_gs_base uint64 // nolint: unused + sysenter_cs uint64 // nolint: unused + sysenter_esp uint64 // nolint: unused + sysenter_eip uint64 // nolint: unused + cr2 uint64 // nolint: unused + reserved_5 [32]uint8 // nolint: unused + g_pat uint64 + dbgctrl uint64 // nolint: unused + br_from uint64 // nolint: unused + br_to uint64 // nolint: unused + last_excp_from uint64 // nolint: unused + last_excp_to uint64 // nolint: unused + reserved_7 [80]uint8 // nolint: unused + pkru uint32 // nolint: unused + reserved_8 [20]uint8 // nolint: unused + reserved_9 uint64 // nolint: unused + rcx uint64 // nolint: unused + rdx uint64 + rbx uint64 // nolint: unused + reserved_10 uint64 // nolint: unused + rbp uint64 // nolint: unused + rsi uint64 // nolint: unused + rdi uint64 // nolint: unused + r8 uint64 // nolint: unused + r9 uint64 // nolint: unused + r10 uint64 // nolint: unused + r11 uint64 // nolint: unused + r12 uint64 // nolint: unused + r13 uint64 // nolint: unused + r14 uint64 // nolint: unused + r15 uint64 // nolint: unused + reserved_11 [16]uint8 // nolint: unused + guest_exit_info_1 uint64 // nolint: unused + guest_exit_info_2 uint64 // nolint: unused + guest_exit_int_info uint64 // nolint: unused + guest_nrip uint64 // nolint: unused + sev_features uint64 + vintr_ctrl uint64 // nolint: unused + guest_exit_code uint64 // nolint: unused + virtual_tom uint64 // nolint: unused + tlb_id uint64 // nolint: unused + pcpu_id uint64 // nolint: unused + event_inj uint64 // nolint: unused + xcr0 uint64 + reserved_12 [16]uint8 // nolint: unused + x87_dp uint64 // nolint: unused + mxcsr uint32 // nolint: unused + x87_ftw uint16 // nolint: unused + x87_fsw uint16 // nolint: unused + x87_fcw uint16 // nolint: unused + x87_fop uint16 // nolint: unused + x87_ds uint16 // nolint: unused + x87_cs uint16 // nolint: unused + x87_rip uint64 // nolint: unused + fpreg_x87 [80]uint8 // nolint: unused + fpreg_xmm [256]uint8 // nolint: unused + fpreg_ymm [256]uint8 // nolint: unused + unused [2448]uint8 // nolint: unused +} + +type vmsaBuilder struct { + apEIP uint64 + vcpuSig VCPUSig +} + +func (v *vmsaBuilder) buildPage(i int) ([]byte, error) { + eip := uint64(0xfffffff0) // BSP (first vcpu) + if i > 0 { + eip = v.apEIP + } + saveArea := sevEsSaveArea{ + es: vmcbSeg{0, 0x93, 0xffff, 0}, + cs: vmcbSeg{0xf000, 0x9b, 0xffff, eip & 0xffff0000}, + ss: vmcbSeg{0, 0x93, 0xffff, 0}, + ds: vmcbSeg{0, 0x93, 0xffff, 0}, + fs: vmcbSeg{0, 0x93, 0xffff, 0}, + gs: vmcbSeg{0, 0x93, 0xffff, 0}, + gdtr: vmcbSeg{0, 0, 0xffff, 0}, + idtr: vmcbSeg{0, 0, 0xffff, 0}, + ldtr: vmcbSeg{0, 0x82, 0xffff, 0}, + tr: vmcbSeg{0, 0x8b, 0xffff, 0}, + efer: 0x1000, // KVM enables EFER_SVME + cr4: 0x40, // KVM enables X86_CR4_MCE + cr0: 0x10, + dr7: 0x400, + dr6: 0xffff0ff0, + rflags: 0x2, + rip: eip & 0xffff, + g_pat: 0x7040600070406, // PAT MSR: See AMD APM Vol 2, Section A.3 + rdx: uint64(v.vcpuSig), + sev_features: 0, // SEV-ES + xcr0: 0x1, + } + page := new(bytes.Buffer) + err := binary.Write(page, binary.LittleEndian, saveArea) + if err != nil { + return []byte{}, err + } + return page.Bytes(), nil +} From dd7562522aa796ad72368b9a12c95a95f9d3a65d Mon Sep 17 00:00:00 2001 From: Dov Murik Date: Mon, 8 May 2023 10:23:52 +0300 Subject: [PATCH 2/2] runtime: pkg/sev: Add kbs utility package for SEV pre-attestation Supports both online and offline modes of interaction with simple-kbs for SEV/SEV-ES confidential guests. Fixes: #6795 Signed-off-by: Dov Murik --- src/runtime/pkg/sev/kbs/kbs.go | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/runtime/pkg/sev/kbs/kbs.go diff --git a/src/runtime/pkg/sev/kbs/kbs.go b/src/runtime/pkg/sev/kbs/kbs.go new file mode 100644 index 0000000000..3c7e20134a --- /dev/null +++ b/src/runtime/pkg/sev/kbs/kbs.go @@ -0,0 +1,33 @@ +// Copyright contributors to AMD SEV/-ES in Go +// +// SPDX-License-Identifier: Apache-2.0 +// + +// Package kbs can be used interact with simple-kbs, the key broker +// server for SEV and SEV-ES pre-attestation + +package kbs + +const ( + Offline = "offline" + OfflineSecretType = "bundle" + OfflineSecretGuid = "e6f5a162-d67f-4750-a67c-5d065f2a9910" + Online = "online" + OnlineBootParam = "online_sev_kbc" + OnlineSecretType = "connection" + OnlineSecretGuid = "1ee27366-0c87-43a6-af48-28543eaf7cb0" +) + +type GuestPreAttestationConfig struct { + Proxy string + Keyset string + LaunchId string + KernelPath string + InitrdPath string + FwPath string + KernelParameters string + CertChainPath string + SecretType string + SecretGuid string + Policy uint32 +}