blob: 11e6f018f00db5be9b94f70221682906f205d36b [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package virtual_device
import (
"errors"
"go.fuchsia.dev/fuchsia/tools/build"
"go.fuchsia.dev/fuchsia/tools/qemu"
fvdpb "go.fuchsia.dev/fuchsia/tools/virtual_device/proto"
)
// qemuTargets maps a common Fuchsia CPU architecture string to its QEMU equivalent.
//
// This should provide a mapping for each value allowed by `isValidArch`.
var qemuTargets = map[string]qemu.Target{
"x64": qemu.TargetEnum.X86_64,
"arm64": qemu.TargetEnum.AArch64,
}
// QEMUCommand sets options to run Fuchsia in QEMU using the given VirtualDevice.
//
// This returns an error if `Validate(fvd, images)` returns an error.
//
// This function is hermetic; It does not lookup or set the path to the QEMU binary, or
// interact with the filesystem or environment in anyway. These actions are left to the
// caller.
//
// The caller is free to set additional options on the builder before calling this function
// or after this function returns.
func QEMUCommand(b *qemu.QEMUCommandBuilder, fvd *fvdpb.VirtualDevice, images build.ImageManifest) error {
if b == nil {
return errors.New("QEMUCommandBuilder cannot be nil")
}
if len(images) == 0 {
return errors.New("image manifest cannot be empty")
}
if err := Validate(fvd, images); err != nil {
return err
}
// Image paths. The previous call to Validate() ensures these exist in the manifest.
drive := ""
initrd := ""
kernel := ""
for _, image := range images {
switch image.Name {
case fvd.Kernel:
kernel = image.Path
case fvd.Initrd:
initrd = image.Path
case fvd.Drive.Image:
drive = image.Path
}
}
if fvd.Drive.IsFilename {
// Drive is a path instead of an image name. Assume the caller verified it exists.
drive = fvd.Drive.Image
}
b.SetKernel(kernel)
b.SetInitrd(initrd)
b.AddVirtioBlkPciDrive(qemu.Drive{
ID: fvd.Drive.Id,
Addr: fvd.Drive.PciAddress,
File: drive,
})
b.SetCPUCount(int(fvd.Hw.CpuCount))
ramBytes, err := parseRAMBytes(fvd.Hw.Ram)
if err != nil {
return err
}
b.SetMemory(ramBytes)
// The call to Validate() above guarantees the target is in this map.
target := qemuTargets[fvd.Hw.Arch]
if err := b.SetTarget(target, fvd.Hw.EnableKvm); err != nil {
return err
}
netdev := qemu.Netdev{ID: "netdev0", MAC: fvd.Hw.Mac}
if fvd.Hw.Tap == nil || fvd.Hw.Tap.Name == "" {
netdev.User = &qemu.NetdevUser{}
} else {
netdev.Tap = &qemu.NetdevTap{Name: fvd.Hw.Tap.Name}
}
b.AddNetwork(netdev)
b.AddKernelArg("zircon.nodename=" + fvd.Nodename)
return nil
}