|  | //go:build amd64 || arm64 || riscv64 | 
|  | // +build amd64 arm64 riscv64 | 
|  |  | 
|  | // Package kexec allows executing subsequent kernels from Linux userspace. | 
|  | package kexec | 
|  |  | 
|  | import ( | 
|  | "bufio" | 
|  | "errors" | 
|  | "fmt" | 
|  | "io" | 
|  | "os" | 
|  | "runtime" | 
|  | "strings" | 
|  |  | 
|  | "golang.org/x/sys/unix" | 
|  | ) | 
|  |  | 
|  | // FileLoad loads the given kernel as the new kernel with the given initramfs | 
|  | // and cmdline. It also performs auxiliary work like adding the ACPI RSDP | 
|  | // physical address to command line if using EFI. The kernel can be started by | 
|  | // calling unix.Reboot(unix.LINUX_REBOOT_CMD_KEXEC). | 
|  | // The underlying syscall is only available on x86_64, arm64 and riscv. | 
|  | // Parts of this function are taken from u-root's kexec package. | 
|  | func FileLoad(kernel, initramfs *os.File, cmdline string) error { | 
|  | passedCmdline := cmdline | 
|  | systab, err := os.Open("/sys/firmware/efi/systab") | 
|  | if os.IsNotExist(err) { | 
|  | // No EFI, nothing to do | 
|  | } else if err != nil { | 
|  | return fmt.Errorf("unable to open EFI systab: %w", err) | 
|  | } else { | 
|  | s := bufio.NewScanner(systab) | 
|  | for s.Scan() { | 
|  | if errors.Is(s.Err(), io.EOF) { | 
|  | // We have no RSDP, no need to pass it | 
|  | break | 
|  | } | 
|  | if err != nil { | 
|  | return fmt.Errorf("failed to read EFI systab: %w", err) | 
|  | } | 
|  | parts := strings.SplitN(s.Text(), "=", 2) | 
|  | // There are two ACPI RDSP revisions, 1.0 and 2.0. | 
|  | // Linux guarantees that the 2.0 always comes before the | 
|  | // 1.0 so just matching and breaking is good enough. | 
|  | if parts[0] == "ACPI20" || parts[0] == "ACPI" { | 
|  | // Technically this could be passed through as parsing a hexa- | 
|  | // decimal address and printing it back does nothing, but in | 
|  | // case unexpected values show up this could cause very hard- | 
|  | // to-debug crashes when the new kernel boots. | 
|  | var acpiRsdp int64 | 
|  | if _, err := fmt.Sscanf(parts[1], "0x%x", &acpiRsdp); err != nil { | 
|  | return fmt.Errorf("failed to parse EFI systab ACP RSDP address: %w", err) | 
|  | } | 
|  | passedCmdline += fmt.Sprintf(" acpi_rsdp=0x%x", acpiRsdp) | 
|  | break | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | var flags int | 
|  | var initramfsfd int | 
|  | if initramfs != nil { | 
|  | initramfsfd = int(initramfs.Fd()) | 
|  | } else { | 
|  | flags |= unix.KEXEC_FILE_NO_INITRAMFS | 
|  | } | 
|  |  | 
|  | if err := unix.KexecFileLoad(int(kernel.Fd()), initramfsfd, passedCmdline, flags); err != nil { | 
|  | return fmt.Errorf("SYS_kexec_file_load(%d, %d, %s, %x) = %v", kernel.Fd(), initramfsfd, cmdline, flags, err) | 
|  | } | 
|  | runtime.KeepAlive(kernel) | 
|  | runtime.KeepAlive(initramfs) | 
|  | return nil | 
|  | } |