diff -Nur -x autom4te.cache grub2_cvspull.bak/conf/i386-pc.rmk grub2_cvspull/conf/i386-pc.rmk --- grub2_cvspull.bak/conf/i386-pc.rmk 2006-07-31 08:21:35.000000000 -0600 +++ grub2_cvspull/conf/i386-pc.rmk 2006-08-03 00:18:02.000000000 -0600 @@ -112,7 +112,7 @@ pkgdata_MODULES = _chain.mod _linux.mod linux.mod normal.mod \ _multiboot.mod chain.mod multiboot.mod reboot.mod halt.mod \ vbe.mod vbetest.mod vbeinfo.mod video.mod gfxterm.mod \ - videotest.mod play.mod bitmap.mod tga.mod + videotest.mod play.mod bitmap.mod tga.mod vmlinux.mod # For _chain.mod. _chain_mod_SOURCES = loader/i386/pc/chainloader.c @@ -164,6 +164,11 @@ _multiboot_mod_CFLAGS = $(COMMON_CFLAGS) _multiboot_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For vmlinux.mod. +vmlinux_mod_SOURCES = loader/i386/pc/vmlinux.c +vmlinux_mod_CFLAGS = $(COMMON_CFLAGS) +vmlinux_mod_LDFLAGS = $(COMMON_LDFLAGS) + # For multiboot.mod. multiboot_mod_SOURCES = loader/i386/pc/multiboot_normal.c multiboot_mod_CFLAGS = $(COMMON_CFLAGS) diff -Nur -x autom4te.cache grub2_cvspull.bak/include/grub/i386/pc/init.h grub2_cvspull/include/grub/i386/pc/init.h --- grub2_cvspull.bak/include/grub/i386/pc/init.h 2005-01-31 14:40:25.000000000 -0700 +++ grub2_cvspull/include/grub/i386/pc/init.h 2006-08-03 00:21:47.000000000 -0600 @@ -48,8 +48,11 @@ /* Get a memory map entry. Return next continuation value. Zero means the end. */ -grub_uint32_t grub_get_mmap_entry (struct grub_machine_mmap_entry *entry, - grub_uint32_t cont); +/*grub_uint32_t grub_get_mmap_entry (struct grub_machine_mmap_entry *entry, + grub_uint32_t cont);*/ +/* exporting for loader/i386/pc/vmlinux.c */ +grub_uint32_t EXPORT_FUNC (grub_get_mmap_entry) + (struct grub_machine_mmap_entry *entry, grub_uint32_t cont); /* Turn on/off Gate A20. */ void grub_gate_a20 (int on); diff -Nur -x autom4te.cache grub2_cvspull.bak/loader/i386/pc/vmlinux.c grub2_cvspull/loader/i386/pc/vmlinux.c --- grub2_cvspull.bak/loader/i386/pc/vmlinux.c 1969-12-31 17:00:00.000000000 -0700 +++ grub2_cvspull/loader/i386/pc/vmlinux.c 2006-08-03 00:08:10.000000000 -0600 @@ -0,0 +1,416 @@ +/* vmlinux.c - load a Linux i386/x86_64 vmlinux.bin kernel image */ +/* August 3 2006, Maciek Nowacki */ + +/* + * notes: + * + * grub leaks memory. This is apparent even with the stock Linux bzImage + * loader (repeat a command like 'linux /bzImage' about twenty times), and it + * seems to be worse with this loader. I am new to grub, but I could not find + * any obvious leakage here. + */ + +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2003 Free Software Foundation, Inc. + * Copyright (C) 2003 NIIBE Yutaka + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static grub_dl_t this_mod; + +#define STATE_ADDRESS 0 +#define STATE_MAJOR 1 +#define STATE_MINOR 2 +#define STATE_RDONLY 3 + +/* include/asm-x86_64/setup.h:#define COMMAND_LINE_SIZE 256 + */ +#define COMMAND_LINE_SIZE 256 + +/* linux/include/asm-x86_64/bootsetup.h: + * #define BOOT_PARAM_SIZE 4096 + */ +#define BOOT_PARAM_SIZE 4096 + +static const struct grub_arg_option options[] = + { + {"address", 'a', 0, "specify address to load kernel (default: -a 0x200000)", 0, ARG_TYPE_STRING}, + {"major", 'M', 0, "set root device major", 0, ARG_TYPE_INT}, + {"minor", 'm', 0, "set root device minor", 0, ARG_TYPE_INT}, + {"ro", 'r', 0, "set root read-only flag", 0, ARG_TYPE_NONE}, + {0, 0, 0, 0, 0, 0} + }; + +/* + * linux/include/asm-x86_64/bootsetup.h: + * #define PARAM ((unsigned char *)x86_boot_params) + * + * This block constitutes the data that GRUB will pass to Linux. It is safely + * outside of the area defined by grub_os_area_addr and grub_os_area_size, and + * will persist until the 'boot' command is given; however, it is past the + * grub_os_area, and Linux cannot deal with this information being past itself + * in physical memory. This will be taken care of in grub_vmlinux_boot(). + */ +static unsigned char x86_boot_params[COMMAND_LINE_SIZE + BOOT_PARAM_SIZE]; + +static grub_err_t +grub_vmlinux_boot (void) +{ + /* declare another boot block here, where it will be on the stack and + * thus in the first megabyte of memory: + */ + unsigned char x86_boot_params_first_meg[COMMAND_LINE_SIZE + + BOOT_PARAM_SIZE]; + + grub_memmove(&x86_boot_params_first_meg,&x86_boot_params, + COMMAND_LINE_SIZE + BOOT_PARAM_SIZE); + + char *cmdline = (char *)&x86_boot_params_first_meg; + struct linux_kernel_header *lh = + (struct linux_kernel_header *)(COMMAND_LINE_SIZE + cmdline); + + grub_addr_t begin = lh->code32_start; + + if(begin < (grub_addr_t)cmdline) + { + grub_printf("Sorry, I can't boot the kernel: the parameter block occurs past the kernel.\n"); + return GRUB_ERR_OUT_OF_RANGE; + } + + lh->cmd_line_ptr=cmdline; + + grub_printf("cmdline relocated to %08x, lh to %08x\n", + (unsigned int)lh->cmd_line_ptr, (unsigned int)lh); + + asm( "movl %0,%%esi" :: "m" (lh) ); + asm("jmp *%0" :: "m" (begin)); + + /* Not reached. */ + return GRUB_ERR_NONE; +} +static grub_err_t +grub_vmlinux_unload (void) +{ + grub_dl_unref(this_mod); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_vmlinux (struct grub_arg_list *state, int argc, char **args) +{ + grub_dl_ref(this_mod); + if(!argc) + { + grub_printf("Please specify a vmlinux.bin-style kernel image.\n"); + return GRUB_ERR_BAD_ARGUMENT; + } + + int i=argc, cl=0; + while(--i) + cl += 1 + grub_strlen(args[i]); + + if(cl > 1 + COMMAND_LINE_SIZE) // Linux has an implicit '\0' at max length +1 + { + grub_printf("Command line is too long by %u characters\n", + cl - (1 + COMMAND_LINE_SIZE)); + + /* truncation doesn't make much sense - let the user handle it */ + return GRUB_ERR_BAD_ARGUMENT; + } + + /* Handle the cmdline first and as one block along with the + * linux_kernel_header struct (lh), since the cmdline code might scribble + * past its bounds by one byte. + */ + + char *cmdline=(char *)&x86_boot_params; + + // a little bit of poison never hurt anyone (XXX replace with memset()) + for(i=COMMAND_LINE_SIZE; i; cmdline[--i]=0x8f); + + /* A note on cmdline, the commandline buffer: + * Potentially, 257 characters could be copied into the commandline buffer. + * However, the last character is always a space, which the statement after + * the loop would overwrite with '\0'. That character therefore never + * contains user data, as it serves only to delineate the end of the buffer. + * Linux will only use at most 256 characters from the buffer. By using an + * extra byte, the code becomes no more complex despite allowing the entire + * buffer to be used for commandline user data. + */ + + grub_uint32_t cont=argc; + i=cl; + while(i) + { + i -= 1 + grub_strlen(args[--cont]); + *grub_stpcpy(i + cmdline, args[cont])=' '; + } + /* cl is 0 if there isn't a commandline; otherwise, cl is the length of the + * commandline plus 1. + */ + cmdline[ cl - (cl?1:0) ] = '\0'; + + /* deal with lh only once the commandline has been sorted out + * Note that the struct linux_kernel_header only describes part of the data + * defined by BOOT_PARAM_SIZE. It's used as a convenience, and since it is + * smaller than BOOT_PARAM_SIZE, it is not always appropriate. + */ + struct linux_kernel_header *lh = + (struct linux_kernel_header *)(cmdline + COMMAND_LINE_SIZE); + + /* XXX replace with memset() */ + for(i = BOOT_PARAM_SIZE; i; ((char *)lh)[--i] = 0xf8); + + if(state[STATE_ADDRESS].set) + { + for(i = grub_strlen(state[STATE_ADDRESS].arg); + i && 'x' !=state[STATE_ADDRESS].arg[--i] + && 'X'!=state[STATE_ADDRESS].arg[i] + ; + ); + + /* Maybe bail if 0 is returned? Or other preposturous addresses? */ + if(i) + lh->code32_start = (grub_addr_t) grub_strtoul( + 1 + i + state[STATE_ADDRESS].arg, 0, 16); + } + else + { + grub_printf("Please specify a load address (see 'vmlinux -h')\n"); + return GRUB_ERR_BAD_ARGUMENT; + } + + /* here is another struct that overlays the BOOT_PARAM region */ + struct linux_kernel_params *lp = (struct linux_kernel_params *) lh; + + /* Without these two at least, console initialization will get stuck. + * This took me a _long_ time to figure out. + * It would be more correct to obtain this information from GRUB. + */ + lp->video_height = 25; + lp->video_width = 80; + lp->have_vga = 1; + + /* This method is probably going away soon - but it's elegant, in my + * opinion, and it avoids string parsing in the kernel. It allows the + * bootloader to refuse data that doesn't describe a device in acceptable + * notation. + */ + if(state[STATE_MAJOR].set && state[STATE_MINOR].set) + lh->root_dev= (grub_strtoul(state[STATE_MAJOR].arg,NULL,0) << 8) + | grub_strtoul(state[STATE_MINOR].arg,NULL,0); + + /* As above. Regardless, here it is. */ + if(state[STATE_RDONLY].set) + lh->root_flags=1; + + /* Don't set the cmdline pointer, since the boot function will move it. */ + /* lh->cmd_line_ptr=cmdline; */ + + /* these need to be set when initrd support is implemented */ + lh->ramdisk_image=0; + lh->ramdisk_size=0; + + /* useless, but shows up /proc/sys/kernel/bootloader_type */ + lh->type_of_loader=GRUB_LINUX_BOOT_LOADER_TYPE; + + /* I have delayed opening and reading the kernel into memory for as long as + * possible, to allow the code to cleanly abort. While it is still possible + * to bail out here without having touched the grub_os_area, it won't be + * for long. + */ + + grub_file_t file = 0; + + file = grub_file_open(args[0]); + + if(!file) + { + grub_printf("Problem during grub_file_open()... aborting.\n"); + return GRUB_ERR_FILE_READ_ERROR; + } + + // XXX clean this up - obsolete #defines +#define OS_AREA_ADDR grub_os_area_addr +#define OS_AREA_SIZE grub_os_area_size +#define LOAD_ADDRESS lh->code32_start + + if(grub_os_area_addr > lh->code32_start) + { + grub_printf("Error: 0x%08x is too low in memory by 0x%08x bytes\n", + lh->code32_start, grub_os_area_addr - lh->code32_start); + grub_file_close(file); + return GRUB_ERR_OUT_OF_RANGE; + } + + if(grub_os_area_addr+grub_os_area_size < lh->code32_start) + { + grub_printf("Error: 0x%08x exceeds the highest available memory location by 0x%08x bytes\n", + lh->code32_start, lh->code32_start - + (grub_os_area_addr + grub_os_area_size) ); + grub_file_close(file); + return GRUB_ERR_OUT_OF_RANGE; + } + if(grub_os_area_addr + grub_os_area_size < + lh->code32_start + (grub_addr_t) grub_file_size(file)) + { + grub_printf("Error: to load at location 0x%08x, 0x%08x more bytes are needed\n", + lh->code32_start, lh->code32_start + + (grub_addr_t) grub_file_size(file) - + (grub_os_area_addr + grub_os_area_size)); + + grub_file_close(file); + return GRUB_ERR_OUT_OF_MEMORY; + } + + /* Now that the grub_os_area is going to be overwritten (everything else + * should be recoverable with no change in state, to this point): + */ + grub_loader_unset(); + + /* I think it is appropriate to cast the result of grub_file_read() since it + * is a signed value, while grub_file_size() returns grub_off_t (unsigned) + * and thus could represent a value that could be too large to be stored as + * a same-sized signed value. + */ + if(grub_file_size(file) != (grub_off_t) grub_file_read(file, + (char *)lh->code32_start, grub_file_size(file))) + { + grub_printf("Error reading %s into memory.\n", args[0]); + grub_file_close(file); + return GRUB_ERR_READ_ERROR; + } + + grub_file_close(file); + + /* Now that the kernel image has been loaded, let's muck about with some + real-mode BIOS calls to dig out the E820 memory maps. + + Note that I had to export grub_get_mmap_entry() for this. + + I'm putting this as close to the end as possible since I don't know how + the grub_get_mmap_entry() calls might affect everything else. Who can + tell? We are at the mercy of the BIOS! (yes, this is a plea to make the + memory map information available...) */ + + /* from linux/include/asm-x86_64/e820.h - seems to be the same for i386 */ + struct e820entry { + grub_uint64_t addr; /* start of memory segment */ + grub_uint64_t size; /* size of memory segment */ + grub_uint32_t type; /* type of memory segment */ + } __attribute__((packed)); + + /* linux/include/asm-x86_64/e820.h: */ + // #define E820MAP 0x2d0 /* our map */ + + struct e820entry *e820map = ((struct e820entry *) ((char *)lh + 0x2d0)); + + struct grub_machine_mmap_entry entry; + lp->mmap_size=0; /* number of e820 regions */ + cont=0; + do + { + cont = grub_get_mmap_entry (&entry,cont); + e820map[lp->mmap_size].addr = entry.addr; + e820map[lp->mmap_size].size = entry.len; + e820map[lp->mmap_size].type = entry.type; + lp->mmap_size++; + } + while(cont); + + + /* Well, that's about it. Let's make it official. */ + + grub_loader_set (grub_vmlinux_boot, grub_vmlinux_unload, 1); + return GRUB_ERR_NONE; +} + +GRUB_MOD_INIT(vmlinux) +{ + this_mod = mod; /* To stop warning. */ + grub_register_command ("vmlinux", grub_cmd_vmlinux, GRUB_COMMAND_FLAG_BOTH, + "vmlinux", "Load a Linux vmlinux.bin kernel image", options); +} + +GRUB_MOD_FINI(vmlinux) +{ + grub_unregister_command ("vmlinux"); +} + +#if 0 + +Some notes on boot parameter offsets. + + // include/asm-x86_64/bootsetup.h contains a wealth of information about + // what needs to go into real_mode_data. A *wealth*! + // It also explains that the first 0x200 bytes of real_mode_data are + // defined by the contents of boot/bootsect.S, and the next 0xcff - which + // takes us to 0xfff, or 4k (0x1000) - are defined in boot/setup.S. + // + // So the layout goes, roughly: + // 0-0x200: bootsect.S - obviously, many of these are obsolete + // 0 _start + // 8 _start2 + // 0x18 msg_loop + // 0x26 die + // 0x31 bugger_off_msg - I think the first real one follows + // ** everything before this point is part of struct screen_info + // 0x1f1 setup_sects // appears to be unused + // 0x1f2 root_flags + // 0x1f4 syssize + // 0x1f6 swap_dev + // 0x1f8 ram_size + // 0x1fa vid_mode + // 0x1fc root_dev + // 0x1fe boot_flag // AUX_DEVICE_INFO - obsolete PS/2 mouse info + // 0x200-0xfff : hmm, this is not all used. The last 0x100 bytes particularly + // (0xf00-0xfff) appear to just be setup.S code. Oh, add 0x200 to each item. + // 0 begtext + // 8 realmode_switch + // 0xc start_sys_seg + // 0x10 type_of_loader // possibly signal multiboot info here? + // 0x11 loadflags + // 0x12 setup_move_size + // 0x14 code32_start // not used by the kernel + // 0x18 ramdisk_image + // 0x1c ramdisk_size + // 0x20 bootsect_kludge // I wonder what the heck this was + // 0x24 heap_end_ptr + // 0x26 padl + // 0x28 cmd_line_ptr + // 0x2c ramdisk_max // seems obsolete + // 0x30 trampoline + // 0xd00 start_of_setup // ok, the last defined thing is 0x230, + // // 'trampoline'. Since the end of our space + // // is 0x1000, that leaves 0xdd0 for memory + // // maps and such. + // 0x228 : NEW_CL_POINTER, defined in arch/x86_64/kernel/head64.c + // also COMMAND_LINE_SIZE is defined as 256 bytes so... hmm. +#endif