mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/yunionio/cloudpods.git
synced 2026-09-20 08:03:53 +08:00
fix(region,host,host-deployer): add win11 tpm support (#23952)
This commit is contained in:
@@ -131,6 +131,7 @@ func init() {
|
||||
cmd.BatchPerform("sync-os-info", &options.ServerIdsOptions{})
|
||||
cmd.BatchPerform("set-root-disk-matcher", &options.ServerSetRootDiskMatcher{})
|
||||
cmd.Perform("disable-auto-merge-snapshot", &options.ServerDisableAutoMergeSnapshot{})
|
||||
cmd.BatchPerform("set-tpm", &options.ServerSetTpmOptions{})
|
||||
cmd.Perform("set-kickstart", &options.ServerKickstartConfigOptions{})
|
||||
cmd.Perform("delete-kickstart", &options.ServerIdOptions{})
|
||||
cmd.Perform("kickstart-complete", &options.ServerKickstartCompleteOptions{})
|
||||
|
||||
@@ -566,7 +566,8 @@ type ServerCreateInput struct {
|
||||
|
||||
// BIOS类型, 若镜像是Windows,并且支持UEFI,则自动会设置为UEFI
|
||||
// emulate: BIOS, UEFI
|
||||
Bios string `json:"bios"`
|
||||
Bios string `json:"bios"`
|
||||
EnableTpm bool `json:"enable_tpm"`
|
||||
|
||||
// Machine类型
|
||||
// emulate: pc, q35
|
||||
|
||||
@@ -348,6 +348,8 @@ const (
|
||||
VM_METADATA_START_VCPU_COUNT = "start_vcpu_count"
|
||||
VM_METADATA_DISABLE_AUTO_MERGE_SNAPSHOT = "disable_auto_merge_snapshot"
|
||||
|
||||
VM_METADATA_ENABLE_TPM = "enable_tpm"
|
||||
|
||||
VM_METADATA_RELEASED_DEVICES = "released_devices"
|
||||
|
||||
VM_METADATA_CPU_NUMA_PIN = "__cpu_numa_pin"
|
||||
|
||||
@@ -6912,6 +6912,15 @@ func (self *SGuest) PerformEnableMemclean(ctx context.Context, userCred mcclient
|
||||
return nil, self.SetMetadata(ctx, api.VM_METADATA_ENABLE_MEMCLEAN, "true", userCred)
|
||||
}
|
||||
|
||||
func (self *SGuest) PerformSetTpm(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
enableTpm := jsonutils.QueryBoolean(data, api.VM_METADATA_ENABLE_TPM, false)
|
||||
if enableTpm {
|
||||
return nil, self.SetMetadata(ctx, api.VM_METADATA_ENABLE_TPM, enableTpm, userCred)
|
||||
} else {
|
||||
return nil, self.RemoveMetadata(ctx, api.VM_METADATA_ENABLE_TPM, userCred)
|
||||
}
|
||||
}
|
||||
|
||||
// 设置操作系统信息
|
||||
func (self *SGuest) PerformSetOsInfo(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerSetOSInfoInput) (jsonutils.JSONObject, error) {
|
||||
drv, err := self.GetDriver()
|
||||
|
||||
@@ -1812,6 +1812,17 @@ func (manager *SGuestManager) validateCreateData(
|
||||
input.OsArch = apis.OS_ARCH_AARCH64
|
||||
}
|
||||
|
||||
// enable tpm on windows 11 image
|
||||
if osDist := imgProperties["os_distribution"]; strings.Contains(osDist, "Windows 11") {
|
||||
input.EnableTpm = true
|
||||
}
|
||||
|
||||
// use uefi boot and q35 machine type on enable tpm
|
||||
if input.EnableTpm {
|
||||
input.Bios = "UEFI"
|
||||
input.Machine = api.VM_MACHINE_TYPE_Q35
|
||||
}
|
||||
|
||||
if imageDiskFormat != "iso" {
|
||||
var imgSupportUEFI *bool
|
||||
var imgSupportBIOS *bool
|
||||
@@ -2717,6 +2728,9 @@ func (guest *SGuest) PostCreate(ctx context.Context, userCred mcclient.TokenCred
|
||||
if jsonutils.QueryBoolean(data, api.VM_METADATA_ENABLE_MEMCLEAN, false) {
|
||||
guest.SetMetadata(ctx, api.VM_METADATA_ENABLE_MEMCLEAN, "true", userCred)
|
||||
}
|
||||
if jsonutils.QueryBoolean(data, api.VM_METADATA_ENABLE_TPM, false) {
|
||||
guest.SetMetadata(ctx, api.VM_METADATA_ENABLE_TPM, "true", userCred)
|
||||
}
|
||||
if jsonutils.QueryBoolean(data, imageapi.IMAGE_DISABLE_USB_KBD, false) {
|
||||
guest.SetMetadata(ctx, imageapi.IMAGE_DISABLE_USB_KBD, "true", userCred)
|
||||
}
|
||||
|
||||
@@ -116,6 +116,8 @@ type SGuestHardwareDesc struct {
|
||||
|
||||
VirtioSerial *SGuestVirtioSerial
|
||||
|
||||
Tpm *SGuestTpm
|
||||
|
||||
// std virtio cirrus vmware qlx none
|
||||
Vga string
|
||||
VgaDevice *SGuestVga `json:",omitempty"`
|
||||
@@ -173,6 +175,13 @@ type SGuestPvpanic struct {
|
||||
Id string
|
||||
}
|
||||
|
||||
type SGuestTpm struct {
|
||||
TpmSock *CharDev
|
||||
|
||||
// default emulator tpm
|
||||
Id string
|
||||
}
|
||||
|
||||
// -device pcie-pci-bridge,id=pci.1,bus=pcie.0 \
|
||||
// -device pci-bridge,id=pci.2,bus=pci.1,chassis_nr=1,addr=0x01 \
|
||||
|
||||
|
||||
@@ -99,6 +99,7 @@ func (s *SKVMGuestInstance) initGuestDevicesDesc(pciRoot, pciBridge *desc.PCICon
|
||||
s.initQgaDesc()
|
||||
s.initPvpanicDesc()
|
||||
s.initIsaSerialDesc()
|
||||
s.initTpmDesc()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -299,6 +299,10 @@ func (s *SKVMGuestInstance) disablePvpanicDev() bool {
|
||||
return s.Desc.Metadata["disable_pvpanic"] == "true"
|
||||
}
|
||||
|
||||
func (s *SKVMGuestInstance) enableTpmDev() bool {
|
||||
return s.Desc.Metadata[api.VM_METADATA_ENABLE_TPM] == "true"
|
||||
}
|
||||
|
||||
func (s *SKVMGuestInstance) getQuorumChildIndex() int64 {
|
||||
if sidx, ok := s.Desc.Metadata[api.QUORUM_CHILD_INDEX]; ok {
|
||||
idx, _ := strconv.ParseInt(sidx, 10, 0)
|
||||
@@ -364,6 +368,7 @@ func (s *SKVMGuestInstance) generateStartScript(data *jsonutils.JSONDict) (strin
|
||||
HomeDir: s.HomeDir(),
|
||||
HugepagesEnabled: s.manager.host.IsHugepagesEnabled(),
|
||||
EnableMemfd: s.isMemcleanEnabled(),
|
||||
EnableTpm: s.enableTpmDev(),
|
||||
PidFilePath: s.GetPidFilePath(),
|
||||
}
|
||||
|
||||
@@ -491,6 +496,28 @@ function nic_mtu() {
|
||||
}
|
||||
`
|
||||
|
||||
if input.EnableTpm {
|
||||
input.OVMFPath = options.HostOptions.SecbootOvmfPath
|
||||
input.OVMFVarsPath = options.HostOptions.SecbootOvmfVarsPath
|
||||
cmd += `
|
||||
function start_swtpm() {
|
||||
local swtpm_binary=$1
|
||||
local swtpm_dir=$2
|
||||
local swtpm_socket=$swtpm_dir/swtpm.sock
|
||||
local swtpm_log=$swtpm_dir/swtpm.log
|
||||
local swtpm_pid=$swtpm_dir/swtpm.pid
|
||||
|
||||
if [ -f "$swtpm_pid" ] && ps -p $(cat "$swtpm_pid") >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
mkdir -p $swtpm_dir
|
||||
$swtpm_binary socket --tpmstate dir=$swtpm_dir --ctrl type=unixio,path=$swtpm_socket --log file=$swtpm_log,level=20 --pid file=$swtpm_pid --tpm2 -d
|
||||
}
|
||||
`
|
||||
cmd += fmt.Sprintf("start_swtpm %s %s\n", options.HostOptions.BinarySwtpmPath, s.getSwtpmDirPath())
|
||||
}
|
||||
|
||||
// Generate Start VM script
|
||||
cmd += `CMD="$QEMU_CMD $QEMU_CMD_KVM_ARG`
|
||||
|
||||
@@ -935,6 +962,21 @@ func (s *SKVMGuestInstance) generateStopScript(data *jsonutils.JSONDict) string
|
||||
cmd += " fi\n"
|
||||
cmd += "done\n"
|
||||
|
||||
if s.enableTpmDev() {
|
||||
// stop swtpm
|
||||
cmd += fmt.Sprintf("SWTPM_PID_FILE=%s\n", s.getSwtpmPidPath())
|
||||
cmd += "if [ -f $SWTPM_PID_FILE ]; then\n"
|
||||
cmd += " SWTPM_PID=`cat $SWTPM_PID_FILE`\n"
|
||||
cmd += " ps -p $SWTPM_PID > /dev/null\n"
|
||||
cmd += " if [ $? -eq 0 ]; then\n"
|
||||
cmd += " echo \"Kill swtpm process $SWTPM_PID\"\n"
|
||||
cmd += " kill -9 $SWTPM_PID > /dev/null 2>&1\n"
|
||||
cmd += " fi\n"
|
||||
cmd += " echo \"Remove swtpm PID $SWTPM_PID_FILE\"\n"
|
||||
cmd += " rm -f $SWTPM_PID_FILE\n"
|
||||
cmd += "fi\n"
|
||||
}
|
||||
|
||||
for _, nic := range nics {
|
||||
if nic.Driver == api.NETWORK_DRIVER_VFIO {
|
||||
dev, _ := s.GetSriovDeviceByNetworkIndex(nic.Index)
|
||||
@@ -1001,6 +1043,22 @@ func (s *SKVMGuestInstance) getPKIDirPath() string {
|
||||
return path.Join(s.HomeDir(), "pki")
|
||||
}
|
||||
|
||||
func (s *SKVMGuestInstance) getSwtpmDirPath() string {
|
||||
return path.Join(s.HomeDir(), "swtpm")
|
||||
}
|
||||
|
||||
func (s *SKVMGuestInstance) getSwtpmSocketPath() string {
|
||||
return path.Join(s.getSwtpmDirPath(), "swtpm.sock")
|
||||
}
|
||||
|
||||
func (s *SKVMGuestInstance) getSwtpmLogPath() string {
|
||||
return path.Join(s.getSwtpmDirPath(), "swtpm.log")
|
||||
}
|
||||
|
||||
func (s *SKVMGuestInstance) getSwtpmPidPath() string {
|
||||
return path.Join(s.getSwtpmDirPath(), "swtpm.pid")
|
||||
}
|
||||
|
||||
func (s *SKVMGuestInstance) makePKIDir() error {
|
||||
output, err := procutils.NewCommand("mkdir", "-p", s.getPKIDirPath()).Output()
|
||||
if err != nil {
|
||||
@@ -1312,6 +1370,19 @@ func (s *SKVMGuestInstance) initIsaSerialDesc() {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SKVMGuestInstance) initTpmDesc() {
|
||||
if s.enableTpmDev() {
|
||||
charDevId := "chrtpm"
|
||||
s.Desc.Tpm = &desc.SGuestTpm{
|
||||
TpmSock: desc.NewCharDev("socket", charDevId, ""),
|
||||
Id: "tpm0",
|
||||
}
|
||||
s.Desc.Tpm.TpmSock.Options = map[string]string{
|
||||
"path": s.getSwtpmSocketPath(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SKVMGuestInstance) getVfioDeviceHotPlugPciControllerType() *desc.PCI_CONTROLLER_TYPE {
|
||||
if s.Desc.Machine == api.VM_MACHINE_TYPE_Q35 || s.Desc.Machine == api.VM_MACHINE_TYPE_ARM_VIRT {
|
||||
_, _, found := s.findUnusedSlotForController(desc.CONTROLLER_TYPE_PCIE_ROOT_PORT, 0)
|
||||
|
||||
@@ -666,6 +666,14 @@ func generatePvpanicDeviceOption(pvpanic *desc.SGuestPvpanic) string {
|
||||
return fmt.Sprintf("-device pvpanic,id=%s,ioport=0x%x", pvpanic.Id, pvpanic.Ioport)
|
||||
}
|
||||
|
||||
func generateTpmDevOptions(tpm *desc.SGuestTpm) []string {
|
||||
opts := make([]string, 0)
|
||||
opts = append(opts, chardevOption(tpm.TpmSock))
|
||||
opts = append(opts, fmt.Sprintf("-tpmdev emulator,id=%s,chardev=%s", tpm.Id, tpm.TpmSock.Id))
|
||||
opts = append(opts, fmt.Sprintf("-device tpm-tis,tpmdev=%s", tpm.Id))
|
||||
return opts
|
||||
}
|
||||
|
||||
func getMigrateOptions(drvOpt QemuOptions, input *GenerateStartOptionsInput) []string {
|
||||
opts := make([]string, 0)
|
||||
if input.NeedMigrate {
|
||||
@@ -692,6 +700,7 @@ type GenerateStartOptionsInput struct {
|
||||
OsName string
|
||||
HugepagesEnabled bool
|
||||
EnableMemfd bool
|
||||
EnableTpm bool
|
||||
|
||||
OVNIntegrationBridge string
|
||||
Devices []string
|
||||
@@ -939,6 +948,10 @@ func GenerateStartOptions(
|
||||
opts = append(opts, generatePvpanicDeviceOption(input.GuestDesc.Pvpanic))
|
||||
}
|
||||
|
||||
if input.GuestDesc.Tpm != nil {
|
||||
opts = append(opts, generateTpmDevOptions(input.GuestDesc.Tpm)...)
|
||||
}
|
||||
|
||||
// move extra options to end of cmdline
|
||||
if len(input.ExtraOptions) != 0 {
|
||||
opts = append(opts, input.ExtraOptions...)
|
||||
|
||||
@@ -17,6 +17,7 @@ package qemu
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@@ -273,20 +274,24 @@ func (o baseOptions) Boot(order *string, enableMenu bool) string {
|
||||
}
|
||||
|
||||
func (o baseOptions) BIOS(ovmfPath, ovmfVarsPath, homedir string) (string, error) {
|
||||
if ovmfVarsPath == "" || !fileutils2.Exists(ovmfVarsPath) {
|
||||
ovmfVarsPath = ovmfPath
|
||||
ovmfVarsName := "OVMF_VARS.fd"
|
||||
if ovmfVarsPath != "" {
|
||||
ovmfVarsName = filepath.Base(ovmfVarsPath)
|
||||
}
|
||||
|
||||
destOvmfVarsPath := path.Join(homedir, "OVMF_VARS.fd")
|
||||
if !fileutils2.Exists(destOvmfVarsPath) {
|
||||
err := procutils.NewRemoteCommandAsFarAsPossible("cp", "-f", ovmfVarsPath, destOvmfVarsPath).Run()
|
||||
guestOvmfVarsPath := path.Join(homedir, ovmfVarsName)
|
||||
if !fileutils2.Exists(guestOvmfVarsPath) {
|
||||
sourceOvmfVarsPath := ovmfPath
|
||||
if ovmfVarsPath != "" {
|
||||
sourceOvmfVarsPath = ovmfVarsPath
|
||||
}
|
||||
err := procutils.NewRemoteCommandAsFarAsPossible("cp", "-f", sourceOvmfVarsPath, guestOvmfVarsPath).Run()
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed copy ovmf vars")
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
"-drive if=pflash,format=raw,unit=0,file=%s,readonly=on -drive if=pflash,format=raw,unit=1,file=%s",
|
||||
ovmfPath, destOvmfVarsPath,
|
||||
ovmfPath, guestOvmfVarsPath,
|
||||
), nil
|
||||
}
|
||||
|
||||
|
||||
@@ -113,9 +113,11 @@ type SHostOptions struct {
|
||||
DnsServer string `help:"Address of host DNS server"`
|
||||
DnsServerLegacy string `help:"Deprecated Address of host DNS server"`
|
||||
|
||||
ChntpwPath string `help:"path to chntpw tool" default:"/usr/local/bin/chntpw.static"`
|
||||
OvmfPath string `help:"Path to OVMF.fd" default:"/opt/cloud/contrib/OVMF.fd"`
|
||||
OvmfVarsPath string `help:"Path to OVMF_VARS.fd" default:"/opt/cloud/contrib/OVMF_VARS.fd"`
|
||||
ChntpwPath string `help:"path to chntpw tool" default:"/usr/local/bin/chntpw.static"`
|
||||
OvmfPath string `help:"Path to OVMF.fd" default:"/opt/cloud/contrib/OVMF.fd"`
|
||||
OvmfVarsPath string `help:"Path to OVMF_VARS.fd" default:"/opt/cloud/contrib/OVMF_VARS.fd"`
|
||||
SecbootOvmfPath string `help:"Path to secboot ovmf fd" default:"/opt/cloud/contrib/OVMF_CODE_4M.secboot.fd"`
|
||||
SecbootOvmfVarsPath string `help:"Path to secboot ovmf vars fd" default:"/opt/cloud/contrib/OVMF_VARS_4M.fd"`
|
||||
|
||||
LinuxDefaultRootUser bool `help:"Default account for linux system is root"`
|
||||
WindowsDefaultAdminUser bool `default:"true" help:"Default account for Windows system is Administrator"`
|
||||
@@ -234,6 +236,7 @@ type SHostOptions struct {
|
||||
LocalBackupTempPath string `help:"the local temporary directory for backup" default:"/opt/cloud/workspace/run/backups"`
|
||||
|
||||
BinaryMemcleanPath string `help:"execute binary memclean path" default:"/opt/yunion/bin/memclean"`
|
||||
BinarySwtpmPath string `help:"swtpm binary path" default:"/usr/bin/swtpm"`
|
||||
|
||||
MaxHotplugVCpuCount int `help:"maximal possible vCPU count that the platform kvm supports"`
|
||||
PcieRootPortCount int `help:"pcie root port count" default:"2"`
|
||||
|
||||
@@ -451,6 +451,7 @@ type ServerCreateOptionalOptions struct {
|
||||
MemSpec string `help:"Memory size Or Instance Type" metavar:"MEMSPEC" json:"-"`
|
||||
CpuSockets int `help:"Cpu sockets"`
|
||||
EnableMemclean bool `help:"clean guest memory after guest exit" json:"enable_memclean"`
|
||||
EnableTpm bool `help:"enable tpm device" json:"enable_tpm"`
|
||||
|
||||
Keypair string `help:"SSH Keypair"`
|
||||
Password string `help:"Default user password"`
|
||||
@@ -598,6 +599,7 @@ func (opts *ServerCreateOptionalOptions) OptionalParams() (*computeapi.ServerCre
|
||||
GuestImageID: opts.GuestImageID,
|
||||
Secgroups: opts.Secgroups,
|
||||
EnableMemclean: opts.EnableMemclean,
|
||||
EnableTpm: opts.EnableTpm,
|
||||
}
|
||||
|
||||
params.ProjectId = opts.Project
|
||||
@@ -1668,6 +1670,15 @@ func (o *ServerSetRootDiskMatcher) Params() (jsonutils.JSONObject, error) {
|
||||
return jsonutils.Marshal(matcher), nil
|
||||
}
|
||||
|
||||
type ServerSetTpmOptions struct {
|
||||
ServerIdsOptions
|
||||
EnableTpm bool `help:"Enable tpm device"`
|
||||
}
|
||||
|
||||
func (o *ServerSetTpmOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return jsonutils.Marshal(o), nil
|
||||
}
|
||||
|
||||
type ServerChangeBillingTypeOptions struct {
|
||||
ServerIdOptions
|
||||
BillingType string `choices:"prepaid|postpaid"`
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -619,6 +620,20 @@ func (w *SWinRegTool) SetDnsServer(nameserver, searchlist string) {
|
||||
|
||||
func (w *SWinRegTool) GetProductName() string {
|
||||
prodKey := `HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProductName`
|
||||
prodName := w.GetRegistry(prodKey)
|
||||
|
||||
if strings.Contains(prodName, "Windows 10") {
|
||||
// check build number to determine whether it's actually Windows 11
|
||||
buildNumStr := w.GetCurrentBuildNumber()
|
||||
if buildNum, err := strconv.Atoi(strings.TrimSpace(buildNumStr)); err == nil && buildNum >= 22000 {
|
||||
prodName = strings.Replace(prodName, "Windows 10", "Windows 11", 1)
|
||||
}
|
||||
}
|
||||
return prodName
|
||||
}
|
||||
|
||||
func (w *SWinRegTool) GetCurrentBuildNumber() string {
|
||||
prodKey := `HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\CurrentBuildNumber`
|
||||
return w.GetRegistry(prodKey)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user