diff --git a/scripts/envsetup.sh b/scripts/envsetup.sh index 2694283d..70037318 100755 --- a/scripts/envsetup.sh +++ b/scripts/envsetup.sh @@ -1335,9 +1335,12 @@ function build_rv_uroot() pushd $RV_UROOT_DIR GOARCH=riscv64 go build GOOS=linux GOARCH=riscv64 $RV_UROOT_DIR/u-root -uroot-source $RV_UROOT_DIR -build bb \ - -uinitcmd="boot" -files ../busybox/busybox:bin/busybox -o $RV_UROOT_DIR/initramfs.cpio \ - -files "$RV_UROOT_DIR/firmware/:lib/firmware/" \ - core boot + -uinitcmd="/init-boot.sh" \ + -files ./cmds/init/init-boot.sh:init-boot.sh \ + -files ../busybox/busybox:bin/busybox \ + -o $RV_UROOT_DIR/initramfs.cpio \ + -files "$RV_UROOT_DIR/firmware/:lib/firmware/" \ + core boot ./cmds/boot/multiboot popd cp $RV_UROOT_DIR/initramfs.cpio $RV_FIRMWARE_INSTALL_DIR/initrd.img } diff --git a/u-root/cmds/boot/multiboot/multiboot.go b/u-root/cmds/boot/multiboot/multiboot.go new file mode 100644 index 00000000..b0070ac5 --- /dev/null +++ b/u-root/cmds/boot/multiboot/multiboot.go @@ -0,0 +1,274 @@ +// Copyright 2017-2020 the u-root Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Command bootpxe implements PXE-based booting followed by local booting. + +package main + +import ( + "context" + "flag" + "fmt" + "log" + "net" + "strings" + "time" + + "github.com/insomniacslk/dhcp/dhcpv4" + "github.com/insomniacslk/dhcp/iana" + "github.com/u-root/u-root/pkg/boot" + "github.com/u-root/u-root/pkg/boot/bootcmd" + "github.com/u-root/u-root/pkg/boot/localboot" + "github.com/u-root/u-root/pkg/boot/menu" + "github.com/u-root/u-root/pkg/boot/netboot" + "github.com/u-root/u-root/pkg/cmdline" + "github.com/u-root/u-root/pkg/curl" + "github.com/u-root/u-root/pkg/dhclient" + "github.com/u-root/u-root/pkg/ipmi" + "github.com/u-root/u-root/pkg/ipmi/ocp" + "github.com/u-root/u-root/pkg/mount" + "github.com/u-root/u-root/pkg/mount/block" + "github.com/u-root/u-root/pkg/sh" + "github.com/u-root/u-root/pkg/ulog" +) + +var ( + ifName = "^e.*" + noLoad = flag.Bool("no-load", false, "get DHCP response, print chosen boot configuration, but do not download + exec it") + noExec = flag.Bool("no-exec", false, "download boot configuration, but do not exec it") + noNetConfig = flag.Bool("no-net-config", false, "get DHCP response, but do not apply the network config it to the kernel interface") + skipBonded = flag.Bool("skip-bonded", false, "Skip NICs that have already been added to a bond") + verbose = flag.Bool("v", false, "Verbose output") + ipv4 = flag.Bool("ipv4", true, "use IPV4") + ipv6 = flag.Bool("ipv6", true, "use IPV6") + cmdAppend = flag.String("cmd", "", "Kernel command to append for each image") + bootfile = flag.String("file", "", "Boot file name (default tftp) or full URI to use instead of DHCP.") + server = flag.String("server", "0.0.0.0", "Server IP (Requires -file for effect)") +) + +const ( + dhcpTimeout = 5 * time.Second + dhcpTries = 3 +) + +func NetbootImages(ifaceNames string) ([]boot.OSImage, error) { + filteredIfs, err := dhclient.Interfaces(ifaceNames) + if err != nil { + return nil, err + } + + if *skipBonded { + filteredIfs = dhclient.FilterBondedInterfaces(filteredIfs, *verbose) + } + + ctx, cancel := context.WithTimeout(context.Background(), (1< 1 { + log.Fatalf("Only one regexp-style argument is allowed, e.g.: " + ifName) + } + if len(flag.Args()) > 0 { + ifName = flag.Args()[0] + } + + var images []boot.OSImage + var err error + maxRetries := 2 + for attempts := 0; attempts < maxRetries; attempts++ { + if *bootfile == "" { + images, err = NetbootImages(ifName) + if err != nil { + log.Printf("PXE boot failed: %v", err) + dumpNetDebugInfo() + } else { + break + } + } else { + log.Printf("Skipping DHCP for manual target..") + var l dhclient.Lease + l, err = newManualLease() + if err == nil { + images, err = netboot.BootImages(context.Background(), ulog.Log, curl.DefaultSchemes, l) + break + } else { + log.Printf("PXE Manual boot failed: %v", err) + } + } + } + setLocalBootViaIPMI() + if err == nil { + for _, img := range images { + img.Edit(func(cmdline string) string { + return cmdline + " " + *cmdAppend + }) + } + + menuEntries := menu.OSImages(*verbose, images...) + menuEntries = append(menuEntries, menu.Reboot{}) + menuEntries = append(menuEntries, menu.StartShell{}) + bootcmd.ShowMenuAndBoot(menuEntries, nil, *noLoad, *noExec) + } else { + localBoot() + } + } else { + localBoot() + } + } + +} diff --git a/u-root/cmds/init/init-boot.sh b/u-root/cmds/init/init-boot.sh new file mode 100755 index 00000000..d04d725c --- /dev/null +++ b/u-root/cmds/init/init-boot.sh @@ -0,0 +1,5 @@ +#!/bin/sh +# Read the random number first to avoid dhcp using random number timeout +dd if=/dev/urandom of=stdout bs=4 count=1 +# pxeboot or localboot +multiboot diff --git a/u-root/pkg/ipmi/ocp/bootorder.go b/u-root/pkg/ipmi/ocp/bootorder.go index d27b72d2..02aebb05 100644 --- a/u-root/pkg/ipmi/ocp/bootorder.go +++ b/u-root/pkg/ipmi/ocp/bootorder.go @@ -55,6 +55,34 @@ func getBootOrder(i *ipmi.IPMI, BootOrder *BootOrder) error { return nil } +func GetBootOrder(i *ipmi.IPMI) (byte, error) { + var bootType byte + recv, err := i.SendRecv(_IPMI_FB_OEM_NET_FUNCTION1, _FB_OEM_GET_BIOS_BOOT_ORDER, nil) + if err != nil { + return LOCAL_BOOT, err + } + bootType = recv[1] + return bootType, nil +} + +func SetLocalBootOrder(i *ipmi.IPMI) error { + var BMCBootOrder BootOrder + BMCBootOrder.bootMode = LOCAL_BOOT + BMCBootOrder.bootSeq[0] = NETWORK_BOOT + BMCBootOrder.bootSeq[1] = LOCAL_BOOT + msg := ipmi.Msg{ + Netfn: _IPMI_FB_OEM_NET_FUNCTION1, + Cmd: _FB_OEM_SET_BIOS_BOOT_ORDER, + Data: unsafe.Pointer(&BMCBootOrder), + DataLen: 6, + } + + if _, err := i.RawSendRecv(msg); err != nil { + return err + } + return nil +} + func setBootOrder(i *ipmi.IPMI, BootOrder *BootOrder) error { msg := ipmi.Msg{ Netfn: _IPMI_FB_OEM_NET_FUNCTION1,