This patch for Linux 1.2.2 causes the 'vmlinux' file produced in the top level of the Linux source tree (e.g. /usr/src/linux) to be compatible with the proposed MultiBoot standard. You can still 'make zImage' and get traditional compressed Linux boot images. Note that, since none of the traditional as86/build/extract/piggyback hacks are needed to create uncompressed MultiBoot-compliant Linux kernel images, it should now be fairly easy to cross-compile Linux from other machines that don't have as86/ld86. I haven't yet tried this myself though. While this patch basically works (at least for me), it is incomplete in a number of respects: * Linux doesn't use all the memory available to it when loaded from a MultiBoot boot loader. Basically, it just finds the largest single usable memory region that isn't occupied by any boot loader-provided data, and ignores everything but that. This should be fairly easy to fix: after the basic bootstrap process, the VM/paging code should go through the list of available regions again and add the rest of the pages to the free list. * There is no support for the funky screen mode selection that's in the normal Linux 16-bit setup code. I think that code really belongs with the Linux console code rather than in the setup code anyway. All that's needed to fix this is the ability to hop back into 16-bit mode while in Linux to make BIOS calls. I'm willing to add that support myself if necessary, but if I remember correctly someone may have already done it. * The hard drive parameter tables aren't passed from the boot loader to the kernel, and the kernel only does a half-***ed job of finding them itself (i.e. your second IDE hard drive will stop working :-) ). Again, this could be solved by the ability to make 16-bit BIOS calls. There are probably other things I've overlooked as well. However, at any rate, this should do as a starting point. Suggestions welcome. Also, if someone with more Linux kernel hacking experience than me wants to take over the Linux support, I'd be very grateful. Bryan diff -crN olin/arch/i386/kernel/Makefile linux/arch/i386/kernel/Makefile *** olin/arch/i386/kernel/Makefile Mon Jan 23 14:14:16 1995 --- linux/arch/i386/kernel/Makefile Sat Apr 1 11:21:03 1995 *************** *** 18,24 **** .S.o: $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o ! OBJS = process.o signal.o entry.o traps.o irq.o vm86.o bios32.o ptrace.o ioport.o ldt.o setup.o all: kernel.o head.o --- 18,24 ---- .S.o: $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o ! OBJS = process.o signal.o entry.o traps.o irq.o vm86.o bios32.o ptrace.o ioport.o ldt.o setup.o pic.o all: kernel.o head.o diff -crN olin/arch/i386/kernel/head.S linux/arch/i386/kernel/head.S *** olin/arch/i386/kernel/head.S Mon Jan 2 06:19:59 1995 --- linux/arch/i386/kernel/head.S Sat Apr 1 09:33:32 1995 *************** *** 26,31 **** --- 26,97 ---- #define CL_BASE_ADDR 0x90000 #define CL_OFFSET 0x90022 + _start: + /* + * This is where we start running if we come from + * the old 16-bit `setup' mechanism. + */ + jmp startup_32 + + + /* MultiBoot header - see multiboot.h. */ + .align 2 + boot_hdr: + .long 0x1BADB002 /* magic */ + .long 0x00010000 /* flags: AOUT_KLUDGE */ + .long boot_hdr /* header_addr */ + .long _start /* load_addr */ + .long _edata /* load_end_addr */ + .long _end /* bss_end_addr */ + .long boot_entry /* entry */ + + /* + * Entrypoint when activated from a MultiBoot + * standard-compliant boot loader. + * All segment registers are flat 32-bit direct-mapped; + * eax points to the multiboot_info structure. + */ + boot_entry: + movl stack_start,%esp + movl %eax,%ebx + + /* + * Initialize eflags. Some BIOS's leave bits like NT set. This would + * confuse the debugger if this code is traced. + */ + pushl $0 + popfl + + /* + * Clear BSS first so that there are no surprises... + */ + xorl %eax,%eax + movl $__edata,%edi + movl $__end,%ecx + subl %edi,%ecx + cld + rep + stosb + + /* + * Now that it's safe to store things in our bss segment, + * stash the boot_info pointer for future use. + */ + movl %ebx,_multiboot_info + + /* + * start system 32-bit setup. We need to re-do some of the things done + * in 16-bit mode for the "real" operations. + */ + call setup_idt + + /* + * Perform other kernel initialization as before. + */ + jmp check_cpu + + + /* * swapper_pg_dir is the main page directory, address 0x00001000 (or at * address 0x00101000 for a compressed boot). *************** *** 95,100 **** --- 161,167 ---- * apply at our cpl of 0 and the stack ought to be aligned already, and * we don't need to preserve eflags. */ + check_cpu: movl $3,_x86 pushfl # push EFLAGS popl %eax # get EFLAGS diff -crN olin/arch/i386/kernel/pic.c linux/arch/i386/kernel/pic.c *** olin/arch/i386/kernel/pic.c Wed Dec 31 17:00:00 1969 --- linux/arch/i386/kernel/pic.c Sat Apr 1 11:20:54 1995 *************** *** 0 **** --- 1,35 ---- + + #include + + /* + * Now we have to reprogram the interrupts :-( + * we put them right after the intel-reserved hardware interrupts, at + * int 0x20-0x2F. There they won't mess up anything. Sadly IBM really + * messed this up with the original PC, and they haven't been able to + * rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f, + * which is used for the internal hardware interrupts as well. We just + * have to reprogram the 8259's, and it isn't fun. + */ + void init_pic() + { + outb(0x11, 0x20); /* initialization sequence to 8259A-1 */ + SLOW_DOWN_IO; + outb(0x11, 0xA0); /* ... and to 8259A-2 */ + SLOW_DOWN_IO; + outb(0x20, 0x21); /* start of hardware int's (0x20) */ + SLOW_DOWN_IO; + outb(0x28, 0xA1); /* start of hardware int's 2 (0x28) */ + SLOW_DOWN_IO; + outb(0x04, 0x21); /* 8259-1 is master */ + SLOW_DOWN_IO; + outb(0x02, 0xA1); /* 8259-2 is slave */ + SLOW_DOWN_IO; + outb(0x01, 0x21); /* 8086 mode for both */ + SLOW_DOWN_IO; + outb(0x01, 0xA1); + SLOW_DOWN_IO; + outb(0xFF, 0xA1); /* mask off all interrupts for now */ + SLOW_DOWN_IO; + outb(0xFB, 0x21); /* mask all irq's but irq2 which is cascaded */ + } + diff -crN olin/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c *** olin/arch/i386/kernel/setup.c Fri Mar 10 17:46:40 1995 --- linux/arch/i386/kernel/setup.c Sat Apr 1 17:44:02 1995 *************** *** 21,26 **** --- 21,27 ---- #include #include #include + #include #include #include *************** *** 59,64 **** --- 60,67 ---- extern char empty_zero_page[PAGE_SIZE]; + struct multiboot_info *multiboot_info; + /* * This is set up by the setup-routine at boot-time */ *************** *** 82,87 **** --- 85,96 ---- char c = ' ', *to = command_line, *from = COMMAND_LINE; int len = 0; + /* Make sure the PIC is set up right. + This used to be done by the 16-bit setup code. */ + init_pic(); + + /* Retrieve the old-style boot parameters if present. + (If not, these will all be zero.) */ ROOT_DEV = ORIG_ROOT_DEV; drive_info = DRIVE_INFO; screen_info = SCREEN_INFO; *************** *** 96,101 **** --- 105,175 ---- if (MOUNT_ROOT_RDONLY) root_mountflags |= MS_RDONLY; memory_start = (unsigned long) &end; + + /* XXX Grab the drive_info from low memory + before we invalidate page 0. */ + if (*((int*)&drive_info) == 0) + { + unsigned short *vec; + + vec = (unsigned short*)(0x41*4); + memcpy(&drive_info, (void*)(vec[0]+vec[1]*16), 16); + + /* XXX hd1 */ + } + + /* If we booted from a MultiBoot boot loader, + retrieve the boot information from the passed structure. */ + /* XXX We assume the info structure is in the first 4MB. */ + if (multiboot_info) + { + unsigned long st, ed; + int a, o; + + /* Take the command line from the multiboot_info. */ + from = (char*)multiboot_info->cmdline.start; + + /* Find the largest unoccupied memory region available + and use that as our bootstrap memory pool. */ + memory_end = memory_start; + for (a = 0; a < multiboot_info->avail.count; a++) + { + /* Find the available region's basic size. */ + st = multiboot_info->avail.regions->start; + ed = multiboot_info->avail.regions->end; + + /* Chop out any overlapping occupied regions. */ + for (o = 0; o < multiboot_info->occupied.count; o++) + { + struct multiboot_region *reg; + + reg = &multiboot_info->occupied.regions[o]; + if ((reg->start <= st) && (reg->end > st)) + st = reg->end; + if ((reg->start > st) && (reg->end < ed)) + { + if ((reg->start - st) + > (ed - reg->end)) + ed = reg->start; + else + st = reg->end; + } + if ((reg->start < ed) && (reg->end >= ed)) + ed = reg->start; + } + + /* Take the largest unoccupied region. */ + if ((ed - st) > (memory_end - memory_start)) + { + memory_start = st; + memory_end = ed; + } + } + + /* XXX Later, go back and soak up all memory available, + including the occupied regions. */ + } + init_task.mm->start_code = TASK_SIZE; init_task.mm->end_code = TASK_SIZE + (unsigned long) &etext; init_task.mm->end_data = TASK_SIZE + (unsigned long) &edata; diff -crN olin/drivers/char/console.c linux/drivers/char/console.c *** olin/drivers/char/console.c Wed Mar 29 12:57:22 1995 --- linux/drivers/char/console.c Sat Apr 1 10:58:54 1995 *************** *** 1894,1900 **** if (tty_register_driver(&console_driver)) panic("Couldn't register console driver\n"); ! con_setsize(ORIG_VIDEO_LINES, ORIG_VIDEO_COLS); video_page = ORIG_VIDEO_PAGE; /* never used */ timer_table[BLANK_TIMER].fn = blank_screen; --- 1894,1903 ---- if (tty_register_driver(&console_driver)) panic("Couldn't register console driver\n"); ! if (ORIG_VIDEO_LINES > 0) ! con_setsize(ORIG_VIDEO_LINES, ORIG_VIDEO_COLS); ! else ! con_setsize(25, 80); video_page = ORIG_VIDEO_PAGE; /* never used */ timer_table[BLANK_TIMER].fn = blank_screen; diff -crN olin/include/linux/multiboot.h linux/include/linux/multiboot.h *** olin/include/linux/multiboot.h Wed Dec 31 17:00:00 1969 --- linux/include/linux/multiboot.h Sat Apr 1 18:14:38 1995 *************** *** 0 **** --- 1,120 ---- + /* + * Copyright (c) 1995-1994 The University of Utah and + * the Computer Systems Laboratory at the University of Utah (CSL). + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Author: Bryan Ford, University of Utah CSL + */ + #ifndef _LINUX_I386_MULTIBOOT_H_ + #define _LINUX_I386_MULTIBOOT_H_ + + struct multiboot_region + { + unsigned start; + unsigned end; + }; + + struct multiboot_rlist + { + int count; + struct multiboot_region *regions; + }; + + struct multiboot_module + { + /* Location and size of the module. */ + struct multiboot_region region; + + /* Command-line associated with this boot module: + a null-terminated ASCII string. + Both start and end are 0 if there is no command line. + The end pointer points at least one byte past the terminating null. */ + struct multiboot_region cmdline; + + /* Reserved; boot loader must initialize to zero. */ + int pad[4]; + }; + + /* The is the structure the boot loader passes to the OS in eax. */ + struct multiboot_info + { + /* List of available physical memory regions. + Can (and probably does) include the memory containing + the kernel, boot modules, this structure, etc. */ + struct multiboot_rlist avail; + + /* Physical memory region occupied by things the boot loader set up + and the OS shouldn't clobber at least until it's all done initializing itself. + This includes the kernel image, boot modules, these structures, + initial processor tables, etc. */ + struct multiboot_rlist occupied; + + /* Command-line for the OS kernel: a null-terminated ASCII string. + Both start and end are 0 if there is no command line. + The end pointer points at least one byte past the terminating null. */ + struct multiboot_region cmdline; + + /* Secondary boot modules loaded with this kernel image. */ + int nmods; + struct multiboot_module *mods; + + /* Reserved; boot loader must initialize to zero. */ + int pad[4]; + }; + + /* This structure must be embedded in the first MULTIBOOT_SEARCH bytes + of the OS boot image, aligned on a 4-byte boundary. */ + struct multiboot_header + { + /* Must be MULTIBOOT_MAGIC */ + unsigned magic; + + /* Feature flags - see below. */ + unsigned flags; + + /* These are only valid if MULTIBOOT_AOUT_KLUDGE is set. */ + unsigned header_addr; + unsigned load_addr; + unsigned load_end_addr; + unsigned bss_end_addr; + unsigned entry; + }; + + /* The entire multiboot_header must be contained + within the first MULTIBOOT_SEARCH bytes of the kernel image. */ + #define MULTIBOOT_SEARCH 8192 + + /* Magic value identifying the multiboot_header. */ + #define MULTIBOOT_MAGIC 0x1badb002 + + /* Features flags for 'flags'. + If a boot loader sees a flag in MULTIBOOT_MUSTKNOW set + and it doesn't understand it, it must fail. */ + #define MULTIBOOT_MUSTKNOW 0x0000ffff + + /* Align all boot modules on page (4KB) boundaries. */ + #define MULTIBOOT_PAGE_ALIGN 0x00000001 + + /* Use the load address fields above instead of the ones in the a.out header + to figure out what to load where, and what to do afterwards. + This should only be needed for a.out kernel images + (ELF and other sensible formats can generally provide the needed information). */ + #define MULTIBOOT_AOUT_KLUDGE 0x00010000 + + + #endif /* _LINUX_I386_MULTIBOOT_H_ */