Multiboot -kernel support
PATCH 8/9 Multiboot -kernel support
To boot Mac OS X a mach bootloader has to be run. David Elliot modified the original i386 Mac OS X BIOS based bootloader to emulate the EFI entries and support multiboot, an easy but straight-forward kernel loading interface founded by grub. To boot multiboot-compliant kernels directly, this adds support for multiboot loading to the -kernel option, if a Linux kernel was not found.
hw/pc.c +229 -4
@@ -493,6 +493,226 @@ static long get_file_size(FILE *f)return size; } +/* Generate an initial boot sector which sets state and jump to +a specified vector */ +static void generate_bootsect_multiboot (uint32_t mh_entry_addr,uint32_t bootinfo) +{ +uint8_t bootsect[512 ], *p, *pgdt; +uint32_t ip; +int i; +int hda; + + hda =drive_get_index (IF_IDE ,0 ,0 ); +if (hda ==-1 ) { +fprintf (stderr,"A disk image must be given for 'hda' when booting " +"a Multiboot kernel\n" ); +exit (1 ); +/* Copy the MSDOS partition table if possible */ + } + +memset (bootsect,0 ,sizeof (bootsect)); +bdrv_read (drives_table[hda].bdrv,0 , bootsect,1 ); + +/* Make sure we have a partition signature */ + bootsect[510 ] =0x55 ; + bootsect[511 ] =0xaa ; + +/* Actual code */ + p = bootsect; + *p++ =0xfa ;/* CLI */ + *p++ =0xfc ;/* CLD */ + +// 660f011528000000 lgdt [0x28] + *p++ =0x66 ;/* 32-bit operand size */ + *p++ =0x67 ;/* 32-bit addr size */ + *p++ =0x0f ;/* LGDT [0x128] */ + *p++ =0x01 ; + *p++ =0x15 ; + pgdt=p;/* we calculate the gdt position later */ + p+=4 ; + +/* get us to protected mode now */ + + *p++ =0x66 ; + *p++ =0xb8 ;/* MOV EAX,0x01 */ + *p++ =0x01 ; + *p++ =0x00 ; + *p++ =0x00 ; + *p++ =0x00 ; + + *p++ =0x0f ;/* MOV CR0,EAX */ + *p++ =0x22 ; + *p++ =0xc0 ; + +/* the JMP sets CS for us and gets us to 32-bit */ + ip =0x00007c00 + (p - bootsect) +8 ;// set i to the IP after the JMP + *p++ =0x66 ;/* 32-bit operand size */ + *p++ =0xea ;/* JMP */ + *p++ = ip;/* IP */ + *p++ = ip >>8 ; + *p++ = ip >>16 ; + *p++ = ip >>24 ; + *p++ =0x08 ; + *p++ =0x00 ; + +/* initialize all other segments */ + *p++ =0xb8 ;/* MOV EAX,0x10 */ + *p++ =0x10 ; + *p++ =0x00 ; + *p++ =0x00 ; + *p++ =0x00 ; +for (i =0 ; i <6 ; i++) { +if (i ==1 )/* Skip CS */ +continue ; + + *p++ =0x8e ;/* MOV <seg>,EAX */ + *p++ =0xc0 + (i <<3 ); + } + +/* EBX contains a pointer to the bootinfo struct */ + *p++ =0xbb ;/* MOV EBX,imm32 */ + *p++ = bootinfo; + *p++ = bootinfo >>8 ; + *p++ = bootinfo >>16 ; + *p++ = bootinfo >>24 ; + +/* EAX has to contain the following magic */ + *p++ =0xb8 ;/* MOV EAX,0x2badb002 */ + *p++ =0x02 ; + *p++ =0xb0 ; + *p++ =0xad ; + *p++ =0x2b ; + +/* Jump off to the kernel */ + *p++ =0xea ;/* JMP */ + *p++ = mh_entry_addr;/* IP */ + *p++ = mh_entry_addr >>8 ; + *p++ = mh_entry_addr >>16 ; + *p++ = mh_entry_addr >>24 ; + *p++ =0x08 ; + *p++ =0x00 ; + + {/* GDT loading */ +uint32_t gdt_base =0x00007c00 + (p - bootsect);// 0x00007c00 is the first IP; +uint32_t gdtr = gdt_base +0x28 ; +uint8_t gdt[] = {// GDT base: 0x00000100 +// 0x00 +0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 , +// 0x08: code segment (base=0, limit=0xfffff, type=32bit code exec/read, DPL=0, 4k) +0xff ,0xff ,0x00 ,0x00 ,0x00 ,0x9a ,0xcf ,0x00 , +// 0x10: data segment (base=0, limit=0xfffff, type=32bit data read/write, DPL=0, 4k) +0xff ,0xff ,0x00 ,0x00 ,0x00 ,0x92 ,0xcf ,0x00 , +// 0x18: code segment (base=0, limit=0x0ffff, type=16bit code exec/read/conf, DPL=0, 1b) +0xff ,0xff ,0x00 ,0x00 ,0x00 ,0x9e ,0x00 ,0x00 , +// 0x20: data segment (base=0, limit=0x0ffff, type=16bit data read/write, DPL=0, 1b) +0xff ,0xff ,0x00 ,0x00 ,0x00 ,0x92 ,0x00 ,0x00 , +// 0x28: gdtdesc +0x27 ,0x00 , gdt_base, gdt_base >>8 , gdt_base >>16 , gdt_base >>24 + }; + +memcpy (p, gdt,sizeof (gdt)); + p+=sizeof (gdt); + *pgdt++ = gdtr; + *pgdt++ = gdtr >>8 ; + *pgdt++ = gdtr >>16 ; + *pgdt++ = gdtr >>24 ; + } + +fprintf (stderr,"qemu: multiboot loader code is %d bytes long.\n" , (int )(p-bootsect)); + +bdrv_set_boot_sector (drives_table[hda].bdrv, bootsect,sizeof (bootsect)); +} + +static int load_multiboot (FILE *f, +const char *kernel_filename, +const char *initrd_filename, +const char *kernel_cmdline, +uint8_t *header) +{ +int i, is_multiboot =0 ; +uint32_t flags =0 ; +// XXX: multiboot header may be within the first 8192 bytes, but header +// is only the first 1024 + + +// Ok, let's see if it is a multiboot image +for (i=0 ; i<(256 -12 ); i+=4 ) {// the header is 12x32bit long +if (ldl_p (header+i) ==0x1BADB002 ) { +uint32_t checksum =ldl_p (header+i+8 ); + flags =ldl_p (header+i+4 ); + checksum += flags; + checksum += (uint32_t )0x1BADB002 ; +if (!checksum) { + is_multiboot =1 ; +break ; + } + } + } + +if (!is_multiboot)return 0 ;// no multiboot +fprintf (stderr,"qemu: I believe we found a multiboot image!\n" ); + +if (flags &0x00000004 ) {// MULTIBOOT_HEADER_HAS_VBE +fprintf (stderr,"qemu: multiboot knows VBE. we don't.\n" ); + } +if (!(flags &0x00010000 )) {// MULTIBOOT_HEADER_HAS_ADDR +// XXX: multiboot knows ELF. we don't. +fprintf (stderr,"qemu: multiboot knows ELF. we don't.\n" ); +return 0 ; + }else { +/* Valid if mh_flags sets MULTIBOOT_HEADER_HAS_ADDR. */ +uint32_t mh_header_addr =ldl_p (header+i+12 ); +uint32_t mh_load_addr =ldl_p (header+i+16 ); +uint32_t mh_load_end_addr =ldl_p (header+i+20 ); +uint32_t mh_bss_end_addr =ldl_p (header+i+24 ); +uint32_t mh_entry_addr =ldl_p (header+i+28 ); +uint8_t *mb_kernel_addr = phys_ram_base + (mh_load_addr); +uint32_t mb_kernel_text_offset = i - (mh_header_addr - mh_load_addr); +uint32_t mb_kernel_size =get_file_size (f) - mb_kernel_text_offset; +uint32_t mb_bootinfo =0x100000 ; +uint32_t tmp_size; + +/* Valid if mh_flags sets MULTIBOOT_HEADER_HAS_VBE. +uint32_t mh_mode_type = ldl_p(header+i+32); +uint32_t mh_width = ldl_p(header+i+36); +uint32_t mh_height = ldl_p(header+i+40); +uint32_t mh_depth = ldl_p(header+i+44); */ + +fprintf (stderr,"multiboot: mh_header_addr = %#x\n" , mh_header_addr); +fprintf (stderr,"multiboot: mh_load_addr = %#x\n" , mh_load_addr); +fprintf (stderr,"multiboot: mh_load_end_addr = %#x\n" , mh_load_end_addr); +fprintf (stderr,"multiboot: mh_bss_end_addr = %#x\n" , mh_bss_end_addr); +fprintf (stderr,"multiboot: mh_entry_addr = %#x\n" , mh_entry_addr); + +fseek (f, mb_kernel_text_offset,SEEK_SET ); + +fprintf (stderr,"qemu: loading multiboot kernel (%#x bytes) at %#zx\n" , + mb_kernel_size, mb_kernel_addr - phys_ram_base); + +if ((tmp_size=fread (mb_kernel_addr,1 , mb_kernel_size, f)) != mb_kernel_size) { +fprintf (stderr,"qemu: read error on multiboot kernel '%s' (%#x != %#x)\n" , kernel_filename, tmp_size, mb_kernel_size); +exit (1 ); + } +fclose (f); + +// the kernel is where we want it to be now +// XXX: add module support (hurd, xen) + +// XXX: add support for: +// - mem_lower (4), mem_upper (8) flags[0] +// - cmdline (12) flags[2] +// - mods_count (20), mods_addr(24) flags[3] +// - syms (28 - 40) flags[4] +// - mmap_length (44), mmmap_addr (48) flags[6] +stl_p (phys_ram_base + mb_bootinfo,2 );// we only support the boot_device info +stl_p (phys_ram_base + mb_bootinfo +12 ,0x8001ffff );// XXX: use the -boot switch + +generate_bootsect_multiboot (mh_entry_addr, mb_bootinfo); + } + +return 1 ;// yes, we are multiboot +} +static void load_linux (const char *kernel_filename,const char *initrd_filename,const char *kernel_cmdline)
@@ -523,10 +743,15 @@ static void load_linux(const char *kerne#if 0 fprintf (stderr,"header magic: %#x\n" ,ldl_p (header+0x202 ));#endif -if (ldl_p (header+0x202 ) ==0x53726448 ) - protocol =lduw_p (header+0x206 ); -else - protocol =0 ; +if (ldl_p (header+0x202 ) ==0x53726448 ) { + protocol =lduw_p (header+0x206 ); + }else { +// This looks like a multiboot kernel. If it is, let's stop +// treating it like Linux. +if (load_multiboot (f,kernel_filename,initrd_filename, kernel_cmdline, header)) +return ; + protocol =0 ; + }if (protocol <0x200 || !(header[0x211 ] &0x01 )) {/* Low kernel */