diff --git a/qemu/qemu.go b/qemu/qemu.go index 9288f59f8a..05cf9e16b9 100644 --- a/qemu/qemu.go +++ b/qemu/qemu.go @@ -2103,6 +2103,54 @@ type Kernel struct { Params string } +// FwCfg allows QEMU to pass entries to the guest +// File and Str are mutually exclusive +type FwCfg struct { + Name string + File string + Str string +} + +// Valid returns true if the FwCfg structure is valid and complete. +func (fwcfg FwCfg) Valid() bool { + if fwcfg.Name == "" { + return false + } + + if fwcfg.File != "" && fwcfg.Str != "" { + return false + } + + if fwcfg.File == "" && fwcfg.Str == "" { + return false + } + + return true +} + +// QemuParams returns the qemu parameters built out of the FwCfg object +func (fwcfg FwCfg) QemuParams(config *Config) []string { + var fwcfgParams []string + var qemuParams []string + + if config.FwCfg.Name != "" { + fwcfgParams = append(fwcfgParams, fmt.Sprintf("name=%q", config.FwCfg.Name)) + + if config.FwCfg.File != "" { + fwcfgParams = append(fwcfgParams, fmt.Sprintf(",file=%q", config.FwCfg.File)) + } + + if config.FwCfg.Str != "" { + fwcfgParams = append(fwcfgParams, fmt.Sprintf(",string=%q", config.FwCfg.Str)) + } + } + + qemuParams = append(qemuParams, "-fw_cfg") + qemuParams = append(qemuParams, strings.Join(fwcfgParams, "")) + + return qemuParams +} + // Knobs regroups a set of qemu boolean settings type Knobs struct { // NoUserConfig prevents qemu from loading user config files. @@ -2236,6 +2284,9 @@ type Config struct { // fds is a list of open file descriptors to be passed to the spawned qemu process fds []*os.File + // FwCfg is the -fw_cfg parameter + FwCfg FwCfg + IOThreads []IOThread // PidFile is the -pidfile parameter @@ -2568,6 +2619,19 @@ func (config *Config) appendLogFile() { } } +func (config *Config) appendFwCfg(logger QMPLog) { + if logger == nil { + logger = qmpNullLogger{} + } + + if !config.FwCfg.Valid() { + logger.Errorf("fw_cfg is not valid: %+v", config.FwCfg) + return + } + + config.qemuParams = append(config.qemuParams, config.FwCfg.QemuParams(config)...) +} + // LaunchQemu can be used to launch a new qemu instance. // // The Config parameter contains a set of qemu parameters and settings. @@ -2595,6 +2659,7 @@ func LaunchQemu(config Config, logger QMPLog) (string, error) { config.appendIncoming() config.appendPidFile() config.appendLogFile() + config.appendFwCfg(logger) if err := config.appendCPUs(); err != nil { return "", err diff --git a/qemu/qemu_test.go b/qemu/qemu_test.go index 30dedf9727..6928ede4bc 100644 --- a/qemu/qemu_test.go +++ b/qemu/qemu_test.go @@ -44,6 +44,9 @@ func testConfigAppend(config *Config, structure interface{}, expected string, t case Machine: config.Machine = s config.appendMachine() + case FwCfg: + config.FwCfg = s + config.appendFwCfg(nil) case Device: config.Devices = []Device{s} @@ -1141,6 +1144,26 @@ func TestBadCPUs(t *testing.T) { } } +func TestBadFwcfg(t *testing.T) { + c := &Config{} + c.appendFwCfg(nil) + if len(c.qemuParams) != 0 { + t.Errorf("Expected empty qemuParams, found %s", c.qemuParams) + } + + c = &Config{ + FwCfg: FwCfg{ + Name: "name=opt/com.mycompany/blob", + File: "./my_blob.bin", + Str: "foo", + }, + } + c.appendFwCfg(nil) + if len(c.qemuParams) != 0 { + t.Errorf("Expected empty qemuParams, found %s", c.qemuParams) + } +} + var ( vIommuString = "-device intel-iommu,intremap=on,device-iotlb=on,caching-mode=on" vIommuNoCacheString = "-device intel-iommu,intremap=on,device-iotlb=on,caching-mode=off" @@ -1164,3 +1187,19 @@ func TestIommu(t *testing.T) { testAppend(iommu, vIommuNoCacheString, t) } + +func TestAppendFwcfg(t *testing.T) { + fwcfgString := "-fw_cfg name=\"opt/com.mycompany/blob\",file=\"./my_blob.bin\"" + fwcfg := FwCfg{ + Name: "opt/com.mycompany/blob", + File: "./my_blob.bin", + } + testAppend(fwcfg, fwcfgString, t) + + fwcfgString = "-fw_cfg name=\"opt/com.mycompany/blob\",string=\"foo\"" + fwcfg = FwCfg{ + Name: "opt/com.mycompany/blob", + Str: "foo", + } + testAppend(fwcfg, fwcfgString, t) +}