From c786d8603e4bc726d138ed027f7df4f4907a4cff Mon Sep 17 00:00:00 2001 From: okuji Date: Sun, 23 Jul 2000 21:21:37 +0000 Subject: [PATCH] comply with the linux/i386 boot protocol version 2.02. --- ChangeLog | 40 +++++++++++++++++++ NEWS | 1 + stage2/asm.S | 22 +++++------ stage2/boot.c | 102 ++++++++++++++++++++++++++++++++++-------------- stage2/shared.h | 55 ++++++++++++++++++++------ 5 files changed, 166 insertions(+), 54 deletions(-) diff --git a/ChangeLog b/ChangeLog index a52e1c32b..e1ed66552 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,43 @@ +2000-07-24 OKUJI Yoshinori + + Comply with the Linux/i386 boot protocol version 2.02. + + * stage2/asm.S [!STAGE1_5] (linux_boot): Set the length of moved + bytes to LINUX_KERNEL_MAXLEN instead of + LINUX_KERNEL_LEN_OFFSET(%eax), since the field is obsolete. + [!STAGE1_5] (big_linux_boot): Don't use SEGMENT or OFFSET. + Instead, embed the segment and the offset in the code itself. + Set %ds, %es, %fs and %gs to %ax (LINUX_INIT_SEG). + * stage2/boot.c (load_image): Rewrite the Linux support code + heavily. Use a structure instead of a batch of macros, to access + a Linux kernel header. + (load_initrd): If MOVETO plus LEN is greater than or equal to + LINUX_INITRD_MAX_ADDRESS, set MOVETO to LINUX_INITRD_MAX_ADDRESS + minus LEN with page aligned. + * stage2/shared.h (LINUX_MAGIC_SIGNATURE): New macro. + (LINUX_DEFAULT_SETUP_SECTS): Likewise. + (LINUX_FLAG_CAN_USE_HEAP): Likewise. + (LINUX_INITRD_MAX_ADDRESS): Likewise. + (LINUX_MAX_SETUP_SECTS): Likewise. + (LINUX_BOOT_LOADER_TYPE): Likewise. + (LINUX_HEAP_END_OFFSET): Likewise. + (LINUX_SETUP_MAXLEN): Removed. + (LINUX_KERNEL_LEN_OFFSET): Likewise. + (LINUX_SETUP_LEN_OFFSET): Likewise. + (LINUX_SETUP_STACK): Set to 0x7F00 instead of 0x3FF4 (why was it + this value?). + (LINUX_SETUP_LOADER): Removed. + (LINUX_SETUP_LOAD_FLAGS): Likewise. + (LINUX_SETUP_CODE_START): Likewise. + (LINUX_SETUP_INITRD): Likewise. + (CL_MY_LOCATION): Set to RAW_ADDR(0x97F00) instead of + RAW_ADDR(0x92000). + (CL_MY_END_ADDR): Set to RAW_addr(0x97FFF) instead of + RAW_ADDR(0x920FF). + (CL_MAGIC_ADDR): Removed. + (CL_OFFSET): Likewise. + [!ASM_FILE] (struct linux_kernel_header): New structure tag. + 2000-07-23 OKUJI Yoshinori * docs/tutorial.texi: Fix some syntax errors and ambiguous diff --git a/NEWS b/NEWS index 02c0bfb72..5242ab2cb 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,7 @@ New in 0.5.96 - XXXX-XX-XX: with this command. * You can specify `--no-mem-option' to the command "kernel", if you want GRUB not to pass a Linux's mem option automatically. +* Now GRUB is compliant with the Linux/i386 boot protocol version 2.02. New in 0.5.95 - 2000-06-27: * NetBSD ELF kernel support is added. You have to specify the new option diff --git a/stage2/asm.S b/stage2/asm.S index 4e2b5b0ab..d7c187394 100644 --- a/stage2/asm.S +++ b/stage2/asm.S @@ -1695,7 +1695,8 @@ ENTRY(linux_boot) /* copy kernel */ movl $LINUX_SETUP, %eax - movl LINUX_KERNEL_LEN_OFFSET(%eax), %ecx + /* XXX: Too many bytes, but there is no bad effect */ + movl $LINUX_KERNEL_MAXLEN, %ecx shll $2, %ecx movl $LINUX_STAGING_AREA, %esi movl $LINUX_KERNEL, %edi @@ -1710,12 +1711,6 @@ ENTRY(big_linux_boot) /* final setup for linux boot */ - movw $LINUX_SETUP_SEG, %ax - movw %ax, segment - - xorl %eax, %eax - movl %eax, offset - call EXT_C(prot_to_real) .code16 @@ -1724,13 +1719,16 @@ ENTRY(big_linux_boot) movw $LINUX_INIT_SEG, %ax movw %ax, %ss + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs /* jump to start */ -#ifdef ABSOLUTE_WITHOUT_ASTERISK - DATA32 ADDR32 ljmp (offset) -#else - DATA32 ADDR32 ljmp *(offset) -#endif + /* ljmp */ + .byte 0xea + .word LINUX_SETUP_SEG + .word 0 .code32 diff --git a/stage2/boot.c b/stage2/boot.c index eefbeddaf..32e0264cf 100644 --- a/stage2/boot.c +++ b/stage2/boot.c @@ -45,6 +45,7 @@ load_image (char *kernel, char *arg, kernel_t suggested_type, kernel_t type = KERNEL_TYPE_NONE; unsigned long flags = 0, text_len = 0, data_len = 0, bss_len = 0; char *str = 0, *str2 = 0; + struct linux_kernel_header *lh; union { struct multiboot_header *mb; @@ -90,6 +91,10 @@ load_image (char *kernel, char *arg, kernel_t suggested_type, } } + /* Use BUFFER as a linux kernel header, if the image is Linux zImage + or bzImage. */ + lh = (struct linux_kernel_header *) buffer; + /* ELF loading supported if multiboot, FreeBSD and NetBSD. */ if ((type == KERNEL_TYPE_MULTIBOOT || grub_strcmp (pu.elf->e_ident + EI_BRAND, "FreeBSD") == 0 @@ -200,31 +205,63 @@ load_image (char *kernel, char *arg, kernel_t suggested_type, exec_type = 1; str = "a.out"; } - else if ((*((unsigned short *) (buffer + BOOTSEC_SIG_OFFSET)) - == BOOTSEC_SIGNATURE) - && ((data_len - = (((long) *((unsigned char *) - (buffer + LINUX_SETUP_LEN_OFFSET))) << 9)) - <= LINUX_SETUP_MAXLEN) - && ((text_len - = (((long) *((unsigned long *) - (buffer + LINUX_KERNEL_LEN_OFFSET))) << 4)), - (data_len + text_len + SECTOR_SIZE) <= ((filemax + 15) & 0xFFFFFFF0))) + else if (lh->boot_flag == BOOTSEC_SIGNATURE + && lh->setup_sects <= LINUX_MAX_SETUP_SECTS) { - int big_linux = buffer[LINUX_SETUP_LOAD_FLAGS] & LINUX_FLAG_BIG_KERNEL; - buffer[LINUX_SETUP_LOADER] = 0x70; + int big_linux = 0; + int setup_sects = lh->setup_sects; + + if (lh->header == LINUX_MAGIC_SIGNATURE && lh->version >= 0x0200) + { + big_linux = (lh->loadflags & LINUX_FLAG_BIG_KERNEL); + lh->type_of_loader = LINUX_BOOT_LOADER_TYPE; + + if (lh->version >= 0x0201) + { + lh->heap_end_ptr = LINUX_HEAP_END_OFFSET; + lh->loadflags |= LINUX_FLAG_CAN_USE_HEAP; + } + + if (lh->version >= 0x0202) + lh->cmd_line_ptr = CL_MY_LOCATION; + else + { + lh->cl_magic = CL_MAGIC; + lh->cl_offset = CL_MY_LOCATION - CL_BASE_ADDR; + lh->setup_move_size + = (unsigned short) (CL_MY_END_ADDR - CL_BASE_ADDR + 1); + } + } + else + { + /* Your kernel is quite old... */ + lh->cl_magic = CL_MAGIC; + lh->cl_offset = CL_MY_LOCATION - CL_BASE_ADDR; + + setup_sects = LINUX_DEFAULT_SETUP_SECTS; + } + + /* If SETUP_SECTS is not set, set it to the default (4). */ + if (! setup_sects) + setup_sects = LINUX_DEFAULT_SETUP_SECTS; + + data_len = setup_sects << 9; + text_len = filemax - data_len - SECTOR_SIZE; + if (! big_linux && text_len > LINUX_KERNEL_MAXLEN) { - printf (" linux 'zImage' kernel too big, try 'make bzImage'\n"); + grub_printf (" linux 'zImage' kernel too big, try 'make bzImage'\n"); grub_close (); errnum = ERR_WONT_FIT; return KERNEL_TYPE_NONE; } - printf (" [Linux-%s, setup=0x%x, size=0x%x]\n", - (big_linux ? "bzImage" : "zImage"), data_len, text_len); + grub_printf (" [Linux-%s, setup=0x%x, size=0x%x]\n", + (big_linux ? "bzImage" : "zImage"), data_len, text_len); - if (mbi.mem_lower >= 608) + /* FIXME: SETUP_SECTS should be supported up to 63. + But do you know there are >640KB conventional memory machines? */ + if (mbi.mem_lower >= 608 && setup_sects < 60) { /* Video mode selection support. What a shit! */ { @@ -254,24 +291,24 @@ load_image (char *kernel, char *arg, kernel_t suggested_type, return KERNEL_TYPE_NONE; } - /* Set the vid mode to VID_MODE. Note that this can work - because i386 architecture is little-endian. */ - grub_memmove (buffer + LINUX_VID_MODE_OFFSET, - (char *) &vid_mode, - sizeof (unsigned short)); + lh->vid_mode = vid_mode; } } memmove ((char *) LINUX_SETUP, buffer, data_len + SECTOR_SIZE); + if (lh->header != LINUX_MAGIC_SIGNATURE || + lh->version < 0x0200) + /* Clear the heap space. */ + grub_memset ((char *) LINUX_SETUP + ((setup_sects - 1) << 9), + 0, + (64 - setup_sects - 1) << 9); + /* copy command-line plus memory hack to staging area */ { char *src = arg; char *dest = (char *) CL_MY_LOCATION; - *((unsigned short *) CL_OFFSET) = CL_MY_LOCATION - CL_BASE_ADDR; - *((unsigned short *) CL_MAGIC_ADDR) = CL_MAGIC; - /* Add a mem option automatically only if the user doesn't specify it explicitly. */ if (! grub_strstr (src, "mem=") @@ -563,8 +600,9 @@ int load_initrd (char *initrd) { int len; - unsigned long *ramdisk, moveto; - + unsigned long moveto; + struct linux_kernel_header *lh; + if (! grub_open (initrd)) return 0; @@ -575,7 +613,10 @@ load_initrd (char *initrd) return 0; } - moveto = ((mbi.mem_upper + 0x400) * 0x400 - len) & 0x3ffff000; + moveto = ((mbi.mem_upper + 0x400) * 0x400 - len) & 0xfffff000; + if (moveto + len >= LINUX_INITRD_MAX_ADDRESS) + moveto = (LINUX_INITRD_MAX_ADDRESS - len) & 0xfffff000; + /* XXX: Linux 2.3.xx has a bug in the memory range check, so avoid the last page. */ moveto -= 0x1000; @@ -583,9 +624,10 @@ load_initrd (char *initrd) printf (" [Linux-initrd @ 0x%x, 0x%x bytes]\n", moveto, len); - ramdisk = (unsigned long *) (LINUX_SETUP + LINUX_SETUP_INITRD); - ramdisk[0] = RAW_ADDR (moveto); - ramdisk[1] = len; + /* FIXME: Should check if the kernel supports INITRD. */ + lh = (struct linux_kernel_header *) LINUX_SETUP; + lh->ramdisk_image = RAW_ADDR (moveto); + lh->ramdisk_size = len; grub_close (); return 1; diff --git a/stage2/shared.h b/stage2/shared.h index a04b58c17..ef4a4ddb1 100644 --- a/stage2/shared.h +++ b/stage2/shared.h @@ -135,22 +135,23 @@ extern char *grub_scratch_mem; * Linux setup parameters */ +#define LINUX_MAGIC_SIGNATURE 0x53726448 /* "HdrS" */ +#define LINUX_DEFAULT_SETUP_SECTS 4 +#define LINUX_FLAG_CAN_USE_HEAP 0x80 +#define LINUX_INITRD_MAX_ADDRESS 0x3C000000 +#define LINUX_MAX_SETUP_SECTS 63 +#define LINUX_BOOT_LOADER_TYPE 0x71 +#define LINUX_HEAP_END_OFFSET (0x7F00 - 0x200) + #define LINUX_STAGING_AREA RAW_ADDR (0x100000) #define LINUX_SETUP RAW_ADDR (0x90000) -#define LINUX_SETUP_MAXLEN 0x1E00 #define LINUX_KERNEL RAW_ADDR (0x10000) #define LINUX_KERNEL_MAXLEN 0x7F000 #define LINUX_SETUP_SEG 0x9020 #define LINUX_INIT_SEG 0x9000 -#define LINUX_KERNEL_LEN_OFFSET 0x1F4 -#define LINUX_SETUP_LEN_OFFSET 0x1F1 -#define LINUX_SETUP_STACK 0x3FF4 +#define LINUX_SETUP_STACK 0x7F00 -#define LINUX_SETUP_LOADER 0x210 -#define LINUX_SETUP_LOAD_FLAGS 0x211 #define LINUX_FLAG_BIG_KERNEL 0x1 -#define LINUX_SETUP_CODE_START 0x214 -#define LINUX_SETUP_INITRD 0x218 /* Linux's video mode selection support. Actually I hate it! */ #define LINUX_VID_MODE_OFFSET 0x1FA @@ -158,12 +159,10 @@ extern char *grub_scratch_mem; #define LINUX_VID_MODE_EXTENDED 0xFFFE #define LINUX_VID_MODE_ASK 0xFFFD -#define CL_MY_LOCATION RAW_ADDR (0x92000) -#define CL_MY_END_ADDR RAW_ADDR (0x920FF) -#define CL_MAGIC_ADDR RAW_ADDR (0x90020) +#define CL_MY_LOCATION RAW_ADDR (0x97F00) +#define CL_MY_END_ADDR RAW_ADDR (0x97FFF) #define CL_MAGIC 0xA33F #define CL_BASE_ADDR RAW_ADDR (0x90000) -#define CL_OFFSET RAW_ADDR (0x90022) /* * General disk stuff @@ -370,6 +369,38 @@ extern char *grub_scratch_mem; #include "mb_header.h" #include "mb_info.h" +/* For the Linux/i386 boot protocol version 2.02. */ +struct linux_kernel_header +{ + char code1[0x0020]; + unsigned short cl_magic; /* Magic number 0xA33F */ + unsigned short cl_offset; /* The offset of command line */ + char code2[0x01F1 - 0x0020 - 2 - 2]; + unsigned char setup_sects; /* The size of the setup in sectors */ + unsigned short root_flags; /* If the root is mounted readonly */ + unsigned short syssize; /* obsolete */ + unsigned short swap_dev; /* obsolete */ + unsigned short ram_size; /* obsolete */ + unsigned short vid_mode; /* Video mode control */ + unsigned short root_dev; /* Default root device number */ + unsigned short boot_flag; /* 0xAA55 magic number */ + unsigned short jump; /* Jump instruction */ + unsigned long header; /* Magic signature "HdrS" */ + unsigned short version; /* Boot protocol version supported */ + unsigned long realmode_swtch; /* Boot loader hook */ + unsigned long start_sys; /* Points to kernel version string */ + unsigned char type_of_loader; /* Boot loader identifier */ + unsigned char loadflags; /* Boot protocol option flags */ + unsigned short setup_move_size; /* Move to high memory size */ + unsigned long code32_start; /* Boot loader hook */ + unsigned long ramdisk_image; /* initrd load address */ + unsigned long ramdisk_size; /* initrd size */ + unsigned long bootsect_kludge; /* obsolete */ + unsigned long heap_end_ptr; /* Free memory after setup end */ + unsigned short pad1; /* Unused */ + unsigned long cmd_line_ptr; /* Points to the kernel command line */ +} __attribute__ ((packed)); + /* Memory map address range descriptor used by GET_MMAP_ENTRY. */ struct mmar_desc {