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

wdenk's avatar
wdenk committed
8
/*
Graeme Russ's avatar
Graeme Russ committed
9
 * Linux x86 zImage and bzImage loading
wdenk's avatar
wdenk committed
10 11
 *
 * based on the procdure described in
12 13 14 15
 * linux/Documentation/i386/boot.txt
 */

#include <common.h>
16
#include <command.h>
Simon Glass's avatar
Simon Glass committed
17
#include <env.h>
18
#include <irq_func.h>
19
#include <malloc.h>
20
#include <acpi/acpi_table.h>
21 22 23 24
#include <asm/io.h>
#include <asm/ptrace.h>
#include <asm/zimage.h>
#include <asm/byteorder.h>
25
#include <asm/bootm.h>
26
#include <asm/bootparam.h>
27 28 29
#ifdef CONFIG_SYS_COREBOOT
#include <asm/arch/timestamp.h>
#endif
30
#include <linux/compiler.h>
31
#include <linux/libfdt.h>
32 33 34

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

46
#define COMMAND_LINE_SIZE	2048
47 48 49 50

static void build_command_line(char *command_line, int auto_boot)
{
	char *env_command_line;
wdenk's avatar
wdenk committed
51

52
	command_line[0] = '\0';
wdenk's avatar
wdenk committed
53

54
	env_command_line =  env_get("bootargs");
wdenk's avatar
wdenk committed
55

56
	/* set console= argument if we use a serial console */
57
	if (!strstr(env_command_line, "console=")) {
58
		if (!strcmp(env_get("stdout"), "serial")) {
wdenk's avatar
wdenk committed
59

60
			/* We seem to use serial console */
wdenk's avatar
wdenk committed
61
			sprintf(command_line, "console=ttyS0,%s ",
62
				env_get("baudrate"));
63 64
		}
	}
wdenk's avatar
wdenk committed
65

66
	if (auto_boot)
67
		strcat(command_line, "auto ");
wdenk's avatar
wdenk committed
68

69
	if (env_command_line)
70
		strcat(command_line, env_command_line);
wdenk's avatar
wdenk committed
71

72 73 74
	printf("Kernel command line: \"%s\"\n", command_line);
}

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

88 89 90
static int get_boot_protocol(struct setup_header *hdr)
{
	if (hdr->header == KERNEL_V2_MAGIC) {
91
		printf("Magic signature found\n");
92
		return hdr->version;
93 94
	} else {
		/* Very old kernel */
95
		printf("Magic signature not found\n");
96
		return 0x0100;
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
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;
}

132
struct boot_params *load_zimage(char *image, unsigned long kernel_size,
133
				ulong *load_addressp)
134 135 136 137 138 139 140 141 142 143 144 145 146 147
{
	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;
wdenk's avatar
wdenk committed
148

149
	/* determine size of setup */
150 151
	if (0 == hdr->setup_sects) {
		printf("Setup Sectors = 0 (defaulting to 4)\n");
152 153
		setup_size = 5 * 512;
	} else {
154
		setup_size = (hdr->setup_sects + 1) * 512;
155
	}
wdenk's avatar
wdenk committed
156

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

159
	if (setup_size > SETUP_MAX_SIZE)
160
		printf("Error: Setup is too large (%d bytes)\n", setup_size);
wdenk's avatar
wdenk committed
161

162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
	/* 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");
		}
	}

179
	/* Determine image type */
180 181
	big_image = (bootproto >= 0x0200) &&
		    (hdr->loadflags & BIG_KERNEL_FLAG);
wdenk's avatar
wdenk committed
182

183
	/* Determine load address */
184
	if (big_image)
185
		*load_addressp = BZIMAGE_LOAD_ADDR;
186
	else
187
		*load_addressp = ZIMAGE_LOAD_ADDR;
wdenk's avatar
wdenk committed
188

189 190 191
	printf("Building boot_params at 0x%8.8lx\n", (ulong)setup_base);
	memset(setup_base, 0, sizeof(*setup_base));
	setup_base->hdr = params->hdr;
wdenk's avatar
wdenk committed
192

193 194 195 196
	if (bootproto >= 0x0204)
		kernel_size = hdr->syssize * 16;
	else
		kernel_size -= setup_size;
wdenk's avatar
wdenk committed
197 198

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

207
			/* Copy the command line */
208
			memmove((void *)0x99000,
209
				(u8 *)setup_base + COMMAND_LINE_OFFSET,
210
				COMMAND_LINE_SIZE);
wdenk's avatar
wdenk committed
211

212
			 /* Relocated */
213
			setup_base = (struct boot_params *)0x90000;
214
		}
wdenk's avatar
wdenk committed
215

216
		/* It is recommended to clear memory up to the 32K mark */
217 218
		memset((u8 *)0x90000 + setup_size, 0,
		       SETUP_MAX_SIZE - setup_size);
219
	}
wdenk's avatar
wdenk committed
220

221 222 223 224 225 226 227 228 229 230 231 232 233
	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;
	}

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

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

	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);
250

251 252 253 254
	if (bootproto == 0x0100) {
		setup_base->screen_info.cl_magic = COMMAND_LINE_MAGIC;
		setup_base->screen_info.cl_offset = COMMAND_LINE_OFFSET;
	}
255
	if (bootproto >= 0x0200) {
256 257
		hdr->type_of_loader = 8;

258
		if (initrd_addr) {
259 260
			printf("Initial RAM disk at linear address "
			       "0x%08lx, size %ld bytes\n",
261
			       initrd_addr, initrd_size);
wdenk's avatar
wdenk committed
262

263 264
			hdr->ramdisk_image = initrd_addr;
			hdr->ramdisk_size = initrd_size;
265 266
		}
	}
wdenk's avatar
wdenk committed
267

268
	if (bootproto >= 0x0201) {
269 270
		hdr->heap_end_ptr = HEAP_END_OFFSET;
		hdr->loadflags |= HEAP_FLAG;
271
	}
wdenk's avatar
wdenk committed
272

273 274 275 276 277 278 279 280 281 282
	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;
		}
283

284 285
		/* build command line at COMMAND_LINE_OFFSET */
		build_command_line(cmd_line, auto_boot);
286 287
	}

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

293 294 295 296
#ifdef CONFIG_GENERATE_ACPI_TABLE
	setup_base->acpi_rsdp_addr = acpi_get_rsdp_addr();
#endif

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

300 301 302 303
#ifdef CONFIG_EFI_STUB
	setup_efi_info(&setup_base->efi_info);
#endif

304
	return 0;
305 306
}

307
int do_zboot(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
308
{
309
	struct boot_params *base_ptr;
Graeme Russ's avatar
Graeme Russ committed
310
	void *bzImage_addr = NULL;
311
	ulong load_address;
Graeme Russ's avatar
Graeme Russ committed
312
	char *s;
313
	ulong bzImage_size = 0;
314 315
	ulong initrd_addr = 0;
	ulong initrd_size = 0;
316 317 318

	disable_interrupts();

319
	if (argc >= 2) {
Graeme Russ's avatar
Graeme Russ committed
320 321
		/* argv[1] holds the address of the bzImage */
		s = argv[1];
322
	} else {
323
		s = env_get("fileaddr");
324
	}
Graeme Russ's avatar
Graeme Russ committed
325 326 327

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

329
	if (argc >= 3) {
Graeme Russ's avatar
Graeme Russ committed
330
		/* argv[2] holds the size of the bzImage */
331
		bzImage_size = simple_strtoul(argv[2], NULL, 16);
332 333 334 335 336 337
	}

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

339
	/* Lets look for */
340
	base_ptr = load_zimage(bzImage_addr, bzImage_size, &load_address);
341

342
	if (!base_ptr) {
343
		puts("## Kernel loading failed ...\n");
344
		return -1;
345
	}
346
	if (setup_zimage(base_ptr, (char *)base_ptr + COMMAND_LINE_OFFSET,
347
			0, initrd_addr, initrd_size)) {
348
		puts("Setting up boot parameters failed ...\n");
349 350 351 352
		return -1;
	}

	/* we assume that the kernel is in place */
353
	return boot_linux_kernel((ulong)base_ptr, load_address, false);
354 355 356
}

U_BOOT_CMD(
357
	zboot, 5, 0,	do_zboot,
358
	"Boot bzImage",
359 360 361 362 363 364 365 366
	"[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"
367
);