Skip to content

Commit

Permalink
Extend kexec to support bzImage files
Browse files Browse the repository at this point in the history
Signed-off-by: Ronald G. Minnich <rminnich@gmail.com>
  • Loading branch information
rminnich committed Sep 4, 2015
1 parent 10e9f13 commit aa08649
Show file tree
Hide file tree
Showing 3 changed files with 272 additions and 20 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,5 @@ Core/
/5.x
/.bash_history
/init
bzImage
vmlinux
190 changes: 190 additions & 0 deletions src/cmds/kexec/header.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
// Copyright 2015 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.
// Thanks to coreboot for documenting the basic layout.
package main

const (
Ram e820type = 1
Reserved = 2
ACPI = 3
NVS = 4
)

const (
E820MAX = 32 // number of entries in E820MAP
)

const (
NotSet boottype = 0
LoadLin = 1
BootSect = 2
SysLinux = 3
EtherBoot = 4
Kernel = 5
)

const (
RamdiskStartMask = 0x07FF
Prompt = 0x8000
Load = 0x4000

CommandLineMagic = 0x7ff
CommandLineSize = 256

HeaderMagic = 0x53726448 // "HdrS" but little-endian constant
)

type e820type uint8
type boottype uint8
type e820entry struct {
Addr uint64
Size uint64
MemType e820type
}

// The header of Linux/i386 kernel
type LinuxHeader struct {
_ [0x1f1]uint8 // 0x000
SetupSects uint8 // 0x1f1
RootFlags uint16 // 0x1f2
Syssize uint32 // 0x1f4 (2.04+)
_ [2]uint8 // 0x1f8
Vidmode uint16 // 0x1fa
RootDev uint16 // 0x1fc
Bootsectormagic uint16 // 0x1fe
// 2.00+
_ [2]uint8 // 0x200
HeaderMagic uint32 // 0x202
Protocolversion uint16 // 0x206
RealModeSwitch uint32 // 0x208
StartSys uint16 // 0x20c
Kveraddr uint16 // 0x20e
TypeOfLoader uint8 // 0x210
Loadflags uint8 // 0x211
Setupmovesize uint16 // 0x212
Code32start uint32 // 0x214
RamdiskImage uint32 // 0x218
RamdiskSize uint32 // 0x21c
_ [4]uint8 // 0x220
// 2.01+
Heapendptr uint16 // 0x224
_ [2]uint8 // 0x226
// 2.02+
Cmdlineptr uint32 // 0x228
// 2.03+
Initrdaddrmax uint32 // 0x22c
// 2.05+
Kernelalignment uint32 // 0x230
Relocatablekernel uint8 // 0x234
Minalignment uint8 // 0x235 (2.10+)
_ [2]uint8 // 0x236
// 2.06+
Cmdlinesize uint32 // 0x238
// 2.07+
Hardwaresubarch uint32 // 0x23c
Hardwaresubarchdata uint64 // 0x240
// 2.08+
Payloadoffset uint32 // 0x248
Payloadlength uint32 // 0x24c
// 2.09+
Setupdata uint64 // 0x250
// 2.10+
Prefaddress uint64 // 0x258
Initsize uint32 // 0x260
}

// Paramters passed to 32-bit part of Linux
type LinuxParams struct {
Origx uint8 // 0x00
Origy uint8 // 0x01
ExtMemK uint16 // 0x02 -- EXTMEMK sits here
OrigVideoPage uint16 // 0x04
OrigVideoMode uint8 // 0x06
OrigVideoCols uint8 // 0x07
_ uint16 // 0x08
OrigVideoeGabx uint16 // 0x0a
_ uint16 // 0x0c
OrigVideoLines uint8 // 0x0e
OrigVideoIsVGA uint8 // 0x0f
OrigVideoPoints uint16 // 0x10

// VESA graphic mode -- linear frame buffer
Lfbwidth uint16 // 0x12
Lfbheight uint16 // 0x14
Lfbdepth uint16 // 0x16
Lfbbase uint32 // 0x18
Lfbsize uint32 // 0x1c
CLMagic uint16 // 0x20
CLOffset uint16 // 0x22
Lfblinelength uint16 // 0x24
Redsize uint8 // 0x26
Redpos uint8 // 0x27
Greensize uint8 // 0x28
Greenpos uint8 // 0x29
Bluesize uint8 // 0x2a
Bluepos uint8 // 0x2b
Rsvdsize uint8 // 0x2c
Rsvdpos uint8 // 0x2d
Vesapmseg uint16 // 0x2e
Vesapmoff uint16 // 0x30
Pages uint16 // 0x32
_ [12]uint8 // 0x34 -- 0x3f reserved for future expansion

//struct apmbiosinfo apmbiosinfo; // 0x40
Apmbiosinfo [0x40]uint8
//struct driveinfostruct driveinfo; // 0x80
Driveinfo [0x20]uint8
//struct sysdesctable sysdesctable; // 0xa0
Sysdesctable [0x140]uint8
Altmemk uint32 // 0x1e0
_ [4]uint8 // 0x1e4
E820mapnr uint8 // 0x1e8
_ [9]uint8 // 0x1e9
MountRootReadonly uint16 // 0x1f2
_ [4]uint8 // 0x1f4
Ramdiskflags uint16 // 0x1f8
_ [2]uint8 // 0x1fa
OrigRootDev uint16 // 0x1fc
_ [1]uint8 // 0x1fe
Auxdeviceinfo uint8 // 0x1ff
_ [2]uint8 // 0x200
Paramblocksignature [4]uint8 // 0x202
Paramblockversion uint16 // 0x206
_ [8]uint8 // 0x208
LoaderType uint8 // 0x210
Loaderflags uint8 // 0x211
_ [2]uint8 // 0x212
KernelStart uint32 // 0x214
Initrdstart uint32 // 0x218
Initrdsize uint32 // 0x21c
_ [8]uint8 // 0x220
Cmdlineptr uint32 // 0x228
Initrdaddrmax uint32 // 0x22c
Kernelalignment uint32 // 0x230
Relocatablekernel uint8 // 0x234
_ [155]uint8 // 0x22c
E820Map [E820MAX]e820entry // 0x2d0
_ [688]uint8 // 0x550
Commandline [CommandLineSize]uint8 // 0x800
_ [1792]uint8 // 0x900 - 0x1000
}

var (
LoaderType = map[boottype] string {
NotSet: "Not set",
LoadLin: "loadlin",
BootSect: "bootsector",
SysLinux: "syslinux",
EtherBoot: "etherboot",
Kernel: "kernel (kexec)",
}
E820 = map[e820type] string {
Ram: "Ram",
Reserved: "Reserved",
ACPI: "ACPI",
NVS: "NVS",
}

)

100 changes: 80 additions & 20 deletions src/cmds/kexec/kexec.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// Copyright 2015 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.

// kexec command in Go.
// Wow, Linux. Just Wow. Who designs this stuff? Because, basically, it's not very good.
// I considered keeping the C declarations and such stuff here but it has not changed
Expand All @@ -18,6 +22,8 @@ package main
import (
"bytes"
"debug/elf"
"encoding/binary"
"errors"
"flag"
"fmt"
"io/ioutil"
Expand Down Expand Up @@ -56,39 +62,95 @@ const (
* This structure is used to hold the arguments that are used when
* loading kernel binaries.
*/
type kexec_segment struct {
type KexecSegment struct {
buf uintptr
bufsz uintptr
mem uintptr
memsize uintptr
}

func (k *kexec_segment) String() string {
func (k *KexecSegment) String() string {
return fmt.Sprintf("[0x%x 0x%x 0x%x 0x%x]", k.buf, k.bufsz, k.mem, k.memsize)
}

type loader func(b []byte) (uintptr, []kexec_segment, error)
type loader func(b []byte) (uintptr, []KexecSegment, error)

var (
kernel = flag.String("k", "vmlinux", "Kernel to load")
loaders = []loader{elfexec, rawexec}
dryrun = flag.Bool("dryrun", false, "Do not do kexec system calls")
loaders = []loader{elfexec, bzImage, rawexec}
)

/* Load a new kernel image as described by the kexec_segment array
func makeseg(b []byte, paddr uintptr) KexecSegment {
return KexecSegment{
buf: uintptr(unsafe.Pointer(&b[0])),
bufsz: uintptr(len(b)),
mem: paddr,
memsize: uintptr(len(b)),
}
}
/* kexec requires page-aligned, page-sized buffers. It also assumes that means 4k. Oh well. */
func pages(size uintptr) []byte {
size = ((size + 4095) >>12)<<12
return make([]byte, size)
}
/* Load a new kernel image as described by the KexecSegment array
* consisting of passed number of segments at the entry-point address.
* The flags allow different useage types.
*/
//extern int kexec_load(void *, size_t, struct kexec_segment *,
//extern int kexec_load(void *, size_t, struct KexecSegment *,
// unsigned long int);

// rawexec either succeeds or Fatals out. It's the last chance.
func rawexec(b []byte) (uintptr, []kexec_segment, error) {
func rawexec(b []byte) (uintptr, []KexecSegment, error) {
var entry uintptr
segs := []kexec_segment{{buf: uintptr(unsafe.Pointer(&b[0])), bufsz: uintptr(len(b)), mem: 0x1000, memsize: uintptr(len(b))}}
segs := []KexecSegment{makeseg(b, 0x1000)}
log.Printf("Using raw image loader")
return entry, segs, nil
}

func elfexec(b []byte) (uintptr, []kexec_segment, error) {
func bzImage(b []byte) (uintptr, []KexecSegment, error) {
var h LinuxHeader
r := bytes.NewReader(b)
binary.Read(r, binary.LittleEndian, &h)
log.Printf("bzImage header %v", h)
log.Printf("magic %x switch %v", h.HeaderMagic, h.RealModeSwitch)
if h.HeaderMagic != HeaderMagic {
return 0, nil, errors.New("Not a bzImage")
}
log.Printf("RamDisk image %x size %x", h.RamdiskImage, h.RamdiskSize)
log.Printf("StartSys %x", h.StartSys)
log.Printf("Boot type: %s(%x)", LoaderType[boottype(h.TypeOfLoader)], h.TypeOfLoader)
log.Printf("SetupSects %d", h.SetupSects)

// Now for the good fun.
l := &LinuxParams{
MountRootReadonly: h.RootFlags,
OrigRootDev: h.RootDev,
OrigVideoMode: 3,
OrigVideoCols: 80,
OrigVideoLines: 25,
OrigVideoIsVGA: 1,
OrigVideoPoints: 16,
LoaderType: 0xff,
// These can be overridden.
CLMagic: CommandLineMagic,
CLOffset: CLOffset,
KernelStart: 0x100000,
}
log.Printf("l %v", l)
segs := []KexecSegment{
makeseg(parameters, LINUX_PARAM_LOC),
makeseg(trampoline, TRAMPOLINE_ENTRY_LOC)
makeseg(trampoline, kernel, kernel_base),
makeseg(cmdline, COMMAND_LINE_LOC),
//makeseg(initrd, initrd_base),
}

return segs, entry, nil
}

func elfexec(b []byte) (uintptr, []KexecSegment, error) {
f, err := elf.NewFile(bytes.NewReader(b))
if err != nil {
return 0, nil, err
Expand All @@ -102,29 +164,23 @@ func elfexec(b []byte) (uintptr, []kexec_segment, error) {
if scount > KEXEC_SEGMENT_MAX {
log.Fatalf("Too many segments: got %v, max is %v", scount, KEXEC_SEGMENT_MAX)
}
segs := make([]kexec_segment, scount)
segs := make([]KexecSegment, scount)
for i, v := range f.Progs {
if v.Type.String() == "PT_LOAD" {
f := v.Open()
// kexec is stupid. Requires the length to be page aligned.
// And, guess what -- that's not always a known thing. But of course
// we all know it's 4K. BAH!
l := uintptr((v.Memsz + 4095)) & (uintptr(^uintptr(4095)))
b := make([]byte, l)
b := pages(uintptr(v.Memsz))
if _, err := f.Read(b[:v.Filesz]); err != nil {
log.Fatalf("Reading %d bytes of program header %d: %v", v.Filesz, i, err)
}
segs[i].buf = uintptr(unsafe.Pointer(&b[0]))
segs[i].bufsz = uintptr(l)
segs[i].mem = uintptr(v.Paddr)
segs[i].memsize = uintptr(l)
segs[i] = makeseg(b, uintptr(v.Paddr))
}
}
log.Printf("Using ELF image loader")
return uintptr(f.Entry), segs, nil
}
func main() {
var err error
var segs []kexec_segment
var segs []KexecSegment
var entry uintptr
flag.Parse()
b, err := ioutil.ReadFile(*kernel)
Expand All @@ -141,6 +197,10 @@ func main() {
log.Printf("%v", s.String())
}

if *dryrun {
log.Printf("Dry run -- exiting now")
return
}
log.Printf("%v %v %v %v %v", syscall.SYS_KEXEC_LOAD, entry, uintptr(len(segs)), uintptr(unsafe.Pointer(&segs[0])), uintptr(0))
e1, e2, err := syscall.Syscall6(syscall.SYS_KEXEC_LOAD, entry, uintptr(len(segs)), uintptr(unsafe.Pointer(&segs[0])), 0, 0, 0)
log.Printf("a %v b %v err %v", e1, e2, err)
Expand Down

0 comments on commit aa08649

Please sign in to comment.