-
Notifications
You must be signed in to change notification settings - Fork 396
/
kexec_load_linux.go
110 lines (98 loc) · 2.69 KB
/
kexec_load_linux.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// Copyright 2015-2021 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.
package kexec
import (
"fmt"
"runtime"
"syscall"
"unsafe"
"golang.org/x/sys/unix"
)
// Load loads the given segments into memory to be executed on a kexec-reboot.
//
// It is assumed that segments is made up of the next kernel's code and text
// segments, and that `entry` is the entry point, either kernel entry point or trampoline.
//
// Load will align segments to page boundaries and deduplicate overlapping ranges.
func Load(entry uintptr, segments Segments, flags uint64) error {
segments, err := AlignAndMerge(segments)
if err != nil {
return fmt.Errorf("could not align segments: %w", err)
}
if !segments.PhysContains(entry) {
return fmt.Errorf("entry point %#v is not contained by any segment", entry)
}
return rawLoad(entry, segments, flags)
}
// ErrKexec is returned by Load if the kexec failed. It describes entry point,
// flags, errno and kernel layout.
type ErrKexec struct {
Entry uintptr
Segments []Segment
Flags uint64
Errno syscall.Errno
}
// Error implements error.
func (e ErrKexec) Error() string {
return fmt.Sprintf("kexec_load(entry=%#x, segments=%s, flags %#x) = errno %s", e.Entry, e.Segments, e.Flags, e.Errno)
}
// kexecSegment defines kernel memory layout.
type kexecSegment struct {
// Buf points to a buffer in user space.
Buf Range
// Phys is a physical address of kernel.
Phys Range
}
func (s Segment) toKexecSegment() kexecSegment {
if s.Buf == nil {
return kexecSegment{
Buf: Range{Start: 0, Size: 0},
Phys: s.Phys,
}
}
return kexecSegment{
Buf: Range{
Start: uintptr((unsafe.Pointer(&s.Buf[0]))),
Size: uint(len(s.Buf)),
},
Phys: s.Phys,
}
}
func (segs Segments) toKexecSegments() []kexecSegment {
var ks []kexecSegment
for _, seg := range segs {
ks = append(ks, seg.toKexecSegment())
}
return ks
}
// rawLoad is a wrapper around kexec_load(2) syscall.
// Preconditions:
// - segments must not overlap
// - segments must be full pages
func rawLoad(entry uintptr, segments Segments, flags uint64) error {
ks := segments.toKexecSegments()
_, _, errno := unix.Syscall6(
unix.SYS_KEXEC_LOAD,
entry,
uintptr(len(segments)),
uintptr(unsafe.Pointer(&ks[0])),
uintptr(flags),
0, 0)
// segments (and all the buffers therein) may have gotten freed after
// evaluating Syscall6 arguments, but before the syscall actually
// happens.
for _, seg := range segments {
runtime.KeepAlive(seg.Buf)
}
runtime.KeepAlive(segments)
if errno != 0 {
return ErrKexec{
Entry: entry,
Segments: segments,
Flags: flags,
Errno: errno,
}
}
return nil
}