diff --git a/hypervisor/x86.go b/hypervisor/x86.go index ea6cce5..2744cf8 100644 --- a/hypervisor/x86.go +++ b/hypervisor/x86.go @@ -2,6 +2,7 @@ package hypervisor import ( "context" + "j5.nz/cc/hypervisor/x86state" ) @@ -33,6 +34,10 @@ const ( // The PC interrupt controllers and interval timer are provided by the backend. // Firmware, disk and display device semantics belong to the caller. type X86 interface { + // SupportedCPUID returns an owned table of accelerator-supported leaves. + SupportedCPUID() ([]x86state.CPUIDEntry, error) + // SetCPUID installs the machine's feature policy before the first Run. + SetCPUID([]x86state.CPUIDEntry) error MapRAM(base, size uint64) ([]byte, error) MapRAMRegions(size uint64, regions []RAMRegion) ([]byte, error) Registers() (x86state.Registers, error) diff --git a/hypervisor/x86state/cpuid.go b/hypervisor/x86state/cpuid.go new file mode 100644 index 0000000..1af9f6d --- /dev/null +++ b/hypervisor/x86state/cpuid.go @@ -0,0 +1,7 @@ +package x86state + +// CPUIDEntry describes one architectural CPUID leaf and subleaf. +type CPUIDEntry struct { + Function, Index, Flags uint32 + Eax, Ebx, Ecx, Edx uint32 +} diff --git a/internal/hv/kvm/cpuid_linux_amd64.go b/internal/hv/kvm/cpuid_linux_amd64.go new file mode 100644 index 0000000..0a2a300 --- /dev/null +++ b/internal/hv/kvm/cpuid_linux_amd64.go @@ -0,0 +1,39 @@ +//go:build linux && amd64 + +package kvm + +import ( + "fmt" + + "j5.nz/cc/hypervisor/x86state" +) + +// SupportedCPUID returns an owned copy of the features the accelerator can run. +func (v *VM) SupportedCPUID() ([]x86state.CPUIDEntry, error) { + entries, err := getSupportedCPUID(v.kvm.fd) + if err != nil { + return nil, err + } + result := make([]x86state.CPUIDEntry, 0, entries.Nr) + for _, e := range cpuidEntries(entries) { + result = append(result, x86state.CPUIDEntry{Function: e.Function, Index: e.Index, Flags: e.Flags, Eax: e.Eax, Ebx: e.Ebx, Ecx: e.Ecx, Edx: e.Edx}) + } + return result, nil +} + +// SetCPUID configures the single CPU before its first execution. Feature and +// topology policy belongs to the machine model, not this accelerator boundary. +func (v *VM) SetCPUID(entries []x86state.CPUIDEntry) error { + if len(v.vcpus) != 1 || len(entries) == 0 || len(entries) > cpuidMaxEntries { + return fmt.Errorf("invalid single-CPU CPUID table") + } + table, err := getSupportedCPUID(v.kvm.fd) + if err != nil { + return err + } + table.Nr = uint32(len(entries)) + for i, e := range entries { + cpuidStorage(table)[i] = kvmCPUIDEntry2{Function: e.Function, Index: e.Index, Flags: e.Flags, Eax: e.Eax, Ebx: e.Ebx, Ecx: e.Ecx, Edx: e.Edx} + } + return setVCPUID(v.vcpufd, table) +}