github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/cmds/core/kexec/kexec_linux.go (about)

     1  // Copyright 2015-2018 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // kexec executes a new kernel over the running kernel (u-root).
     6  //
     7  // Synopsis:
     8  //     kexec [--initrd=FILE] [--command-line=STRING] [-l] [-e] [KERNELIMAGE]
     9  //
    10  // Description:
    11  //		 Loads a kernel for later execution.
    12  //
    13  // Options:
    14  //      --append string        Append to the kernel command line
    15  //  -c, --cmdline string       Append to the kernel command line
    16  //  -d, --debug                Print debug info (default true)
    17  //  -e, --exec                 Execute a currently loaded kernel
    18  //  -x, --extra string         Add a cpio containing extra files
    19  //      --initramfs string     Use file as the kernel's initial ramdisk
    20  //  -i, --initrd string        Use file as the kernel's initial ramdisk
    21  //  -l, --load                 Load the new kernel into the current kernel
    22  //  -L, --loadsyscall          Use the kexec load syscall (not file_load) (default true)
    23  //      --module stringArray   Load multiboot module with command line args (e.g --module="mod arg1")
    24  //  -p, --purgatory string     pick a purgatory, use '-p xyz' to get a list (default "default")
    25  //      --reuse-cmdline        Use the kernel command line from running system
    26  
    27  package main
    28  
    29  import (
    30  	"io"
    31  	"log"
    32  	"os"
    33  	"strings"
    34  
    35  	flag "github.com/spf13/pflag"
    36  
    37  	"github.com/mvdan/u-root-coreutils/pkg/boot"
    38  	"github.com/mvdan/u-root-coreutils/pkg/boot/kexec"
    39  	"github.com/mvdan/u-root-coreutils/pkg/boot/linux"
    40  	"github.com/mvdan/u-root-coreutils/pkg/boot/multiboot"
    41  	"github.com/mvdan/u-root-coreutils/pkg/boot/purgatory"
    42  	"github.com/mvdan/u-root-coreutils/pkg/cmdline"
    43  	"github.com/mvdan/u-root-coreutils/pkg/uio"
    44  )
    45  
    46  type options struct {
    47  	cmdline      string
    48  	debug        bool
    49  	dtb          string
    50  	exec         bool
    51  	extra        string
    52  	initramfs    string
    53  	load         bool
    54  	loadSyscall  bool
    55  	mmapInitrd   bool
    56  	mmapKernel   bool
    57  	modules      []string
    58  	purgatory    string
    59  	reuseCmdline bool
    60  }
    61  
    62  func registerFlags() *options {
    63  	o := &options{}
    64  	flag.StringVarP(&o.cmdline, "cmdline", "c", "", "Append to the kernel command line")
    65  	flag.StringVar(&o.cmdline, "append", "", "Append to the kernel command line")
    66  	flag.BoolVarP(&o.debug, "debug", "d", false, "Print debug info")
    67  	flag.StringVar(&o.dtb, "dtb", "", "FILE used as the flatten device tree blob")
    68  	flag.BoolVarP(&o.exec, "exec", "e", false, "Execute a currently loaded kernel")
    69  	flag.StringVarP(&o.extra, "extra", "x", "", "Add a cpio containing extra files")
    70  	flag.StringVarP(&o.initramfs, "initrd", "i", "", "Use file as the kernel's initial ramdisk")
    71  	flag.StringVar(&o.initramfs, "initramfs", "", "Use file as the kernel's initial ramdisk")
    72  	flag.BoolVarP(&o.load, "load", "l", false, "Load the new kernel into the current kernel")
    73  	flag.BoolVarP(&o.loadSyscall, "loadsyscall", "L", false, "Use the kexec_load syscall (not kexec_file_load)")
    74  	flag.BoolVar(&o.mmapInitrd, "mmap-initrd", true, "Mmap initrd file into virtual buffer, other than directly reading it (Only supported in Arm64 classic load mode for now)")
    75  	flag.BoolVar(&o.mmapKernel, "mmap-kernel", true, "Mmap kernel file into virtual buffer, other than directly reading it (Only supported in Arm64 classi load mode for now)")
    76  	flag.StringArrayVar(&o.modules, "module", nil, `Load multiboot module with command line args (e.g --module="mod arg1")`)
    77  
    78  	// This is broken out as it is almost never to be used. But it is valueable, nonetheless.
    79  	flag.StringVarP(&o.purgatory, "purgatory", "p", "default", "picks a purgatory only if loading a Linux kernel with kexec_load, use '-p xyz' to get a list")
    80  	flag.BoolVar(&o.reuseCmdline, "reuse-cmdline", false, "Use the kernel command line from running system")
    81  	return o
    82  }
    83  
    84  func main() {
    85  	opts := registerFlags()
    86  	flag.Parse()
    87  
    88  	if opts.debug {
    89  		linux.Debug = log.Printf
    90  		purgatory.Debug = log.Printf
    91  	}
    92  
    93  	if (!opts.exec && flag.NArg() == 0) || flag.NArg() > 1 {
    94  		flag.PrintDefaults()
    95  		log.Fatalf("usage: kexec [flags] kernelname OR kexec -e")
    96  	}
    97  
    98  	if opts.cmdline != "" && opts.reuseCmdline {
    99  		flag.PrintDefaults()
   100  		log.Fatalf("--reuse-cmdline and other command line options are mutually exclusive")
   101  	}
   102  
   103  	if !opts.load && !opts.exec {
   104  		opts.load = true
   105  		opts.exec = true
   106  	}
   107  
   108  	newCmdline := opts.cmdline
   109  	if opts.reuseCmdline {
   110  		procCmdLine := cmdline.NewCmdLine()
   111  		if procCmdLine.Err != nil {
   112  			log.Fatal("Couldn't read /proc/cmdline")
   113  		} else {
   114  			newCmdline = procCmdLine.Raw
   115  		}
   116  	}
   117  
   118  	if err := purgatory.Select(opts.purgatory); err != nil {
   119  		log.Fatal(err)
   120  	}
   121  	if opts.load {
   122  		kernelpath := flag.Arg(0)
   123  		kernel, err := os.Open(kernelpath)
   124  		if err != nil {
   125  			log.Fatal(err)
   126  		}
   127  		defer kernel.Close()
   128  		var image boot.OSImage
   129  		if err := multiboot.Probe(kernel); err == nil {
   130  			image = &boot.MultibootImage{
   131  				Modules: multiboot.LazyOpenModules(opts.modules),
   132  				Kernel:  kernel,
   133  				Cmdline: newCmdline,
   134  			}
   135  		} else {
   136  			var files []io.ReaderAt
   137  			if len(opts.extra) > 0 {
   138  				initrd, err := boot.CreateInitrd(strings.Fields(opts.extra)...)
   139  				if err != nil {
   140  					log.Fatal(err)
   141  				}
   142  				files = append(files, initrd)
   143  			}
   144  			if opts.initramfs != "" {
   145  				for _, n := range strings.Fields(opts.initramfs) {
   146  					files = append(files, uio.NewLazyFile(n))
   147  				}
   148  			}
   149  			var i io.ReaderAt
   150  			if len(files) > 0 {
   151  				i = boot.CatInitrds(files...)
   152  			}
   153  
   154  			var dtb io.ReaderAt
   155  			if len(opts.dtb) > 0 {
   156  				dtb, err = os.Open(opts.dtb)
   157  				if err != nil {
   158  					log.Fatalf("Failed to open dtb file %s: %v", opts.dtb, err)
   159  				}
   160  			}
   161  			image = &boot.LinuxImage{
   162  				Kernel:      uio.NewLazyFile(kernelpath),
   163  				Initrd:      i,
   164  				Cmdline:     newCmdline,
   165  				LoadSyscall: opts.loadSyscall,
   166  				KexecOpts: linux.KexecOptions{
   167  					DTB:        dtb,
   168  					MmapKernel: opts.mmapKernel,
   169  					MmapRamfs:  opts.mmapInitrd,
   170  				},
   171  			}
   172  		}
   173  		if err := image.Load(opts.debug); err != nil {
   174  			log.Fatal(err)
   175  		}
   176  	}
   177  
   178  	if opts.exec {
   179  		if err := kexec.Reboot(); err != nil {
   180  			log.Fatalf("%v", err)
   181  		}
   182  	}
   183  }