From b4704b8ba13ae09620f5990646b7316c0cb8134b Mon Sep 17 00:00:00 2001 From: Rolf Neugebauer Date: Mon, 15 Jan 2018 13:49:07 +0000 Subject: [PATCH 1/6] config: Add 'ucode' to the kernel schema This optional option will allow users to specify a CPU microcode cpio archive to be prepended to the initrd file. Signed-off-by: Rolf Neugebauer --- src/moby/config.go | 4 ++++ src/moby/schema.go | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/moby/config.go b/src/moby/config.go index ac2ef3884..fc1107d5a 100644 --- a/src/moby/config.go +++ b/src/moby/config.go @@ -36,6 +36,7 @@ type KernelConfig struct { Cmdline string `yaml:"cmdline,omitempty" json:"cmdline,omitempty"` Binary string `yaml:"binary,omitempty" json:"binary,omitempty"` Tar *string `yaml:"tar,omitempty" json:"tar,omitempty"` + UCode *string `yaml:"ucode,omitempty" json:"ucode,omitempty"` ref *reference.Spec } @@ -289,6 +290,9 @@ func AppendConfig(m0, m1 Moby) (Moby, error) { if m1.Kernel.Tar != nil { moby.Kernel.Tar = m1.Kernel.Tar } + if m1.Kernel.UCode != nil { + moby.Kernel.UCode = m1.Kernel.UCode + } if m1.Kernel.ref != nil { moby.Kernel.ref = m1.Kernel.ref } diff --git a/src/moby/schema.go b/src/moby/schema.go index 2ca2c3c4f..7c2771214 100644 --- a/src/moby/schema.go +++ b/src/moby/schema.go @@ -13,7 +13,8 @@ var schema = string(` "image": {"type": "string"}, "cmdline": {"type": "string"}, "binary": {"type": "string"}, - "tar": {"type": "string"} + "tar": {"type": "string"}, + "ucode": {"type": "string"} } }, "file": { From 0c1b1def13cddfaff320acde1ab30a27e56a2e97 Mon Sep 17 00:00:00 2001 From: Rolf Neugebauer Date: Mon, 15 Jan 2018 14:42:12 +0000 Subject: [PATCH 2/6] build: Add ucode to the intermediate tar ball if specified This extends the kernel filter to also look for the CPU microcode file if specified in the YAML. If found, the ucode cpio archive is placed into the intermediate tar file as '/boot/ucode.cpio'. Signed-off-by: Rolf Neugebauer --- src/moby/build.go | 56 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/src/moby/build.go b/src/moby/build.go index 2575428d4..bdd8c79ea 100644 --- a/src/moby/build.go +++ b/src/moby/build.go @@ -176,9 +176,9 @@ func Build(m Moby, w io.Writer, pull bool, tp string) error { dupMap := map[string]string{} if m.Kernel.ref != nil { - // get kernel and initrd tarball from container + // get kernel and initrd tarball and ucode cpio archive from container log.Infof("Extract kernel image: %s", m.Kernel.ref) - kf := newKernelFilter(iw, m.Kernel.Cmdline, m.Kernel.Binary, m.Kernel.Tar) + kf := newKernelFilter(iw, m.Kernel.Cmdline, m.Kernel.Binary, m.Kernel.Tar, m.Kernel.UCode) err := ImageTar(m.Kernel.ref, "", kf, enforceContentTrust(m.Kernel.ref.String(), &m.Trust), pull, "") if err != nil { return fmt.Errorf("Failed to extract kernel image and tarball: %v", err) @@ -259,13 +259,15 @@ type kernelFilter struct { cmdline string kernel string tar string + ucode string discard bool foundKernel bool foundKTar bool + foundUCode bool } -func newKernelFilter(tw *tar.Writer, cmdline string, kernel string, tar *string) *kernelFilter { - tarName, kernelName := "kernel.tar", "kernel" +func newKernelFilter(tw *tar.Writer, cmdline string, kernel string, tar, ucode *string) *kernelFilter { + tarName, kernelName, ucodeName := "kernel.tar", "kernel", "" if tar != nil { tarName = *tar if tarName == "none" { @@ -275,7 +277,10 @@ func newKernelFilter(tw *tar.Writer, cmdline string, kernel string, tar *string) if kernel != "" { kernelName = kernel } - return &kernelFilter{tw: tw, cmdline: cmdline, kernel: kernelName, tar: tarName} + if ucode != nil { + ucodeName = *ucode + } + return &kernelFilter{tw: tw, cmdline: cmdline, kernel: kernelName, tar: tarName, ucode: ucodeName} } func (k *kernelFilter) finishTar() error { @@ -329,16 +334,19 @@ func (k *kernelFilter) WriteHeader(hdr *tar.Header) error { } k.foundKernel = true k.discard = false - whdr := &tar.Header{ - Name: "boot", - Mode: 0755, - Typeflag: tar.TypeDir, - } - if err := tw.WriteHeader(whdr); err != nil { - return err + // If we handled the ucode, /boot already exist. + if !k.foundUCode { + whdr := &tar.Header{ + Name: "boot", + Mode: 0755, + Typeflag: tar.TypeDir, + } + if err := tw.WriteHeader(whdr); err != nil { + return err + } } // add the cmdline in /boot/cmdline - whdr = &tar.Header{ + whdr := &tar.Header{ Name: "boot/cmdline", Mode: 0644, Size: int64(len(k.cmdline)), @@ -363,6 +371,28 @@ func (k *kernelFilter) WriteHeader(hdr *tar.Header) error { k.foundKTar = true k.discard = false k.buffer = new(bytes.Buffer) + case k.ucode: + k.foundUCode = true + k.discard = false + // If we handled the kernel, /boot already exist. + if !k.foundKernel { + whdr := &tar.Header{ + Name: "boot", + Mode: 0755, + Typeflag: tar.TypeDir, + } + if err := tw.WriteHeader(whdr); err != nil { + return err + } + } + whdr := &tar.Header{ + Name: "boot/ucode.cpio", + Mode: hdr.Mode, + Size: hdr.Size, + } + if err := tw.WriteHeader(whdr); err != nil { + return err + } default: k.discard = true } From 2b1a611babe45dbf35fd784a591f6cc98b64c2e3 Mon Sep 17 00:00:00 2001 From: Rolf Neugebauer Date: Mon, 15 Jan 2018 15:17:31 +0000 Subject: [PATCH 3/6] output: Extract ucode if present For now the backends for the different formats do not yet use the extracted ucode cpio archive, but '// TODO' are placed for the backends which should eventually handle it. Signed-off-by: Rolf Neugebauer --- src/initrd/initrd.go | 11 ++++++++--- src/moby/linuxkit.go | 2 +- src/moby/output.go | 32 ++++++++++++++++++-------------- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/initrd/initrd.go b/src/initrd/initrd.go index d479fdf42..2a4a27a06 100644 --- a/src/initrd/initrd.go +++ b/src/initrd/initrd.go @@ -110,13 +110,13 @@ func CopyTar(w *Writer, r *tar.Reader) (written int64, err error) { } } -// CopySplitTar copies a tar stream into an initrd, but splits out kernel and cmdline -func CopySplitTar(w *Writer, r *tar.Reader) (kernel []byte, cmdline string, err error) { +// CopySplitTar copies a tar stream into an initrd, but splits out kernel, cmdline, and ucode +func CopySplitTar(w *Writer, r *tar.Reader) (kernel []byte, cmdline string, ucode []byte, err error) { for { var thdr *tar.Header thdr, err = r.Next() if err == io.EOF { - return kernel, cmdline, nil + return kernel, cmdline, ucode, nil } if err != nil { return @@ -134,6 +134,11 @@ func CopySplitTar(w *Writer, r *tar.Reader) (kernel []byte, cmdline string, err return } cmdline = string(buf) + case "boot/ucode.cpio": + ucode, err = ioutil.ReadAll(r) + if err != nil { + return + } case "boot": // skip this entry default: diff --git a/src/moby/linuxkit.go b/src/moby/linuxkit.go index 67449e372..bc15a0896 100644 --- a/src/moby/linuxkit.go +++ b/src/moby/linuxkit.go @@ -72,7 +72,7 @@ func ensureLinuxkitImage(name string) error { return err } defer image.Close() - kernel, initrd, cmdline, err := tarToInitrd(image) + kernel, initrd, cmdline, _, err := tarToInitrd(image) if err != nil { return fmt.Errorf("Error converting to initrd: %v", err) } diff --git a/src/moby/output.go b/src/moby/output.go index 5cd649104..ed8d57426 100644 --- a/src/moby/output.go +++ b/src/moby/output.go @@ -27,10 +27,11 @@ const ( var outFuns = map[string]func(string, io.Reader, int) error{ "kernel+initrd": func(base string, image io.Reader, size int) error { - kernel, initrd, cmdline, err := tarToInitrd(image) + kernel, initrd, cmdline, _, err := tarToInitrd(image) if err != nil { return fmt.Errorf("Error converting to initrd: %v", err) } + // TODO: Handle ucode err = outputKernelInitrd(base, kernel, initrd, cmdline) if err != nil { return fmt.Errorf("Error writing kernel+initrd output: %v", err) @@ -38,10 +39,11 @@ var outFuns = map[string]func(string, io.Reader, int) error{ return nil }, "tar-kernel-initrd": func(base string, image io.Reader, size int) error { - kernel, initrd, cmdline, err := tarToInitrd(image) + kernel, initrd, cmdline, _, err := tarToInitrd(image) if err != nil { return fmt.Errorf("Error converting to initrd: %v", err) } + // TODO: Handle ucode if err := outputKernelInitrdTarball(base, kernel, initrd, cmdline); err != nil { return fmt.Errorf("Error writing kernel+initrd tarball output: %v", err) } @@ -62,10 +64,11 @@ var outFuns = map[string]func(string, io.Reader, int) error{ return nil }, "raw-bios": func(base string, image io.Reader, size int) error { - kernel, initrd, cmdline, err := tarToInitrd(image) + kernel, initrd, cmdline, _, err := tarToInitrd(image) if err != nil { return fmt.Errorf("Error converting to initrd: %v", err) } + // TODO: Handle ucode err = outputImg(rawBios, base+"-bios.img", kernel, initrd, cmdline) if err != nil { return fmt.Errorf("Error writing raw-bios output: %v", err) @@ -73,7 +76,7 @@ var outFuns = map[string]func(string, io.Reader, int) error{ return nil }, "raw-efi": func(base string, image io.Reader, size int) error { - kernel, initrd, cmdline, err := tarToInitrd(image) + kernel, initrd, cmdline, _, err := tarToInitrd(image) if err != nil { return fmt.Errorf("Error converting to initrd: %v", err) } @@ -86,7 +89,7 @@ var outFuns = map[string]func(string, io.Reader, int) error{ "aws": func(base string, image io.Reader, size int) error { filename := base + ".raw" log.Infof(" %s", filename) - kernel, initrd, cmdline, err := tarToInitrd(image) + kernel, initrd, cmdline, _, err := tarToInitrd(image) if err != nil { return fmt.Errorf("Error converting to initrd: %v", err) } @@ -97,7 +100,7 @@ var outFuns = map[string]func(string, io.Reader, int) error{ return nil }, "gcp": func(base string, image io.Reader, size int) error { - kernel, initrd, cmdline, err := tarToInitrd(image) + kernel, initrd, cmdline, _, err := tarToInitrd(image) if err != nil { return fmt.Errorf("Error converting to initrd: %v", err) } @@ -110,10 +113,11 @@ var outFuns = map[string]func(string, io.Reader, int) error{ "qcow2-bios": func(base string, image io.Reader, size int) error { filename := base + ".qcow2" log.Infof(" %s", filename) - kernel, initrd, cmdline, err := tarToInitrd(image) + kernel, initrd, cmdline, _, err := tarToInitrd(image) if err != nil { return fmt.Errorf("Error converting to initrd: %v", err) } + // TODO: Handle ucode err = outputLinuxKit("qcow2", filename, kernel, initrd, cmdline, size) if err != nil { return fmt.Errorf("Error writing qcow2 output: %v", err) @@ -121,7 +125,7 @@ var outFuns = map[string]func(string, io.Reader, int) error{ return nil }, "vhd": func(base string, image io.Reader, size int) error { - kernel, initrd, cmdline, err := tarToInitrd(image) + kernel, initrd, cmdline, _, err := tarToInitrd(image) if err != nil { return fmt.Errorf("Error converting to initrd: %v", err) } @@ -132,7 +136,7 @@ var outFuns = map[string]func(string, io.Reader, int) error{ return nil }, "dynamic-vhd": func(base string, image io.Reader, size int) error { - kernel, initrd, cmdline, err := tarToInitrd(image) + kernel, initrd, cmdline, _, err := tarToInitrd(image) if err != nil { return fmt.Errorf("Error converting to initrd: %v", err) } @@ -143,7 +147,7 @@ var outFuns = map[string]func(string, io.Reader, int) error{ return nil }, "vmdk": func(base string, image io.Reader, size int) error { - kernel, initrd, cmdline, err := tarToInitrd(image) + kernel, initrd, cmdline, _, err := tarToInitrd(image) if err != nil { return fmt.Errorf("Error converting to initrd: %v", err) } @@ -220,16 +224,16 @@ func Formats(base string, image string, formats []string, size int) error { return nil } -func tarToInitrd(r io.Reader) ([]byte, []byte, string, error) { +func tarToInitrd(r io.Reader) ([]byte, []byte, string, []byte, error) { w := new(bytes.Buffer) iw := initrd.NewWriter(w) tr := tar.NewReader(r) - kernel, cmdline, err := initrd.CopySplitTar(iw, tr) + kernel, cmdline, ucode, err := initrd.CopySplitTar(iw, tr) if err != nil { - return []byte{}, []byte{}, "", err + return []byte{}, []byte{}, "", []byte{}, err } iw.Close() - return kernel, w.Bytes(), cmdline, nil + return kernel, w.Bytes(), cmdline, ucode, nil } func tarInitrdKernel(kernel, initrd []byte, cmdline string) (*bytes.Buffer, error) { From 1d244544387fc2f8fc99c3f42f7e3da65b8295f2 Mon Sep 17 00:00:00 2001 From: Rolf Neugebauer Date: Mon, 15 Jan 2018 15:45:16 +0000 Subject: [PATCH 4/6] output: Add ucode.cpio to the tar-kernel-initrd format Signed-off-by: Rolf Neugebauer --- src/moby/output.go | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/moby/output.go b/src/moby/output.go index ed8d57426..30523586d 100644 --- a/src/moby/output.go +++ b/src/moby/output.go @@ -39,12 +39,11 @@ var outFuns = map[string]func(string, io.Reader, int) error{ return nil }, "tar-kernel-initrd": func(base string, image io.Reader, size int) error { - kernel, initrd, cmdline, _, err := tarToInitrd(image) + kernel, initrd, cmdline, ucode, err := tarToInitrd(image) if err != nil { return fmt.Errorf("Error converting to initrd: %v", err) } - // TODO: Handle ucode - if err := outputKernelInitrdTarball(base, kernel, initrd, cmdline); err != nil { + if err := outputKernelInitrdTarball(base, kernel, initrd, cmdline, ucode); err != nil { return fmt.Errorf("Error writing kernel+initrd tarball output: %v", err) } return nil @@ -332,7 +331,7 @@ func outputKernelInitrd(base string, kernel []byte, initrd []byte, cmdline strin return ioutil.WriteFile(base+"-cmdline", []byte(cmdline), os.FileMode(0644)) } -func outputKernelInitrdTarball(base string, kernel []byte, initrd []byte, cmdline string) error { +func outputKernelInitrdTarball(base string, kernel []byte, initrd []byte, cmdline string, ucode []byte) error { log.Debugf("output kernel/initrd tarball: %s %s", base, cmdline) log.Infof(" %s", base+"-initrd.tar") f, err := os.Create(base + "-initrd.tar") @@ -374,5 +373,18 @@ func outputKernelInitrdTarball(base string, kernel []byte, initrd []byte, cmdlin if _, err := tw.Write([]byte(cmdline)); err != nil { return err } + if len(ucode) != 0 { + hdr := &tar.Header{ + Name: "ucode.cpio", + Mode: 0644, + Size: int64(len(ucode)), + } + if err := tw.WriteHeader(hdr); err != nil { + return err + } + if _, err := tw.Write(ucode); err != nil { + return err + } + } return tw.Close() } From 5d5a13526b9dd9394b53e79ca33c650360cad272 Mon Sep 17 00:00:00 2001 From: Rolf Neugebauer Date: Mon, 15 Jan 2018 16:03:55 +0000 Subject: [PATCH 5/6] output: Add support for CPU ucode to the kernel+initrd format This prepends 'ucode.cpio' to the initrd if present. Padding should not be necessary as the ucode.cpio should be padded to the right size. Signed-off-by: Rolf Neugebauer --- src/moby/output.go | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/src/moby/output.go b/src/moby/output.go index 30523586d..e21a70b3a 100644 --- a/src/moby/output.go +++ b/src/moby/output.go @@ -27,12 +27,11 @@ const ( var outFuns = map[string]func(string, io.Reader, int) error{ "kernel+initrd": func(base string, image io.Reader, size int) error { - kernel, initrd, cmdline, _, err := tarToInitrd(image) + kernel, initrd, cmdline, ucode, err := tarToInitrd(image) if err != nil { return fmt.Errorf("Error converting to initrd: %v", err) } - // TODO: Handle ucode - err = outputKernelInitrd(base, kernel, initrd, cmdline) + err = outputKernelInitrd(base, kernel, initrd, cmdline, ucode) if err != nil { return fmt.Errorf("Error writing kernel+initrd output: %v", err) } @@ -317,15 +316,29 @@ func outputRPi3(image, filename string, filesystem io.Reader) error { return dockerRun(filesystem, output, true, image) } -func outputKernelInitrd(base string, kernel []byte, initrd []byte, cmdline string) error { +func outputKernelInitrd(base string, kernel []byte, initrd []byte, cmdline string, ucode []byte) error { log.Debugf("output kernel/initrd: %s %s", base, cmdline) - log.Infof(" %s %s %s", base+"-kernel", base+"-initrd.img", base+"-cmdline") - err := ioutil.WriteFile(base+"-initrd.img", initrd, os.FileMode(0644)) - if err != nil { - return err + + if len(ucode) != 0 { + log.Infof(" %s ucode+%s %s", base+"-kernel", base+"-initrd.img", base+"-cmdline") + if err := ioutil.WriteFile(base+"-initrd.img", ucode, os.FileMode(0644)); err != nil { + return err + } + f, err := os.OpenFile(base+"-initrd.img", os.O_APPEND|os.O_WRONLY, 0644) + if err != nil { + return err + } + defer f.Close() + if _, err = f.Write(initrd); err != nil { + return err + } + } else { + log.Infof(" %s %s %s", base+"-kernel", base+"-initrd.img", base+"-cmdline") + if err := ioutil.WriteFile(base+"-initrd.img", initrd, os.FileMode(0644)); err != nil { + return err + } } - err = ioutil.WriteFile(base+"-kernel", kernel, os.FileMode(0644)) - if err != nil { + if err := ioutil.WriteFile(base+"-kernel", kernel, os.FileMode(0644)); err != nil { return err } return ioutil.WriteFile(base+"-cmdline", []byte(cmdline), os.FileMode(0644)) From 1d995e198a91904a1d1464d6b00dfc9278d40084 Mon Sep 17 00:00:00 2001 From: Rolf Neugebauer Date: Mon, 15 Jan 2018 16:08:45 +0000 Subject: [PATCH 6/6] docs: Document the 'ucode' kernel option. Signed-off-by: Rolf Neugebauer --- docs/yaml.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/yaml.md b/docs/yaml.md index b926e5fc5..c5ba3a3ac 100644 --- a/docs/yaml.md +++ b/docs/yaml.md @@ -47,6 +47,10 @@ contain a kernel modules directory. `cmdline` specifies the kernel command line To override the names, you can specify the kernel image name with `binary: bzImage` and the tar image with `tar: kernel.tar` or the empty string or `none` if you do not want to use a tarball at all. +Kernel packages may also contain a cpio archive containing CPU microcode which needs prepending to +the initrd. To select this option, recommended when booting on bare metal, add `ucode: intel-ucode.cpio` +to the kernel section. + ## `init` The `init` section is a list of images that are used for the `init` system and are unpacked directly