Goto sanos source index

//
// start.c
//
// Kernel initialization
//
// 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/krnl.h>

#define KERNEL_CONFIG  "/etc/krnl.ini"

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

#ifdef GPL
char *copyright = 
"This program is free software; you can redistribute it and/or modify it under\n"
"the terms of the GNU General Public License as published by the Free Software\n"
"Foundation; either version 2 of the License, or (at your option) any later\n"
"version.\n"
"\n"
"This program is distributed in the hope that it will be useful, but WITHOUT\n"
"ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n"
"FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n"
"\n"
"You should have received a copy of the GNU General Public License along with\n"
"this program; if not, write to the Free Software Foundation,\n"
"Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n";
#endif

#define ONPANIC_HALT      EXITOS_HALT
#define ONPANIC_REBOOT    EXITOS_REBOOT
#define ONPANIC_DEBUG     EXITOS_DEBUG
#define ONPANIC_POWEROFF  EXITOS_POWEROFF

struct thread *mainthread;
struct section *krnlcfg;
int onpanic = ONPANIC_HALT;
struct netif *nic;
struct peb *peb;
char krnlopts[KRNLOPTS_LEN];

void main(void *arg);

int license()
{
  return LICENSE;
}

void stop(int mode)
{
  suspend_all_user_threads();
  umount_all();
  tcp_shutdown();
  msleep(200);

  switch (mode)
  {
    case EXITOS_HALT:
      kprintf("kernel: system stopped\n");
      break;

    case EXITOS_POWEROFF:
      if (apm_enabled)
      {
        kprintf("kernel: power down...\n");
        apm_power_off();
      }
      else
        kprintf("kernel: power management not enabled, system stopped...\n");
      break;

    case EXITOS_REBOOT:
      kprintf("kernel: rebooting...\n");
      reboot();
      break;

    case EXITOS_DEBUG:
      dbg_break();
      break;
  }

  while (1)
  {
    cli();
    halt();
  }
}

void panic(char *msg)
{
  static int inpanic = 0;

  if (inpanic)
  {
    kprintf(KERN_EMERG "double panic: %s, halting\n", msg);
    cli();
    halt();
  }

  inpanic = 1;
  kprintf(KERN_EMERG "panic: %s\n", msg);

  if (onpanic == ONPANIC_DEBUG)
  {
    if (debugging) dbg_output(msg);
    dbg_break();
  }
  else
    stop(onpanic);
}

static int load_kernel_config()
{
  struct file *f;
  int size;
  int rc;
  struct stat64 buffer;
  char *props;

  rc = open(KERNEL_CONFIG, O_RDONLY | O_BINARY, 0, &f);
  if (rc < 0) return rc;

  fstat(f, &buffer);
  size = (int) buffer.st_size;

  props = (char *) kmalloc(size + 1);
  if (!props) 
  {
    close(f);
    destroy(f);
    return -ENOMEM;
  }

  rc = read(f, props, size);
  if (rc < 0)
  {
    free(props);
    close(f);
    destroy(f);
    return rc;
  }

  close(f);
  destroy(f);

  props[size] = 0;

  krnlcfg = parse_properties(props);
  free(props);

  return 0;
}

static int version_proc(struct proc_file *pf, void *arg)
{
  hmodule_t krnl = (hmodule_t) OSBASE;
  struct verinfo *ver;
  time_t ostimestamp;
  char osname[32];
  struct tm tm;

  ostimestamp = get_image_header(krnl)->header.timestamp;
  gmtime_r(&ostimestamp, &tm);

  ver = get_version_info(krnl);
  if (!ver) return -ENOENT;
  if (get_version_value(krnl, "ProductName", osname, sizeof(osname)) < 0) strcpy(osname, "Sanos");

  pprintf(pf, "%s version %d.%d.%d.%d", osname, ver->file_major_version, ver->file_minor_version, ver->file_release_number, ver->file_build_number);

  if (ver->file_flags & VER_FLAG_PRERELEASE) pprintf(pf, " prerelease");
  if (ver->file_flags & VER_FLAG_PATCHED) pprintf(pf, " patch");
  if (ver->file_flags & VER_FLAG_PRIVATEBUILD) pprintf(pf, " private");
  if (ver->file_flags & VER_FLAG_DEBUG) pprintf(pf, " debug");

  pprintf(pf, " (%04d-%02d-%02d %02d:%02d:%02d)", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
  pprintf(pf, "\n");

  return 0;
}

static int copyright_proc(struct proc_file *pf, void *arg)
{
  hmodule_t krnl = (hmodule_t) OSBASE;
  char copy[128];
  char legal[128];

  if (get_version_value(krnl, "LegalCopyright", copy, sizeof(copy)) < 0) *copy = 0;
  if (get_version_value(krnl, "LegalTrademarks", legal, sizeof(legal)) < 0) *legal = 0;

  version_proc(pf, arg);
  pprintf(pf, "%s %s\n\n", copy, legal);
  proc_write(pf, copyright, strlen(copyright));
  return 0;
}

void __stdcall start(void *hmod, char *opts, int reserved2)
{
  // Copy kernel options
  strcpy(krnlopts, opts);
  if (get_option(opts, "silent", NULL, 0, NULL) != NULL) kprint_enabled = 0;

  // Initialize screen
  init_video();
  //clear_screen();

  // Display banner
  kprintf(KERN_INFO "boot: starting kernel\n");
  if (*krnlopts) kprintf(KERN_INFO "options: %s\n", krnlopts);

  // Initialize CPU
  init_cpu();

  // Initialize page frame database
  init_pfdb();

  // Initialize page directory
  init_pdir();

  // Initialize kernel heap
  init_kmem();

  // Initialize kernel allocator
  init_malloc();

  // Initialize virtual memory manager
  init_vmm();

  // Flush tlb
  flushtlb();

  // Register memory management procs
  register_proc_inode("memmap", memmap_proc, NULL);
  register_proc_inode("memusage", memusage_proc, NULL);
  register_proc_inode("memstat", memstat_proc, NULL);
  register_proc_inode("physmem", physmem_proc, NULL);
  register_proc_inode("pdir", pdir_proc, NULL);
  register_proc_inode("kmem", kmem_proc, NULL);
  register_proc_inode("kmodmem", kmodmem_proc, NULL);
  register_proc_inode("kheap", kheapstat_proc, NULL);
  register_proc_inode("vmem", vmem_proc, NULL);

  register_proc_inode("cpu", cpu_proc, NULL);

  // Initialize interrupts, floating-point support, and real-time clock
  init_pic();
  init_trap();
  init_fpu();
  init_pit();
  
  // Initialize timers, scheduler, and handle manager
  init_timers();
  init_sched();
  init_handles();
  init_syscall();

  // Enable interrupts and calibrate delay
  __asm { sti };
  calibrate_delay();

  // Start main task and dispatch to idle task
  mainthread = create_kernel_thread(main, 0, PRIORITY_NORMAL, "init");
  
  idle_task();
}

void init_net()
{
  stats_init();
  netif_init();
  ether_init();
  pbuf_init();
  arp_init();
  ip_init();
  udp_init();
  raw_init();
  dhcp_init();
  tcp_init();
  socket_init();
  loopif_init();

  register_ether_netifs();
}

void main(void *arg)
{
  unsigned long *stacktop;
  struct thread *t;

  void *imgbase;
  void *entrypoint;
  unsigned long stack_reserve;
  unsigned long stack_commit;
  struct image_header *imghdr;
  struct verinfo *ver;
  char bootdev[8];
  int rc;
  char *str;
  struct file *cons;

  // Allocate and initialize PEB
  peb = mmap((void *) PEB_ADDRESS, PAGESIZE, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE, 'PEB');
  if (!peb) panic("unable to allocate PEB");
  memset(peb, 0, PAGESIZE);
  peb->fast_syscalls_supported = (cpu.features & CPU_FEATURE_SEP) != 0;

  // Enumerate root host buses and units
  enum_host_bus();

  // Initialize boot device drivers
  init_hd();
  init_fd();

  // Initialize built-in file systems
  init_vfs();
  init_dfs();
  init_devfs();
  init_procfs();
  init_pipefs();
  init_smbfs();
  init_cdfs();
 
  // Open boot device
  if ((syspage->ldrparams.bootdrv & 0xF0) == 0xF0)
  {
    create_initrd();
    strcpy(bootdev, "initrd");
  }
  else if (syspage->ldrparams.bootdrv & 0x80)
  {
    if (syspage->ldrparams.bootpart == -1)
      sprintf(bootdev, "hd%c", '0' + (syspage->ldrparams.bootdrv & 0x7F));
    else
      sprintf(bootdev, "hd%c%c", '0' + (syspage->ldrparams.bootdrv & 0x7F), 'a' + syspage->ldrparams.bootpart);
  }
  else
    sprintf(bootdev, "fd%c", '0' + (syspage->ldrparams.bootdrv & 0x7F));

  kprintf(KERN_INFO "mount: root on device %s\n", bootdev);

  // Mount file systems
  rc = mount("dfs", "/", bootdev, "", NULL);
  if (rc < 0) panic("error mounting root filesystem");

  rc = mount("devfs", "/dev", NULL, NULL, NULL);
  if (rc < 0) panic("error mounting dev filesystem");

  rc = mount("procfs", "/proc", NULL, NULL, NULL);
  if (rc < 0) panic("error mounting proc filesystem");

  // Load kernel configuration
  rc = load_kernel_config();
  if (rc < 0) kprintf(KERN_ERR "%s: error %d loading kernel configuration\n", KERNEL_CONFIG, rc);

  // Determine kernel panic action
  str = get_property(krnlcfg, "kernel", "onpanic", "halt");
  if (strcmp(str, "halt") == 0)
    onpanic = ONPANIC_HALT;
  else if (strcmp(str, "reboot") == 0)
    onpanic = ONPANIC_REBOOT;
  else if (strcmp(str, "debug") == 0)
    onpanic = ONPANIC_DEBUG;
  else if (strcmp(str, "poweroff") == 0)
    onpanic = ONPANIC_POWEROFF;

  // Set path separator
  pathsep = *get_property(krnlcfg, "kernel", "pathsep", "");
  if (pathsep != PS1 && pathsep != PS2) pathsep = PS1;
  curdir[0] = pathsep;
  curdir[1] = 0;
  strcpy(peb->curdir, curdir);
  peb->pathsep = pathsep;

  // Initialize module loader
  init_kernel_modules();

  get_version_value((hmodule_t) OSBASE, "ProductName", peb->osname, sizeof peb->osname);
  peb->ostimestamp = get_image_header((hmodule_t) OSBASE)->header.timestamp;
  ver = get_version_info((hmodule_t) OSBASE);
  if (ver) memcpy(&peb->osversion, ver, sizeof(struct verinfo));

  // Install device drivers
  install_drivers();

  // Initialize network
  init_net();

  // Install /proc/version and /proc/copyright handler
  register_proc_inode("version", version_proc, NULL);
  register_proc_inode("copyright", copyright_proc, NULL);

  // Allocate handles for stdin, stdout and stderr
  open("/dev/console", O_RDWR, S_IREAD | S_IWRITE, &cons);
  if (halloc(&cons->iob.object) != 0) panic("unexpected stdin handle");
  if (halloc(&cons->iob.object) != 1) panic("unexpected stdout handle");
  if (halloc(&cons->iob.object) != 2) panic("unexpected stderr handle");

  // Load os.dll in user address space
  imgbase = load_image_file("/bin/os.dll", 1);
  if (!imgbase) panic("unable to load os.dll");
  imghdr = get_image_header(imgbase);
  stack_reserve = imghdr->optional.size_of_stack_reserve;
  stack_commit = imghdr->optional.size_of_stack_commit;
  entrypoint = get_entrypoint(imgbase);

  // Initialize initial user thread
  t = self();
  if (init_user_thread(t, entrypoint) < 0) panic("unable to initialize initial user thread");
  if (allocate_user_stack(t, stack_reserve, stack_commit) < 0) panic("unable to allocate stack for initial user thread");
  t->hndl = halloc(&t->object);
  mark_thread_running();

  kprintf(KERN_INFO "mem: %dMB total, %dKB used, %dKB free, %dKB reserved\n", 
          maxmem * PAGESIZE / (1024 * 1024), 
          (totalmem - freemem) * PAGESIZE / 1024, 
          freemem * PAGESIZE / 1024, (maxmem - totalmem) * PAGESIZE / 1024);

  // Place arguments to start routine on stack
  stacktop = (unsigned long *) t->tib->stacktop;
  *(--stacktop) = 0;
  *(--stacktop) = 0;
  *(--stacktop) = (unsigned long) imgbase;
  *(--stacktop) = 0;

  // Jump into user mode
  __asm
  {
    mov eax, stacktop
    mov ebx, entrypoint

    push SEL_UDATA + SEL_RPL3
    push eax
    pushfd
    push SEL_UTEXT + SEL_RPL3
    push ebx
    iretd

    cli
    hlt
  }
}