/*
* Copyright (c) 2010, 2012 Richard Braun.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*
* Early initialization procedure for x86.
*
* This module is separated in assembly and C code. The former is where
* the first instructions are run, and where actions that aren't possible,
* easy or clean in C are performed.
*
* When the boot loader passes control to the kernel, the main processor is
* in protected mode, paging is disabled, and some boot data are availabe
* outside the kernel. This module first sets up a basic physical memory
* allocator so that it can allocate page tables without corrupting the
* boot data. The .boot section is linked at physical addresses, so that
* it can run with and without paging enabled. The page tables must properly
* configure an identity mapping so that this remains true as long as
* initialization code and data are used. Once the VM system is available,
* boot data are copied in kernel allocated buffers and their original pages
* are freed.
*
* On amd64, 64-bit code cannot run in legacy or compatibility mode. In order
* to walk the boot data structures, the kernel must either run 32-bit code
* (e.g. converting ELF32 to ELF64 objects before linking them) or establish
* a temporary identity mapping for the first 4 GiB of physical memory. As a
* way to simplify development, and make it possible to use 64-bit code
* almost everywhere, the latter solution is implemented (a small part of
* 32-bit code is required until the identity mapping is in place). Mentions
* to "enabling paging" do not refer to this initial identity mapping.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*
* Macros used by the very early panic function.
*/
#define INIT_CGAMEM ((uint16_t *)0xb8000)
#define INIT_CGACHARS (80 * 25)
#define INIT_CGACOLOR 0x7
char boot_stack[BOOT_STACK_SIZE] __aligned(DATA_ALIGN) __initdata;
char boot_ap_stack[BOOT_STACK_SIZE] __aligned(DATA_ALIGN) __bootdata;
unsigned long boot_ap_id __bootdata;
unsigned long boot_ap_stack_addr __bootdata;
#ifdef __LP64__
pmap_pte_t boot_pml4[PMAP_L4_NR_PTES] __aligned(PAGE_SIZE) __bootdata;
pmap_pte_t boot_pdpt[PMAP_L3_NR_PTES] __aligned(PAGE_SIZE) __bootdata;
pmap_pte_t boot_pdir[4 * PMAP_L2_NR_PTES] __aligned(PAGE_SIZE) __bootdata;
#endif /* __LP64__ */
/*
* Copies of the multiboot data passed by the boot loader.
*/
static struct multiboot_raw_info boot_raw_mbi __bootdata;
static struct multiboot_info boot_mbi __initdata;
void __boot
boot_panic(const char *msg)
{
uint16_t *ptr, *end;
const char *s;
ptr = INIT_CGAMEM;
end = ptr + INIT_CGACHARS;
s = (void *)BOOT_VTOP((unsigned long)"panic: ");
while ((ptr < end) && (*s != '\0'))
*ptr++ = (INIT_CGACOLOR << 8) | *s++;
s = BOOT_VTOP(msg);
while ((ptr < end) && (*s != '\0'))
*ptr++ = (INIT_CGACOLOR << 8) | *s++;
while (ptr < end)
*ptr++ = (INIT_CGACOLOR << 8) | ' ';
cpu_halt();
/* Never reached */
}
pmap_pte_t * __boot
boot_setup_paging(const struct multiboot_raw_info *mbi, unsigned long eax)
{
if (eax != MULTIBOOT_LOADER_MAGIC)
boot_panic("not started by a multiboot compliant boot loader");
if (!(mbi->flags & MULTIBOOT_LOADER_MEMORY))
boot_panic("missing basic memory information");
/*
* Save the multiboot data passed by the boot loader, initialize the
* bootstrap allocator and set up paging.
*/
boot_raw_mbi = *mbi;
if ((mbi->flags & MULTIBOOT_LOADER_MODULES) && (mbi->mods_count == 0))
boot_raw_mbi.flags &= ~MULTIBOOT_LOADER_MODULES;
biosmem_bootstrap(&boot_raw_mbi);
return pmap_setup_paging();
}
static void __init
boot_show_version(void)
{
printk(KERNEL_NAME "/" QUOTE(X86_MACHINE) " " KERNEL_VERSION
#ifdef X86_PAE
" PAE"
#endif /* X86_PAE */
"\n");
}
static void * __init
boot_save_memory(uint32_t addr, size_t size)
{
unsigned long map_addr;
size_t map_size;
const void *src;
void *copy;
src = vm_kmem_map_pa(addr, size, &map_addr, &map_size);
if (src == NULL)
panic("boot: unable to map boot data in kernel map");
copy = kmem_alloc(size);
if (copy == NULL)
panic("boot: unable to allocate memory for boot data copy");
memcpy(copy, src, size);
vm_kmem_unmap_pa(map_addr, map_size);
return copy;
}
static void __init
boot_save_mod(struct multiboot_module *dest_mod,
const struct multiboot_raw_module *src_mod)
{
unsigned long map_addr;
size_t size, map_size;
const void *src;
void *copy;
size = src_mod->mod_end - src_mod->mod_start;
src = vm_kmem_map_pa(src_mod->mod_start, size, &map_addr, &map_size);
if (src == NULL)
panic("boot: unable to map module in kernel map");
copy = kmem_alloc(size);
if (copy == NULL)
panic("boot: unable to allocate memory for module copy");
memcpy(copy, src, size);
vm_kmem_unmap_pa(map_addr, map_size);
dest_mod->mod_start = copy;
dest_mod->mod_end = copy + size;
if (src_mod->string == 0)
dest_mod->string = NULL;
else
dest_mod->string = boot_save_memory(src_mod->string, src_mod->reserved);
}
static void __init
boot_save_mods(void)
{
const struct multiboot_raw_module *src;
struct multiboot_module *dest;
unsigned long map_addr;
size_t size, map_size;
uint32_t i;
if (!(boot_raw_mbi.flags & MULTIBOOT_LOADER_MODULES)) {
boot_mbi.mods_addr = NULL;
boot_mbi.mods_count = boot_raw_mbi.mods_count;
return;
}
size = boot_raw_mbi.mods_count * sizeof(struct multiboot_raw_module);
src = vm_kmem_map_pa(boot_raw_mbi.mods_addr, size, &map_addr, &map_size);
if (src == NULL)
panic("boot: unable to map module table in kernel map");
size = boot_raw_mbi.mods_count * sizeof(struct multiboot_module);
dest = kmem_alloc(size);
if (dest == NULL)
panic("boot: unable to allocate memory for the module table");
for (i = 0; i < boot_raw_mbi.mods_count; i++)
boot_save_mod(&dest[i], &src[i]);
vm_kmem_unmap_pa(map_addr, map_size);
boot_mbi.mods_addr = dest;
boot_mbi.mods_count = boot_raw_mbi.mods_count;
}
/*
* Copy boot data in kernel allocated memory.
*
* At this point, the only required boot data are the modules and the command
* line strings. Optionally, the kernel can use the symbol table, if passed by
* the boot loader. Once the boot data are managed as kernel buffers, their
* backing pages can be freed.
*/
static void __init
boot_save_data(void)
{
boot_mbi.flags = boot_raw_mbi.flags;
if (boot_mbi.flags & MULTIBOOT_LOADER_CMDLINE)
boot_mbi.cmdline = boot_save_memory(boot_raw_mbi.cmdline,
boot_raw_mbi.unused0);
else
boot_mbi.cmdline = NULL;
boot_save_mods();
strace_setup(&boot_raw_mbi);
}
void __init
boot_main(void)
{
trap_setup();
cpu_setup();
thread_bootstrap();
pmap_bootstrap();
cga_setup();
boot_show_version();
cpu_check(cpu_current());
cpu_info(cpu_current());
biosmem_setup();
vm_setup();
boot_save_data();
biosmem_free_usable();
vm_phys_info();
pic_setup();
pit_setup();
kernel_main();
/* Never reached */
}
void __init
boot_ap_main(void)
{
cpu_ap_setup();
thread_ap_bootstrap();
pmap_ap_bootstrap();
kernel_ap_main();
/* Never reached */
}