Skip to content

Commit

Permalink
Merge pull request #145 from deeglaze/kdsworkaround
Browse files Browse the repository at this point in the history
Extend v3 support to KDS and change proto representation
  • Loading branch information
deeglaze authored Dec 3, 2024
2 parents 64cd695 + 9af2767 commit aef6141
Show file tree
Hide file tree
Showing 18 changed files with 784 additions and 270 deletions.
115 changes: 82 additions & 33 deletions abi/abi.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,17 @@ const (
ExtraPlatformInfoV0Size = 8

// CpuidProductMask keeps only the SevProduct-relevant bits from the CPUID(1).EAX result.
CpuidProductMask = 0x0fff0f0f
CpuidProductMask = 0x0fff0fff
extendedFamilyShift = 20
extendedModelShift = 16
familyShift = 8
sevExtendedFamily = 0xA
sevFamily = 0xF
milanExtendedModel = 0
genoaExtendedModel = 1
modelShift = 4
// Combined extended values
zen3zen4Family = 0x19
zen5Family = 0x1A
milanModel = 0 | 1
genoaModel = (1 << 4) | 1
turinModel = 2

// ReportVersion2 is set by the SNP API specification
// https://web.archive.org/web/20231222054111if_/http://www.amd.com/content/dam/amd/en/documents/epyc-technical-docs/specifications/56860.pdf
Expand Down Expand Up @@ -487,9 +490,7 @@ func ReportToProto(data []uint8) (*pb.Report, error) {
mbzLo := 0x188
if r.Version == ReportVersion3 {
mbzLo = 0x18B
r.CpuidFamId = []byte{data[0x188]}
r.CpuidModId = []byte{data[0x189]}
r.CpuidStep = []byte{data[0x18A]}
r.Cpuid1EaxFms = FmsToCpuid1Eax(data[0x188], data[0x189], data[0x18A])
}

if err := mbz(data, mbzLo, 0x1A0); err != nil {
Expand Down Expand Up @@ -635,9 +636,10 @@ func ReportToAbiBytes(r *pb.Report) ([]byte, error) {

// Add CPUID information if this is a version 3 report.
if r.Version == ReportVersion3 {
data[0x188] = r.CpuidFamId[0]
data[0x189] = r.CpuidModId[0]
data[0x18A] = r.CpuidStep[0]
family, model, stepping := FmsFromCpuid1Eax(r.Cpuid1EaxFms)
data[0x188] = family
data[0x189] = model
data[0x18A] = stepping
}

copy(data[0x1A0:0x1E0], r.ChipId[:])
Expand Down Expand Up @@ -903,59 +905,106 @@ func (c *CertTable) Proto() *pb.CertificateChain {
// See assembly implementations in cpuid_*.s
var cpuid func(op uint32) (eax, ebx, ecx, edx uint32)

// SevProductFromCpuid1Eax returns the SevProduct that is represented by cpuid(1).eax.
func SevProductFromCpuid1Eax(eax uint32) *pb.SevProduct {
// FmsToCpuid1Eax returns the masked CPUID_1_EAX value that represents the given
// family, model, stepping (FMS) values.
func FmsToCpuid1Eax(family, model, stepping byte) uint32 {
var extendedFamily byte

familyID := family
if family >= 0xf {
extendedFamily = family - 0xf
familyID = 0xf
}
extendedModel := model >> 4
modelID := model & 0xf
return (uint32(extendedFamily) << extendedFamilyShift) |
(uint32(extendedModel) << extendedModelShift) |
(uint32(familyID) << familyShift) |
(uint32(modelID) << modelShift) |
(uint32(stepping & 0xf))
}

// FmsFromCpuid1Eax returns the family, model, stepping (FMS) values extracted from a
// CPUID_1_EAX value.
func FmsFromCpuid1Eax(eax uint32) (byte, byte, byte) {
// 31:28 reserved
// 27:20 Extended Family ID
extendedFamily := (eax >> extendedFamilyShift) & 0xff
extendedFamily := byte((eax >> extendedFamilyShift) & 0xff)
// 19:16 Extended Model ID
extendedModel := (eax >> extendedModelShift) & 0xf
extendedModel := byte((eax >> extendedModelShift) & 0xf)
// 15:14 reserved
// 11:8 Family ID
family := (eax >> familyShift) & 0xf
familyID := byte((eax >> familyShift) & 0xf)
// 7:4 Model
modelID := byte((eax >> modelShift) & 0xf)
// 3:0 Stepping
stepping := eax & 0xf
family := extendedFamily + familyID
model := (extendedModel << 4) | modelID
stepping := byte(eax & 0xf)
return family, model, stepping
}

// SevProductFromCpuid1Eax returns the SevProduct that is represented by cpuid(1).eax.
func SevProductFromCpuid1Eax(eax uint32) *pb.SevProduct {
family, model, stepping := FmsFromCpuid1Eax(eax)
// Ah, Fh, {0h,1h} values from the KDS specification,
// section "Determining the Product Name".
var productName pb.SevProduct_SevProductName
unknown := func() {
productName = pb.SevProduct_SEV_PRODUCT_UNKNOWN
stepping = 0 // Reveal nothing.
}
// Product information specified by processor programming reference publications.
if extendedFamily == sevExtendedFamily && family == sevFamily {
switch extendedModel {
case milanExtendedModel:
switch family {
case zen3zen4Family:
switch model {
case milanModel:
productName = pb.SevProduct_SEV_PRODUCT_MILAN
case genoaExtendedModel:
case genoaModel:
productName = pb.SevProduct_SEV_PRODUCT_GENOA
default:
productName = pb.SevProduct_SEV_PRODUCT_UNKNOWN
stepping = 0 // Reveal nothing.
unknown()
}
case zen5Family:
switch model {
case turinModel:
productName = pb.SevProduct_SEV_PRODUCT_TURIN
default:
unknown()
}
default:
unknown()
}
return &pb.SevProduct{
Name: productName,
MachineStepping: &wrapperspb.UInt32Value{Value: stepping},
MachineStepping: &wrapperspb.UInt32Value{Value: uint32(stepping)},
}
}

// MaskedCpuid1EaxFromSevProduct returns the Cpuid1Eax value expected from the given product
// when masked with CpuidProductMask.
func MaskedCpuid1EaxFromSevProduct(product *pb.SevProduct) uint32 {
var stepping uint32
if product == nil {
return 0
}
var family, model, stepping byte
if product.MachineStepping != nil {
stepping = product.MachineStepping.Value & 0xf
stepping = byte(product.MachineStepping.Value & 0xf)
}
extendedFamily := uint32(sevExtendedFamily) << extendedFamilyShift
family := uint32(sevFamily) << familyShift

var extendedModel uint32
switch product.Name {
case pb.SevProduct_SEV_PRODUCT_MILAN:
extendedModel = milanExtendedModel
family = zen3zen4Family
model = milanModel
case pb.SevProduct_SEV_PRODUCT_GENOA:
extendedModel = genoaExtendedModel
family = zen3zen4Family
model = genoaModel
case pb.SevProduct_SEV_PRODUCT_TURIN:
family = zen5Family
model = turinModel
default:
return 0
}
return extendedFamily | family | stepping | (extendedModel << extendedModelShift)
return FmsToCpuid1Eax(family, model, stepping)
}

// SevProduct returns the SEV product enum for the CPU that runs this
Expand Down
38 changes: 23 additions & 15 deletions abi/abi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package abi
import (
"bytes"
"encoding/hex"
"fmt"
"math/rand"
"runtime"
"strings"
Expand Down Expand Up @@ -61,9 +62,7 @@ var (
author_key_digest: '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
report_id: '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
report_id_ma: '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
cpuid_fam_id: '\x00\x00'
cpuid_mod_id: '\x00\x00'
cpuid_step: '\x00\x00'
cpuid1eax_fms: 0
chip_id: '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
signature: '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
`
Expand Down Expand Up @@ -396,17 +395,26 @@ func TestSevProduct(t *testing.T) {
MachineStepping: &wrapperspb.UInt32Value{Value: 0},
},
},
{
eax: 0x00b00f21,
want: &spb.SevProduct{
Name: spb.SevProduct_SEV_PRODUCT_TURIN,
MachineStepping: &wrapperspb.UInt32Value{Value: 1},
},
},
}
for _, tc := range tcs {
cpuid = func(uint32) (uint32, uint32, uint32, uint32) { return tc.eax, 0, 0, 0 }
got := SevProduct()
if diff := cmp.Diff(got, tc.want, protocmp.Transform()); diff != "" {
t.Errorf("SevProduct() = %+v, want %+v. Diff: %s", got, tc.want, diff)
}
got2 := SevProductFromCpuid1Eax(tc.eax)
if diff := cmp.Diff(got2, got, protocmp.Transform()); diff != "" {
t.Errorf("SevProductFromCpuid1Eax(0x%x) = %+v, want %+v. Diff: %s", tc.eax, got2, tc.want, diff)
}
t.Run(fmt.Sprintf("EAX_0x%x", tc.eax), func(t *testing.T) {
cpuid = func(uint32) (uint32, uint32, uint32, uint32) { return tc.eax, 0, 0, 0 }
got := SevProduct()
if diff := cmp.Diff(got, tc.want, protocmp.Transform()); diff != "" {
t.Errorf("SevProduct() = %+v, want %+v. Diff: %s", got, tc.want, diff)
}
got2 := SevProductFromCpuid1Eax(tc.eax)
if diff := cmp.Diff(got2, got, protocmp.Transform()); diff != "" {
t.Errorf("SevProductFromCpuid1Eax(0x%x) = %+v, want %+v. Diff: %s", tc.eax, got2, tc.want, diff)
}
})
}
}

Expand Down Expand Up @@ -446,9 +454,9 @@ func TestExtendedPlatformCertTable(t *testing.T) {
eax uint32
stepping uint32
}{
{name: "Genoa-B2 cruft", pname: spb.SevProduct_SEV_PRODUCT_GENOA, eax: 0x00a10f12, stepping: 2},
{name: "Milan-B1 cruft", pname: spb.SevProduct_SEV_PRODUCT_MILAN, eax: 0x00a00f11, stepping: 1},
{name: "Milan-B0", pname: spb.SevProduct_SEV_PRODUCT_MILAN, eax: 0x00a00f00, stepping: 0},
{name: "Genoa-B2", pname: spb.SevProduct_SEV_PRODUCT_GENOA, eax: 0x00a10f12, stepping: 2},
{name: "Milan-B1", pname: spb.SevProduct_SEV_PRODUCT_MILAN, eax: 0x00a00f11, stepping: 1},
{name: "Milan-B0", pname: spb.SevProduct_SEV_PRODUCT_MILAN, eax: 0x00a00f10, stepping: 0},
}
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/google/go-sev-guest
go 1.19

require (
github.com/golang/protobuf v1.5.0
github.com/google/go-cmp v0.5.7
github.com/google/go-configfs-tsm v0.2.2
github.com/google/logger v1.1.1
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-configfs-tsm v0.2.2 h1:YnJ9rXIOj5BYD7/0DNnzs8AOp7UcvjfTvt215EWcs98=
Expand All @@ -18,6 +21,7 @@ golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
26 changes: 25 additions & 1 deletion kds/kds.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,19 @@ var (
"Genoa-B0": {Name: pb.SevProduct_SEV_PRODUCT_GENOA, MachineStepping: uint0},
"Genoa-B1": {Name: pb.SevProduct_SEV_PRODUCT_GENOA, MachineStepping: uint1},
"Genoa-B2": {Name: pb.SevProduct_SEV_PRODUCT_GENOA, MachineStepping: uint2},
"Turin-B0": {Name: pb.SevProduct_SEV_PRODUCT_TURIN, MachineStepping: uint0},
"Turin-B1": {Name: pb.SevProduct_SEV_PRODUCT_TURIN, MachineStepping: uint1},
}
milanSteppingVersions = []string{"B0", "B1"}
genoaSteppingVersions = []string{"B0", "B1", "B2"}
turinSteppingVersions = []string{"B0", "B1"}

// ProductLineCpuid associates the CPUID_1_EAX value (Stepping 0) to its AMD product name.
ProductLineCpuid = map[uint32]string{
0x00a00f10: "Milan",
0x00a10f10: "Genoa",
0x00b00f20: "Turin",
}
)

// TCBVersion is a 64-bit bitfield of different security patch levels of AMD firmware and microcode.
Expand Down Expand Up @@ -731,6 +741,8 @@ func ProductLine(product *pb.SevProduct) string {
return "Milan"
case pb.SevProduct_SEV_PRODUCT_GENOA:
return "Genoa"
case pb.SevProduct_SEV_PRODUCT_TURIN:
return "Turin"
default:
return "Unknown"
}
Expand Down Expand Up @@ -785,12 +797,22 @@ func ProductName(product *pb.SevProduct) string {
if int(stepping) >= len(genoaSteppingVersions) {
return "unmappedGenoaStepping"
}
return fmt.Sprintf("Milan-%s", genoaSteppingVersions[stepping])
return fmt.Sprintf("Genoa-%s", genoaSteppingVersions[stepping])
case pb.SevProduct_SEV_PRODUCT_TURIN:
if int(stepping) >= len(turinSteppingVersions) {
return "unmappedTurinStepping"
}
return fmt.Sprintf("Turin-%s", turinSteppingVersions[stepping])
default:
return "Unknown"
}
}

// ProductLineFromFms returns the product name used in the KDS endpoint to fetch VCEK certificates.
func ProductLineFromFms(fms uint32) string {
return ProductLine(abi.SevProductFromCpuid1Eax(fms))
}

// ParseProduct returns the SevProductName for a product name without the stepping suffix.
//
// Deprecated: Use ParseProductLine
Expand All @@ -809,6 +831,8 @@ func ParseProductLine(productLine string) (*pb.SevProduct, error) {
return &pb.SevProduct{Name: pb.SevProduct_SEV_PRODUCT_MILAN}, nil
case "Genoa":
return &pb.SevProduct{Name: pb.SevProduct_SEV_PRODUCT_GENOA}, nil
case "Turin":
return &pb.SevProduct{Name: pb.SevProduct_SEV_PRODUCT_TURIN}, nil
default:
return nil, fmt.Errorf("unknown AMD SEV product: %q", productLine)
}
Expand Down
1 change: 1 addition & 0 deletions proto/fakekds.proto
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ message Certificates {
bytes chip_id = 1; // Should be 64 bytes
map<uint64, bytes> tcb_certs = 2;
string hostname = 3;
uint32 fms = 4;
}
repeated ChipTCBCerts chip_certs = 1;
}
29 changes: 19 additions & 10 deletions proto/fakekds/fakekds.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit aef6141

Please sign in to comment.