Skip to content
This repository has been archived by the owner on Jul 11, 2023. It is now read-only.

Commit

Permalink
Use the kexec-classic command if available and go to 32-bit entry
Browse files Browse the repository at this point in the history
This resolves a lot of problems starting up various distros,
since linux sets up graphics in the 32-bit code, and u-root
kexec can only start at the 64-bit entry point today.

Signed-off-by: Ronald G Minnich <rminnich@gmail.com>
  • Loading branch information
rminnich committed May 1, 2021
1 parent b292151 commit dfc1429
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 4 deletions.
9 changes: 5 additions & 4 deletions cmds/webboot/webboot.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,6 @@ func (i *ISO) exec(uiEvents <-chan ui.Event, boot bool) error {
}

verbose("Get configs: %+v", configs)
if !boot {
return fmt.Errorf("Booting is disabled (see --dryrun flag).")
}

entries := []menu.Entry{}
for _, config := range configs {
Expand Down Expand Up @@ -105,7 +102,11 @@ func (i *ISO) exec(uiEvents <-chan ui.Event, boot bool) error {
return err
}

err = bootiso.BootCachedISO(config.image, kernelParams.String())
if !boot {
s := fmt.Sprintf("config.image %s, kernelparams.String() %s", config.image, kernelParams.String())
return fmt.Errorf("Booting is disabled (see --dryrun flag), but otherwise would be [%s].", s)
}
err = bootiso.BootCachedISO(config.image, kernelParams.String() + " waitusb=10")
}

// If kexec succeeds, we should not arrive here
Expand Down
105 changes: 105 additions & 0 deletions pkg/bootiso/bootiso.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,25 @@ import (
"crypto/md5"
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
"hash"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
"path"
"strings"

"github.com/u-root/u-root/pkg/boot"
"github.com/u-root/u-root/pkg/boot/grub"
"github.com/u-root/u-root/pkg/boot/kexec"
"github.com/u-root/u-root/pkg/boot/syslinux"
"github.com/u-root/u-root/pkg/boot/util"
"github.com/u-root/u-root/pkg/mount"
"github.com/u-root/u-root/pkg/mount/loop"
"github.com/u-root/u-root/pkg/uio"
"golang.org/x/sys/unix"
)

Expand Down Expand Up @@ -232,6 +236,97 @@ func BootFromPmem(isoPath string, configLabel string, configType string) error {
return nil
}

// next two functions hoisted from u-root kexec. We will remove
// them when the u-root kexec becomes capable of using the 32-bit
// entry point. 32-bit entry is essential to working on chromebooks.

func copyToFile(r io.Reader) (*os.File, error) {
f, err := ioutil.TempFile("", "webboot")
if err != nil {
return nil, err
}
defer f.Close()
if _, err := io.Copy(f, r); err != nil {
return nil, err
}
if err := f.Sync(); err != nil {
return nil, err
}

readOnlyF, err := os.Open(f.Name())
if err != nil {
return nil, err
}
return readOnlyF, nil
}

// kexecCmd boots via the classic kexec command, if it exists
func cmdKexecLoad(li *boot.LinuxImage, verbose bool) error {
if li.Kernel == nil {
return errors.New("LinuxImage.Kernel must be non-nil")
}

kernel, initrd := uio.Reader(util.TryGzipFilter(li.Kernel)), uio.Reader(li.Initrd)
if verbose {
// In verbose mode, print a dot every 5MiB. It is not pretty,
// but it at least proves the files are still downloading.
progress := func(r io.Reader, dot string) io.Reader {
return &uio.ProgressReader{
R: r,
Symbol: dot,
Interval: 5 * 1024 * 1024,
W: os.Stdout,
}
}
kernel = progress(kernel, "K")
initrd = progress(initrd, "I")
}

// It seams inefficient to always copy, in particular when the reader
// is an io.File but that's not sufficient, os.File could be a socket,
// a pipe or some other strange thing. Also kexec_file_load will fail
// (similar to execve) if anything as the file opened for writing.
// That's unfortunately something we can't guarantee here - unless we
// make a copy of the file and dump it somewhere.
k, err := copyToFile(kernel)
if err != nil {
return err
}
defer k.Close()
kargs := []string{"-d", "-l", "--entry-32bit", "--command-line=" + li.Cmdline}
var i *os.File
if li.Initrd != nil {
i, err = copyToFile(initrd)
if err != nil {
return err
}
defer i.Close()
kargs = append(kargs, "--initrd="+i.Name())
}

log.Printf("Kernel: %s", k.Name())
kargs = append(kargs, k.Name())
if i != nil {
log.Printf("Initrd: %s", i.Name())
}
log.Printf("Command line: %s", li.Cmdline)
log.Printf("Kexec args: %q", kargs)

out, err := exec.Command("/sbin/kexec", kargs...).CombinedOutput()
if err != nil {
err = fmt.Errorf("Load failed; output %q, err %v", out, err)
}
return err
}

func cmdKexecReboot(verbose bool) error {
o, err := exec.Command("/sbin/kexec", "-d", "-e").CombinedOutput()
if err != nil {
err = fmt.Errorf("Exec failed; output %q, err %v", o, err)
}
return err
}

func BootCachedISO(osImage boot.OSImage, kernelParams string) error {
// Need to convert from boot.OSImage to boot.LinuxImage to edit the Cmdline
linuxImage, ok := osImage.(*boot.LinuxImage)
Expand All @@ -241,6 +336,16 @@ func BootCachedISO(osImage boot.OSImage, kernelParams string) error {

linuxImage.Cmdline = linuxImage.Cmdline + " " + kernelParams

// We prefer to use the kexec command for now, if possible, as it can
// use the 32-bit entry point.
if _, err := os.Stat("/sbin/kexec"); err != nil {
if err := cmdKexecLoad(linuxImage, true); err != nil {
return err
}
if err := cmdKexecReboot(true); err != nil {
return err
}
}
if err := linuxImage.Load(true); err != nil {
return err
}
Expand Down
7 changes: 7 additions & 0 deletions webboot.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,13 @@ func main() {
"go", "run", "github.com/u-root/u-root/.",
"-files", "/etc/ssl/certs",
}

// Try to find the system kexec. We can not use LookPath as people
// building this might have the u-root kexec in their path.
if _, err := os.Stat("/sbin/kexec"); err == nil {
args = append(args, "-files=/sbin/kexec")
}

if *wifi {
args = append(args,
"-files", extraBinMust("iwconfig"),
Expand Down

0 comments on commit dfc1429

Please sign in to comment.