3 * Supports bzImage, zImage and Image format.
5 * Based on work by Steve Gehlbach.
6 * Portions are taken from mkelfImage.
8 * 2003-09 by SONE Takeshi
12 #include "kernel/kernel.h"
13 #include "libopenbios/bindings.h"
14 #include "libopenbios/sys_info.h"
16 #include "libc/diskio.h"
21 #define strtoull_with_suffix strtol
23 #define LINUX_PARAM_LOC 0x90000
24 #define COMMAND_LINE_LOC 0x91000
25 #define GDT_LOC 0x92000
26 #define STACK_LOC 0x93000
28 #define E820MAX 32 /* number of entries in E820MAP */
30 unsigned long long addr
; /* start of memory segment */
31 unsigned long long size
; /* size of memory segment */
32 unsigned long type
; /* type of memory segment */
34 #define E820_RESERVED 2
35 #define E820_ACPI 3 /* usable as RAM once ACPI tables have been read */
39 /* The header of Linux/i386 kernel */
41 uint8_t reserved1
[0x1f1]; /* 0x000 */
42 uint8_t setup_sects
; /* 0x1f1 */
43 uint16_t root_flags
; /* 0x1f2 */
44 uint8_t reserved2
[6]; /* 0x1f4 */
45 uint16_t vid_mode
; /* 0x1fa */
46 uint16_t root_dev
; /* 0x1fc */
47 uint16_t boot_sector_magic
; /* 0x1fe */
49 uint8_t reserved3
[2]; /* 0x200 */
50 uint8_t header_magic
[4]; /* 0x202 */
51 uint16_t protocol_version
; /* 0x206 */
52 uint32_t realmode_swtch
; /* 0x208 */
53 uint16_t start_sys
; /* 0x20c */
54 uint16_t kver_addr
; /* 0x20e */
55 uint8_t type_of_loader
; /* 0x210 */
56 uint8_t loadflags
; /* 0x211 */
57 uint16_t setup_move_size
; /* 0x212 */
58 uint32_t code32_start
; /* 0x214 */
59 uint32_t ramdisk_image
; /* 0x218 */
60 uint32_t ramdisk_size
; /* 0x21c */
61 uint8_t reserved4
[4]; /* 0x220 */
63 uint16_t heap_end_ptr
; /* 0x224 */
64 uint8_t reserved5
[2]; /* 0x226 */
66 uint32_t cmd_line_ptr
; /* 0x228 */
68 uint32_t initrd_addr_max
; /* 0x22c */
69 } __attribute__ ((packed
));
72 /* Paramters passed to 32-bit part of Linux
73 * This is another view of the structure above.. */
75 uint8_t orig_x
; /* 0x00 */
76 uint8_t orig_y
; /* 0x01 */
77 uint16_t ext_mem_k
; /* 0x02 -- EXT_MEM_K sits here */
78 uint16_t orig_video_page
; /* 0x04 */
79 uint8_t orig_video_mode
; /* 0x06 */
80 uint8_t orig_video_cols
; /* 0x07 */
81 uint16_t unused2
; /* 0x08 */
82 uint16_t orig_video_ega_bx
; /* 0x0a */
83 uint16_t unused3
; /* 0x0c */
84 uint8_t orig_video_lines
; /* 0x0e */
85 uint8_t orig_video_isVGA
; /* 0x0f */
86 uint16_t orig_video_points
; /* 0x10 */
88 /* VESA graphic mode -- linear frame buffer */
89 uint16_t lfb_width
; /* 0x12 */
90 uint16_t lfb_height
; /* 0x14 */
91 uint16_t lfb_depth
; /* 0x16 */
92 uint32_t lfb_base
; /* 0x18 */
93 uint32_t lfb_size
; /* 0x1c */
94 uint16_t cl_magic
; /* 0x20 */
95 #define CL_MAGIC_VALUE 0xA33F
96 uint16_t cl_offset
; /* 0x22 */
97 uint16_t lfb_linelength
; /* 0x24 */
98 uint8_t red_size
; /* 0x26 */
99 uint8_t red_pos
; /* 0x27 */
100 uint8_t green_size
; /* 0x28 */
101 uint8_t green_pos
; /* 0x29 */
102 uint8_t blue_size
; /* 0x2a */
103 uint8_t blue_pos
; /* 0x2b */
104 uint8_t rsvd_size
; /* 0x2c */
105 uint8_t rsvd_pos
; /* 0x2d */
106 uint16_t vesapm_seg
; /* 0x2e */
107 uint16_t vesapm_off
; /* 0x30 */
108 uint16_t pages
; /* 0x32 */
109 uint8_t reserved4
[12]; /* 0x34 -- 0x3f reserved for future expansion */
111 //struct apm_bios_info apm_bios_info; /* 0x40 */
112 uint8_t apm_bios_info
[0x40];
113 //struct drive_info_struct drive_info; /* 0x80 */
114 uint8_t drive_info
[0x20];
115 //struct sys_desc_table sys_desc_table; /* 0xa0 */
116 uint8_t sys_desc_table
[0x140];
117 uint32_t alt_mem_k
; /* 0x1e0 */
118 uint8_t reserved5
[4]; /* 0x1e4 */
119 uint8_t e820_map_nr
; /* 0x1e8 */
120 uint8_t reserved6
[9]; /* 0x1e9 */
121 uint16_t mount_root_rdonly
; /* 0x1f2 */
122 uint8_t reserved7
[4]; /* 0x1f4 */
123 uint16_t ramdisk_flags
; /* 0x1f8 */
124 #define RAMDISK_IMAGE_START_MASK 0x07FF
125 #define RAMDISK_PROMPT_FLAG 0x8000
126 #define RAMDISK_LOAD_FLAG 0x4000
127 uint8_t reserved8
[2]; /* 0x1fa */
128 uint16_t orig_root_dev
; /* 0x1fc */
129 uint8_t reserved9
[1]; /* 0x1fe */
130 uint8_t aux_device_info
; /* 0x1ff */
131 uint8_t reserved10
[2]; /* 0x200 */
132 uint8_t param_block_signature
[4]; /* 0x202 */
133 uint16_t param_block_version
; /* 0x206 */
134 uint8_t reserved11
[8]; /* 0x208 */
135 uint8_t loader_type
; /* 0x210 */
136 #define LOADER_TYPE_LOADLIN 1
137 #define LOADER_TYPE_BOOTSECT_LOADER 2
138 #define LOADER_TYPE_SYSLINUX 3
139 #define LOADER_TYPE_ETHERBOOT 4
140 #define LOADER_TYPE_KERNEL 5
141 uint8_t loader_flags
; /* 0x211 */
142 uint8_t reserved12
[2]; /* 0x212 */
143 uint32_t kernel_start
; /* 0x214 */
144 uint32_t initrd_start
; /* 0x218 */
145 uint32_t initrd_size
; /* 0x21c */
146 uint8_t reserved12_5
[8]; /* 0x220 */
147 uint32_t cmd_line_ptr
; /* 0x228 */
148 uint8_t reserved13
[164]; /* 0x22c */
149 struct e820entry e820_map
[E820MAX
]; /* 0x2d0 */
150 uint8_t reserved16
[688]; /* 0x550 */
151 #define COMMAND_LINE_SIZE 256
152 /* Command line is copied here by 32-bit i386/kernel/head.S.
153 * So I will follow the boot protocol, rather than putting it
154 * directly here. --ts1 */
155 uint8_t command_line
[COMMAND_LINE_SIZE
]; /* 0x800 */
156 uint8_t reserved17
[1792]; /* 0x900 - 0x1000 */
159 static uint64_t forced_memsize
;
162 static unsigned long file_size(void)
164 long long fpos
, fsize
;
166 /* Save current position */
169 /* Go to end of file and get position */
173 /* Go back to old position */
180 /* Load the first part the file and check if it's Linux */
181 static uint32_t load_linux_header(struct linux_header
*hdr
)
186 if (read_io(fd
, hdr
, sizeof *hdr
) != sizeof *hdr
) {
187 debug("Can't read Linux header\n");
190 if (hdr
->boot_sector_magic
!= 0xaa55) {
191 debug("Not a Linux kernel image\n");
195 /* Linux is found. Print some information */
196 if (memcmp(hdr
->header_magic
, "HdrS", 4) != 0) {
197 /* This may be floppy disk image or something.
198 * Perform a simple (incomplete) sanity check. */
199 if (hdr
->setup_sects
>= 16
200 || file_size() - (hdr
->setup_sects
<<9) >= 512<<10) {
201 debug("This looks like a bootdisk image but not like Linux...\n");
205 printf("Possible very old Linux");
206 /* This kernel does not even have a protocol version.
207 * Force the value. */
208 hdr
->protocol_version
= 0; /* pre-2.00 */
210 printf("Found Linux");
211 if (hdr
->protocol_version
>= 0x200 && hdr
->kver_addr
) {
213 seek_io(fd
, hdr
->kver_addr
+ 0x200);
214 if (read_io(fd
, kver
, sizeof kver
) != 0) {
216 printf(" version %s", kver
);
219 debug(" (protocol %#x)", hdr
->protocol_version
);
221 if (hdr
->protocol_version
>= 0x200) {
222 debug(" (loadflags %#x)", hdr
->loadflags
);
223 load_high
= hdr
->loadflags
& 1;
227 kern_addr
= 0x100000;
229 printf(" zImage or Image");
237 /* Set up parameters for 32-bit kernel */
239 init_linux_params(struct linux_params
*params
, struct linux_header
*hdr
)
241 debug("Setting up paramters at %#lx\n", virt_to_phys(params
));
242 memset(params
, 0, sizeof *params
);
244 /* Copy some useful values from header */
245 params
->mount_root_rdonly
= hdr
->root_flags
;
246 params
->orig_root_dev
= hdr
->root_dev
;
249 * This assumes we have VGA in standard 80x25 text mode,
250 * just like our vga.c does.
251 * Cursor position is filled later to allow some more printf's. */
252 params
->orig_video_mode
= 3;
253 params
->orig_video_cols
= 80;
254 params
->orig_video_lines
= 25;
255 params
->orig_video_isVGA
= 1;
256 params
->orig_video_points
= 16;
258 params
->loader_type
= 0xff; /* Unregistered Linux loader */
263 set_memory_size(struct linux_params
*params
, struct sys_info
*info
)
268 struct e820entry
*linux_map
;
269 struct memrange
*filo_map
;
271 linux_map
= params
->e820_map
;
272 filo_map
= info
->memrange
;
273 for (i
= 0; i
< info
->n_memranges
; i
++, linux_map
++, filo_map
++) {
275 /* Convert to BIOS e820 style */
276 linux_map
->addr
= filo_map
->base
;
277 linux_map
->size
= filo_map
->size
;
278 linux_map
->type
= E820_RAM
;
279 debug("%016Lx - %016Lx\n", linux_map
->addr
,
280 linux_map
->addr
+ linux_map
->size
);
281 params
->e820_map_nr
= i
+1;
284 /* Find out top of RAM. XXX This ignores hole above 1MB */
285 end
= filo_map
->base
+ filo_map
->size
;
286 if (end
< (1ULL << 32)) { /* don't count memory above 4GB */
288 ramtop
= (uint32_t) end
;
291 debug("ramtop=%#x\n", ramtop
);
292 /* Size of memory above 1MB in KB */
293 params
->alt_mem_k
= (ramtop
- (1<<20)) >> 10;
294 /* old style, 64MB max */
295 if (ramtop
>= (64<<20))
296 params
->ext_mem_k
= (63<<10);
298 params
->ext_mem_k
= params
->alt_mem_k
;
299 debug("ext_mem_k=%d, alt_mem_k=%d\n", params
->ext_mem_k
, params
->alt_mem_k
);
304 * Some parameters, like initrd=<file>, are not passed to kernel,
305 * we are responsible to process them.
306 * Parameters for kernel are copied to kern_cmdline. Returns name of initrd.
308 static char *parse_command_line(const char *orig_cmdline
, char *kern_cmdline
)
310 const char *start
, *sep
, *end
, *val
;
321 *kern_cmdline
= '\0';
326 debug("original command line: \"%s\"\n", orig_cmdline
);
327 debug("kernel command line at %#lx\n", virt_to_phys(kern_cmdline
));
329 start
= orig_cmdline
;
330 while (*start
== ' ')
333 end
= strchr(start
, ' ');
335 end
= start
+ strlen(start
);
336 sep
= strchr(start
, '=');
337 if (!sep
|| sep
> end
)
340 if (len
>= sizeof(name
))
341 len
= sizeof(name
) - 1;
342 memcpy(name
, start
, len
);
353 /* Only initrd= and mem= are handled here. vga= is not,
354 * which I believe is a paramter to the realmode part of Linux,
355 * which we don't execute. */
356 if (strcmp(name
, "initrd") == 0) {
358 printf("Missing filename to initrd parameter\n");
360 initrd
= malloc(len
+ 1);
361 memcpy(initrd
, val
, len
);
363 debug("initrd=%s\n", initrd
);
365 /* Don't pass this to kernel */
367 } else if (strcmp(name
, "mem") == 0) {
369 printf("Missing value for mem parameter\n");
371 forced_memsize
= strtoull_with_suffix(val
, (char**)&val
, 0);
372 if (forced_memsize
== 0)
373 printf("Invalid mem option, ignored\n");
375 printf("Garbage after mem=<size>, ignored\n");
378 debug("mem=%llu\n", (unsigned long long)forced_memsize
);
380 /* mem= is for both loader and kernel */
386 /* Copy to kernel command line buffer */
388 kern_cmdline
[k_len
++] = ' '; /* put separator */
390 if (k_len
+ len
>= COMMAND_LINE_SIZE
) {
391 len
= COMMAND_LINE_SIZE
- k_len
- 1;
393 printf("Kernel command line is too long; truncated to "
394 "%d bytes\n", COMMAND_LINE_SIZE
-1);
398 memcpy(kern_cmdline
+ k_len
, start
, len
);
403 while (*start
== ' ')
406 kern_cmdline
[k_len
] = 0;
407 debug("kernel command line (%d bytes): \"%s\"\n", k_len
, kern_cmdline
);
412 /* Set command line location */
413 static void set_command_line_loc(struct linux_params
*params
,
414 struct linux_header
*hdr
)
416 if (hdr
->protocol_version
>= 0x202) {
418 params
->cmd_line_ptr
= COMMAND_LINE_LOC
;
421 params
->cl_magic
= CL_MAGIC_VALUE
;
422 params
->cl_offset
= COMMAND_LINE_LOC
- LINUX_PARAM_LOC
;
426 /* Load 32-bit part of kernel */
427 static int load_linux_kernel(struct linux_header
*hdr
, uint32_t kern_addr
)
429 uint32_t kern_offset
, kern_size
;
431 if (hdr
->setup_sects
== 0)
432 hdr
->setup_sects
= 4;
433 kern_offset
= (hdr
->setup_sects
+ 1) * 512;
434 seek_io(fd
, kern_offset
);
435 kern_size
= file_size() - kern_offset
;
436 debug("offset=%#x addr=%#x size=%#x\n", kern_offset
, kern_addr
, kern_size
);
440 printf("Attempt to load up to end of device as kernel; "
441 "specify the image size\n");
446 printf("Loading kernel... ");
447 if ((uint32_t)read_io(fd
, phys_to_virt(kern_addr
), kern_size
) != kern_size
) {
448 printf("Can't read kernel\n");
456 static int load_initrd(struct linux_header
*hdr
, uint32_t kern_end
,
457 struct linux_params
*params
, const char *initrd_file
)
460 uint32_t start
, end
, size
;
463 fd
= open_io(initrd_file
);
465 printf("Can't open initrd: %s\n", initrd_file
);
471 printf("Attempt to load up to end of device as initrd; "
472 "specify the image size\n");
480 /* Find out the kernel's restriction on how high the initrd can be
482 if (hdr
->protocol_version
>= 0x203)
483 max
= hdr
->initrd_addr_max
;
485 max
= 0x38000000; /* Hardcoded value for older kernels */
487 /* FILO itself is at the top of RAM. (relocated)
488 * So, try putting initrd just below us. */
489 end
= virt_to_phys(_start
);
493 /* If "mem=" option is given, we have to put the initrd within
494 * the specified range. */
495 if (forced_memsize
) {
496 forced
= forced_memsize
;
499 /* If the "mem=" is lower, it's easy */
503 /* Otherwise, see if we can put it above us */
504 if (virt_to_phys(_end
) + size
<= forced
)
505 end
= forced
; /* Ok */
510 start
&= ~0xfff; /* page align */
513 debug("start=%#x end=%#x\n", start
, end
);
515 if (start
< kern_end
) {
516 printf("Initrd is too big to fit in memory\n");
520 printf("Loading initrd... ");
521 if ((uint32_t)read_io(fd
, phys_to_virt(start
), size
) != size
) {
522 printf("Can't read initrd\n");
527 params
->initrd_start
= start
;
528 params
->initrd_size
= size
;
535 static void hardware_setup(void)
540 /* Make sure any coprocessor is properly reset.. */
544 /* we're getting screwed again and again by this problem of the 8259.
545 * so we're going to leave this lying around for inclusion into
546 * crt0.S on an as-needed basis.
548 * well, that went ok, I hope. Now we have to reprogram the interrupts :-(
549 * we put them right after the intel-reserved hardware interrupts, at
550 * int 0x20-0x2F. There they won't mess up anything. Sadly IBM really
551 * messed this up with the original PC, and they haven't been able to
552 * rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f,
553 * which is used for the internal hardware interrupts as well. We just
554 * have to reprogram the 8259's, and it isn't fun.
557 outb(0x11, 0x20); /* initialization sequence to 8259A-1 */
558 outb(0x11, 0xA0); /* and to 8259A-2 */
560 outb(0x20, 0x21); /* start of hardware int's (0x20) */
561 outb(0x28, 0xA1); /* start of hardware int's 2 (0x28) */
563 outb(0x04, 0x21); /* 8259-1 is master */
564 outb(0x02, 0xA1); /* 8259-2 is slave */
566 outb(0x01, 0x21); /* 8086 mode for both */
569 outb(0xFF, 0xA1); /* mask off all interrupts for now */
570 outb(0xFB, 0x21); /* mask all irq's but irq2 which is cascaded */
574 static int start_linux(uint32_t kern_addr
)
577 //extern int cursor_x, cursor_y;
579 ctx
= init_context(phys_to_virt(STACK_LOC
), 4096, 0);
583 ctx
->npc
= kern_addr
+ 4;
585 debug("pc=%#x\n", kern_addr
);
586 printf("Jumping to entry point...\n");
589 /* Update VGA cursor position.
590 * This must be here because the printf changes the value! */
591 params
->orig_x
= cursor_x
;
592 params
->orig_y
= cursor_y
;
596 ctx
= switch_to(ctx
);
598 /* It's impossible but... */
599 printf("Returned with o0=%#llx\n", ctx
->regs
[REG_O0
]);
601 return ctx
->regs
[REG_O0
];
604 int linux_load(struct sys_info
*info
, const char *file
, const char *cmdline
)
606 struct linux_header hdr
;
607 struct linux_params
*params
;
608 uint32_t kern_addr
, kern_size
;
609 char *initrd_file
= NULL
;
615 kern_addr
= load_linux_header(&hdr
);
616 if (kern_addr
== 0) {
618 return LOADER_NOT_SUPPORT
;
621 debug("[sparc64] Booting kernel '%s' ", file
);
623 debug("with parameters '%s'\n", cmdline
);
625 debug("without parameters.\n");
627 params
= phys_to_virt(LINUX_PARAM_LOC
);
628 init_linux_params(params
, &hdr
);
629 set_memory_size(params
, info
);
630 initrd_file
= parse_command_line(cmdline
, phys_to_virt(COMMAND_LINE_LOC
));
631 set_command_line_loc(params
, &hdr
);
633 kern_size
= load_linux_kernel(&hdr
, kern_addr
);
634 if (kern_size
== 0) {
641 if (load_initrd(&hdr
, kern_addr
+kern_size
, params
, initrd_file
)
651 start_linux(kern_addr
);