blob: d97190c2cfa48384e0689f7173de9af5e9863692 [file] [log] [blame]
/*
* Copyright (c) 2009 Corey Tabaka
* Copyright (c) 2015 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <err.h>
#include <trace.h>
#include <arch/x86/mmu.h>
#include <platform.h>
#include "platform_p.h"
#include <platform/pc.h>
#include <platform/multiboot.h>
#include <platform/console.h>
#include <platform/keyboard.h>
#include <dev/pci.h>
#include <dev/uart.h>
#include <arch/x86.h>
#include <arch/mmu.h>
#include <malloc.h>
#include <string.h>
#include <assert.h>
#include <kernel/vm.h>
extern multiboot_info_t *_multiboot_info;
#define DEFAULT_MEMEND (16*1024*1024)
#ifdef WITH_KERNEL_VM
extern int _end;
static uintptr_t _heap_end = (uintptr_t)DEFAULT_MEMEND;
#else
extern uintptr_t _heap_end;
#endif
extern uint64_t __code_start;
extern uint64_t __code_end;
extern uint64_t __rodata_start;
extern uint64_t __rodata_end;
extern uint64_t __data_start;
extern uint64_t __data_end;
extern uint64_t __bss_start;
extern uint64_t __bss_end;
extern void pci_init(void);
extern void arch_mmu_init(void);
/* Address width including virtual/physical address*/
uint8_t g_vaddr_width = 0;
uint8_t g_paddr_width = 0;
/* Kernel global CR3 */
map_addr_t g_CR3 = 0;
void platform_init_mmu_mappings(void)
{
struct map_range range;
arch_flags_t access;
map_addr_t *init_table, phy_init_table;
uint32_t addr_width;
/* getting the address width from CPUID instr */
/* Bits 07-00: Physical Address width info */
/* Bits 15-08: Linear Address width info */
addr_width = x86_get_address_width();
g_paddr_width = (uint8_t)(addr_width & 0xFF);
g_vaddr_width = (uint8_t)((addr_width >> 8) & 0xFF);
/* Creating the First page in the page table hirerachy */
/* Can be pml4, pdpt or pdt based on x86_64, x86 PAE mode & x86 non-PAE mode respectively */
init_table = memalign(PAGE_SIZE, PAGE_SIZE);
ASSERT(init_table);
memset(init_table, 0, PAGE_SIZE);
phy_init_table = (map_addr_t)X86_VIRT_TO_PHYS(init_table);
/* kernel code section mapping */
access = ARCH_MMU_FLAG_PERM_RO;
range.start_vaddr = range.start_paddr = (map_addr_t) &__code_start;
range.size = ((map_addr_t)&__code_end) - ((map_addr_t)&__code_start);
x86_mmu_map_range(phy_init_table, &range, access);
/* kernel data section mapping */
access = 0;
#if defined(ARCH_X86_64) || defined(PAE_MODE_ENABLED)
access |= ARCH_MMU_FLAG_PERM_NO_EXECUTE;
#endif
range.start_vaddr = range.start_paddr = (map_addr_t) &__data_start;
range.size = ((map_addr_t)&__data_end) - ((map_addr_t)&__data_start);
x86_mmu_map_range(phy_init_table, &range, access);
/* kernel rodata section mapping */
access = ARCH_MMU_FLAG_PERM_RO;
#if defined(ARCH_X86_64) || defined(PAE_MODE_ENABLED)
access |= ARCH_MMU_FLAG_PERM_NO_EXECUTE;
#endif
range.start_vaddr = range.start_paddr = (map_addr_t) &__rodata_start;
range.size = ((map_addr_t)&__rodata_end) - ((map_addr_t)&__rodata_start);
x86_mmu_map_range(phy_init_table, &range, access);
/* kernel bss section and kernel heap mappings */
access = 0;
#ifdef ARCH_X86_64
access |= ARCH_MMU_FLAG_PERM_NO_EXECUTE;
#endif
range.start_vaddr = range.start_paddr = (map_addr_t) &__bss_start;
range.size = ((map_addr_t)_heap_end) - ((map_addr_t)&__bss_start);
x86_mmu_map_range(phy_init_table, &range, access);
/* Mapping for BIOS, devices */
access = 0;
range.start_vaddr = range.start_paddr = (map_addr_t) 0;
range.size = ((map_addr_t)&__code_start);
x86_mmu_map_range(phy_init_table, &range, access);
/* Moving to the new CR3 */
g_CR3 = (map_addr_t)phy_init_table;
x86_set_cr3((map_addr_t)phy_init_table);
}
#if WITH_KERNEL_VM
struct mmu_initial_mapping mmu_initial_mappings[] = {
/* all of detected memory */
{
.phys = MEMBASE,
.virt = MEMBASE,
.size = DEFAULT_MEMEND - MEMBASE,
.flags = 0,
.name = "memory"
},
/* null entry to terminate the list */
{ 0 }
};
/* set up the size of the identity map of physical ram to virtual at the base
* of the kernel to match what we detected in platform_init_multiboot_info()
*/
void initial_mapping_init(void)
{
/* tweak the amount of physical memory map we have mapped
* in the mmu_initial_mappings table, which is used by the vm
* for reverse lookups of memory in the kernel area */
mmu_initial_mappings[0].size = _heap_end - mmu_initial_mappings[0].virt;
}
static pmm_arena_t mem_arena = {
.name = "memory",
.base = MEMBASE, /* start 2MB into memory */
.size = DEFAULT_MEMEND, /* default amount of memory in case we don't have multiboot */
.priority = 1,
.flags = PMM_ARENA_FLAG_KMAP
};
/* set up the size of the physical memory map based on the end of memory we detected in
* platform_init_multiboot_info()
*/
void mem_arena_init(void)
{
uintptr_t mem_base = ((uintptr_t)MEMBASE);
uintptr_t mem_size = (uintptr_t)_heap_end - (uintptr_t)mem_base;
mem_arena.base = PAGE_ALIGN(mem_base);
mem_arena.size = PAGE_ALIGN(mem_size);
}
#endif
void platform_init_multiboot_info(void)
{
if (_multiboot_info) {
if (_multiboot_info->flags & MB_INFO_MEM_SIZE) {
_heap_end = _multiboot_info->mem_upper * 1024;
}
if (_multiboot_info->flags & MB_INFO_MMAP) {
memory_map_t *mmap = (memory_map_t *)(uintptr_t)(_multiboot_info->mmap_addr - 4);
for (uint i = 0; i < _multiboot_info->mmap_length / sizeof(memory_map_t); i++) {
if (mmap[i].type == MB_MMAP_TYPE_AVAILABLE && mmap[i].base_addr_low >= _heap_end) {
_heap_end = mmap[i].base_addr_low + mmap[i].length_low;
} else if (mmap[i].type != MB_MMAP_TYPE_AVAILABLE && mmap[i].base_addr_low >= _heap_end) {
/*
* break on first memory hole above default heap end for now.
* later we can add facilities for adding free chunks to the
* heap for each segregated memory region.
*/
break;
}
}
}
}
}
void platform_early_init(void)
{
platform_init_uart();
/* get the text console working */
platform_init_console();
/* initialize the interrupt controller */
platform_init_interrupts();
/* initialize the timer */
platform_init_timer();
/* look at multiboot to determine our memory size */
platform_init_multiboot_info();
#ifdef WITH_KERNEL_VM
initial_mapping_init();
mem_arena_init();
pmm_add_arena(&mem_arena);
#endif
}
void platform_init(void)
{
uart_init();
platform_init_keyboard();
#if defined(ARCH_X86)
pci_init();
#endif
/* MMU init for x86 Archs done after the heap is setup */
// XXX move this into arch/
arch_mmu_init();
platform_init_mmu_mappings();
}
/* vim: set noexpandtab: */