kops/vendor/github.com/google/go-sev-guest/client/linuxabi/linux_abi.go

335 lines
10 KiB
Go

// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package linuxabi describes the /dev/sev-guest ioctl command ABI.
package linuxabi
import (
"errors"
"fmt"
"reflect"
"unsafe"
)
// EsResult is the status code type for Linux's GHCB communication results.
type EsResult int
// ioctl bits for x86-64
const (
iocNrbits = 8
iocTypebits = 8
iocSizebits = 14
iocDirbits = 2
iocNrshift = 0
iocTypeshift = (iocNrshift + iocNrbits)
iocSizeshift = (iocTypeshift + iocTypebits)
iocDirshift = (iocSizeshift + iocSizebits)
iocWrite = 1
iocRead = 2
// Linux /dev/sev-guest ioctl interface
iocTypeSnpGuestReq = 'S'
iocSnpWithoutNr = ((iocWrite | iocRead) << iocDirshift) |
(iocTypeSnpGuestReq << iocTypeshift) |
// unsafe.Sizeof(snpUserGuestRequest)
(32 << iocSizeshift)
// IocSnpGetReport is the ioctl command for getting an attestation report
IocSnpGetReport = iocSnpWithoutNr | (0x0 << iocNrshift)
// IocSnpGetDerivedKey is the ioctl command for getting a key derived from measured components and
// either the VCEK or VMRK.
IocSnpGetDerivedKey = iocSnpWithoutNr | (0x1 << iocNrshift)
// IocSnpGetReport is the ioctl command for getting an extended attestation report that includes
// certificate information.
IocSnpGetExtendedReport = iocSnpWithoutNr | (0x2 << iocNrshift)
// The message version for MSG_REPORT_REQ in the SNP API. Specified as 1.
guestMsgVersion = 1
// These numbers are from the uapi header sev_guest.h
snpResportRespSize = 4000
msgReportReqHeaderSize = 0x20
SnpReportRespReportSize = snpResportRespSize - msgReportReqHeaderSize
)
const (
// EsOk denotes success.
EsOk EsResult = iota
// EsUnsupported denotes that the requested operation is not supported.
EsUnsupported
// EsVmmError denotes that the virtual machine monitor was in an unexpected state.
EsVmmError
// EsDecodeFailed denotes that instruction decoding failed.
EsDecodeFailed
// EsException denotes that the GHCB communication caused an exception.
EsException
// EsRetry is the code for a retry instruction emulation
EsRetry
)
// SevEsErr is an error that interprets SEV-ES guest-host communication results.
type SevEsErr struct {
Result EsResult
}
func (err *SevEsErr) Error() string {
if err.Result == EsUnsupported {
return "requested operation not supported"
}
if err.Result == EsVmmError {
return "unexpected state from the VMM"
}
if err.Result == EsDecodeFailed {
return "instruction decoding failed"
}
if err.Result == EsException {
return "instruction caused exception"
}
if err.Result == EsRetry {
return "retry instruction emulation"
}
return "unknown error"
}
// SnpReportReqABI is Linux's sev-guest ioctl abi for sending a GET_REPORT request. See
// include/uapi/linux/sev-guest.h
type SnpReportReqABI struct {
// ReportData to be included in the report
ReportData [64]uint8
// Vmpl is the SEV-SNP VMPL level to be included in the report.
// The kernel must have access to the corresponding VMPCK.
Vmpl uint32
reserved [28]byte
}
// SnpReportRespABI is Linux's sev-guest ioctl abi for receiving a GET_REPORT response.
// The size is expected to be snpReportRespSize.
type SnpReportRespABI struct {
Status uint32
ReportSize uint32
reserved [0x20 - 8]byte
// Data is the response data, see SEV-SNP spec for the format
Data [SnpReportRespReportSize]uint8
}
// ABI returns the same object since it doesn't need a separate representation across the interface.
func (r *SnpReportReqABI) ABI() BinaryConversion { return r }
// Pointer returns a pointer to the object itself.
func (r *SnpReportReqABI) Pointer() unsafe.Pointer {
return unsafe.Pointer(r)
}
// Finish is a no-op.
func (r *SnpReportReqABI) Finish(_ BinaryConvertible) error { return nil }
// ABI returns the same object since it doesn't need a separate representation across the interface.
func (r *SnpReportRespABI) ABI() BinaryConversion { return r }
// Pointer returns a pointer to the object itself.
func (r *SnpReportRespABI) Pointer() unsafe.Pointer {
return unsafe.Pointer(r)
}
// Finish checks the status of the message and translates it to a Golang error.
func (r *SnpReportRespABI) Finish(_ BinaryConvertible) error {
if r.Status != 0 {
switch r.Status {
case 0x16: // Value from MSG_REPORT_RSP specification in SNP API.
return errors.New("get_report had invalid parameters")
default:
return fmt.Errorf("unknown status: 0x%x", r.Status)
}
}
return nil
}
// SnpDerivedKeyReqABI is the ABI representation of a request to the SEV guest device to derive a
// key from specified information.
type SnpDerivedKeyReqABI struct {
// RootKeySelect is all reserved bits except bit 0 for UseVMRK (1) or UseVCEK (0).
RootKeySelect uint32
reserved uint32
GuestFieldSelect uint64
// Vmpl to mix into the key. Must be greater than or equal to current Vmpl.
Vmpl uint32
// GuestSVN to mix into the key. Must be less than or equal to GuestSVN at launch.
GuestSVN uint32
// TCBVersion to mix into the key. Must be less than or equal to the CommittedTcb.
TCBVersion uint64
}
// Pointer returns a pointer to the object.
func (r *SnpDerivedKeyReqABI) Pointer() unsafe.Pointer { return unsafe.Pointer(r) }
// Finish is a no-op.
func (r *SnpDerivedKeyReqABI) Finish(BinaryConvertible) error { return nil }
// ABI returns the ABI representation of this object.
func (r *SnpDerivedKeyReqABI) ABI() BinaryConversion { return r }
// SnpDerivedKeyRespABI represents the response to an SnpDerivedKeyReq.
type SnpDerivedKeyRespABI struct {
Status uint32
reserved [0x20 - 4]byte
Data [32]byte
}
// ABI returns the object itself.
func (r *SnpDerivedKeyRespABI) ABI() BinaryConversion { return r }
// Pointer returns a pointer to the object itself.
func (r *SnpDerivedKeyRespABI) Pointer() unsafe.Pointer { return unsafe.Pointer(r) }
// Finish is a no-op.
func (r *SnpDerivedKeyRespABI) Finish(BinaryConvertible) error {
switch r.Status {
case 0:
return nil
case 0x16:
return errors.New("msg_key_req error: invalid parameters")
default:
return fmt.Errorf("msg_key_req unknown status code: 0x%x", r.Status)
}
}
// SnpExtendedReportReqABI is Linux's sev-guest ioctl abi for sending a GET_EXTENDED_REPORT request.
type SnpExtendedReportReqABI struct {
Data SnpReportReqABI
// Where to copy the certificate blob.
CertsAddress unsafe.Pointer
// length of the certificate blob
CertsLength uint32
}
// SnpExtendedReportReq is close to Linux's sev-guest ioctl abi for sending a GET_EXTENDED_REPORT request,
// but uses safer types for the Ioctl interface.
type SnpExtendedReportReq struct {
Data SnpReportReqABI
// Certs receives the certificate blob after the extended report request.
Certs []byte
// CertsLength is the length of the certificate blob.
CertsLength uint32
}
// Pointer returns a pointer so the object itself.
func (r *SnpExtendedReportReqABI) Pointer() unsafe.Pointer {
return unsafe.Pointer(r)
}
// Finish writes back the changed CertsLength value.
func (r *SnpExtendedReportReqABI) Finish(b BinaryConvertible) error {
s, ok := b.(*SnpExtendedReportReq)
if !ok {
return fmt.Errorf("Finish argument is %v. Expects a *SnpExtendedReportReq", reflect.TypeOf(b))
}
s.CertsLength = r.CertsLength
return nil
}
// ABI returns an object that can cross the ABI boundary and copy back changes to the original
// object.
func (r *SnpExtendedReportReq) ABI() BinaryConversion {
var certsAddress unsafe.Pointer
if len(r.Certs) != 0 {
certsAddress = unsafe.Pointer(&r.Certs[0])
}
return &SnpExtendedReportReqABI{
Data: r.Data,
CertsAddress: certsAddress,
CertsLength: r.CertsLength,
}
}
// SnpUserGuestRequestABI is Linux's sev-guest ioctl abi for issuing a guest message.
type SnpUserGuestRequestABI struct {
GuestMsgVersion uint32
// Request and response structure address.
ReqData unsafe.Pointer
RespData unsafe.Pointer
// firmware error code on failure (see psp-sev.h in Linux kernel)
FwErr uint64
}
type snpUserGuestRequestConversion struct {
abi SnpUserGuestRequestABI
reqConv BinaryConversion
respConv BinaryConversion
}
// SnpUserGuestRequest is Linux's sev-guest ioctl interface for issuing a guest message. The
// types here enhance runtime safety when using Ioctl as an interface.
type SnpUserGuestRequest struct {
// Request and response structure address.
ReqData BinaryConvertible
RespData BinaryConvertible
// firmware error code on failure (see psp-sev.h in Linux kernel)
FwErr uint64
}
// ABI returns an object that can cross the ABI boundary and copy back changes to the original
// object.
func (r *SnpUserGuestRequest) ABI() BinaryConversion {
result := &snpUserGuestRequestConversion{
reqConv: r.ReqData.ABI(),
respConv: r.RespData.ABI(),
}
result.abi.GuestMsgVersion = guestMsgVersion
result.abi.ReqData = result.reqConv.Pointer()
result.abi.RespData = result.respConv.Pointer()
return result
}
// Pointer returns a pointer to the object that crosses the ABI boundary.
func (r *snpUserGuestRequestConversion) Pointer() unsafe.Pointer {
return unsafe.Pointer(&r.abi)
}
// Finish writes back the FwErr and any changes to the request or response objects.
func (r *snpUserGuestRequestConversion) Finish(b BinaryConvertible) error {
s, ok := b.(*SnpUserGuestRequest)
if !ok {
return fmt.Errorf("Finish argument is %v. Expects a *SnpUserGuestRequestSafe", reflect.TypeOf(b))
}
if err := r.reqConv.Finish(s.ReqData); err != nil {
return fmt.Errorf("could not finalize request data: %v", err)
}
if err := r.respConv.Finish(s.RespData); err != nil {
return fmt.Errorf("could not finalize response data: %v", err)
}
s.FwErr = r.abi.FwErr
return nil
}
// BinaryConversion is an interface that abstracts a "stand-in" object that passes through an ABI
// boundary and can finalize changes to the original object.
type BinaryConversion interface {
Pointer() unsafe.Pointer
Finish(BinaryConvertible) error
}
// BinaryConvertible is an interface for an object that can produce a partner BinaryConversion
// object to allow its representation to pass the ABI boundary.
type BinaryConvertible interface {
ABI() BinaryConversion
}