--- /dev/null
+#include <lwk/kernel.h>
+#include <lwk/init.h>
+#include <lwk/cpuinfo.h>
+#include <lwk/bootmem.h>
+#include <lwk/smp.h>
+#include <arch/bootsetup.h>
+#include <arch/e820.h>
+#include <arch/page.h>
+#include <arch/sections.h>
+#include <arch/proto.h>
+#include <arch/mpspec.h>
+#include <arch/pda.h>
+#include <arch/io_apic.h>
+
+/**
+ * Bitmap of of PTE/PMD entry flags that are supported.
+ * This is AND'ed with a PTE/PMD entry before it is installed.
+ */
+unsigned long __supported_pte_mask __read_mostly = ~0UL;
+
+/**
+ * Bitmap of features enabled in the CR4 register.
+ */
+unsigned long mmu_cr4_features;
+
+/**
+ * Start and end addresses of the initrd image.
+ */
+paddr_t __initdata initrd_start;
+paddr_t __initdata initrd_end;
+
+/**
+ * The init_task ELF image.
+ */
+paddr_t __initdata init_elf_image;
+
+/**
+ * Base address and size of the Extended BIOS Data Area.
+ */
+paddr_t __initdata ebda_addr;
+size_t __initdata ebda_size;
+#define EBDA_ADDR_POINTER 0x40E
+
+/**
+ * Finds the address and length of the Extended BIOS Data Area.
+ */
+static void __init
+discover_ebda(void)
+{
+ /*
+ * There is a real-mode segmented pointer pointing to the
+ * 4K EBDA area at 0x40E
+ */
+ ebda_addr = *(unsigned short *)__va(EBDA_ADDR_POINTER);
+ ebda_addr <<= 4;
+
+ ebda_size = *(unsigned short *)__va(ebda_addr);
+
+ /* Round EBDA up to pages */
+ if (ebda_size == 0)
+ ebda_size = 1;
+ ebda_size <<= 10;
+ ebda_size = round_up(ebda_size + (ebda_addr & ~PAGE_MASK), PAGE_SIZE);
+ if (ebda_size > 64*1024)
+ ebda_size = 64*1024;
+}
+
+/**
+ * This sets up the bootstrap memory allocator. It is a simple
+ * bitmap based allocator that tracks memory at a page grandularity.
+ * Once the bootstrap process is complete, each unallocated page
+ * is added to the real memory allocator's free pool. Memory allocated
+ * during bootstrap remains allocated forever, unless explicitly
+ * freed before turning things over to the real memory allocator.
+ */
+static void __init
+setup_bootmem_allocator(
+ unsigned long start_pfn,
+ unsigned long end_pfn
+)
+{
+ unsigned long bootmap_size, bootmap;
+
+ bootmap_size = bootmem_bootmap_pages(end_pfn)<<PAGE_SHIFT;
+ bootmap = find_e820_area(0, end_pfn<<PAGE_SHIFT, bootmap_size);
+ if (bootmap == -1L)
+ panic("Cannot find bootmem map of size %ld\n",bootmap_size);
+ bootmap_size = init_bootmem(bootmap >> PAGE_SHIFT, end_pfn);
+ e820_bootmem_free(0, end_pfn << PAGE_SHIFT);
+ reserve_bootmem(bootmap, bootmap_size);
+}
+
+/**
+ * Mark in-use memory regions as reserved.
+ * This prevents the bootmem allocator from allocating them.
+ */
+static void __init
+reserve_memory(void)
+{
+ /* Reserve the kernel page table memory */
+ reserve_bootmem(table_start << PAGE_SHIFT,
+ (table_end - table_start) << PAGE_SHIFT);
+
+ /* Reserve kernel memory */
+ reserve_bootmem(__pa_symbol(&_text),
+ __pa_symbol(&_end) - __pa_symbol(&_text));
+
+ /* Reserve physical page 0... it's a often a special BIOS page */
+ reserve_bootmem(0, PAGE_SIZE);
+
+ /* Reserve the Extended BIOS Data Area memory */
+ if (ebda_addr)
+ reserve_bootmem(ebda_addr, ebda_size);
+
+ /* Reserve SMP trampoline */
+ reserve_bootmem(SMP_TRAMPOLINE_BASE, 2*PAGE_SIZE);
+
+ /* Find and reserve boot-time SMP configuration */
+ find_mp_config();
+
+ /* Reserve memory used by the initrd image */
+ if (LOADER_TYPE && INITRD_START) {
+ if (INITRD_START + INITRD_SIZE <= (end_pfn << PAGE_SHIFT)) {
+ printk(KERN_DEBUG
+ "reserving memory used by initrd image\n");
+ printk(KERN_DEBUG
+ " INITRD_START=0x%lx, INITRD_SIZE=%ld bytes\n",
+ (unsigned long) INITRD_START,
+ (unsigned long) INITRD_SIZE);
+ reserve_bootmem(INITRD_START, INITRD_SIZE);
+ initrd_start = INITRD_START;
+ initrd_end = initrd_start+INITRD_SIZE;
+ init_elf_image = initrd_start;
+ } else {
+ printk(KERN_ERR
+ "initrd extends beyond end of memory "
+ "(0x%08lx > 0x%08lx)\ndisabling initrd\n",
+ (unsigned long)(INITRD_START + INITRD_SIZE),
+ (unsigned long)(end_pfn << PAGE_SHIFT));
+ initrd_start = 0;
+ }
+ }
+}
+
+/**
+ * This initializes a per-CPU area for each CPU.
+ *
+ * TODO: The PDA and per-CPU areas are pretty tightly wound. It should be
+ * possible to make the per-CPU area *be* the PDA, or put another way,
+ * point %GS at the per-CPU area rather than the PDA. All of the PDA's
+ * current contents would become normal per-CPU variables.
+ */
+static void __init
+setup_per_cpu_areas(void)
+{
+ int i;
+ size_t size;
+
+ /*
+ * There is an ELF section containing all per-CPU variables
+ * surrounded by __per_cpu_start and __per_cpu_end symbols.
+ * We create a copy of this ELF section for each CPU.
+ */
+ size = ALIGN(__per_cpu_end - __per_cpu_start, SMP_CACHE_BYTES);
+
+ for_each_cpu_mask (i, cpu_present_map) {
+ char *ptr;
+
+ ptr = alloc_bootmem_aligned(size, PAGE_SIZE);
+ if (!ptr)
+ panic("Cannot allocate cpu data for CPU %d\n", i);
+
+ /*
+ * Pre-bias data_offset by subtracting its offset from
+ * __per_cpu_start. Later, per_cpu() will calculate a
+ * per_cpu variable's address with:
+ *
+ * addr = offset_in_percpu_ELF_section + data_offset
+ * = (__per_cpu_start + offset) + (ptr - __per_cpu_start)
+ * = offset + ptr
+ */
+ cpu_pda(i)->data_offset = ptr - __per_cpu_start;
+
+ memcpy(ptr, __per_cpu_start, __per_cpu_end - __per_cpu_start);
+ }
+}
+
+static inline int get_family(int cpuid)
+{
+ int base = (cpuid>>8) & 0xf;
+ int extended = (cpuid>>20) &0xff;
+
+ return (0xf == base) ? base + extended : base;
+}
+
+/**
+ * Architecture specific initialization.
+ * This is called from start_kernel() in init/main.c.
+ *
+ * NOTE: Ordering is usually important. Do not move things
+ * around unless you know what you are doing.
+ */
+void __init
+setup_arch(void)
+{
+ /*
+ * Figure out which memory regions are usable and which are reserved.
+ * This builds the "e820" map of memory from info provided by the
+ * BIOS.
+ */
+ setup_memory_region();
+
+ /*
+ * Get the bare minimum info about the bootstrap CPU... the
+ * one we're executing on right now. Latter on, the full
+ * boot_cpu_data and cpu_info[boot_cpu_id] structures will be
+ * filled in completely.
+ */
+ boot_cpu_data.logical_id = 0;
+ early_identify_cpu(&boot_cpu_data);
+
+ /*
+ * Find the Extended BIOS Data Area.
+ * (Not sure why exactly we need this, probably don't.)
+ */
+ discover_ebda();
+
+ /*
+ * Initialize the kernel page tables.
+ * The kernel page tables map an "identity" map of all physical memory
+ * starting at virtual address PAGE_OFFSET. When the kernel executes,
+ * it runs inside of the identity map... memory below PAGE_OFFSET is
+ * from whatever task was running when the kernel got invoked.
+ */
+ init_kernel_pgtables(0, (end_pfn_map << PAGE_SHIFT));
+
+ /*
+ * Initialize the bootstrap dynamic memory allocator.
+ * alloc_bootmem() will work after this.
+ */
+ setup_bootmem_allocator(0, end_pfn);
+ reserve_memory();
+
+ /*
+ * Get the multiprocessor configuration...
+ * number of CPUs, PCI bus info, APIC info, etc.
+ */
+ get_mp_config();
+
+ /*
+ * Initialize resources. Resources reserve sections of normal memory
+ * (iomem) and I/O ports (ioport) for devices and other system
+ * resources. For each resource type, there is a tree which tracks
+ * which regions are in use. This eliminates the possiblity of
+ * conflicts... e.g., two devices trying to use the same iomem region.
+ */
+ init_resources();
+
+ /*
+ * Initialize per-CPU areas, one per CPU.
+ * Variables defined with DEFINE_PER_CPU() end up in the per-CPU area.
+ * This provides a mechanism for different CPUs to refer to their
+ * private copy of the variable using the same name
+ * (e.g., get_cpu_var(foo)).
+ */
+ setup_per_cpu_areas();
+
+ /*
+ * Initialize the IDT table and interrupt handlers.
+ */
+ interrupts_init();
+
+ /*
+ * Map the APICs into the kernel page tables.
+ *
+ * Each CPU has its own Local APIC. All Local APICs are memory mapped
+ * to the same virtual address region. A CPU accesses its Local APIC by
+ * accessing the region. A CPU cannot access another CPU's Local APIC.
+ *
+ * Each Local APIC is connected to all IO APICs in the system. Each IO
+ * APIC is mapped to a different virtual address region. A CPU accesses
+ * a given IO APIC by accessing the appropriate region. All CPUs can
+ * access all IO APICs.
+ */
+ lapic_map();
+ ioapic_map();
+
+ /*
+ * Initialize the virtual system call code/data page.
+ * The vsyscall page is mapped into every task's address space at a
+ * well-known address. User code can call functions in this page
+ * directly, providing a light-weight mechanism for read-only system
+ * calls such as gettimeofday().
+ */
+ vsyscall_map();
+
+ cpu_init();
+
+ current->cpumask = cpu_present_map;
+
+ ioapic_init();
+
+ lapic_set_timer(1000000000);
+}
+