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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
|
/*
* uImage support for PowerPC
*/
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/types.h>
#include <image.h>
#include <getopt.h>
#include <arch/options.h>
#include "../../kexec.h"
#include "../../kexec-syscall.h"
#include "kexec-ppc.h"
#include "fixup_dtb.h"
#include <kexec-uImage.h>
#include "crashdump-powerpc.h"
#include <limits.h>
int create_flatten_tree(struct kexec_info *, unsigned char **, unsigned long *,
char *);
/* See options.h -- add any more there, too. */
static const struct option options[] = {
KEXEC_ARCH_OPTIONS
{"command-line", 1, 0, OPT_APPEND},
{"append", 1, 0, OPT_APPEND},
{"ramdisk", 1, 0, OPT_RAMDISK},
{"initrd", 1, 0, OPT_RAMDISK},
{"dtb", 1, 0, OPT_DTB},
{"reuse-node", 1, 0, OPT_NODES},
{0, 0, 0, 0},
};
static const char short_options[] = KEXEC_ARCH_OPT_STR;
void uImage_ppc_usage(void)
{
printf(
" --command-line=STRING Set the kernel command line to STRING.\n"
" --append=STRING Set the kernel command line to STRING.\n"
" --ramdisk=<filename> Initial RAM disk.\n"
" --initrd=<filename> same as --ramdisk\n"
" --dtb=<filename> Specify device tree blob file.\n"
" --reuse-node=node Specify nodes which should be taken from /proc/device-tree.\n"
" Can be set multiple times.\n"
);
}
int uImage_ppc_probe(const char *buf, off_t len)
{
return uImage_probe(buf, len, IH_ARCH_PPC);
}
static int ppc_load_bare_bits(int argc, char **argv, const char *buf,
off_t len, struct kexec_info *info, unsigned int load_addr,
unsigned int ep)
{
char *command_line, *cmdline_buf, *crash_cmdline;
int command_line_len;
char *dtb;
unsigned int addr;
unsigned long dtb_addr;
unsigned long dtb_addr_actual;
#define FIXUP_ENTRYS (20)
char *fixup_nodes[FIXUP_ENTRYS + 1];
int cur_fixup = 0;
int opt;
int ret;
char *seg_buf = NULL;
off_t seg_size = 0;
unsigned long long hole_addr;
unsigned long max_addr;
char *blob_buf = NULL;
off_t blob_size = 0;
cmdline_buf = NULL;
command_line = NULL;
dtb = NULL;
max_addr = LONG_MAX;
while ((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
switch (opt) {
default:
/* Ignore core options */
if (opt < OPT_ARCH_MAX) {
break;
}
case '?':
usage();
return -1;
case OPT_APPEND:
command_line = optarg;
break;
case OPT_RAMDISK:
ramdisk = optarg;
break;
case OPT_DTB:
dtb = optarg;
break;
case OPT_NODES:
if (cur_fixup >= FIXUP_ENTRYS) {
fprintf(stderr, "The number of entries for the fixup is too large\n");
exit(1);
}
fixup_nodes[cur_fixup] = optarg;
cur_fixup++;
break;
}
}
if (ramdisk && reuse_initrd)
die("Can't specify --ramdisk or --initrd with --reuseinitrd\n");
command_line_len = 0;
if (command_line) {
command_line_len = strlen(command_line) + 1;
} else {
command_line = get_command_line();
command_line_len = strlen(command_line) + 1;
}
fixup_nodes[cur_fixup] = NULL;
/*
* len contains the length of the whole kernel image except the bss
* section. The 1 MiB should cover it. The purgatory and the dtb are
* allocated from memtop down towards zero so we should never get too
* close to the bss :)
*/
ret = valid_memory_range(info, load_addr, load_addr + (len + (1 * 1024 * 1024)));
if (!ret) {
printf("Can't add kernel to addr 0x%08x len %ld\n",
load_addr, len + (1 * 1024 * 1024));
return -1;
}
add_segment(info, buf, len, load_addr, len + (1 * 1024 * 1024));
if (info->kexec_flags & KEXEC_ON_CRASH) {
crash_cmdline = xmalloc(COMMAND_LINE_SIZE);
memset((void *)crash_cmdline, 0, COMMAND_LINE_SIZE);
} else
crash_cmdline = NULL;
if (info->kexec_flags & KEXEC_ON_CRASH) {
ret = load_crashdump_segments(info, crash_cmdline,
max_addr, 0);
if (ret < 0) {
return -1;
}
}
cmdline_buf = xmalloc(COMMAND_LINE_SIZE);
memset((void *)cmdline_buf, 0, COMMAND_LINE_SIZE);
if (command_line)
strncat(cmdline_buf, command_line, command_line_len);
if (crash_cmdline)
strncat(cmdline_buf, crash_cmdline,
sizeof(crash_cmdline) -
strlen(crash_cmdline) - 1);
elf_rel_build_load(info, &info->rhdr, (const char *)purgatory,
purgatory_size, 0, -1, -1, 0);
/* Here we need to initialize the device tree, and find out where
* it is going to live so we can place it directly after the
* kernel image */
if (dtb) {
/* Grab device tree from buffer */
blob_buf = slurp_file(dtb, &blob_size);
} else {
create_flatten_tree(info, (unsigned char **)&blob_buf,
(unsigned long *)&blob_size, cmdline_buf);
}
if (!blob_buf || !blob_size)
die("Device tree seems to be an empty file.\n");
/* initial fixup for device tree */
blob_buf = fixup_dtb_init(info, blob_buf, &blob_size, load_addr, &dtb_addr);
if (ramdisk) {
seg_buf = slurp_file(ramdisk, &seg_size);
/* Load ramdisk at top of memory */
hole_addr = add_buffer(info, seg_buf, seg_size, seg_size,
0, dtb_addr + blob_size, max_addr, -1);
ramdisk_base = hole_addr;
ramdisk_size = seg_size;
}
if (reuse_initrd) {
ramdisk_base = initrd_base;
ramdisk_size = initrd_size;
}
if (info->kexec_flags & KEXEC_ON_CRASH && ramdisk_base != 0) {
if ( (ramdisk_base < crash_base) ||
(ramdisk_base > crash_base + crash_size) ) {
printf("WARNING: ramdisk is above crashkernel region!\n");
}
else if (ramdisk_base + ramdisk_size > crash_base + crash_size) {
printf("WARNING: ramdisk overflows crashkernel region!\n");
}
}
/* Perform final fixup on devie tree, i.e. everything beside what
* was done above */
fixup_dtb_finalize(info, blob_buf, &blob_size, fixup_nodes,
cmdline_buf);
dtb_addr_actual = add_buffer(info, blob_buf, blob_size, blob_size, 0, dtb_addr,
load_addr + KERNEL_ACCESS_TOP, 1);
if (dtb_addr_actual != dtb_addr) {
printf("dtb_addr_actual: %lx, dtb_addr: %lx\n", dtb_addr_actual, dtb_addr);
die("Error device tree not loadded to address it was expecting to be loaded too!\n");
}
/* set various variables for the purgatory */
addr = ep;
elf_rel_set_symbol(&info->rhdr, "kernel", &addr, sizeof(addr));
addr = dtb_addr;
elf_rel_set_symbol(&info->rhdr, "dt_offset", &addr, sizeof(addr));
#define PUL_STACK_SIZE (16 * 1024)
addr = locate_hole(info, PUL_STACK_SIZE, 0, 0, -1, 1);
addr += PUL_STACK_SIZE;
elf_rel_set_symbol(&info->rhdr, "stack", &addr, sizeof(addr));
/* No allocation past here in order not to overwrite the stack */
#undef PUL_STACK_SIZE
addr = elf_rel_get_addr(&info->rhdr, "purgatory_start");
info->entry = (void *)addr;
return 0;
}
int uImage_ppc_load(int argc, char **argv, const char *buf, off_t len,
struct kexec_info *info)
{
struct Image_info img;
int ret;
ret = uImage_load(buf, len, &img);
if (ret)
return ret;
return ppc_load_bare_bits(argc, argv, img.buf, img.len, info,
img.base, img.ep);
}
|