zimage.c 8.9 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0+
W
wdenk 已提交
2
/*
3
 * Copyright (c) 2011 The Chromium OS Authors.
W
wdenk 已提交
4
 * (C) Copyright 2002
A
Albert ARIBAUD 已提交
5
 * Daniel Engström, Omicron Ceti AB, <daniel@omicron.se>
W
wdenk 已提交
6 7
 */

W
wdenk 已提交
8
/*
G
Graeme Russ 已提交
9
 * Linux x86 zImage and bzImage loading
W
wdenk 已提交
10 11
 *
 * based on the procdure described in
W
wdenk 已提交
12 13 14 15
 * linux/Documentation/i386/boot.txt
 */

#include <common.h>
S
Simon Glass 已提交
16
#include <env.h>
17
#include <malloc.h>
18
#include <asm/acpi_table.h>
W
wdenk 已提交
19 20 21 22
#include <asm/io.h>
#include <asm/ptrace.h>
#include <asm/zimage.h>
#include <asm/byteorder.h>
23
#include <asm/bootm.h>
24
#include <asm/bootparam.h>
25 26 27
#ifdef CONFIG_SYS_COREBOOT
#include <asm/arch/timestamp.h>
#endif
28
#include <linux/compiler.h>
29
#include <linux/libfdt.h>
W
wdenk 已提交
30 31 32

/*
 * Memory lay-out:
W
wdenk 已提交
33
 *
W
wdenk 已提交
34
 * relative to setup_base (which is 0x90000 currently)
W
wdenk 已提交
35 36
 *
 *	0x0000-0x7FFF	Real mode kernel
W
wdenk 已提交
37 38 39
 *	0x8000-0x8FFF	Stack and heap
 *	0x9000-0x90FF	Kernel command line
 */
40 41 42
#define DEFAULT_SETUP_BASE	0x90000
#define COMMAND_LINE_OFFSET	0x9000
#define HEAP_END_OFFSET		0x8e00
W
wdenk 已提交
43

44
#define COMMAND_LINE_SIZE	2048
W
wdenk 已提交
45 46 47 48

static void build_command_line(char *command_line, int auto_boot)
{
	char *env_command_line;
W
wdenk 已提交
49

W
wdenk 已提交
50
	command_line[0] = '\0';
W
wdenk 已提交
51

52
	env_command_line =  env_get("bootargs");
W
wdenk 已提交
53

W
wdenk 已提交
54
	/* set console= argument if we use a serial console */
55
	if (!strstr(env_command_line, "console=")) {
56
		if (!strcmp(env_get("stdout"), "serial")) {
W
wdenk 已提交
57

W
wdenk 已提交
58
			/* We seem to use serial console */
W
wdenk 已提交
59
			sprintf(command_line, "console=ttyS0,%s ",
60
				env_get("baudrate"));
W
wdenk 已提交
61 62
		}
	}
W
wdenk 已提交
63

64
	if (auto_boot)
W
wdenk 已提交
65
		strcat(command_line, "auto ");
W
wdenk 已提交
66

67
	if (env_command_line)
W
wdenk 已提交
68
		strcat(command_line, env_command_line);
W
wdenk 已提交
69

W
wdenk 已提交
70 71 72
	printf("Kernel command line: \"%s\"\n", command_line);
}

73
static int kernel_magic_ok(struct setup_header *hdr)
W
wdenk 已提交
74
{
75
	if (KERNEL_MAGIC != hdr->boot_flag) {
76 77 78
		printf("Error: Invalid Boot Flag "
			"(found 0x%04x, expected 0x%04x)\n",
			hdr->boot_flag, KERNEL_MAGIC);
W
wdenk 已提交
79
		return 0;
80 81
	} else {
		printf("Valid Boot Flag\n");
82
		return 1;
W
wdenk 已提交
83
	}
84
}
W
wdenk 已提交
85

86 87 88
static int get_boot_protocol(struct setup_header *hdr)
{
	if (hdr->header == KERNEL_V2_MAGIC) {
89
		printf("Magic signature found\n");
90
		return hdr->version;
W
wdenk 已提交
91 92
	} else {
		/* Very old kernel */
93
		printf("Magic signature not found\n");
94
		return 0x0100;
W
wdenk 已提交
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
static int setup_device_tree(struct setup_header *hdr, const void *fdt_blob)
{
	int bootproto = get_boot_protocol(hdr);
	struct setup_data *sd;
	int size;

	if (bootproto < 0x0209)
		return -ENOTSUPP;

	if (!fdt_blob)
		return 0;

	size = fdt_totalsize(fdt_blob);
	if (size < 0)
		return -EINVAL;

	size += sizeof(struct setup_data);
	sd = (struct setup_data *)malloc(size);
	if (!sd) {
		printf("Not enough memory for DTB setup data\n");
		return -ENOMEM;
	}

	sd->next = hdr->setup_data;
	sd->type = SETUP_DTB;
	sd->len = fdt_totalsize(fdt_blob);
	memcpy(sd->data, fdt_blob, sd->len);
	hdr->setup_data = (unsigned long)sd;

	return 0;
}

130
struct boot_params *load_zimage(char *image, unsigned long kernel_size,
131
				ulong *load_addressp)
132 133 134 135 136 137 138 139 140 141 142 143 144 145
{
	struct boot_params *setup_base;
	int setup_size;
	int bootproto;
	int big_image;

	struct boot_params *params = (struct boot_params *)image;
	struct setup_header *hdr = &params->hdr;

	/* base address for real-mode segment */
	setup_base = (struct boot_params *)DEFAULT_SETUP_BASE;

	if (!kernel_magic_ok(hdr))
		return 0;
W
wdenk 已提交
146

W
wdenk 已提交
147
	/* determine size of setup */
148 149
	if (0 == hdr->setup_sects) {
		printf("Setup Sectors = 0 (defaulting to 4)\n");
W
wdenk 已提交
150 151
		setup_size = 5 * 512;
	} else {
152
		setup_size = (hdr->setup_sects + 1) * 512;
W
wdenk 已提交
153
	}
W
wdenk 已提交
154

155 156
	printf("Setup Size = 0x%8.8lx\n", (ulong)setup_size);

157
	if (setup_size > SETUP_MAX_SIZE)
W
wdenk 已提交
158
		printf("Error: Setup is too large (%d bytes)\n", setup_size);
W
wdenk 已提交
159

160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
	/* determine boot protocol version */
	bootproto = get_boot_protocol(hdr);

	printf("Using boot protocol version %x.%02x\n",
	       (bootproto & 0xff00) >> 8, bootproto & 0xff);

	if (bootproto >= 0x0200) {
		if (hdr->setup_sects >= 15) {
			printf("Linux kernel version %s\n",
				(char *)params +
				hdr->kernel_version + 0x200);
		} else {
			printf("Setup Sectors < 15 - "
				"Cannot print kernel version.\n");
		}
	}

W
wdenk 已提交
177
	/* Determine image type */
178 179
	big_image = (bootproto >= 0x0200) &&
		    (hdr->loadflags & BIG_KERNEL_FLAG);
W
wdenk 已提交
180

181
	/* Determine load address */
182
	if (big_image)
183
		*load_addressp = BZIMAGE_LOAD_ADDR;
184
	else
185
		*load_addressp = ZIMAGE_LOAD_ADDR;
W
wdenk 已提交
186

187 188 189
	printf("Building boot_params at 0x%8.8lx\n", (ulong)setup_base);
	memset(setup_base, 0, sizeof(*setup_base));
	setup_base->hdr = params->hdr;
W
wdenk 已提交
190

191 192 193 194
	if (bootproto >= 0x0204)
		kernel_size = hdr->syssize * 16;
	else
		kernel_size -= setup_size;
W
wdenk 已提交
195 196

	if (bootproto == 0x0100) {
197 198 199 200
		/*
		 * A very old kernel MUST have its real-mode code
		 * loaded at 0x90000
		 */
201
		if ((ulong)setup_base != 0x90000) {
W
wdenk 已提交
202
			/* Copy the real-mode kernel */
203 204
			memmove((void *)0x90000, setup_base, setup_size);

W
wdenk 已提交
205
			/* Copy the command line */
206
			memmove((void *)0x99000,
207
				(u8 *)setup_base + COMMAND_LINE_OFFSET,
208
				COMMAND_LINE_SIZE);
W
wdenk 已提交
209

210
			 /* Relocated */
211
			setup_base = (struct boot_params *)0x90000;
W
wdenk 已提交
212
		}
W
wdenk 已提交
213

W
wdenk 已提交
214
		/* It is recommended to clear memory up to the 32K mark */
215 216
		memset((u8 *)0x90000 + setup_size, 0,
		       SETUP_MAX_SIZE - setup_size);
W
wdenk 已提交
217
	}
W
wdenk 已提交
218

219 220 221 222 223 224 225 226 227 228 229 230 231
	if (big_image) {
		if (kernel_size > BZIMAGE_MAX_SIZE) {
			printf("Error: bzImage kernel too big! "
				"(size: %ld, max: %d)\n",
				kernel_size, BZIMAGE_MAX_SIZE);
			return 0;
		}
	} else if ((kernel_size) > ZIMAGE_MAX_SIZE) {
		printf("Error: zImage kernel too big! (size: %ld, max: %d)\n",
		       kernel_size, ZIMAGE_MAX_SIZE);
		return 0;
	}

232 233
	printf("Loading %s at address %lx (%ld bytes)\n",
	       big_image ? "bzImage" : "zImage", *load_addressp, kernel_size);
234

235
	memmove((void *)*load_addressp, image + setup_size, kernel_size);
236 237 238 239 240 241 242 243 244 245 246 247

	return setup_base;
}

int setup_zimage(struct boot_params *setup_base, char *cmd_line, int auto_boot,
		 unsigned long initrd_addr, unsigned long initrd_size)
{
	struct setup_header *hdr = &setup_base->hdr;
	int bootproto = get_boot_protocol(hdr);

	setup_base->e820_entries = install_e820_map(
		ARRAY_SIZE(setup_base->e820_map), setup_base->e820_map);
248

249 250 251 252
	if (bootproto == 0x0100) {
		setup_base->screen_info.cl_magic = COMMAND_LINE_MAGIC;
		setup_base->screen_info.cl_offset = COMMAND_LINE_OFFSET;
	}
W
wdenk 已提交
253
	if (bootproto >= 0x0200) {
254 255
		hdr->type_of_loader = 8;

W
wdenk 已提交
256
		if (initrd_addr) {
257 258
			printf("Initial RAM disk at linear address "
			       "0x%08lx, size %ld bytes\n",
W
wdenk 已提交
259
			       initrd_addr, initrd_size);
W
wdenk 已提交
260

261 262
			hdr->ramdisk_image = initrd_addr;
			hdr->ramdisk_size = initrd_size;
W
wdenk 已提交
263 264
		}
	}
W
wdenk 已提交
265

W
wdenk 已提交
266
	if (bootproto >= 0x0201) {
267 268
		hdr->heap_end_ptr = HEAP_END_OFFSET;
		hdr->loadflags |= HEAP_FLAG;
W
wdenk 已提交
269
	}
W
wdenk 已提交
270

271 272 273 274 275 276 277 278 279 280
	if (cmd_line) {
		if (bootproto >= 0x0202) {
			hdr->cmd_line_ptr = (uintptr_t)cmd_line;
		} else if (bootproto >= 0x0200) {
			setup_base->screen_info.cl_magic = COMMAND_LINE_MAGIC;
			setup_base->screen_info.cl_offset =
				(uintptr_t)cmd_line - (uintptr_t)setup_base;

			hdr->setup_move_size = 0x9100;
		}
281

282 283
		/* build command line at COMMAND_LINE_OFFSET */
		build_command_line(cmd_line, auto_boot);
W
wdenk 已提交
284 285
	}

286 287 288 289 290
#ifdef CONFIG_INTEL_MID
	if (bootproto >= 0x0207)
		hdr->hardware_subarch = X86_SUBARCH_INTEL_MID;
#endif

291 292 293 294
#ifdef CONFIG_GENERATE_ACPI_TABLE
	setup_base->acpi_rsdp_addr = acpi_get_rsdp_addr();
#endif

295
	setup_device_tree(hdr, (const void *)env_get_hex("fdtaddr", 0));
296 297
	setup_video(&setup_base->screen_info);

298 299 300 301
#ifdef CONFIG_EFI_STUB
	setup_efi_info(&setup_base->efi_info);
#endif

302
	return 0;
W
wdenk 已提交
303 304
}

305 306 307 308 309 310 311
void setup_pcat_compatibility(void)
	__attribute__((weak, alias("__setup_pcat_compatibility")));

void __setup_pcat_compatibility(void)
{
}

312
int do_zboot(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
313
{
314
	struct boot_params *base_ptr;
G
Graeme Russ 已提交
315
	void *bzImage_addr = NULL;
316
	ulong load_address;
G
Graeme Russ 已提交
317
	char *s;
318
	ulong bzImage_size = 0;
319 320
	ulong initrd_addr = 0;
	ulong initrd_size = 0;
321 322 323 324 325 326

	disable_interrupts();

	/* Setup board for maximum PC/AT Compatibility */
	setup_pcat_compatibility();

327
	if (argc >= 2) {
G
Graeme Russ 已提交
328 329
		/* argv[1] holds the address of the bzImage */
		s = argv[1];
330
	} else {
331
		s = env_get("fileaddr");
332
	}
G
Graeme Russ 已提交
333 334 335

	if (s)
		bzImage_addr = (void *)simple_strtoul(s, NULL, 16);
336

337
	if (argc >= 3) {
G
Graeme Russ 已提交
338
		/* argv[2] holds the size of the bzImage */
339
		bzImage_size = simple_strtoul(argv[2], NULL, 16);
340 341 342 343 344 345
	}

	if (argc >= 4)
		initrd_addr = simple_strtoul(argv[3], NULL, 16);
	if (argc >= 5)
		initrd_size = simple_strtoul(argv[4], NULL, 16);
346

347
	/* Lets look for */
348
	base_ptr = load_zimage(bzImage_addr, bzImage_size, &load_address);
349

350
	if (!base_ptr) {
351
		puts("## Kernel loading failed ...\n");
352
		return -1;
353
	}
354
	if (setup_zimage(base_ptr, (char *)base_ptr + COMMAND_LINE_OFFSET,
355
			0, initrd_addr, initrd_size)) {
356
		puts("Setting up boot parameters failed ...\n");
357 358 359 360
		return -1;
	}

	/* we assume that the kernel is in place */
361
	return boot_linux_kernel((ulong)base_ptr, load_address, false);
362 363 364
}

U_BOOT_CMD(
365
	zboot, 5, 0,	do_zboot,
366
	"Boot bzImage",
367 368 369 370 371 372 373 374
	"[addr] [size] [initrd addr] [initrd size]\n"
	"      addr -        The optional starting address of the bzimage.\n"
	"                    If not set it defaults to the environment\n"
	"                    variable \"fileaddr\".\n"
	"      size -        The optional size of the bzimage. Defaults to\n"
	"                    zero.\n"
	"      initrd addr - The address of the initrd image to use, if any.\n"
	"      initrd size - The size of the initrd image to use, if any.\n"
375
);