mirror of https://github.com/kubernetes/kops.git
335 lines
10 KiB
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
|
|
}
|