Goto sanos source index

//
// mkboot.c
//
// Kernel install utility
//
// Copyright (C) 2012 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 <stdio.h>
#include <string.h>
#include <unistd.h>
#include <os.h>
#include <os/mbr.h>
#include <os/dev.h>
#include <os/dfs.h>
#include <os/tss.h>
#include <os/seg.h>
#include <os/syspage.h>

void usage() {
  fprintf(stderr, "usage: mkboot [ options ]\n\n");
  fprintf(stderr, "  -b <bootsect> Install new boot sector\n");
  fprintf(stderr, "  -d <path>     Filesystem where boot components will be installed\n");
  fprintf(stderr, "  -k <kernel>   Install new kernel\n");
  fprintf(stderr, "  -l <osldr>    Install new boot loader\n");
  fprintf(stderr, "  -o <options>  Kernel options\n");
}

int read_boot_sector(int fd, struct boot_sector *bsect) {
  int rc;

  rc = read(fd, bsect, sizeof(struct boot_sector));
  if (rc < 0) {
    perror("error reading boot sector");
    return -1;
  }
  if (rc != sizeof(struct boot_sector)) {
    fprintf(stderr, "error: Wrong size for boot sector (%d bytes)\n", rc);
    return -1;
  }
  return 0;
}

int install_boot_sector(char *devname, char *bootfile) {
  int dev;
  int fd;
  struct boot_sector bsect;
  unsigned short ldrsize;
  unsigned long ldrstart;
  int rc;

  // Read existing boot sector to get loader parameters
  dev = open(devname, O_RDWR | O_BINARY);
  if (dev < 0) {
    perror(devname);
    return -1;
  }
  if (read_boot_sector(dev, &bsect) < 0) {
    close(dev);
    return -1;
  }
  ldrsize = bsect.ldrsize;
  ldrstart = bsect.ldrstart;

  // Read new bootstrap
  fd = open(bootfile, O_BINARY);
  if (fd < 0) {
    perror(bootfile);
    close(dev);
    return -1;
  }
  if (read_boot_sector(fd, &bsect) < 0) {
    close(dev);
    close(fd);
    return -1;
  }
  close(fd);

  // Check for valid boot sector
  if (bsect.signature != MBR_SIGNATURE) {
    fprintf(stderr, "Invalid boot sector signature\n");
    close(dev);
    return -1; 
  }

  // Patch loader parameters into boot sector
  bsect.ldrsize = ldrsize;
  bsect.ldrstart = ldrstart;

  // Write bootstrap to boot sector
  rc = lseek(dev, 0, SEEK_SET);
  if (rc < 0) {
    perror(devname);
    close(dev);
    return -1;
  }

  rc = write(dev, &bsect, sizeof(struct boot_sector));
  if (rc < 0) {
    printf("Unable to write boot sector\n");
    close(dev);
    return -1;
  }

  close(dev);
  return 0;
}

int install_loader(char *devname, char *ldrfile, char *krnlopts) {
  int dev = -1;
  int ldr = -1;
  char *image = NULL;
  int size;
  int rc;
  int n;
  int blocksize;
  struct boot_sector bsect;
  char ssect[SECTORSIZE];
  struct superblock *super;

  // Open device
  dev = open(devname, O_RDWR | O_BINARY);
  if (dev < 0) {
    perror(devname);
    goto error;
  }

  // Read boot sector from device
  rc = lseek(dev, 0 * SECTORSIZE, SEEK_SET);
  if (rc < 0) {
    perror(devname);
    goto error;
  }
  if (read_boot_sector(dev, &bsect) < 0) goto error;

  // Read loader image
  ldr = open(ldrfile, O_BINARY);
  if (ldr < 0) {
    perror(ldrfile);
    goto error;
  }

  size = filelength(ldr);
  image = (char *) malloc(size);
  if (!image) {
    perror("malloc"); 
    goto error;
  }

  rc = read(ldr, image, size);
  if (rc < 0) {
    perror(ldrfile);
    goto error;
  }

  // Check signature
  if (size < 2 || image[0] != 'M' || image[1] != 'Z') {
    fprintf(stderr, "%s: Invalid boot loader signature\n", ldrfile);
    goto error;
  }

  // Read super block from device
  rc = lseek(dev, 1 * SECTORSIZE, SEEK_SET);
  if (rc < 0) {
    perror(devname);
    goto error;
  }

  rc = read(dev, ssect, SECTORSIZE);
  if (rc < 0) {
    perror(devname);
    goto error;
  }

  super = (struct superblock *) ssect;
  blocksize = 1 << super->log_block_size;

  // Calculate loader start and size in sectors (used by bootstrap)
  bsect.ldrstart = super->first_reserved_block * (blocksize / SECTORSIZE);
  bsect.ldrsize = size / SECTORSIZE;
  if (size > (int) super->reserved_blocks * blocksize) {
    printf("Loader too big (max %d bytes)\n", super->reserved_blocks * blocksize);
    goto error;
  }

  // Patch kernel options into loader
  if (krnlopts) {
    int optspos;
    
    optspos = *(unsigned short *) (image + KRNLOPTS_POSOFS);
    strcpy(image + optspos, krnlopts);
  }

  // Install loader into reserved blocks of file system
  rc = lseek(dev, super->first_reserved_block * blocksize, SEEK_SET);
  if (rc < 0) {
    perror(devname);
    goto error;
  }
  
  for (n = 0; n < size / blocksize; n++) {
    rc = write(dev, image + n * blocksize, blocksize);
    if (rc < 0) {
      perror(devname);
      goto error;
    }
  }

  // Write boot sector with patched loader parameters
  lseek(dev, 0, SEEK_SET);
  write(dev, &bsect, sizeof(struct boot_sector));

  close(ldr);
  close(dev);
  free(image);
  return 0;

error:
  if (dev != -1) close(dev);
  if (ldr != -1) close(ldr);
  if (image) free(image);
  return -1;
}

int install_kernel(char *target, char *krnlfile) {
  int fin;
  int fout;
  int bytes;
  char block[512];
  char targetfn[MAXPATH];

  // Open new kernel file
  fin = open(krnlfile, O_BINARY);
  if (fin < 0) {
    perror(krnlfile);
    return -1;
  }

  // Remove old kernel
  sprintf(targetfn, "%s/boot/krnl.dll", target);
  unlink(targetfn);

  // Install kernel on target using reserved inode
  fout = open(targetfn, O_SPECIAL | (DFS_INODE_KRNL << 24), 0744);
  if (fout < 0) {
    perror(targetfn);
    close(fin);
    return -1;
  }
  fchmod(fout, 0644);

  // Copy kernel
  while ((bytes = read(fin, block, sizeof block)) > 0) {
    write(fout, block, bytes);
  }

  close(fin);
  close(fout);
  return 0;
}

int main(int argc, char *argv[]) {
  char *bootfs = "/";
  char devname[MAXPATH];
  char *bootfile = NULL;
  char *ldrfile = NULL;
  char *krnlfile = NULL;
  char *krnlopts = NULL;
  struct statfs fs;
  int c;
  int rc;

  // Parse command line options
  while ((c = getopt(argc, argv, "b:d:hk:l:o:")) != EOF) {
    switch (c) {
      case 'b':
        bootfile = optarg;
        break;

      case 'd':
        bootfs = optarg;
        break;

      case 'k':
        krnlfile = optarg;
        break;

      case 'l':
        ldrfile = optarg;
        break;

      case 'o':
        krnlopts = optarg;
        break;

      case 'h':
      default:
        usage();
        return 1;
    }
  }

  // Get boot file system information
  if (statfs(bootfs, &fs) < 0) {
    perror(bootfs);
    return 1;
  }
  strcpy(devname, "/dev/");
  strcat(devname, fs.mntfrom);

  // Check boot file system
  if (strcmp(fs.fstype, "dfs") != 0 || strcmp(fs.mntto, bootfs) != 0) {
    printf("%s: cannot install on %s (%s)\n", devname, fs.fstype, fs.mntto);
    return 1;
  }

  // Install new boot sector
  if (bootfile != NULL) {
    printf("Installing boot sector %s on %s\n", bootfile, devname);
    rc = install_boot_sector(devname, bootfile);
    if (rc < 0)  return 1;
  }

  // Install new loader
  if (krnlopts != NULL) {
    if (strlen(krnlopts) > KRNLOPTS_LEN - 1) {
      printf("Kernel options too big\n");
      return 1;
    }
    if (ldrfile == NULL) ldrfile = "/boot/osldr.dll";
  }
  if (ldrfile != NULL) {
    printf("Installing loader %s on %s", ldrfile, devname);
    if (krnlopts) printf(" with options %s", krnlopts);
    printf("\n");
    rc = install_loader(devname, ldrfile, krnlopts);
    if (rc < 0)  return 1;
  }

  // Install new kernel
  if (krnlfile != NULL) {
    printf("Installing kernel %s on %s\n", krnlfile, devname);
    rc = install_kernel(fs.mntto, krnlfile);
    if (rc < 0)  return 1;
  }

  return 0;
}