From 8aa811540f7b907f14dc06c3265eb9dc54d6be48 Mon Sep 17 00:00:00 2001 From: Justin Cormack Date: Fri, 4 Aug 2017 11:16:25 +0100 Subject: [PATCH 1/2] Support UEFI ISO boot on hyperkit Signed-off-by: Justin Cormack --- docs/platform-hyperkit.md | 13 ++-- src/cmd/linuxkit/run_hyperkit.go | 75 ++++++++++++++++--- src/cmd/linuxkit/vendor.conf | 2 +- .../vendor/github.com/moby/hyperkit/README.md | 2 +- .../github.com/moby/hyperkit/go/hyperkit.go | 58 ++++++++++---- .../moby/hyperkit/src/include/xhyve/virtio.h | 2 +- 6 files changed, 117 insertions(+), 35 deletions(-) diff --git a/docs/platform-hyperkit.md b/docs/platform-hyperkit.md index 9fcb3ce57..32a5c9446 100644 --- a/docs/platform-hyperkit.md +++ b/docs/platform-hyperkit.md @@ -12,9 +12,8 @@ Alternatively, you can install HyperKit and VPNKit standalone and use it without ## Boot -The HyperKit backend currently only supports booting the -`kernel+initrd` output from `moby` (technically we could support EFI -boot as well). +The HyperKit backend currently supports booting the +`kernel+initrd` output from `moby`, and EFI ISOs using the EFI firmware. ## Console @@ -28,8 +27,8 @@ HyperKit does not provide a console device. ## Disks The HyperKit backend support configuring a persistent disk using the -standard `linuxkit` `-disk` syntax. Currently, only one disk is -supported and the disk is in raw format. +standard `linuxkit` `-disk` syntax. Multiple disks are +supported and the disks are in raw format. ## Power management @@ -128,8 +127,8 @@ there are a number of packages, such as `vsudd`, which enable tighter integration of the VM with the host (see below). The HyperKit backend also allows passing custom userdata into the -[metadata pacakge](./metadata.md) using the `-data` command-line -option. +[metadata package](./metadata.md) using the `-data` command-line +option. This attaches a CD device with the data on. ### `vsudd` unix domain socket forwarding diff --git a/src/cmd/linuxkit/run_hyperkit.go b/src/cmd/linuxkit/run_hyperkit.go index d13a3cef2..c909ff650 100644 --- a/src/cmd/linuxkit/run_hyperkit.go +++ b/src/cmd/linuxkit/run_hyperkit.go @@ -46,6 +46,15 @@ func runHyperKit(args []string) { vsockports := flags.String("vsock-ports", "", "List of vsock ports to forward from the guest on startup (comma separated). A unix domain socket for each port will be created in the state directory") networking := flags.String("networking", hyperkitNetworkingDefault, "Networking mode. Valid options are 'default', 'docker-for-mac', 'vpnkit[,socket-path]', 'vmnet' and 'none'. 'docker-for-mac' connects to the network used by Docker for Mac. 'vpnkit' connects to the VPNKit socket specified. If socket-path is omitted a new VPNKit instance will be started and 'vpnkit_eth.sock' will be created in the state directory. 'vmnet' uses the Apple vmnet framework, requires root/sudo. 'none' disables networking.`") + // Boot type; we try to determine automatically + uefiBoot := flags.Bool("uefi", false, "Use UEFI boot") + isoBoot := flags.Bool("iso", false, "Boot image is an ISO") + kernelBoot := flags.Bool("kernel", false, "Boot image is kernel+initrd+cmdline 'path'-kernel/-initrd/-cmdline") + + // Paths and settings for UEFI firware + // Note, the default uses the firmware shipped with Docker for Mac + fw := flags.String("fw", "/Applications/Docker.app/Contents/Resources/uefi/UEFI.fd", "Path to OVMF firmware for UEFI boot") + if err := flags.Parse(args); err != nil { log.Fatal("Unable to parse args") } @@ -55,7 +64,48 @@ func runHyperKit(args []string) { flags.Usage() os.Exit(1) } - prefix := remArgs[0] + path := remArgs[0] + prefix := path + + _, err := os.Stat(path) + stat := err == nil + + var isoPaths []string + + // if the path does not exist, must be trying to do a kernel boot + if !stat { + _, err = os.Stat(path + "-kernel") + statKernel := err == nil + if statKernel { + *kernelBoot = true + } else { + log.Fatalf("Cannot find kernel file (%s): %v", path+"-kernel", err) + } + _, err = os.Stat(path + "-initrd.img") + statInitrd := err == nil + if !statInitrd { + log.Fatalf("Cannot find initrd file (%s): %v", path+"-initrd.img", err) + } + } else { + // if path ends in .iso they meant an ISO + if strings.HasSuffix(path, ".iso") { + *isoBoot = true + prefix = strings.TrimSuffix(path, ".iso") + // hyperkit only supports UEFI ISO boot at present + *uefiBoot = true + } + } + + if *isoBoot { + isoPaths = append(isoPaths, path) + } + + if *uefiBoot { + _, err := os.Stat(*fw) + if err != nil { + log.Fatalf("Cannot open UEFI firmware file (%s): %v", *fw, err) + } + } if *state == "" { *state = prefix + "-state" @@ -64,7 +114,6 @@ func runHyperKit(args []string) { log.Fatalf("Could not create state directory: %v", err) } - isoPath := "" if *data != "" { var d []byte if _, err := os.Stat(*data); os.IsNotExist(err) { @@ -75,10 +124,11 @@ func runHyperKit(args []string) { log.Fatalf("Cannot read user data: %v", err) } } - isoPath = filepath.Join(*state, "data.iso") + isoPath := filepath.Join(*state, "data.iso") if err := WriteMetadataISO(isoPath, d); err != nil { log.Fatalf("Cannot write user data ISO: %v", err) } + isoPaths = append(isoPaths, isoPath) } vpnKitKey := "" @@ -99,9 +149,12 @@ func runHyperKit(args []string) { vmUUID := uuid.NewV4().String() // Run - cmdline, err := ioutil.ReadFile(prefix + "-cmdline") - if err != nil { - log.Fatalf("Cannot open cmdline file: %v", err) + var cmdline []byte + if *kernelBoot { + cmdline, err = ioutil.ReadFile(prefix + "-cmdline") + if err != nil { + log.Fatalf("Cannot open cmdline file: %v", err) + } } // Create new HyperKit instance (w/o networking for now) @@ -175,11 +228,15 @@ func runHyperKit(args []string) { log.Fatalf("Invalid networking mode: %s", netMode[0]) } - h.Kernel = prefix + "-kernel" - h.Initrd = prefix + "-initrd.img" + if *kernelBoot { + h.Kernel = prefix + "-kernel" + h.Initrd = prefix + "-initrd.img" + } else { + h.Bootrom = *fw + } h.VPNKitKey = vpnKitKey h.UUID = vmUUID - h.ISOImage = isoPath + h.ISOImages = isoPaths h.VSock = true h.CPUs = *cpus h.Memory = *mem diff --git a/src/cmd/linuxkit/vendor.conf b/src/cmd/linuxkit/vendor.conf index a5940701e..9dfceaaa1 100644 --- a/src/cmd/linuxkit/vendor.conf +++ b/src/cmd/linuxkit/vendor.conf @@ -12,7 +12,7 @@ github.com/googleapis/gax-go 8c5154c0fe5bf18cf649634d4c6df50897a32751 github.com/gophercloud/gophercloud 2804b72cf099b41d2e25c8afcca786f9f962ddee github.com/jmespath/go-jmespath bd40a432e4c76585ef6b72d3fd96fb9b6dc7b68d github.com/mitchellh/go-ps 4fdf99ab29366514c69ccccddab5dc58b8d84062 -github.com/moby/hyperkit 2dbe405bfb05a93b2237516d1bffed850a4685f3 +github.com/moby/hyperkit a82b409a87f12fa3306813410c37f4eed270efac github.com/packethost/packngo 91d54000aa56874149d348a884ba083c41d38091 github.com/radu-matei/azure-sdk-for-go 3b12823551999669c9a325a32472508e0af7978e github.com/radu-matei/azure-vhd-utils e52754d5569d2a643a7775f72ff2a6cf524f4c25 diff --git a/src/cmd/linuxkit/vendor/github.com/moby/hyperkit/README.md b/src/cmd/linuxkit/vendor/github.com/moby/hyperkit/README.md index f3846618c..74f6feb87 100644 --- a/src/cmd/linuxkit/vendor/github.com/moby/hyperkit/README.md +++ b/src/cmd/linuxkit/vendor/github.com/moby/hyperkit/README.md @@ -38,7 +38,7 @@ via `brew` and using `opam` to install the appropriate libraries: $ brew install opam libev $ opam init $ eval `opam config env` - $ opam install uri qcow.0.10.0 qcow-tool mirage-block-unix.2.7.0 conf-libev logs fmt mirage-unix prometheus-app + $ opam install uri qcow.0.10.3 conduit.1.0.0 lwt.3.1.0 qcow-tool mirage-block-unix.2.7.0 conf-libev logs fmt mirage-unix prometheus-app Notes: diff --git a/src/cmd/linuxkit/vendor/github.com/moby/hyperkit/go/hyperkit.go b/src/cmd/linuxkit/vendor/github.com/moby/hyperkit/go/hyperkit.go index 338b4e1fe..46c069321 100644 --- a/src/cmd/linuxkit/vendor/github.com/moby/hyperkit/go/hyperkit.go +++ b/src/cmd/linuxkit/vendor/github.com/moby/hyperkit/go/hyperkit.go @@ -71,8 +71,10 @@ type Socket9P struct { // DiskConfig contains the path to a disk image and an optional size if the image needs to be created. type DiskConfig struct { - Path string `json:"path"` - Size int `json:"size"` + Path string `json:"path"` + Size int `json:"size"` + Format string `json:"format"` + Driver string `json:"driver"` } // HyperKit contains the configuration of the hyperkit VM @@ -90,7 +92,7 @@ type HyperKit struct { // Disks contains disk images to use/create. Disks []DiskConfig `json:"disks"` // ISOImage is the (optional) path to a ISO image to attach - ISOImage string `json:"iso"` + ISOImages []string `json:"iso"` // VSock enables the virtio-socket device and exposes it on the host VSock bool `json:"vsock"` // VSockPorts is a list of guest VSock ports that should be exposed as sockets on the host @@ -108,6 +110,8 @@ type HyperKit struct { Kernel string `json:"kernel"` // Initrd is the path to the initial ramdisk to boot off Initrd string `json:"initrd"` + // Bootrom is the path to a boot rom eg for UEFI boot + Bootrom string `json:"bootrom"` // CPUs is the number CPUs to configure CPUs int `json:"cpus"` @@ -222,9 +226,9 @@ func (h *HyperKit) execute(cmdline string) error { if h.Console == ConsoleStdio && !isTerminal(os.Stdout) && h.StateDir == "" { return fmt.Errorf("If ConsoleStdio is set but stdio is not a terminal, StateDir must be specified") } - if h.ISOImage != "" { - if _, err = os.Stat(h.ISOImage); os.IsNotExist(err) { - return fmt.Errorf("ISO %s does not exist", h.ISOImage) + for _, image := range h.ISOImages { + if _, err = os.Stat(image); os.IsNotExist(err) { + return fmt.Errorf("ISO %s does not exist", image) } } if h.VSock && h.StateDir == "" { @@ -233,11 +237,17 @@ func (h *HyperKit) execute(cmdline string) error { if !h.VSock && len(h.VSockPorts) > 0 { return fmt.Errorf("To forward vsock ports vsock must be enabled") } - if _, err = os.Stat(h.Kernel); os.IsNotExist(err) { - return fmt.Errorf("Kernel %s does not exist", h.Kernel) - } - if _, err = os.Stat(h.Initrd); os.IsNotExist(err) { - return fmt.Errorf("initrd %s does not exist", h.Initrd) + if h.Bootrom == "" { + if _, err = os.Stat(h.Kernel); os.IsNotExist(err) { + return fmt.Errorf("Kernel %s does not exist", h.Kernel) + } + if _, err = os.Stat(h.Initrd); os.IsNotExist(err) { + return fmt.Errorf("initrd %s does not exist", h.Initrd) + } + } else { + if _, err = os.Stat(h.Bootrom); os.IsNotExist(err) { + return fmt.Errorf("Bootrom %s does not exist", h.Bootrom) + } } // Create files @@ -428,7 +438,18 @@ func (h *HyperKit) buildArgs(cmdline string) { } for _, p := range h.Disks { - a = append(a, "-s", fmt.Sprintf("%d:0,virtio-blk,%s", nextSlot, p.Path)) + // Default the driver to virtio-blk + driver := "virtio-blk" + if p.Driver != "" { + driver = p.Driver + } + arg := fmt.Sprintf("%d:0,%s,%s", nextSlot, driver, p.Path) + + // Add on a format instruction if specified. + if p.Format != "" { + arg += ",format=" + p.Format + } + a = append(a, "-s", arg) nextSlot++ } @@ -441,8 +462,8 @@ func (h *HyperKit) buildArgs(cmdline string) { nextSlot++ } - if h.ISOImage != "" { - a = append(a, "-s", fmt.Sprintf("%d,ahci-cd,%s", nextSlot, h.ISOImage)) + for _, image := range h.ISOImages { + a = append(a, "-s", fmt.Sprintf("%d,ahci-cd,%s", nextSlot, image)) nextSlot++ } @@ -460,8 +481,13 @@ func (h *HyperKit) buildArgs(cmdline string) { a = append(a, "-l", fmt.Sprintf("com1,autopty=%s/tty,log=%s/console-ring", h.StateDir, h.StateDir)) } - kernArgs := fmt.Sprintf("kexec,%s,%s,earlyprintk=serial %s", h.Kernel, h.Initrd, cmdline) - a = append(a, "-f", kernArgs) + if h.Bootrom == "" { + kernArgs := fmt.Sprintf("kexec,%s,%s,earlyprintk=serial %s", h.Kernel, h.Initrd, cmdline) + a = append(a, "-f", kernArgs) + } else { + kernArgs := fmt.Sprintf("bootrom,%s,,", h.Bootrom) + a = append(a, "-f", kernArgs) + } h.Arguments = a h.CmdLine = h.HyperKit + " " + strings.Join(a, " ") diff --git a/src/cmd/linuxkit/vendor/github.com/moby/hyperkit/src/include/xhyve/virtio.h b/src/cmd/linuxkit/vendor/github.com/moby/hyperkit/src/include/xhyve/virtio.h index b3773a671..13b8c5901 100644 --- a/src/cmd/linuxkit/vendor/github.com/moby/hyperkit/src/include/xhyve/virtio.h +++ b/src/cmd/linuxkit/vendor/github.com/moby/hyperkit/src/include/xhyve/virtio.h @@ -212,7 +212,7 @@ struct vring_used { #define VIRTIO_VENDOR 0x1AF4 #define VIRTIO_DEV_NET 0x1000 #define VIRTIO_DEV_BLOCK 0x1001 -#define VIRTIO_DEV_RANDOM 0x1002 +#define VIRTIO_DEV_RANDOM 0x1005 #define VIRTIO_DEV_9P 0x1009 #define VIRTIO_DEV_SOCK 0x103f /* In the legacy range. */ From 317ddb09dfde9d594912f0a2055a1a5883f5d0a6 Mon Sep 17 00:00:00 2001 From: Justin Cormack Date: Fri, 4 Aug 2017 11:42:30 +0100 Subject: [PATCH 2/2] Do not try to autodetect UEFI mode Signed-off-by: Justin Cormack --- src/cmd/linuxkit/run_hyperkit.go | 4 +++- src/cmd/linuxkit/run_qemu.go | 5 ----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/cmd/linuxkit/run_hyperkit.go b/src/cmd/linuxkit/run_hyperkit.go index c909ff650..4fdf5f9d2 100644 --- a/src/cmd/linuxkit/run_hyperkit.go +++ b/src/cmd/linuxkit/run_hyperkit.go @@ -92,7 +92,9 @@ func runHyperKit(args []string) { *isoBoot = true prefix = strings.TrimSuffix(path, ".iso") // hyperkit only supports UEFI ISO boot at present - *uefiBoot = true + if !*uefiBoot { + log.Fatalf("Hyperkit requires --uefi to be set to boot an ISO") + } } } diff --git a/src/cmd/linuxkit/run_qemu.go b/src/cmd/linuxkit/run_qemu.go index 3f7fc013c..b18fa20c0 100644 --- a/src/cmd/linuxkit/run_qemu.go +++ b/src/cmd/linuxkit/run_qemu.go @@ -174,11 +174,6 @@ func runQemu(args []string) { *isoBoot = true prefix = strings.TrimSuffix(path, ".iso") } - // autodetect EFI ISO from our default naming - if strings.HasSuffix(path, "-efi.iso") { - *uefiBoot = true - prefix = strings.TrimSuffix(path, "-efi.iso") - } } if *state == "" {