Goto sanos source index

//
// loadkrnl.c
//
// Kernel loader
//
// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 
// 1. Redistributions of source code must retain the above copyright 
//    notice, this list of conditions and the following disclaimer.  
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.  
// 3. Neither the name of the project nor the names of its contributors
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission. 
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
// SUCH DAMAGE.
// 

#include <os.h>
#include <os/pdir.h>
#include <os/tss.h>
#include <os/seg.h>
#include <os/syspage.h>
#include <os/mbr.h>
#include <os/dfs.h>
#include <os/pe.h>
#include <os/dev.h>

void kprintf(const char *fmt,...);
void panic(char *msg);

extern unsigned long krnlentry;
extern int bootpart;

int boot_read(void *buffer, size_t count, blkno_t blkno);
char *alloc_heap(int numpages);

char bsect[SECTORSIZE];
char ssect[SECTORSIZE];
char gsect[SECTORSIZE];
char isect[SECTORSIZE];
blkno_t blockdir[1024];

void load_kernel(int bootdrv) {
  struct master_boot_record *mbr;
  struct superblock *sb;
  struct groupdesc *group;
  struct inodedesc *inode;
  int blocksize;
  int blks_per_sect;
  int kernelsize;
  int kernelpages;
  char *kerneladdr;
  struct dos_header *doshdr;
  struct image_header *imghdr;
  char *addr;
  blkno_t blkno;
  int i;
  int j;
  pte_t *pt;
  int imgpages;
  int start;
  char *label;
  struct boot_sector *bootsect;

  //kprintf("Loading kernel");

  // Determine active boot partition if booting from harddisk
  if (bootdrv & 0x80 && (bootdrv & 0xF0) != 0xF0) {
    mbr = (struct master_boot_record *) bsect;
    if (boot_read(mbr, SECTORSIZE, 0) != SECTORSIZE) {
      panic("unable to read master boot record");
    }

    if (mbr->signature != MBR_SIGNATURE) panic("invalid boot signature");

    bootsect = (struct boot_sector *) bsect;
    label = bootsect->label;
    if (label[0] == 'S' && label[1] == 'A' && label[2] == 'N' && label[3] == 'O' && label[4] == 'S') {
      // Disk does not have a partition table
      start = 0;
      bootpart = -1;
    } else {
      // Find active partition
      bootpart = -1;
      for (i = 0; i < 4; i++) {
        if (mbr->parttab[i].bootid == 0x80) {
          bootpart = i;
          start = mbr->parttab[i].relsect;
        }
      }

      if (bootpart == -1) panic("no bootable partition on boot drive");
    }
  } else {
    start = 0;
    bootpart = 0;
  }

  // Read super block from boot device
  sb = (struct superblock *) ssect;
  if (boot_read(sb, SECTORSIZE, 1 + start) != SECTORSIZE) {
    panic("unable to read super block from boot device");
  }

  // Check signature and version
  if (sb->signature != DFS_SIGNATURE) panic("invalid DFS signature");
  if (sb->version != DFS_VERSION) panic("invalid DFS version");
  blocksize = 1 << sb->log_block_size;
  blks_per_sect =  blocksize / SECTORSIZE;

  // Read first group descriptor
  group = (struct groupdesc *) gsect;
  if (boot_read(group, SECTORSIZE, sb->groupdesc_table_block * blks_per_sect + start) != SECTORSIZE) {
    panic("unable to read group descriptor from boot device");
  }

  // Read inode for kernel
  inode = (struct inodedesc *) isect;
  if (boot_read(isect, SECTORSIZE, group->inode_table_block * blks_per_sect + start) != SECTORSIZE) {
    panic("unable to read kernel inode from boot device");
  }
  inode += DFS_INODE_KRNL;

  // Calculate kernel size
  kernelsize = (int) inode->size;
  kernelpages = PAGES(kernelsize);
  //kprintf("Kernel size %d KB\n", kernelsize / 1024);

  // Allocate page table for kernel
  if (kernelpages > PTES_PER_PAGE) panic("kernel too big");
  pt = (pte_t *) alloc_heap(1);
  pdir[PDEIDX(OSBASE)] = (unsigned long) pt | PT_PRESENT | PT_WRITABLE;

  // Allocate pages for kernel
  kerneladdr = alloc_heap(kernelpages);

  // Read kernel from boot device
  if (inode->depth == 0) {
    addr = kerneladdr;
    for (i = 0; i < (int) inode->blocks; i++) {
      if (boot_read(addr, blocksize, inode->blockdir[i] * blks_per_sect + start) != blocksize) {
        panic("error reading kernel from boot device");
      }
      addr += blocksize;
    }
  } else if (inode->depth == 1) {
    addr = kerneladdr;
    blkno = 0;
    for (i = 0; i < DFS_TOPBLOCKDIR_SIZE; i++) {
      if (boot_read(blockdir, blocksize, inode->blockdir[i] * blks_per_sect + start) != blocksize) {
        panic("error reading kernel inode dir from boot device");
      }

      for (j = 0; j < (int) (blocksize / sizeof(blkno_t)); j++) {
        if (boot_read(addr, blocksize, blockdir[j] * blks_per_sect + start) != blocksize) {
          panic("error reading kernel inode dir from boot device");
        }
        
        addr += blocksize;

        blkno++;
        if (blkno == inode->blocks) break;
      }

      if (blkno == inode->blocks) break;
    }
  } else {
    panic("unsupported inode depth");
  }

  // Determine entry point for kernel
  doshdr = (struct dos_header *) kerneladdr;
  imghdr = (struct image_header *) (kerneladdr + doshdr->e_lfanew);
  krnlentry = imghdr->optional.address_of_entry_point + OSBASE;

  // Allocate pages for .data section
  imgpages = PAGES(imghdr->optional.size_of_image);
  alloc_heap(imgpages - kernelpages);

  // Relocate resource data and clear uninitialized data
  if (imghdr->header.number_of_sections == 4) {
    struct image_section_header *data = &imghdr->sections[2];
    struct image_section_header *rsrc = &imghdr->sections[3];
    memcpy(kerneladdr + rsrc->virtual_address, kerneladdr + rsrc->pointer_to_raw_data, rsrc->size_of_raw_data);
    memset(kerneladdr + data->virtual_address + data->size_of_raw_data, 0, data->virtual_size - data->size_of_raw_data);
  }

  // Map kernel into vitual address space
  for (i = 0; i < imgpages; i++) pt[i] = (unsigned long) (kerneladdr + i * PAGESIZE) | PT_PRESENT | PT_WRITABLE;
}