/* * GRUB -- GRand Unified Bootloader * Copyright (C) 1996 Erich Boleyn * * 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. */ #define _BOOT_C #include "shared.h" #include "freebsd.h" #include "imgact_aout.h" #include "i386-elf.h" char *cur_cmdline; static int cur_addr; entry_func entry_addr; static struct mod_list mll[99]; /* * The next two functions, 'load_image' and 'load_module', are the building * blocks of the multiboot loader component. They handle essentially all * of the gory details of loading in a bootable image and the modules. */ int load_image(void) { int len, i, exec_type, align_4k = 1, type = 0; unsigned long flags = 0, text_len, data_len, bss_len; char *str, *str2; union { struct multiboot_header *mb; struct exec *aout; Elf32_Ehdr *elf; } pu; /* presuming that MULTIBOOT_SEARCH is large enough to encompass an executable header */ unsigned char buffer[MULTIBOOT_SEARCH]; /* sets the header pointer to point to the beginning of the buffer by default */ pu.aout = (struct exec *) buffer; if (!open(cur_cmdline)) return 0; if (!(len = read((int)buffer, MULTIBOOT_SEARCH)) || len < 32) { if (!errnum) errnum = ERR_EXEC_FORMAT; return 0; } for (i = 0; i < len; i++) { if (MULTIBOOT_FOUND((int)(buffer+i), len-i)) { flags = ((struct multiboot_header *) (buffer+i))->flags; if (flags & MULTIBOOT_UNSUPPORTED) { errnum = ERR_BOOT_FEATURES; return 0; } type = 'm'; str2 = "Multiboot"; break; } } /* ELF loading only supported if kernel using multiboot */ if (type == 'm' && len > sizeof(Elf32_Ehdr) && BOOTABLE_I386_ELF((*((Elf32_Ehdr *)buffer)))) { entry_addr = (entry_func) pu.elf->e_entry; if (((int)entry_addr) < 0x100000) errnum = ERR_BELOW_1MB; /* don't want to deal with ELF program header at some random place in the file -- this generally won't happen */ if (pu.elf->e_phoff == 0 || pu.elf->e_phnum == 0 || ((pu.elf->e_phoff + (pu.elf->e_phentsize * pu.elf->e_phnum)) >= len)) errnum = ERR_EXEC_FORMAT; exec_type = 0; str = "elf"; } else if (flags & MULTIBOOT_AOUT_KLUDGE) { pu.mb = (struct multiboot_header *) (buffer+i); entry_addr = (entry_func) pu.mb->entry_addr; cur_addr = pu.mb->load_addr; /* first offset into file */ filepos = i - (pu.mb->header_addr - cur_addr); text_len = pu.mb->load_end_addr - cur_addr; data_len = 0; bss_len = pu.mb->bss_end_addr - pu.mb->load_end_addr; if (pu.mb->header_addr < pu.mb->load_addr || pu.mb->load_end_addr <= pu.mb->load_addr || pu.mb->bss_end_addr < pu.mb->load_end_addr || (pu.mb->header_addr - pu.mb->load_addr) > i) errnum = ERR_EXEC_FORMAT; if (cur_addr < 0x100000) errnum = ERR_BELOW_1MB; pu.aout = (struct exec *) buffer; exec_type = 2; str = "kludge"; } else if (len > sizeof(struct exec) && !N_BADMAG((*(pu.aout)))) { entry_addr = (entry_func) pu.aout->a_entry; if (!type) { /* * If it doesn't have a Multiboot header, then presume * it is either a FreeBSD or NetBSD executable. If so, * then use a magic number of normal ordering, ZMAGIC to * determine if it is FreeBSD. * * This is all because freebsd and netbsd seem to require * masking out some address bits... differently for each * one... plus of course we need to know which booting * method to use. */ if (buffer[0] == 0xb && buffer[1] == 1) { type = 'f'; entry_addr = (entry_func) (((int)entry_addr) & 0xFFFFFF); str2 = "FreeBSD"; } else { type = 'n'; entry_addr = (entry_func) (((int)entry_addr) & 0xF00000); if (N_GETMAGIC((*(pu.aout))) != NMAGIC) align_4k = 0; str2 = "NetBSD"; } } cur_addr = (int) entry_addr; /* first offset into file */ filepos = N_TXTOFF((*(pu.aout))); text_len = pu.aout->a_text; data_len = pu.aout->a_data; bss_len = pu.aout->a_bss; if (cur_addr < 0x100000) errnum = ERR_BELOW_1MB; 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 short *) (buffer+LINUX_KERNEL_LEN_OFFSET))) << 4)) <= LINUX_KERNEL_MAXLEN) && (data_len+text_len+SECTOR_SIZE) <= ((filemax+15)&0xFFFFFFF0)) { printf(" Loading: [format=Linux-piggyback, setup=0x%x, size=0x%x]\n", data_len, text_len); if (mbi.mem_lower >= 608) { bcopy(buffer, (char *)LINUX_SETUP, data_len+SECTOR_SIZE); /* copy command-line plus memory hack to staging area */ { char *src = cur_cmdline; char *dest = (char *) (CL_MY_LOCATION+4); bcopy("mem=", (char *)CL_MY_LOCATION, 4); *((unsigned short *) CL_OFFSET) = CL_MY_LOCATION-CL_BASE_ADDR; *((unsigned short *) CL_MAGIC_ADDR) = CL_MAGIC; dest = convert_to_ascii(dest, 'u', (mbi.mem_upper+0x400)); *(dest++) = 'K'; *(dest++) = ' '; while (*src && *src != ' ') src++; while (((int)dest) < CL_MY_END_ADDR && (*(dest++) = *(src++))); *dest = 0; } /* offset into file */ filepos = data_len+SECTOR_SIZE; if (read(LINUX_STAGING_AREA, text_len) >= (text_len-16)) return 'l'; else if (!errnum) errnum = ERR_EXEC_FORMAT; } else errnum = ERR_WONT_FIT; } else /* no recognizable format */ errnum = ERR_EXEC_FORMAT; /* return if error */ if (errnum) return 0; /* fill the multiboot info structure */ mbi.cmdline = (int)cur_cmdline; mbi.mods_count = 0; mbi.mods_addr = 0; mbi.boot_device = (saved_drive << 24) | saved_partition; mbi.flags &= ~(MB_INFO_MODS | MB_INFO_AOUT_SYMS | MB_INFO_ELF_SHDR); mbi.syms.a.tabsize = 0; mbi.syms.a.strsize = 0; mbi.syms.a.addr = 0; mbi.syms.a.pad = 0; printf(" Loading: [format=%s-%s", str2, str); str = ""; if (exec_type) /* can be loaded like a.out */ { if (flags & MULTIBOOT_AOUT_KLUDGE) str = "-and-data"; printf(", loadaddr=0x%x, text%s=0x%x", cur_addr, str, text_len); /* read text, then read data */ if (read(cur_addr, text_len) == text_len) { cur_addr += text_len; if (!(flags & MULTIBOOT_AOUT_KLUDGE)) { /* we have to align to a 4K boundary */ if (align_4k) cur_addr = (cur_addr + 0xFFF) & 0xFFFFF000; else printf(", C"); printf(", data=0x%x", data_len); if (read(cur_addr, data_len) != data_len && !errnum) errnum = ERR_EXEC_FORMAT; cur_addr += data_len; } if (!errnum) { bzero((char*)cur_addr, bss_len); cur_addr += bss_len; printf(", bss=0x%x", str, bss_len); } } else if (!errnum) errnum = ERR_EXEC_FORMAT; if (!errnum && pu.aout->a_syms && pu.aout->a_syms < (filemax - filepos)) { int symtab_err, orig_addr = cur_addr; /* we should align to a 4K boundary here for good measure */ cur_addr = (cur_addr + 0xFFF) & 0xFFFFF000; mbi.syms.a.addr = cur_addr; *(((int *)cur_addr)++) = pu.aout->a_syms; printf(", symtab=0x%x", pu.aout->a_syms); if (read(cur_addr, pu.aout->a_syms) == pu.aout->a_syms) { cur_addr += pu.aout->a_syms; mbi.syms.a.tabsize = pu.aout->a_syms; if (read((int)(&i), sizeof(int)) == sizeof(int)) { *(((int *)cur_addr)++) = i; mbi.syms.a.strsize = i; i -= sizeof(int); printf(", strtab=0x%x", i); symtab_err = (read(cur_addr, i) != i); cur_addr += i; } else symtab_err = 1; } else symtab_err = 1; if (symtab_err) { printf("(bad)"); cur_addr = orig_addr; mbi.syms.a.tabsize = 0; mbi.syms.a.strsize = 0; mbi.syms.a.addr = 0; } else mbi.flags |= MB_INFO_AOUT_SYMS; } } else /* ELF executable */ { int loaded = 0, memaddr, memsiz, filesiz; Elf32_Phdr *phdr; /* reset this to zero for now */ cur_addr = 0; /* scan for program segments */ for (i = 0; i < pu.elf->e_phnum; i++) { phdr = (Elf32_Phdr *) (pu.elf->e_phoff + ((int)buffer) + (pu.elf->e_phentsize * i)); if (phdr->p_type == PT_LOAD) { /* offset into file */ filepos = phdr->p_offset; filesiz = phdr->p_filesz; memaddr = phdr->p_vaddr; memsiz = phdr->p_memsz; if (memaddr < 0x100000) errnum = ERR_BELOW_1MB; /* make sure we only load what we're supposed to! */ if (filesiz > memsiz) filesiz = memsiz; /* mark memory as used */ if (cur_addr < memaddr+memsiz) cur_addr = memaddr+memsiz; printf(", <0x%x:0x%x:0x%x>", str, memaddr, filesiz, memsiz-filesiz); /* increment number of segments */ loaded++; /* load the segment */ if (memcheck(memaddr, memsiz) && read(memaddr, filesiz) == filesiz) { if (memsiz > filesiz) bzero((char *)(memaddr+filesiz), memsiz-filesiz); } else break; } } if (!errnum) { if (!loaded) errnum = ERR_EXEC_FORMAT; else { /* XXX load symbols */ } } } if (!errnum) printf(", entry=0x%x]\n", str, (int)entry_addr); else { putchar('\n'); type = 0; } return type; } int load_module(void) { int len; /* if we are supposed to load on 4K boundaries */ cur_addr = (cur_addr + 0xFFF) & 0xFFFFF000; if (!open(cur_cmdline) || !(len = read(cur_addr, -1))) return 0; /* these two simply need to be set if any modules are loaded at all */ mbi.flags |= MB_INFO_MODS; mbi.mods_addr = (int)mll; mll[mbi.mods_count].cmdline = (int)cur_cmdline; mll[mbi.mods_count].mod_start = cur_addr; cur_addr += len; mll[mbi.mods_count].mod_end = cur_addr; mll[mbi.mods_count].pad = 0; /* increment number of modules included */ mbi.mods_count++; return 1; } /* * All "*_boot" commands depend on the images being loaded into memory * correctly, the variables in this file being set up correctly, and * the root partition being set in the 'saved_drive' and 'saved_partition' * variables. */ void bsd_boot(int type, int bootdev) { char *str; int clval = 0, i; struct bootinfo bi; while (*(++cur_cmdline) && *cur_cmdline != ' '); str = cur_cmdline; while (*str) { if (*str == '-') { while(*str && *str != ' ') { if (*str == 'C') clval |= RB_CDROM; if (*str == 'a') clval |= RB_ASKNAME; if (*str == 'b') clval |= RB_HALT; if (*str == 'c') clval |= RB_CONFIG; if (*str == 'd') clval |= RB_KDB; if (*str == 'h') clval |= RB_SERIAL; if (*str == 'r') clval |= RB_DFLTROOT; if (*str == 's') clval |= RB_SINGLE; if (*str == 'v') clval |= RB_VERBOSE; str++; } continue; } str++; } if (type == 'f') { clval |= RB_BOOTINFO; bi.bi_version = BOOTINFO_VERSION; *cur_cmdline = 0; while ((--cur_cmdline) > (char *)(mbi.cmdline) && *cur_cmdline != '/'); if (*cur_cmdline == '/') bi.bi_kernelname = cur_cmdline+1; else bi.bi_kernelname = 0; bi.bi_nfs_diskless = 0; bi.bi_n_bios_used = 0; /* this field is apparently unused */ for(i = 0; i < N_BIOS_GEOM; i++) bi.bi_bios_geom[i] = get_diskinfo(i + 0x80); bi.bi_size = sizeof(struct bootinfo); bi.bi_memsizes_valid = 1; bi.bi_basemem = mbi.mem_lower; bi.bi_extmem = mbi.mem_upper; bi.bi_symtab = mbi.syms.a.addr; bi.bi_esymtab = mbi.syms.a.addr + 4 + mbi.syms.a.tabsize + mbi.syms.a.strsize; /* call entry point */ (*entry_addr)(clval, bootdev, 0, 0, 0, ((int)(&bi))); } else { /* * We now pass the various bootstrap parameters to the loaded * image via the argument list. * * This is the official list: * * arg0 = 8 (magic) * arg1 = boot flags * arg2 = boot device * arg3 = start of symbol table (0 if not loaded) * arg4 = end of symbol table (0 if not loaded) * arg5 = transfer address from image * arg6 = transfer address for next image pointer * arg7 = conventional memory size (640) * arg8 = extended memory size (8196) * * ...in actuality, we just pass the parameters used by the kernel. */ /* call entry point */ (*entry_addr)(clval, bootdev, 0, (mbi.syms.a.addr + 4 + mbi.syms.a.tabsize + mbi.syms.a.strsize), mbi.mem_upper, mbi.mem_lower); } }