mirror of https://github.com/dzavalishin/oskit/
364 lines
11 KiB
C
Executable File
364 lines
11 KiB
C
Executable File
/*
|
|
* Copyright (c) 1995-1998 University of Utah and the Flux Group.
|
|
* All rights reserved.
|
|
*
|
|
* This file is part of the Flux OSKit. The OSKit is free software, also known
|
|
* as "open source;" you can redistribute it and/or modify it under the terms
|
|
* of the GNU General Public License (GPL), version 2, as published by the Free
|
|
* Software Foundation (FSF). To explore alternate licensing terms, contact
|
|
* the University of Utah at csl-dist@cs.utah.edu or +1-801-585-3271.
|
|
*
|
|
* The OSKit 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 GPL for more details. You should have
|
|
* received a copy of the GPL along with the OSKit; see the file COPYING. If
|
|
* not, write to the FSF, 59 Temple Place #330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include <oskit/x86/pc/rtc.h>
|
|
#include <oskit/x86/pc/phys_lmm.h>
|
|
#include <oskit/x86/pc/dos_io.h>
|
|
#include <oskit/x86/types.h>
|
|
#include <oskit/x86/multiboot.h>
|
|
#include <oskit/x86/base_cpu.h>
|
|
#include <oskit/x86/base_vm.h>
|
|
#include <oskit/exec/exec.h>
|
|
#include <oskit/exec/a.out.h>
|
|
#include <oskit/c/malloc.h>
|
|
#include <oskit/c/string.h>
|
|
#include <oskit/c/stdio.h>
|
|
#include <oskit/c/fcntl.h>
|
|
#include <oskit/c/assert.h>
|
|
#include <oskit/lmm.h>
|
|
|
|
#include "boot.h"
|
|
|
|
struct multiboot_info *boot_info;
|
|
static struct multiboot_module *boot_mods;
|
|
|
|
struct multiboot_header boot_kern_hdr;
|
|
void *boot_kern_image;
|
|
|
|
static dos_fd_t boot_kern_fd;
|
|
static unsigned boot_kern_ofs;
|
|
static struct exec_info boot_kern_info;
|
|
|
|
static
|
|
int kimg_read(void *handle, oskit_addr_t file_ofs, void *buf,
|
|
oskit_size_t size, oskit_size_t *out_actual)
|
|
{
|
|
oskit_addr_t newpos;
|
|
int rc;
|
|
|
|
rc = dos_seek(boot_kern_fd, boot_kern_ofs + file_ofs, 0, &newpos);
|
|
if (rc != 0)
|
|
return rc;
|
|
return dos_read(boot_kern_fd, buf, size, out_actual);
|
|
}
|
|
|
|
static
|
|
int kimg_read_exec_1(void *handle, oskit_addr_t file_ofs, oskit_size_t file_size,
|
|
oskit_addr_t mem_addr, oskit_size_t mem_size,
|
|
exec_sectype_t section_type)
|
|
{
|
|
if (!(section_type & EXEC_SECTYPE_ALLOC))
|
|
return 0;
|
|
|
|
assert(mem_size > 0);
|
|
if (mem_addr < boot_kern_hdr.load_addr)
|
|
boot_kern_hdr.load_addr = mem_addr;
|
|
if (mem_addr+file_size > boot_kern_hdr.load_end_addr)
|
|
boot_kern_hdr.load_end_addr = mem_addr+file_size;
|
|
if (mem_addr+mem_size > boot_kern_hdr.bss_end_addr)
|
|
boot_kern_hdr.bss_end_addr = mem_addr+mem_size;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static
|
|
int kimg_read_exec_2(void *handle, oskit_addr_t file_ofs, oskit_size_t file_size,
|
|
oskit_addr_t mem_addr, oskit_size_t mem_size,
|
|
exec_sectype_t section_type)
|
|
{
|
|
oskit_size_t actual;
|
|
int rc;
|
|
|
|
if (!(section_type & EXEC_SECTYPE_ALLOC))
|
|
return 0;
|
|
|
|
assert(mem_size > 0);
|
|
assert(mem_addr >= boot_kern_hdr.load_addr);
|
|
assert(mem_addr+file_size <= boot_kern_hdr.load_end_addr);
|
|
assert(mem_addr+mem_size <= boot_kern_hdr.bss_end_addr);
|
|
|
|
rc = kimg_read(handle, file_ofs,
|
|
boot_kern_image + mem_addr - boot_kern_hdr.load_addr,
|
|
file_size, &actual);
|
|
if (rc != 0)
|
|
return rc;
|
|
if (actual != file_size)
|
|
return EX_CORRUPT;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void load_kernel(int argc, char **argv)
|
|
{
|
|
static char search[MULTIBOOT_SEARCH+sizeof(struct multiboot_header)];
|
|
struct multiboot_header *h;
|
|
oskit_size_t actual;
|
|
int i, err;
|
|
|
|
/* Scan for the multiboot_header. */
|
|
kimg_read(0, 0, search, sizeof(search), &actual);
|
|
for (i = 0; ; i += 4)
|
|
{
|
|
if (i >= MULTIBOOT_SEARCH)
|
|
panic("kernel image has no multiboot_header");
|
|
h = (struct multiboot_header*)(search + i);
|
|
if (h->magic == MULTIBOOT_MAGIC
|
|
&& !(h->magic + h->flags + h->checksum))
|
|
break;
|
|
}
|
|
if (h->flags & MULTIBOOT_MUSTKNOW & ~MULTIBOOT_MEMORY_INFO)
|
|
panic("unknown multiboot_header flag bits %08x",
|
|
h->flags & MULTIBOOT_MUSTKNOW & ~MULTIBOOT_MEMORY_INFO);
|
|
boot_kern_hdr = *h;
|
|
|
|
if (h->flags & MULTIBOOT_AOUT_KLUDGE)
|
|
{
|
|
/*
|
|
* Allocate memory and load the kernel into it.
|
|
* We do this before reserving the memory
|
|
* for the final kernel location,
|
|
* because the code in boot_start.c
|
|
* to copy the kernel to its final location
|
|
* can handle overlapping sources and destinations,
|
|
* and this way we may not need as much memory during bootup.
|
|
*/
|
|
boot_kern_image = mustmalloc(h->load_end_addr - h->load_addr);
|
|
kimg_read(0, i + h->load_addr - h->header_addr, boot_kern_image,
|
|
h->load_end_addr - h->load_addr, &actual);
|
|
assert(actual == h->load_end_addr - h->load_addr);
|
|
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* No a.out-kludge information available;
|
|
* attempt to interpret the exec header instead,
|
|
* using the simple interpreter in libexec.a.
|
|
* Perform the "load" in two passes.
|
|
* In the first pass,
|
|
* find the number of sections the load image contains
|
|
* and reserve the physical memory containing each section.
|
|
* Also, initialize the boot_kern_hdr
|
|
* to reflect the extent of the image.
|
|
* In the second pass, load the sections into a temporary area
|
|
* that can be copied to the final location
|
|
* all at once by do_boot.S.
|
|
*/
|
|
|
|
boot_kern_hdr.load_addr = 0xffffffff;
|
|
boot_kern_hdr.load_end_addr = 0;
|
|
boot_kern_hdr.bss_end_addr = 0;
|
|
|
|
if ((err = exec_load(kimg_read, kimg_read_exec_1, 0,
|
|
&boot_kern_info)) != 0)
|
|
panic("cannot load kernel image 1: error code %d", err);
|
|
boot_kern_hdr.entry = boot_kern_info.entry;
|
|
|
|
/*
|
|
* Allocate memory to load the kernel into.
|
|
* It's OK to malloc this
|
|
* before reserving the memory the kernel will occupy,
|
|
* because do_boot.S can deal with
|
|
* overlapping source and destination.
|
|
*/
|
|
assert(boot_kern_hdr.load_addr < boot_kern_hdr.load_end_addr);
|
|
assert(boot_kern_hdr.load_end_addr < boot_kern_hdr.bss_end_addr);
|
|
boot_kern_image = mustmalloc(boot_kern_hdr.load_end_addr -
|
|
boot_kern_hdr.load_addr);
|
|
|
|
if ((err = exec_load(kimg_read, kimg_read_exec_2, 0,
|
|
&boot_kern_info)) != 0)
|
|
panic("cannot load kernel image 2: error code %d", err);
|
|
assert(boot_kern_hdr.entry == boot_kern_info.entry);
|
|
}
|
|
|
|
/*
|
|
* Reserve the memory that the kernel will eventually occupy.
|
|
* All malloc calls after this are guaranteed
|
|
* to stay out of this region.
|
|
*/
|
|
lmm_remove_free(&malloc_lmm,
|
|
(void *)phystokv(boot_kern_hdr.load_addr),
|
|
phystokv(boot_kern_hdr.bss_end_addr)
|
|
- phystokv(boot_kern_hdr.load_addr));
|
|
|
|
printf("kernel at %08x-%08x text+data %d bss %d\n",
|
|
boot_kern_hdr.load_addr, boot_kern_hdr.bss_end_addr,
|
|
boot_kern_hdr.load_end_addr - boot_kern_hdr.load_addr,
|
|
boot_kern_hdr.bss_end_addr - boot_kern_hdr.load_end_addr);
|
|
assert(boot_kern_hdr.load_addr < boot_kern_hdr.load_end_addr);
|
|
assert(boot_kern_hdr.load_end_addr < boot_kern_hdr.bss_end_addr);
|
|
if (boot_kern_hdr.load_addr < 0x1000)
|
|
panic("kernel wants to be loaded too low!");
|
|
#if 0
|
|
if (boot_kern_hdr.bss_end_addr > phys_mem_max)
|
|
panic("kernel wants to be loaded beyond available physical memory!");
|
|
#endif
|
|
if ((boot_kern_hdr.load_addr < 0x100000)
|
|
&& (boot_kern_hdr.bss_end_addr > 0xa0000))
|
|
panic("kernel wants to be loaded on top of I/O space!");
|
|
|
|
boot_info = (struct multiboot_info*)mustcalloc(sizeof(*boot_info), 1);
|
|
|
|
/* Build a command line to pass to the kernel. */
|
|
#if 0
|
|
init_cmdline();
|
|
#endif
|
|
|
|
/*
|
|
* Find the extended memory available to the kernel we're loading.
|
|
* Note that although we ourselves (the DOS boot loader)
|
|
* use only the memory that DOS and the BIOS say is available,
|
|
* we use the values in NVRAM to pass to the kernel we're loading.
|
|
* This is because we must be a good citizen in the DOS environment
|
|
* since we rely on DOS I/O services to load the kernel,
|
|
* but once the kernel is loaded, it can blow away DOS completely
|
|
* and use all available memory for its own purposes.
|
|
*/
|
|
boot_info->flags |= MULTIBOOT_MEMORY;
|
|
boot_info->mem_lower = (rtcin(RTC_BASEHI) << 8) | rtcin(RTC_BASELO);
|
|
boot_info->mem_upper = (rtcin(RTC_DEXTHI) << 8) | rtcin(RTC_DEXTLO);
|
|
printf("Memory reported by NVRAM: %d lower %d upper\n",
|
|
boot_info->mem_lower, boot_info->mem_upper);
|
|
}
|
|
|
|
/*
|
|
* Check for a self-contained boot image attached to our executable,
|
|
* and if one exists, boot it.
|
|
*/
|
|
static void boot_self_contained(int argc, char **argv)
|
|
{
|
|
struct loadhdr
|
|
{
|
|
unsigned magic;
|
|
unsigned tabsize;
|
|
};
|
|
struct loadmod
|
|
{
|
|
oskit_addr_t mod_start;
|
|
oskit_addr_t mod_end;
|
|
oskit_addr_t string_start;
|
|
oskit_addr_t string_end;
|
|
};
|
|
|
|
dos_fd_t exe_fd;
|
|
unsigned short exehdr[3];
|
|
oskit_size_t size, actual, newpos;
|
|
unsigned off;
|
|
struct loadhdr loadhdr;
|
|
struct loadmod *loadmods;
|
|
int loadnmods;
|
|
int i;
|
|
|
|
/* Open our executable file. */
|
|
if (dos_open(argv[0], O_RDONLY, 0, &exe_fd))
|
|
return;
|
|
|
|
/*
|
|
* Read the first part of the EXE header,
|
|
* to find out how long the original EXE file is.
|
|
*/
|
|
if (dos_read(exe_fd, exehdr, sizeof(exehdr), &actual)
|
|
|| (actual != sizeof(exehdr))
|
|
|| (exehdr[0] != 0x5a4d))
|
|
return;
|
|
off = (unsigned)exehdr[2] * 512;
|
|
if (exehdr[1] != 0)
|
|
off -= 512 - exehdr[1];
|
|
|
|
/* Read the boot image digest header, if there is one. */
|
|
if (dos_seek(exe_fd, off, SEEK_SET, &newpos)
|
|
|| (newpos != off)
|
|
|| (dos_read(exe_fd, &loadhdr, sizeof(loadhdr), &actual))
|
|
|| (actual != sizeof(loadhdr))
|
|
|| (loadhdr.magic != 0xf00baabb)
|
|
|| (loadhdr.tabsize == 0))
|
|
return;
|
|
|
|
/*
|
|
* OK, there's definitely a self-contained boot image here,
|
|
* so now we're committed to using it.
|
|
* From now on we don't return from this function.
|
|
*/
|
|
|
|
/* Read the boot modules array */
|
|
loadnmods = loadhdr.tabsize / sizeof(struct loadmod);
|
|
size = loadnmods * sizeof(struct loadmod);
|
|
loadmods = mustmalloc(size);
|
|
if (dos_read(exe_fd, loadmods, size, &actual)
|
|
|| (actual != size))
|
|
goto corrupt;
|
|
|
|
/* Load the kernel image */
|
|
boot_kern_fd = exe_fd;
|
|
boot_kern_ofs = off + loadmods[0].mod_start;
|
|
load_kernel(argc, argv);
|
|
|
|
/* Initialize the boot module entries in the boot_info. */
|
|
boot_info->flags |= MULTIBOOT_MODS;
|
|
boot_info->mods_count = loadnmods-1;
|
|
boot_mods = (struct multiboot_module*)mustcalloc(
|
|
boot_info->mods_count * sizeof(*boot_mods), 1);
|
|
boot_info->mods_addr = kvtophys(boot_mods);
|
|
for (i = 0; i < boot_info->mods_count; i++)
|
|
{
|
|
struct loadmod *lm = &loadmods[1+i];
|
|
struct multiboot_module *bm = &boot_mods[i];
|
|
void *modaddr;
|
|
char *modstring;
|
|
|
|
/* Load the boot module */
|
|
assert(lm->mod_end > lm->mod_start);
|
|
size = lm->mod_end - lm->mod_start;
|
|
modaddr = mustmalloc(size);
|
|
if (dos_seek(exe_fd, off + lm->mod_start, SEEK_SET, &newpos)
|
|
|| (newpos != off + lm->mod_start)
|
|
|| dos_read(exe_fd, modaddr, size, &actual)
|
|
|| (actual != size))
|
|
goto corrupt;
|
|
bm->mod_start = kvtophys(modaddr);
|
|
bm->mod_end = bm->mod_start + size;
|
|
|
|
/* Also provide the string associated with the module. */
|
|
assert(lm->string_end > lm->string_start);
|
|
size = lm->string_end - lm->string_start;
|
|
modstring = mustmalloc(size);
|
|
if (dos_seek(exe_fd, off + lm->string_start, SEEK_SET, &newpos)
|
|
|| (newpos != off + lm->string_start)
|
|
|| dos_read(exe_fd, modstring, size, &actual)
|
|
|| (actual != size))
|
|
goto corrupt;
|
|
bm->string = kvtophys(modstring);
|
|
|
|
bm->reserved = 0;
|
|
}
|
|
|
|
/* Just boot it! */
|
|
boot_start(boot_info);
|
|
|
|
corrupt:
|
|
panic("corrupt boot image");
|
|
}
|
|
|
|
void main(int argc, char **argv)
|
|
{
|
|
/* Try to boot a self-contained boot image first */
|
|
boot_self_contained(argc, argv);
|
|
|
|
panic("dosboot currently only supports self-contained images");
|
|
}
|