Skip to content

Commit

Permalink
Added EFI/UEFI support
Browse files Browse the repository at this point in the history
  • Loading branch information
0xef53 committed Oct 11, 2022
1 parent 853a0a5 commit 6e8d282
Show file tree
Hide file tree
Showing 16 changed files with 1,115 additions and 362 deletions.
677 changes: 494 additions & 183 deletions api/services/machines/v1/machines.pb.go

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions api/services/machines/v1/machines.proto
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ service MachineService {
rpc List(ListMachinesRequest) returns (ListMachinesResponse) { }
rpc ListNames(ListMachinesRequest) returns (ListNamesResponse) { }

rpc SetFirmware(SetFirmwareRequest) returns (google.protobuf.Empty) { }

rpc SetMemLimits(SetMemLimitsRequest) returns (google.protobuf.Empty) { }
rpc SetCPULimits(SetCPULimitsRequest) returns (google.protobuf.Empty) { }
rpc SetCPUSockets(SetCPUSocketsRequest) returns (google.protobuf.Empty) { }
Expand Down Expand Up @@ -123,6 +125,12 @@ message ListNamesResponse {
repeated string machines = 1;
}

message SetFirmwareRequest {
string name = 1;
string image = 2;
bool remove_conf = 3;
}

message SetMemLimitsRequest {
string name = 1;
int64 actual = 2;
Expand Down
573 changes: 408 additions & 165 deletions api/types/types.pb.go

Large diffs are not rendered by default.

24 changes: 14 additions & 10 deletions api/types/types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import "github.com/gogo/protobuf/gogoproto/gogo.proto";
option go_package = "github.com/0xef53/kvmrun/api/types;types";

message MachineOpts {
message Firmware {
string image = 1;
}
message Memory {
int64 actual = 1;
int64 total = 2;
Expand Down Expand Up @@ -63,16 +66,17 @@ message MachineOpts {
map<string, string> envs = 3;
}
string machine_type = 1;
Memory memory = 2;
CPU cpu = 3 [(gogoproto.customname) = "CPU"];
repeated InputDevice inputs = 4;
repeated Cdrom cdrom = 5;
repeated Disk storage = 6;
repeated NetIface network = 7;
VirtioVSock vsock_device = 8 [(gogoproto.customname) = "VSockDev"];
CloudInit cloudinit_drive = 9 [(gogoproto.customname) = "CIDrive"];
Kernel kernel = 10;
repeated BackendProxy proxy = 11;
Firmware firmware = 2;
Memory memory = 3;
CPU cpu = 4 [(gogoproto.customname) = "CPU"];
repeated InputDevice inputs = 5;
repeated Cdrom cdrom = 6;
repeated Disk storage = 7;
repeated NetIface network = 8;
VirtioVSock vsock_device = 9 [(gogoproto.customname) = "VSockDev"];
CloudInit cloudinit_drive = 10 [(gogoproto.customname) = "CIDrive"];
Kernel kernel = 11;
repeated BackendProxy proxy = 12;
}

message Machine {
Expand Down
50 changes: 50 additions & 0 deletions cmd/vmm/boot_options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package main

import (
"context"
"strings"

pb "github.com/0xef53/kvmrun/api/services/machines/v1"

cli "github.com/urfave/cli/v2"
grpc "google.golang.org/grpc"
)

var bootCommands = &cli.Command{
Name: "boot",
Usage: "manage machine boot parameters (bios/uefi mode)",
HideHelp: true,
Category: "Configuration",
Subcommands: []*cli.Command{
cmdBootSet,
},
}

var cmdBootSet = &cli.Command{
Name: "set",
Usage: "set various boot parameters",
ArgsUsage: "VMNAME",
HideHelp: true,
Flags: []cli.Flag{
&cli.StringFlag{Name: "firmware", Value: "", DefaultText: "not set", Usage: "firmware image file `file`"},
},
Action: func(c *cli.Context) error {
return executeGRPC(c, setBootParameters)
},
}

func setBootParameters(ctx context.Context, vmname string, c *cli.Context, conn *grpc.ClientConn) error {
if p := c.String("firmware"); len(p) > 0 {
req := pb.SetFirmwareRequest{
Name: vmname,
Image: p,
RemoveConf: strings.ToLower(p) == "default",
}

if _, err := pb.NewMachineServiceClient(conn).SetFirmware(ctx, &req); err != nil {
return err
}
}

return nil
}
1 change: 1 addition & 0 deletions cmd/vmm/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ func main() {
cmdInspect,
memoryCommands,
cpuCommands,
bootCommands,
inputsCommands,
cdromCommands,
storageCommands,
Expand Down
2 changes: 1 addition & 1 deletion debian/control
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Standards-Version: 3.9.4
Package: kvmrun
Architecture: any
Depends: ${shlibs:Depends}, qemu-kvm | qemu (>= 1:2.5), qemu-kvm | qemu (<< 1:5.1), socat
Recommends: bash-completion, kvmrun-network, kvmrun-tools, kpartx
Recommends: bash-completion, ovmf, kvmrun-network, kvmrun-tools, kpartx
Description: Interface for running and managing KVM virtual machines
Kvmrun is a suite of tools that provide a command line interface
for creating and managing virtual machines based on QEMU-KVM.
Expand Down
5 changes: 5 additions & 0 deletions kvmrun/cmdline_qemu_i440fx.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,11 @@ func (b *qemuCommandLine_i440fx) gen() ([]string, error) {
// Disable default devices
args = append(args, "-nodefaults", "-no-user-config")

// Firmware
if p := b.vmconf.GetFirmwareImage(); len(p) > 0 {
args = append(args, "-bios", p)
}

// Memory
args = append(args, "-m", fmt.Sprintf("%dM", b.vmconf.GetTotalMem()))

Expand Down
10 changes: 9 additions & 1 deletion kvmrun/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ type Instance interface {
Status() (InstanceState, error)
GetMachineType() *QemuMachine
SetMachineType(string) error
GetFirmwareImage() string
SetFirmwareImage(string) error
RemoveFirmwareConf() error
IsIncoming() bool

Save() error
Expand Down Expand Up @@ -88,7 +91,8 @@ type InstanceProperties struct {
name string `json:"-"`
uid int `json:"-"`

MachineType string `json:"machine_type,omitempty"`
MachineType string `json:"machine_type,omitempty"`
Firmware QemuFirmware `json:"firmware,omitempty"`

Mem Memory `json:"memory"`
CPU Processor `json:"cpu"`
Expand Down Expand Up @@ -134,6 +138,10 @@ func (p InstanceProperties) GetMachineType() *QemuMachine {
return &QemuMachine{name: p.MachineType, Chipset: QEMU_CHIPSET_UNKNOWN}
}

func (p InstanceProperties) GetFirmwareImage() string {
return p.Firmware.Image
}

func (p InstanceProperties) GetActualCPUs() int {
return p.CPU.Actual
}
Expand Down
14 changes: 14 additions & 0 deletions kvmrun/instance_conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,20 @@ func (c *InstanceConf) SetMachineType(t string) error {
return nil
}

func (c *InstanceConf) SetFirmwareImage(p string) error {
if len(p) > 0 {
c.Firmware.Image = p
}

return nil
}

func (c *InstanceConf) RemoveFirmwareConf() error {
c.Firmware.Image = ""

return nil
}

func (c *InstanceConf) SetActualMem(s int) error {
if s < 1 {
return fmt.Errorf("invalid memory size: cannot be less than 1")
Expand Down
17 changes: 16 additions & 1 deletion kvmrun/instance_qemu.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ func GetInstanceQemu(vmname string, mon *qmp.Monitor) (Instance, error) {

var gr errgroup.Group

gr.Go(func() error { return inner.initFirmware() })
gr.Go(func() error { return inner.initCPU() })
gr.Go(func() error { return inner.initMemory() })
gr.Go(func() error { return inner.initInputDevices() })
Expand Down Expand Up @@ -194,6 +195,12 @@ func (r *InstanceQemu) initMachine() error {
return nil
}

func (r *InstanceQemu) initFirmware() error {
r.Firmware.Image = r.startupConf.GetFirmwareImage()

return nil
}

func (r InstanceQemu) Pid() int {
return r.pid
}
Expand Down Expand Up @@ -392,7 +399,15 @@ func (r *InstanceQemu) SetCPUQuota(quota int) error {
return nil
}

func (r InstanceQemu) SetMachineType(t string) error {
func (r InstanceQemu) SetMachineType(_ string) error {
return ErrNotImplemented
}

func (r InstanceQemu) SetFirmwareImage(_ string) error {
return ErrNotImplemented
}

func (r *InstanceQemu) RemoveFirmwareConf() error {
return ErrNotImplemented
}

Expand Down
4 changes: 4 additions & 0 deletions kvmrun/qemu.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,7 @@ type QemuMachine struct {
func (m *QemuMachine) String() string {
return m.name
}

type QemuFirmware struct {
Image string `json:"image,omitempty"`
}
2 changes: 1 addition & 1 deletion kvmrun/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"fmt"
)

var Version = KvmrunVersion{1, 0, 2}
var Version = KvmrunVersion{1, 1, 0}

type KvmrunVersion struct {
Major int `json:"major"`
Expand Down
68 changes: 68 additions & 0 deletions services/machines/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
pb "github.com/0xef53/kvmrun/api/services/machines/v1"
pb_types "github.com/0xef53/kvmrun/api/types"

empty "github.com/golang/protobuf/ptypes/empty"
log "github.com/sirupsen/logrus"
grpc_codes "google.golang.org/grpc/codes"
grpc_status "google.golang.org/grpc/status"
Expand Down Expand Up @@ -58,6 +59,21 @@ func (s *ServiceServer) Create(ctx context.Context, req *pb.CreateMachineRequest
vmc.SetCPUQuota(int(req.Options.CPU.Quota))
vmc.SetCPUModel(req.Options.CPU.Model)

// TODO: should be validated before
if req.Options.Firmware != nil {
switch req.Options.Firmware.Image {
case "bios", "legacy":
req.Options.Firmware.Image = ""
case "efi", "uefi":
if _, fname, err := s.LookForFile("OVMF.fd", "/usr/share/ovmf", "/usr/share/qemu"); err == nil {
req.Options.Firmware.Image = fname
} else {
return err
}
}
vmc.SetFirmwareImage(req.Options.Firmware.Image)
}

if err := vmc.Save(); err != nil {
return err
}
Expand Down Expand Up @@ -246,3 +262,55 @@ func (s *ServiceServer) ListNames(ctx context.Context, req *pb.ListMachinesReque

return &pb.ListNamesResponse{Machines: names}, nil
}

func (s *ServiceServer) SetFirmware(ctx context.Context, req *pb.SetFirmwareRequest) (*empty.Empty, error) {
err := s.RunFuncTask(ctx, req.Name, func(l *log.Entry) error {
vm, err := s.GetMachine(req.Name)
if err != nil {
return err
}

switch req.Image {
case "bios", "legacy":
// For now just remove the whole section
req.RemoveConf = true
case "efi", "uefi":
// Try to find the OVMF.fd file
_, fname, err := s.LookForFile("OVMF.fd", "/usr/share/ovmf", "/usr/share/qemu")
if err != nil {
return err
}

req.Image = fname
}

if req.RemoveConf {
if err := vm.C.RemoveFirmwareConf(); err != nil {
return err
}
return vm.C.Save()
}

if fi, err := os.Stat(req.Image); err == nil {
if fi.IsDir() {
return grpc_status.Errorf(grpc_codes.InvalidArgument, "not a file: %s", req.Image)
}
} else {
if os.IsNotExist(err) {
return grpc_status.Errorf(grpc_codes.InvalidArgument, "not found: %s", req.Image)
}
}

if err := vm.C.SetFirmwareImage(req.Image); err != nil {
return err
}

return vm.C.Save()
})

if err != nil {
return nil, err
}

return new(empty.Empty), nil
}
6 changes: 6 additions & 0 deletions services/machines/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ func machineToProto(vm *kvmrun.Machine, vmstate kvmrun.InstanceState, t time.Dur
}
}

if p := vmi.GetFirmwareImage(); len(p) != 0 {
opts.Firmware = &pb_types.MachineOpts_Firmware{
Image: p,
}
}

vsock := vmi.GetVSockDevice()

if vsock != nil {
Expand Down
16 changes: 16 additions & 0 deletions services/service_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,22 @@ func (s *ServiceServer) MachineDownFile(vmname string) string {
return filepath.Join(filepath.Join(kvmrun.CONFDIR, vmname, "down"))
}

func (s *ServiceServer) LookForFile(basename string, dirs ...string) (string, string, error) {
for _, d := range dirs {
fullname := filepath.Join(d, basename)
switch _, err := os.Stat(fullname); {
case err == nil:
return d, filepath.Clean(fullname), nil
case os.IsNotExist(err):
continue
default:
return "", "", err
}
}

return "", "", fmt.Errorf("unable to find: %s", basename)
}

func (s *ServiceServer) newContext(ctx context.Context) context.Context {
var tag string

Expand Down

0 comments on commit 6e8d282

Please sign in to comment.