File:  [GnuMach] / OSKit-Mach / oskit / x86 / main.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Wed Sep 2 04:55:50 2020 UTC (5 years, 10 months ago) by root
Branches: MAIN, GNU
CVS tags: HEAD, Final-Commit
GNU OSKit-Mach

/* main entrypoint (from oskit multiboot_main) for Mach.
   It turns out this file is almost entirely machine-dependent.

   This file defines `main', which is the entrypoint from the oskit.
   It calls `setup_main' (kern/startup.c) after setting up the machine,
   interrupts, paging, and `machine_slot[0]'; setup_main never returns.
   This file defines `machine_init', which is called after VM is set up.
*/

#include <oskit/clientos.h>
#include <oskit/machine/base_multiboot.h>
#include <oskit/machine/base_stack.h>
#include <oskit/machine/physmem.h>
#include <oskit/x86/base_cpu.h>
#include <oskit/x86/debug_reg.h>
#include <oskit/lmm.h>
#include <oskit/machine/phys_lmm.h>
#include <oskit/machine/base_stack.h>

#include <mach/vm_param.h>
#include <mach/vm_prot.h>
#include <mach/machine.h>

#include <oskit/x86/proc_reg.h>
#include <oskit/x86/paging.h>
#include <oskit/x86/base_vm.h>
#include <oskit/c/unistd.h>
#include <oskit/dev/dev.h>
#include <oskit/smp.h>
#include "gdt.h"

#include <string.h>

#include "vm_param.h"
#include <kern/time_out.h>
#include <sys/time.h>
#include <vm/vm_page.h>
#include <vm/vm_map.h>
#include <kern/zalloc.h>
#include <i386/machspl.h>
#include <i386/pmap.h>
#include <i386/mp_desc.h>

#include <kern/cpu_number.h>

/* As of 2000-12-21 the oskit has an incorrect value for this constant
   in <oskit/x86/proc_reg.h>, so we redefine it with the correct one.  */
#undef	CR4_PGE
#define CR4_PGE	0x80

static void my_exit (int), (*his_exit) (int);

/* XXX move to intel/pmap.h */
extern pt_entry_t *kernel_page_dir;


#include "assert.h"

extern char	version[];

char **kernel_argv;
char *kernel_cmdline;		/* XXX */

void
setup_machine_slot (int mycpu)
{
  struct cpu_info info;
  cpuid (&info);

  /* Examine the CPU model information provided by the oskit,
     and set machine_slot[0] to describe the CPU to users who ask.  */
  switch (info.family)
    {
    default:
    case CPU_FAMILY_386:
      machine_slot[mycpu].cpu_type = CPU_TYPE_I386;
      break;
    case CPU_FAMILY_486:
      machine_slot[mycpu].cpu_type = CPU_TYPE_I486;
      break;
    case CPU_FAMILY_PENTIUM:
      machine_slot[mycpu].cpu_type = CPU_TYPE_PENTIUM;
      break;
    case CPU_FAMILY_PENTIUM_PRO:
      machine_slot[mycpu].cpu_type = CPU_TYPE_PENTIUMPRO;
      break;
    }
  machine_slot[mycpu].cpu_subtype = CPU_SUBTYPE_AT386;
  machine_slot[mycpu].is_cpu = TRUE;
}

int
main (int argc, char **argv)
{
  oskit_clientos_init ();

  printf ("Welcome to %s!\r\n", version);

  /*
   * Initialize the PIC prior to any possible call to an spl.
   */
  picinit();

  /* Tell the oskit base_* code what virtual addresses we are using
     to map the linear address space.  It can't keep using the 1:1
     kvtolin mapping once we flush the direct mapping below.
     We must do this before pmap_bootstrap, because pmap_bootstrap
     uses kvtolin to decide where to put its mappings!  */
  linear_base_va = -LINEAR_MIN_KERNEL_ADDRESS;

  /* This allocates the kernel page tables and initializes kernel_pmap.  */
  pmap_bootstrap();

  /*
   * Turn paging on.
   * We'll have to temporarily install a direct mapping
   * between physical memory and low linear memory,
   * until we start using our new kernel segment descriptors.
   * One page table (4MB) should do the trick.
   * Also, set the WP bit so that on 486 or better processors
   * page-level write protection works in kernel mode.
   */
  kernel_page_dir[lin2pdenum(0)] =
    kernel_page_dir[lin2pdenum(LINEAR_MIN_KERNEL_ADDRESS)];
  paging_enable((oskit_addr_t) kernel_page_dir);
  set_cr0 (get_cr0 () | CR0_WP);

  if (base_cpuid.feature_flags & CPUF_PAGE_GLOBAL_EXT) {
    /*
     * The processor supports the "global" bit to avoid flushing kernel TLB
     * entries, if we turn it on.  pmap_bootstrap checks this feature flag
     * and begins use the global bit in page table entries.  But according
     * to the x86 specs we cannot set this bit before we do enable_paging
     * above; setting CR4_PGE first doesn't work on some processors, in fact.
     */
    set_cr4 (get_cr4 () | CR4_PGE);
  }

  /*
   * Initialize and activate the real i386 protected-mode structures.
   */
  base_gdt_init();	/* reinitialize with linear_base_va */
  gdt_init();
  idt_init();
  int_init();
  ldt_init();

  base_cpu_load();		/* Load all the new tables into the CPU.  */

  /* Now reload the TSS using our slot instead of the oskit's.  */
  base_gdt[sel_idx (KERNEL_TSS)].access &= ~ACC_TSS_BUSY;
  set_tr(KERNEL_TSS);

  /* Arrange a callback to our special exit function below, so we can
     try to return to a state the generic oskit reboot code can cope with.  */
  his_exit = oskit_libc_exit;
  oskit_libc_exit = &my_exit;

  /* Get rid of the temporary direct mapping and flush it out of the TLB.  */
  kernel_page_dir[lin2pdenum(0)] = 0;
  inval_tlb();

  /* Interrupt stacks are allocated in physical memory,
     while kernel stacks are allocated in kernel virtual memory,
     so phys_last_addr serves as a convenient dividing point.  */
  int_stack_high = phys_mem_max;

#if NCPUS > 1
  smp_init_paging ();		/* This maps physical memory SMP needs.  */
  if (smp_init () != 0)
    printf ("SMP initialization failed!\n");
  else
    {
      int ncpus;
      printf ("SMP initialized (maximum %d supported by this kernel).");
      ncpus = smp_get_num_cpus ();
      if (ncpus == 1)
	printf ("Running on a uniprocessor.\n");
      else
	{
	  printf ("Detected %d CPUs.\n");
	  if (ncpus > NCPUS)
	    printf ("WARNING: This kernel can only use %d CPUs.\n"
		    "The remaining %d CPUs will be completely idle!"
		    "\nYou should recompile your kernel with --enable-cpus=%d"
		    " or higher.\n",
		    NCPUS, ncpus - NCPUS, ncpus);
	}
      master_cpu = smp_find_cur_cpu ();
      ivect[SMP_IPI_VECTOR] = (int (*)())pmap_update_interrupt;
      intpri[SMP_IPI_VECTOR] = SPL1;
      mp_desc_init (master_cpu);
    }
  interrupt_stack_alloc ();
#endif

  setup_machine_slot (master_cpu);
  machine_slot[master_cpu].running = TRUE;

  kernel_argv = argv;		/* Stash our args for user_bootstrap to use. */

  { /* XXX */
    static char cmdline[1024];
    int i;
    strcpy (cmdline, argv[0]);
    for (i = 1; i < argc; ++i) {
      strcat (cmdline, " ");
      strcat (cmdline, argv[i]);
    }
    assert (strlen (cmdline) < sizeof cmdline);
    kernel_cmdline = cmdline;
  }

  /* Start the system.  This function does not return.  */
  setup_main();
  /* NOTREACHED */
  return -1;
}

/* This is the function we install in `oskit_libc_exit' to be called
   by _exit, panic, et al.  The oskit provided an original function
   (now stored in `his_exit'), that will only work properly if we are
   using direct-mapped physical addresses.  So we provide here a
   replacement that switches to direct linear addressing and moves
   to a physical-addressed stack and PC to call the oskit's function.  */
static void
my_exit (int rc)
{
  /* Restore direct virtual->physical mapping and switch to
     direct linear addressing code segment, so the oskit
     can cope when it tries to turn paging off.  */

  kernel_page_dir[lin2pdenum(0)]
    = kernel_page_dir[lin2pdenum(LINEAR_MIN_KERNEL_ADDRESS)];
  set_cr4 (get_cr4 () &~ CR4_PGE);
  set_pdbr (kvtophys (kernel_page_dir));

  /* Reload the oskit's GDT slots.  */
  base_gdt_init ();
  base_gdt_load ();

  asm volatile ("	ljmp	%0,$1f	\n" /* Switch to LINEAR_CS,  */
		"1:	movw	%w1,%%ds\n" /* Switch %ds to LINEAR_DS.  */
		"	movw	%w1,%%es\n" /* Switch %es to LINEAR_DS.  */
		"	movw	%w1,%%ss\n" /* Switch %ss to LINEAR_DS.  */
		"	movl	%2,%%esp\n" /* and to phys-addr base_stack.  */
		"	pushl	%3	\n" /* Push argument (RC).  */
		"	pushl	%4	\n" /* Push bogus return address.  */
		"	jmp	%*%5" : : /* Jump to oskit, never return.  */
		"i" (LINEAR_CS), "r" (LINEAR_DS),
		"ir" (kvtophys (&base_stack_end)),
		"ir" (rc), "ir" (0),
		"r" (kvtophys (his_exit)));
  /* NOTREACHED */
}

boolean_t pmap_valid_page(x)
	vm_offset_t x;
{
	/* XXX is this OK?  What does it matter for?  */
	return (((phys_mem_min <= x) && (x < phys_mem_max)) &&
		!(((boot_info.mem_lower * 1024) <= x) && (x < 1024*1024)));
}



#include <mach/time_value.h>

startrtclock()
{
	clkstart();
}

static void
inittodr()
{
  oskit_timespec_t ts;
  oskit_error_t rc;
  spl_t s;

  rc = oskit_rtc_get (&ts);
  if (rc)
    panic (__FUNCTION__);

  s = splhigh();
#undef tv_sec			/* oy */
  time.seconds = ts.tv_sec;
  time.microseconds = (ts.tv_nsec + 999) / 1000;
  splx(s);
}

/* This is called from host_set_time at splhigh to reset the hardware clock
   to the new value of `time'.  */
void
resettodr()
{
  oskit_timespec_t ts = { time.seconds, time.microseconds * 1000 };
  oskit_rtc_set (&ts);
}



void
machine_idle (int mycpu)
{
  asm volatile ("hlt" : : : "memory");
}

void
halt_cpu ()
{
  while (1)
    asm volatile ("cli; hlt");
}

void
halt_all_cpus(reboot)
	boolean_t	reboot;
{
  exit(reboot ? 0 : 1);
}


void machine_init()
{
	/*
	 * Set up to use floating point.
	 */
	init_fpu();

	/*
	 * Get the time
	 */
	inittodr();

	/*
	 * Unmap page 0 to trap NULL references.
	 * If there is real memory in the first physical page,
	 * then it will not be accessible through the normal
	 * direct mapping, so we need to take it out of the LMM.
	 */
	if (phys_mem_min < PAGE_SIZE)
	  {
	    void *block = lmm_alloc_gen (&malloc_lmm,
					 PAGE_SIZE - phys_mem_min, 0, 0, 0,
					 phys_mem_min,
					 PAGE_SIZE - phys_mem_min);
	    if ((oskit_addr_t) block != phys_mem_min)
	      panic ("cannot allocate first page [%#x,%#x) from physical LMM!",
		     phys_mem_min, PAGE_SIZE);
	    else
	      {
		/*
		 * So we have this partial page that we can't use where it is.
		 * Rather than waste it, let's map it in someplace else
		 * and donate it to someplace that can always use a little
		 * chunk of extra wired kernel virtual memory: the zone system.
		 */
		extern vm_map_t zone_map; /* zalloc.c */
		extern zone_t vm_page_zone; /* vm_resident.c */
		vm_offset_t kva;
		kern_return_t kr;
		kr = kmem_alloc_pageable (zone_map, &kva, PAGE_SIZE);
		if (kr != KERN_SUCCESS)
		  panic ("machine_init: kmem_alloc_pageable zone_map: %#x",
			 kr);
		pmap_enter (kernel_pmap, kva, 0,
			    VM_PROT_READ | VM_PROT_WRITE, TRUE);
		kva += phys_mem_min; /* Skip mapping below phys_mem_min.  */
		/*
		 * The vm_page_zone is always a needy soul early in life.
		 */
		zcram (vm_page_zone, kva, PAGE_SIZE - phys_mem_min);
	      }
	  }
	pmap_unmap_page_zero();

	/* Catch interrupt stack overflow.  */
	set_b0 (kvtolin (&base_stack_start), DR7_LEN_4, DR7_RW_DATA);
	base_gdt_load();	/* necessary after setting debug regs */

	/* That loaded the oskit's selectors.  Now reload ours.  */
	asm volatile("ljmp %0,$1f\n\t1:" : : "i" (KERNEL_CS));
	set_ds(KERNEL_DS);
	set_es(KERNEL_DS);
	set_ss(KERNEL_DS);
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.