diff --git a/ChangeLog b/ChangeLog index e6ffba2c4..6b977b685 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,74 @@ +2003-01-17 Yoshinori K. Okuji + + * include/pupa/i386/pc/linux.h: New file. + * loader/i386/pc/linux.c: Likewise. + + * loader/i386/pc/chainloader.c (pupa_chainloader_boot_sector): + Removed. + (pupa_chainloader_unload): Return PUPA_ERR_NONE. + (pupa_rescue_cmd_chainloader): Read the image to 0x7C00 instead + of PUPA_CHAINLOADER_BOOT_SECTOR. + + * kern/i386/pc/startup.S: Include pupa/machine/linux.h. + (pupa_linux_prot_size): New variable. + (pupa_linux_tmp_addr): Likewise. + (pupa_linux_real_addr): Likewise. + (pupa_linux_boot_zimage): New function. + (pupa_linux_boot_bzimage): Likewise. + + * kern/i386/pc/init.c (struct mem_region): New structure. + (MAX_REGIONS): New macro. + (mem_regions): New variable. + (num_regions): Likewise. + (pupa_os_area_addr): Likewise. + (pupa_os_area_size): Likewise. + (pupa_lower_mem): Likewise. + (pupa_upper_mem): Likewise. + (add_mem_region): New function. + (compact_mem_regions): Likewise. + (pupa_machine_init): Set PUPA_LOWER_MEM and PUPA_UPPER_MEM to + the size of the conventional memory and that of so-called upper + memory (before the first memory hole). + Instead of adding each found region to free memory, use + add_mem_region and add them after removing overlaps. + Also, add only 1/4 of the upper memory to free memory. The rest + is used for loading OS images. Maybe this is ad hoc, but this + makes it much easier to relocate OS images when booting. + + * kern/rescue.c (pupa_rescue_cmd_module): Removed. + (pupa_enter_rescue_mode): Don't register initrd and module. + + * kern/mm.c: Include pupa/dl.h. + + * kern/main.c: Include pupa/file.h and pupa/device.h. + + * kern/loader.c (pupa_loader_load_module_func): Removed. + (pupa_loader_load_module): Likewise. + + * kern/dl.c (pupa_dl_load): Use the suffix ``.mod'' instead of + ``.o''. + + * include/pupa/i386/pc/loader.h (pupa_linux_prot_size): Declared. + (pupa_linux_tmp_addr): Likewise. + (pupa_linux_real_addr): Likewise. + (pupa_linux_boot_zimage): Likewise. + (pupa_linux_boot_bzimage): Likewise. + + * include/pupa/i386/pc/init.h (pupa_lower_mem): Declared. + (pupa_upper_mem): Likewise. + (pupa_gate_a20): Don't export, because turning off Gate A20 in a + module is too dangerous. + + * include/pupa/loader.h (pupa_os_area_addr): Declared. + (pupa_os_area_size): Likewise. + (pupa_loader_set): Remove the first argument. Loader doesn't + manage modules or initrd any longer. + (pupa_loader_load_module): Removed. + + * conf/i386-pc.rmk (pkgdata_MODULES): Added linux.mod. + (linux_mod_SOURCES): New variable. + (linux_mod_CFLAGS): Likewise. + 2003-01-07 Yoshinori K. Okuji * util/i386/pc/pupa-setup.c (setup): Convert the endianness of diff --git a/NEWS b/NEWS index ad72cbd42..974707acc 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,8 @@ New in 0.7: * New commands, "prefix", "insmod", "rmmod" and "lsmod" are added into the rescue mode to manipulate PUPA modules. +* Linux support is added. Initrd is not support yet. + New in 0.6 - 2002-12-27, Yoshinori K. Okuji: diff --git a/conf/i386-pc.mk b/conf/i386-pc.mk index 3744d0894..af2ae4140 100644 --- a/conf/i386-pc.mk +++ b/conf/i386-pc.mk @@ -393,7 +393,7 @@ genmoddep-util_genmoddep.d: util/genmoddep.c # Modules. -pkgdata_MODULES = chain.mod fat.mod +pkgdata_MODULES = chain.mod fat.mod linux.mod # For chain.mod. chain_mod_SOURCES = loader/i386/pc/chainloader.c @@ -472,6 +472,45 @@ fat_mod-fs_fat.d: fs/fat.c -include fat_mod-fs_fat.d fat_mod_CFLAGS = $(COMMON_CFLAGS) + +# For linux.mod. +linux_mod_SOURCES = loader/i386/pc/linux.c +CLEANFILES += linux.mod mod-linux.o mod-linux.c pre-linux.o linux_mod-loader_i386_pc_linux.o def-linux.lst und-linux.lst +MOSTLYCLEANFILES += linux_mod-loader_i386_pc_linux.d +DEFSYMFILES += def-linux.lst +UNDSYMFILES += und-linux.lst + +linux.mod: pre-linux.o mod-linux.o + -rm -f $@ + $(LD) -r -o $@ $^ + $(STRIP) --strip-unneeded -K pupa_mod_init -K pupa_mod_fini -R .note -R .comment $@ + +pre-linux.o: linux_mod-loader_i386_pc_linux.o + -rm -f $@ + $(LD) -r -o $@ $^ + +mod-linux.o: mod-linux.c + $(CC) $(CPPFLAGS) $(CFLAGS) $(linux_mod_CFLAGS) -c -o $@ $< + +mod-linux.c: moddep.lst genmodsrc.sh + sh $(srcdir)/genmodsrc.sh 'linux' $< > $@ || (rm -f $@; exit 1) + +def-linux.lst: pre-linux.o + $(NM) -g --defined-only -P -p $< | sed 's/^\([^ ]*\).*/\1 linux/' > $@ + +und-linux.lst: pre-linux.o + echo 'linux' > $@ + $(NM) -u -P -p $< >> $@ + +linux_mod-loader_i386_pc_linux.o: loader/i386/pc/linux.c + $(CC) -Iloader/i386/pc -I$(srcdir)/loader/i386/pc $(CPPFLAGS) $(CFLAGS) $(linux_mod_CFLAGS) -c -o $@ $< + +linux_mod-loader_i386_pc_linux.d: loader/i386/pc/linux.c + set -e; $(CC) -Iloader/i386/pc -I$(srcdir)/loader/i386/pc $(CPPFLAGS) $(CFLAGS) $(linux_mod_CFLAGS) -M $< | sed 's,linux\.o[ :]*,linux_mod-loader_i386_pc_linux.o $@ : ,g' > $@; [ -s $@ ] || rm -f $@ + +-include linux_mod-loader_i386_pc_linux.d + +linux_mod_CFLAGS = $(COMMON_CFLAGS) CLEANFILES += moddep.lst pkgdata_DATA += moddep.lst moddep.lst: $(DEFSYMFILES) $(UNDSYMFILES) genmoddep diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk index cfa6550d7..132f4189d 100644 --- a/conf/i386-pc.rmk +++ b/conf/i386-pc.rmk @@ -60,7 +60,7 @@ pupa_setup_SOURCES = util/i386/pc/pupa-setup.c util/i386/pc/biosdisk.c \ genmoddep_SOURCES = util/genmoddep.c # Modules. -pkgdata_MODULES = chain.mod fat.mod +pkgdata_MODULES = chain.mod fat.mod linux.mod # For chain.mod. chain_mod_SOURCES = loader/i386/pc/chainloader.c @@ -69,3 +69,7 @@ chain_mod_CFLAGS = $(COMMON_CFLAGS) # For fat.mod. fat_mod_SOURCES = fs/fat.c fat_mod_CFLAGS = $(COMMON_CFLAGS) + +# For linux.mod. +linux_mod_SOURCES = loader/i386/pc/linux.c +linux_mod_CFLAGS = $(COMMON_CFLAGS) diff --git a/include/grub/i386/pc/init.h b/include/grub/i386/pc/init.h index cb9128607..e6396ee0c 100644 --- a/include/grub/i386/pc/init.h +++ b/include/grub/i386/pc/init.h @@ -23,6 +23,10 @@ #include #include +/* FIXME: Should these be declared in memory.h? */ +extern pupa_size_t EXPORT_VAR(pupa_lower_mem); +extern pupa_size_t EXPORT_VAR(pupa_upper_mem); + /* Get the memory size in KB. If EXTENDED is zero, return conventional memory, otherwise return extended memory. */ pupa_uint16_t pupa_get_memsize (int extended); @@ -45,6 +49,6 @@ pupa_uint32_t pupa_get_mmap_entry (struct pupa_machine_mmap_entry *entry, pupa_uint32_t cont); /* Turn on/off Gate A20. */ -void EXPORT_FUNC(pupa_gate_a20) (int on); +void pupa_gate_a20 (int on); #endif /* ! PUPA_INIT_MACHINE_HEADER */ diff --git a/include/grub/i386/pc/linux.h b/include/grub/i386/pc/linux.h new file mode 100644 index 000000000..942b09df5 --- /dev/null +++ b/include/grub/i386/pc/linux.h @@ -0,0 +1,85 @@ +/* + * PUPA -- Preliminary Universal Programming Architecture for GRUB + * Copyright (C) 1999,2000,2001,2002 Free Software Foundation, Inc. + * Copyright (C) 2003 Yoshinori K. Okuji + * + * 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. + */ + +#ifndef PUPA_LINUX_MACHINE_HEADER +#define PUPA_LINUX_MACHINE_HEADER 1 + +#define PUPA_LINUX_MAGIC_SIGNATURE 0x53726448 /* "HdrS" */ +#define PUPA_LINUX_DEFAULT_SETUP_SECTS 4 +#define PUPA_LINUX_FLAG_CAN_USE_HEAP 0x80 +#define PUPA_LINUX_INITRD_MAX_ADDRESS 0x38000000 +#define PUPA_LINUX_MAX_SETUP_SECTS 64 +#define PUPA_LINUX_BOOT_LOADER_TYPE 0x72 +#define PUPA_LINUX_HEAP_END_OFFSET (0x9000 - 0x200) + +#define PUPA_LINUX_BZIMAGE_ADDR 0x100000 +#define PUPA_LINUX_ZIMAGE_ADDR 0x10000 +#define PUPA_LINUX_OLD_REAL_MODE_ADDR 0x90000 +#define PUPA_LINUX_SETUP_STACK 0x9000 + +#define PUPA_LINUX_FLAG_BIG_KERNEL 0x1 + +/* Linux's video mode selection support. Actually I hate it! */ +#define PUPA_LINUX_VID_MODE_NORMAL 0xFFFF +#define PUPA_LINUX_VID_MODE_EXTENDED 0xFFFE +#define PUPA_LINUX_VID_MODE_ASK 0xFFFD + +#define PUPA_LINUX_CL_OFFSET 0x9000 +#define PUPA_LINUX_CL_END_OFFSET 0x90FF +#define PUPA_LINUX_SETUP_MOVE_SIZE 0x9100 +#define PUPA_LINUX_CL_MAGIC 0xA33F + +#ifndef ASM_FILE + +/* For the Linux/i386 boot protocol version 2.03. */ +struct linux_kernel_header +{ + pupa_uint8_t code1[0x0020]; + pupa_uint16_t cl_magic; /* Magic number 0xA33F */ + pupa_uint16_t cl_offset; /* The offset of command line */ + pupa_uint8_t code2[0x01F1 - 0x0020 - 2 - 2]; + pupa_uint8_t setup_sects; /* The size of the setup in sectors */ + pupa_uint16_t root_flags; /* If the root is mounted readonly */ + pupa_uint16_t syssize; /* obsolete */ + pupa_uint16_t swap_dev; /* obsolete */ + pupa_uint16_t ram_size; /* obsolete */ + pupa_uint16_t vid_mode; /* Video mode control */ + pupa_uint16_t root_dev; /* Default root device number */ + pupa_uint16_t boot_flag; /* 0xAA55 magic number */ + pupa_uint16_t jump; /* Jump instruction */ + pupa_uint32_t header; /* Magic signature "HdrS" */ + pupa_uint16_t version; /* Boot protocol version supported */ + pupa_uint32_t realmode_swtch; /* Boot loader hook */ + pupa_uint32_t start_sys; /* Points to kernel version string */ + pupa_uint8_t type_of_loader; /* Boot loader identifier */ + pupa_uint8_t loadflags; /* Boot protocol option flags */ + pupa_uint16_t setup_move_size; /* Move to high memory size */ + pupa_uint32_t code32_start; /* Boot loader hook */ + pupa_uint32_t ramdisk_image; /* initrd load address */ + pupa_uint32_t ramdisk_size; /* initrd size */ + pupa_uint32_t bootsect_kludge; /* obsolete */ + pupa_uint16_t heap_end_ptr; /* Free memory after setup end */ + pupa_uint16_t pad1; /* Unused */ + char *cmd_line_ptr; /* Points to the kernel command line */ +} __attribute__ ((packed)); + +#endif /* ! ASM_FILE */ + +#endif /* ! PUPA_LINUX_MACHINE_HEADER */ diff --git a/include/grub/i386/pc/loader.h b/include/grub/i386/pc/loader.h index d2dcc9136..b81a65c1d 100644 --- a/include/grub/i386/pc/loader.h +++ b/include/grub/i386/pc/loader.h @@ -23,6 +23,13 @@ #include #include +extern pupa_uint32_t EXPORT_VAR(pupa_linux_prot_size); +extern char *EXPORT_VAR(pupa_linux_tmp_addr); +extern char *EXPORT_VAR(pupa_linux_real_addr); + +void EXPORT_FUNC(pupa_linux_boot_zimage) (void) __attribute__ ((noreturn)); +void EXPORT_FUNC(pupa_linux_boot_bzimage) (void) __attribute__ ((noreturn)); + /* This is an asm part of the chainloader. */ void EXPORT_FUNC(pupa_chainloader_real_boot) (int drive, void *part_addr) __attribute__ ((noreturn)); diff --git a/include/grub/loader.h b/include/grub/loader.h index ea2d049cd..7d190406d 100644 --- a/include/grub/loader.h +++ b/include/grub/loader.h @@ -26,12 +26,12 @@ #include #include -void EXPORT_FUNC(pupa_loader_set) (pupa_err_t (*load_module) (int argc, - char *argv[]), - pupa_err_t (*boot) (void), +extern pupa_addr_t EXPORT_VAR(pupa_os_area_addr); +extern pupa_size_t EXPORT_VAR(pupa_os_area_size); + +void EXPORT_FUNC(pupa_loader_set) (pupa_err_t (*boot) (void), pupa_err_t (*unload) (void)); -pupa_err_t EXPORT_FUNC(pupa_loader_load_module) (int argc, char *argv[]); pupa_err_t EXPORT_FUNC(pupa_loader_boot) (void); #endif /* ! PUPA_LOADER_HEADER */ diff --git a/kern/dl.c b/kern/dl.c index 8bf0cdeba..7066aded7 100644 --- a/kern/dl.c +++ b/kern/dl.c @@ -569,11 +569,11 @@ pupa_dl_load (const char *name) pupa_fatal ("module dir is not initialized yet"); filename = (char *) pupa_malloc (pupa_strlen (pupa_dl_dir) + 1 - + pupa_strlen (name) + 3); + + pupa_strlen (name) + 4 + 1); if (! filename) return 0; - pupa_sprintf (filename, "%s/%s.o", pupa_dl_dir, name); + pupa_sprintf (filename, "%s/%s.mod", pupa_dl_dir, name); mod = pupa_dl_load_file (filename); pupa_free (filename); diff --git a/kern/i386/pc/init.c b/kern/i386/pc/init.c index 604cc29b2..7e0db5c03 100644 --- a/kern/i386/pc/init.c +++ b/kern/i386/pc/init.c @@ -28,6 +28,22 @@ #include #include #include +#include + +struct mem_region +{ + pupa_addr_t addr; + pupa_size_t size; +}; + +#define MAX_REGIONS 32 + +static struct mem_region mem_regions[MAX_REGIONS]; +static int num_regions; + +pupa_addr_t pupa_os_area_addr; +pupa_size_t pupa_os_area_size; +pupa_size_t pupa_lower_mem, pupa_upper_mem; static char * make_install_device (void) @@ -51,32 +67,79 @@ make_install_device (void) return pupa_prefix; } +/* Add a memory region. */ +static void +add_mem_region (pupa_addr_t addr, pupa_size_t size) +{ + if (num_regions == MAX_REGIONS) + /* Ignore. */ + return; + + mem_regions[num_regions].addr = addr; + mem_regions[num_regions].size = size; + num_regions++; +} + +/* Compact memory regions. */ +static void +compact_mem_regions (void) +{ + int i, j; + + /* Sort them. */ + for (i = 0; i < num_regions - 1; i++) + for (j = i + 1; j < num_regions; j++) + if (mem_regions[i].addr > mem_regions[j].addr) + { + struct mem_region tmp = mem_regions[i]; + mem_regions[i] = mem_regions[j]; + mem_regions[j] = tmp; + } + + /* Merge overlaps. */ + for (i = 0; i < num_regions - 1; i++) + if (mem_regions[i].addr + mem_regions[i].size >= mem_regions[i + 1].addr) + { + j = i + 1; + + if (mem_regions[i].addr + mem_regions[i].size + < mem_regions[j].addr + mem_regions[j].size) + mem_regions[i].size = (mem_regions[j].addr + mem_regions[j].size + - mem_regions[i].addr); + + pupa_memmove (mem_regions + j, mem_regions + j + 1, + (num_regions - j - 1) * sizeof (struct mem_region)); + i--; + } +} + void pupa_machine_init (void) { pupa_uint32_t cont; struct pupa_machine_mmap_entry *entry = (struct pupa_machine_mmap_entry *) PUPA_MEMORY_MACHINE_SCRATCH_ADDR; - pupa_size_t lower_mem = (pupa_get_memsize (0) << 10); pupa_addr_t end_addr = pupa_get_end_addr (); - + int i; + /* Initialize the console as early as possible. */ pupa_console_init (); + pupa_lower_mem = pupa_get_memsize (0) << 10; + /* Sanity check. */ - if (lower_mem < PUPA_MEMORY_MACHINE_RESERVED_END) + if (pupa_lower_mem < PUPA_MEMORY_MACHINE_RESERVED_END) pupa_fatal ("too small memory"); /* Turn on Gate A20 to access >1MB. */ pupa_gate_a20 (1); /* Add the lower memory into free memory. */ - if (lower_mem >= PUPA_MEMORY_MACHINE_RESERVED_END) - pupa_mm_init_region ((void *) PUPA_MEMORY_MACHINE_RESERVED_END, - lower_mem - PUPA_MEMORY_MACHINE_RESERVED_END); + if (pupa_lower_mem >= PUPA_MEMORY_MACHINE_RESERVED_END) + add_mem_region (PUPA_MEMORY_MACHINE_RESERVED_END, + pupa_lower_mem - PUPA_MEMORY_MACHINE_RESERVED_END); - pupa_mm_init_region ((void *) end_addr, - PUPA_MEMORY_MACHINE_RESERVED_START - end_addr); + add_mem_region (end_addr, PUPA_MEMORY_MACHINE_RESERVED_START - end_addr); /* Check if pupa_get_mmap_entry works. */ cont = pupa_get_mmap_entry (entry, 0); @@ -104,7 +167,7 @@ pupa_machine_init (void) len = ((addr + entry->len > 0xFFFFFFFF) ? 0xFFFFFFFF - addr : (pupa_size_t) entry->len); - pupa_mm_init_region ((void *) addr, len); + add_mem_region (addr, len); } next: @@ -121,20 +184,39 @@ pupa_machine_init (void) if (eisa_mmap) { if ((eisa_mmap & 0xFFFF) == 0x3C00) - pupa_mm_init_region ((void *) 0x100000, - (eisa_mmap << 16) + 0x100000 * 15); + add_mem_region (0x100000, (eisa_mmap << 16) + 0x100000 * 15); else { - pupa_mm_init_region ((void *) 0x100000, - (eisa_mmap & 0xFFFF) << 10); - pupa_mm_init_region ((void *) 0x1000000, eisa_mmap << 16); + add_mem_region (0x100000, (eisa_mmap & 0xFFFF) << 10); + add_mem_region (0x1000000, eisa_mmap << 16); } } else - pupa_mm_init_region ((void *) 0x100000, - (pupa_size_t) pupa_get_memsize (1) << 10); + add_mem_region (0x100000, pupa_get_memsize (1) << 10); } + compact_mem_regions (); + + /* Add the memory regions to free memory, except for the region starting + from 1MB. This region is partially used for loading OS images. + For now, 1/4 of this is added to free memory. */ + for (i = 0; i < num_regions; i++) + if (mem_regions[i].addr == 0x100000) + { + pupa_size_t quarter = mem_regions[i].size >> 2; + + pupa_upper_mem = mem_regions[i].size; + pupa_os_area_addr = mem_regions[i].addr; + pupa_os_area_size = mem_regions[i].size - quarter; + pupa_mm_init_region ((void *) (pupa_os_area_addr + pupa_os_area_size), + quarter); + } + else + pupa_mm_init_region ((void *) mem_regions[i].addr, mem_regions[i].size); + + if (! pupa_os_area_addr) + pupa_fatal ("no upper memory"); + /* The memory system was initialized, thus register built-in devices. */ pupa_biosdisk_init (); diff --git a/kern/i386/pc/startup.S b/kern/i386/pc/startup.S index a1f12cf23..cafcc3810 100644 --- a/kern/i386/pc/startup.S +++ b/kern/i386/pc/startup.S @@ -49,7 +49,8 @@ #include #include #include - +#include + #define ABS(x) ((x) - EXT_C(start) + PUPA_BOOT_MACHINE_KERNEL_ADDR + 0x200) .file "startup.S" @@ -282,6 +283,71 @@ FUNCTION(pupa_chainloader_real_boot) .code32 +/* + * void pupa_linux_boot_zimage (void) + */ +VARIABLE(pupa_linux_prot_size) + .long 0 +VARIABLE(pupa_linux_tmp_addr) + .long 0 +VARIABLE(pupa_linux_real_addr) + .long 0 + +FUNCTION(pupa_linux_boot_zimage) + /* copy the kernel */ + movl EXT_C(pupa_linux_prot_size), %ecx + addl $3, %ecx + shrl $2, %ecx + movl $PUPA_LINUX_BZIMAGE_ADDR, %esi + movl $PUPA_LINUX_ZIMAGE_ADDR, %edi + cld + rep + movsl + +FUNCTION(pupa_linux_boot_bzimage) + call EXT_C(pupa_dl_unload_all) + + movl EXT_C(pupa_linux_real_addr), %ebx + + /* copy the real mode code */ + movl EXT_C(pupa_linux_tmp_addr), %esi + movl %ebx, %edi + movl $PUPA_LINUX_SETUP_MOVE_SIZE, %ecx + cld + rep + movsb + + /* change %ebx to the segment address */ + shrl $4, %ebx + movl %ebx, %eax + addl $0x20, %eax + movw %ax, linux_setup_seg + + /* XXX new stack pointer in safe area for calling functions */ + movl $0x4000, %esp + call EXT_C(pupa_stop_floppy) + + /* final setup for linux boot */ + call prot_to_real + .code16 + + cli + movw %bx, %ss + movw $PUPA_LINUX_SETUP_STACK, %sp + + movw %bx, %ds + movw %bx, %es + movw %bx, %fs + movw %bx, %gs + + /* ljmp */ + .byte 0xea + .word 0 +linux_setup_seg: + .word 0 + .code32 + + /* * These next two routines, "real_to_prot" and "prot_to_real" are structured * in a very specific way. Be very careful when changing them. diff --git a/kern/loader.c b/kern/loader.c index f7d8ebc89..65ee654bf 100644 --- a/kern/loader.c +++ b/kern/loader.c @@ -22,40 +22,25 @@ #include #include -static pupa_err_t (*pupa_loader_load_module_func) (int argc, char *argv[]); static pupa_err_t (*pupa_loader_boot_func) (void); static pupa_err_t (*pupa_loader_unload_func) (void); static int pupa_loader_loaded; void -pupa_loader_set (pupa_err_t (*load_module) (int argc, char *argv[]), - pupa_err_t (*boot) (void), +pupa_loader_set (pupa_err_t (*boot) (void), pupa_err_t (*unload) (void)) { if (pupa_loader_loaded && pupa_loader_unload_func) if (pupa_loader_unload_func () != PUPA_ERR_NONE) return; - pupa_loader_load_module_func = load_module; pupa_loader_boot_func = boot; pupa_loader_unload_func = unload; pupa_loader_loaded = 1; } -pupa_err_t -pupa_loader_load_module (int argc, char *argv[]) -{ - if (! pupa_loader_loaded) - return pupa_error (PUPA_ERR_NO_KERNEL, "no loaded kernel"); - - if (! pupa_loader_load_module_func) - return pupa_error (PUPA_ERR_BAD_OS, "module not supported"); - - return pupa_loader_load_module_func (argc, argv); -} - pupa_err_t pupa_loader_boot (void) { diff --git a/kern/main.c b/kern/main.c index 80eb6f7f1..a870b84e4 100644 --- a/kern/main.c +++ b/kern/main.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include /* Return the end of the core image. */ pupa_addr_t diff --git a/kern/mm.c b/kern/mm.c index 5f8e6c9fe..7ba30fcb0 100644 --- a/kern/mm.c +++ b/kern/mm.c @@ -24,6 +24,7 @@ #include #include #include +#include /* Magic words. */ #define PUPA_MM_FREE_MAGIC 0x2d3c2808 @@ -95,6 +96,10 @@ pupa_mm_init_region (void *addr, pupa_size_t size) pupa_mm_header_t h; pupa_mm_region_t r, *p, q; +#if 0 + pupa_printf ("%s:%d: addr=%p, size=%u\n", __FILE__, __LINE__, addr, size); +#endif + /* If this region is too small, ignore it. */ if (size < PUPA_MM_ALIGN * 2) return; diff --git a/kern/rescue.c b/kern/rescue.c index abec35034..36b197998 100644 --- a/kern/rescue.c +++ b/kern/rescue.c @@ -337,13 +337,6 @@ pupa_rescue_cmd_info (void) } #endif -/* (module|initrd) FILE [ARGS] */ -static void -pupa_rescue_cmd_module (int argc, char *argv[]) -{ - pupa_loader_load_module (argc, argv); -} - /* root [DEVICE] */ static void pupa_rescue_cmd_root (int argc, char *argv[]) @@ -607,12 +600,8 @@ pupa_enter_rescue_mode (void) "show the contents of a file"); pupa_rescue_register_command ("help", pupa_rescue_cmd_help, "show this message"); - pupa_rescue_register_command ("initrd", pupa_rescue_cmd_module, - "load an initrd"); pupa_rescue_register_command ("ls", pupa_rescue_cmd_ls, "list devices or files"); - pupa_rescue_register_command ("module", pupa_rescue_cmd_module, - "load an OS module"); pupa_rescue_register_command ("root", pupa_rescue_cmd_root, "set the root device"); pupa_rescue_register_command ("dump", pupa_rescue_cmd_dump, diff --git a/loader/i386/pc/chainloader.c b/loader/i386/pc/chainloader.c index cfcc88dbb..5562bb289 100644 --- a/loader/i386/pc/chainloader.c +++ b/loader/i386/pc/chainloader.c @@ -32,9 +32,6 @@ #include #include -/* Allocate space statically, because this is very small anyway. */ -static char pupa_chainloader_boot_sector[PUPA_DISK_SECTOR_SIZE]; - static pupa_dl_t my_mod; static pupa_err_t @@ -81,6 +78,7 @@ static pupa_err_t pupa_chainloader_unload (void) { pupa_dl_unref (my_mod); + return PUPA_ERR_NONE; } static void @@ -110,8 +108,8 @@ pupa_rescue_cmd_chainloader (int argc, char *argv[]) goto fail; /* Read the first block. */ - if (pupa_file_read (file, pupa_chainloader_boot_sector, - PUPA_DISK_SECTOR_SIZE) != PUPA_DISK_SECTOR_SIZE) + if (pupa_file_read (file, (char *) 0x7C00, PUPA_DISK_SECTOR_SIZE) + != PUPA_DISK_SECTOR_SIZE) { if (pupa_errno == PUPA_ERR_NONE) pupa_error (PUPA_ERR_BAD_OS, "too small"); @@ -120,8 +118,7 @@ pupa_rescue_cmd_chainloader (int argc, char *argv[]) } /* Check the signature. */ - signature = *((pupa_uint16_t *) (pupa_chainloader_boot_sector - + PUPA_DISK_SECTOR_SIZE - 2)); + signature = *((pupa_uint16_t *) (0x7C00 + PUPA_DISK_SECTOR_SIZE - 2)); if (signature != pupa_le_to_cpu16 (0xaa55) && ! force) { pupa_error (PUPA_ERR_BAD_OS, "invalid signature"); @@ -129,7 +126,7 @@ pupa_rescue_cmd_chainloader (int argc, char *argv[]) } pupa_file_close (file); - pupa_loader_set (0, pupa_chainloader_boot, pupa_chainloader_unload); + pupa_loader_set (pupa_chainloader_boot, pupa_chainloader_unload); return; fail: diff --git a/loader/i386/pc/linux.c b/loader/i386/pc/linux.c new file mode 100644 index 000000000..8dbffe8b4 --- /dev/null +++ b/loader/i386/pc/linux.c @@ -0,0 +1,306 @@ +/* linux.c - boot Linux zImage or bzImage */ +/* + * PUPA -- Preliminary Universal Programming Architecture for GRUB + * Copyright (C) 1999,2000,2001,2002 Free Software Foundation, Inc. + * Copyright (C) 2003 Yoshinori K. Okuji + * + * 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 +#include +#include + +static pupa_dl_t my_mod; + +static int big_linux; +static pupa_size_t linux_mem_size; +static int loaded; + +static pupa_err_t +pupa_linux_boot (void) +{ + if (big_linux) + pupa_linux_boot_bzimage (); + else + pupa_linux_boot_zimage (); + + /* Never reach here. */ + return PUPA_ERR_NONE; +} + +static pupa_err_t +pupa_linux_unload (void) +{ + pupa_dl_unref (my_mod); + loaded = 0; + return PUPA_ERR_NONE; +} + +static void +pupa_rescue_cmd_linux (int argc, char *argv[]) +{ + pupa_file_t file = 0; + struct linux_kernel_header lh; + pupa_uint8_t setup_sects; + pupa_size_t real_size, prot_size; + int i; + char *dest; + + pupa_dl_ref (my_mod); + + if (argc == 0) + { + pupa_error (PUPA_ERR_BAD_ARGUMENT, "no kernel specified"); + goto fail; + } + + file = pupa_file_open (argv[0]); + if (! file) + goto fail; + + if (pupa_file_size (file) > (pupa_ssize_t) pupa_os_area_size) + { + pupa_error (PUPA_ERR_OUT_OF_RANGE, "too big kernel"); + goto fail; + } + + if (pupa_file_read (file, (char *) &lh, sizeof (lh)) != sizeof (lh)) + { + pupa_error (PUPA_ERR_READ_ERROR, "cannot read the linux header"); + goto fail; + } + + if (lh.boot_flag != pupa_cpu_to_le16 (0xaa55)) + { + pupa_error (PUPA_ERR_BAD_OS, "invalid magic number"); + goto fail; + } + + if (lh.setup_sects > PUPA_LINUX_MAX_SETUP_SECTS) + { + pupa_error (PUPA_ERR_BAD_OS, "too many setup sectors"); + goto fail; + } + + big_linux = 0; + setup_sects = lh.setup_sects; + linux_mem_size = 0; + + if (lh.header == pupa_cpu_to_le32 (PUPA_LINUX_MAGIC_SIGNATURE) + && pupa_le_to_cpu16 (lh.version) >= 0x0200) + { + big_linux = (lh.loadflags & PUPA_LINUX_FLAG_BIG_KERNEL); + lh.type_of_loader = PUPA_LINUX_BOOT_LOADER_TYPE; + + /* Put the real mode part at as a high location as possible. */ + pupa_linux_real_addr = (char *) (pupa_lower_mem + - PUPA_LINUX_SETUP_MOVE_SIZE); + /* But it must not exceed the traditional area. */ + if (pupa_linux_real_addr > (char *) PUPA_LINUX_OLD_REAL_MODE_ADDR) + pupa_linux_real_addr = (char *) PUPA_LINUX_OLD_REAL_MODE_ADDR; + + if (pupa_le_to_cpu16 (lh.version) >= 0x0201) + { + lh.heap_end_ptr = pupa_cpu_to_le16 (PUPA_LINUX_HEAP_END_OFFSET); + lh.loadflags |= PUPA_LINUX_FLAG_CAN_USE_HEAP; + } + + if (pupa_le_to_cpu16 (lh.version) >= 0x0202) + lh.cmd_line_ptr = pupa_linux_real_addr + PUPA_LINUX_CL_OFFSET; + else + { + lh.cl_magic = pupa_cpu_to_le16 (PUPA_LINUX_CL_MAGIC); + lh.cl_offset = pupa_cpu_to_le16 (PUPA_LINUX_CL_OFFSET); + lh.setup_move_size = pupa_cpu_to_le16 (PUPA_LINUX_SETUP_MOVE_SIZE); + } + } + else + { + /* Your kernel is quite old... */ + lh.cl_magic = pupa_cpu_to_le16 (PUPA_LINUX_CL_MAGIC); + lh.cl_offset = pupa_cpu_to_le16 (PUPA_LINUX_CL_OFFSET); + + setup_sects = PUPA_LINUX_DEFAULT_SETUP_SECTS; + + pupa_linux_real_addr = (char *) PUPA_LINUX_OLD_REAL_MODE_ADDR; + } + + /* If SETUP_SECTS is not set, set it to the default (4). */ + if (! setup_sects) + setup_sects = PUPA_LINUX_DEFAULT_SETUP_SECTS; + + real_size = setup_sects << PUPA_DISK_SECTOR_BITS; + prot_size = pupa_file_size (file) - real_size - PUPA_DISK_SECTOR_SIZE; + + pupa_linux_tmp_addr = (char *) PUPA_LINUX_BZIMAGE_ADDR + prot_size; + + if (! big_linux + && prot_size > (pupa_size_t) (pupa_linux_real_addr + - (char *) PUPA_LINUX_ZIMAGE_ADDR)) + { + pupa_error (PUPA_ERR_BAD_OS, "too big zImage, use bzImage instead"); + goto fail; + } + + if (pupa_linux_real_addr + PUPA_LINUX_SETUP_MOVE_SIZE + > (char *) pupa_lower_mem) + { + pupa_error (PUPA_ERR_OUT_OF_RANGE, "too small lower memory"); + goto fail; + } + + pupa_printf (" [Linux-%s, setup=0x%x, size=0x%x]\n", + big_linux ? "bzImage" : "zImage", real_size, prot_size); + + for (i = 1; i < argc; i++) + if (pupa_memcmp (argv[i], "vga=", 4) == 0) + { + /* Video mode selection support. */ + pupa_uint16_t vid_mode; + char *val = argv[i] + 4; + + if (pupa_strcmp (val, "normal") == 0) + vid_mode = PUPA_LINUX_VID_MODE_NORMAL; + else if (pupa_strcmp (val, "ext") == 0) + vid_mode = PUPA_LINUX_VID_MODE_EXTENDED; + else if (pupa_strcmp (val, "ask") == 0) + vid_mode = PUPA_LINUX_VID_MODE_ASK; + else + vid_mode = (pupa_uint16_t) pupa_strtoul (val, 0, 0); + + if (pupa_errno) + goto fail; + + lh.vid_mode = pupa_cpu_to_le16 (vid_mode); + } + else if (pupa_memcmp (argv[i], "mem=", 4) == 0) + { + char *val = argv[i] + 4; + + linux_mem_size = pupa_strtoul (val, &val, 0); + + if (pupa_errno) + { + pupa_errno = PUPA_ERR_NONE; + linux_mem_size = 0; + } + else + { + int shift = 0; + + switch (pupa_tolower (val[0])) + { + case 'g': + shift += 10; + case 'm': + shift += 10; + case 'k': + shift += 10; + default: + break; + } + + /* Check an overflow. */ + if (linux_mem_size > (~0UL >> shift)) + linux_mem_size = 0; + else + linux_mem_size <<= shift; + } + } + + /* Put the real mode code at the temporary address. */ + pupa_memmove (pupa_linux_tmp_addr, &lh, sizeof (lh)); + pupa_file_read (file, pupa_linux_tmp_addr + sizeof (lh), + real_size + PUPA_DISK_SECTOR_SIZE - sizeof (lh)); + + if (lh.header != pupa_cpu_to_le32 (PUPA_LINUX_MAGIC_SIGNATURE) + || pupa_le_to_cpu16 (lh.version) < 0x0200) + /* Clear the heap space. */ + pupa_memset (pupa_linux_tmp_addr + + ((setup_sects + 1) << PUPA_DISK_SECTOR_BITS), + 0, + ((PUPA_LINUX_MAX_SETUP_SECTS - setup_sects - 1) + << PUPA_DISK_SECTOR_BITS)); + + /* Copy kernel parameters. */ + for (i = 1, dest = pupa_linux_tmp_addr + PUPA_LINUX_CL_OFFSET; + i < argc + && dest + pupa_strlen (argv[i]) < (pupa_linux_tmp_addr + + PUPA_LINUX_CL_END_OFFSET); + i++, *dest++ = ' ') + { + pupa_strcpy (dest, argv[i]); + dest += pupa_strlen (argv[i]); + } + + if (i != 1) + dest--; + + *dest = '\0'; + + pupa_file_read (file, (char *) PUPA_LINUX_BZIMAGE_ADDR, prot_size); + + if (pupa_errno == PUPA_ERR_NONE) + { + pupa_linux_prot_size = prot_size; + pupa_loader_set (pupa_linux_boot, pupa_linux_unload); + loaded = 1; + } + + fail: + + if (file) + pupa_file_close (file); + + if (pupa_errno != PUPA_ERR_NONE) + { + pupa_dl_unref (my_mod); + loaded = 0; + } +} + +static void +pupa_rescue_cmd_initrd (int argc, char *argv[]) +{ + pupa_error (PUPA_ERR_NOT_IMPLEMENTED_YET, "not implemented yet"); +} + +PUPA_MOD_INIT +{ + pupa_rescue_register_command ("linux", + pupa_rescue_cmd_linux, + "load linux"); + pupa_rescue_register_command ("initrd", + pupa_rescue_cmd_initrd, + "load initrd"); + my_mod = mod; +} + +PUPA_MOD_FINI +{ + pupa_rescue_unregister_command ("linux"); + pupa_rescue_unregister_command ("initrd"); +}