From c51062ce87f24cff42276bb1086f7025db9dcf10 Mon Sep 17 00:00:00 2001 From: Aaron Lindsay Date: Mon, 17 Dec 2012 00:35:23 -0500 Subject: [PATCH] arch/i386: Boot to kernel main() with proper initial segmentation/paging setup --- arch/i386/i386_main.c | 30 ------ arch/i386/include/arch/segmentation.h | 26 +++++ arch/i386/kernel.ld | 19 +++- arch/i386/kernel.mk | 3 +- arch/i386/start.S | 135 +++++++++++++++++++++----- 5 files changed, 153 insertions(+), 60 deletions(-) delete mode 100644 arch/i386/i386_main.c create mode 100644 arch/i386/include/arch/segmentation.h diff --git a/arch/i386/i386_main.c b/arch/i386/i386_main.c deleted file mode 100644 index 621f859..0000000 --- a/arch/i386/i386_main.c +++ /dev/null @@ -1,30 +0,0 @@ -#include - -void main(); - -void i386_main(void) -{ - extern uint32_t magic; - - /* Uncomment the following if you want to be able to access the multiboot header */ - /* extern void *mbd; */ - - if ( magic != 0x2BADB002 ) - { - /* Something went not according to specs. Print an error */ - /* message and halt, but do *not* rely on the multiboot */ - /* data structure. */ - } - - /* You could either use multiboot.h */ - /* (http://www.gnu.org/software/grub/manual/multiboot/multiboot.html#multiboot_002eh) */ - /* or do your offsets yourself. The following is merely an example. */ - //char * boot_loader_name =(char*) ((long*)mbd)[16]; - - /* Print a letter to screen to see everything is working: */ - unsigned char *videoram = (unsigned char *)0xB8000; - videoram[0] = 65; /* character 'A' */ - videoram[1] = 0x07; /* light grey (7) on black (0). */ - - main(); -} diff --git a/arch/i386/include/arch/segmentation.h b/arch/i386/include/arch/segmentation.h new file mode 100644 index 0000000..058d7d3 --- /dev/null +++ b/arch/i386/include/arch/segmentation.h @@ -0,0 +1,26 @@ +/* + Copyright (C) 2012, Aaron Lindsay + + This file is part of Aedrix. + + 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 2 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define SEGMENT_SELECTOR_KERNEL_PRIVILEGE 0x0 +#define SEGMENT_SELECTOR_KERNEL_CS (0x10 | SEGMENT_SELECTOR_KERNEL_PRIVILEGE) +#define SEGMENT_SELECTOR_KERNEL_DS (0x18 | SEGMENT_SELECTOR_KERNEL_PRIVILEGE) +#define SEGMENT_SELECTOR_USER_PRIVILEGE 0x3 +#define SEGMENT_SELECTOR_USER_CS (0x20 | SEGMENT_SELECTOR_USER_PRIVILEGE) +#define SEGMENT_SELECTOR_USER_DS (0x28 | SEGMENT_SELECTOR_USER_PRIVILEGE) diff --git a/arch/i386/kernel.ld b/arch/i386/kernel.ld index 095a4a2..5a1e95a 100644 --- a/arch/i386/kernel.ld +++ b/arch/i386/kernel.ld @@ -2,9 +2,12 @@ ENTRY (start) SECTIONS { - . = 0x00100000; - .text ALIGN (0x1000) : { *(.text*) *(.rodata*) } - .init : { + . = 0xc0100000; + kernel_start = .; + .text ALIGN(0x1000) : AT(ADDR(.text) - 0xc0000000) { + *(.text*) *(.rodata*) + } + .init : AT(ADDR(.init) - 0xc0000000) { early_initcalls_start = .; *(.earlyinitcalls*) early_initcalls_end = .; @@ -12,7 +15,13 @@ SECTIONS *(.driversubsysinitcalls*) *(.deviceinitcalls*) initcalls_end = .; + *(.init*) } - .data ALIGN (0x1000) : { *(.data*) } - .bss : { *(.bss*) *(COMMON*) } + .data ALIGN(0x1000) : AT(ADDR(.data) - 0xc0000000) { + *(.data*) + } + .bss : AT(ADDR(.bss) - 0xc0000000) { + *(.bss*) *(COMMON*) + } + kernel_end = .; } diff --git a/arch/i386/kernel.mk b/arch/i386/kernel.mk index c72a14d..18b5468 100644 --- a/arch/i386/kernel.mk +++ b/arch/i386/kernel.mk @@ -22,8 +22,7 @@ aedrix-boot.img: aedrix-kernel.elf $(V)mcopy -i "$@" aedrix-kernel.elf ::kernel.bin $(V)mcopy -i "$@" arch/i386/syslinux.cfg ::syslinux.cfg -OBJS_$(d) := $(d)/start.o \ - $(d)/i386_main.o +OBJS_$(d) := $(d)/start.o KOBJS += $(OBJS_$(d)) diff --git a/arch/i386/start.S b/arch/i386/start.S index 4f13569..72ba07c 100644 --- a/arch/i386/start.S +++ b/arch/i386/start.S @@ -18,39 +18,128 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -.global start /* making entry point visible to linker */ +#include -/* setting up the Multiboot header - see GRUB docs for details */ -.set ALIGN, 1<<0 /* align loaded modules on page boundaries */ -.set MEMINFO, 1<<1 /* provide memory map */ -.set FLAGS, ALIGN | MEMINFO /* this is the Multiboot 'flag' field */ -.set MAGIC, 0x1BADB002 /* 'magic number' lets bootloader find the header */ -.set CHECKSUM, -(MAGIC + FLAGS) /* checksum required */ +#define KERNEL_START_VIRTUAL 0xc0000000 +#define KERNEL_START_PAGE (KERNEL_START_VIRTUAL >> 22) +#define virt_to_phys(addr) (addr - KERNEL_START_VIRTUAL) + +#define STACK_SIZE 0x4000 /* 16k */ + +/* setup multiboot header */ +#define ALIGN (1<<0) /* align loaded modules on page boundaries */ +#define MEMINFO (1<<1) /* provide memory map */ +#define FLAGS (ALIGN | MEMINFO) /* this is the Multiboot 'flag' field */ +#define MAGIC 0x1BADB002 /* 'magic number' lets bootloader find the header */ +#define CHECKSUM (-(MAGIC + FLAGS)) /* checksum required */ .align 4 .long MAGIC .long FLAGS .long CHECKSUM -/* reserve initial kernel stack space */ -.set STACKSIZE, 0x4000 /* that is, 16k. */ -.align 32 -.lcomm stack, STACKSIZE /* reserve 16k stack on a doubleword boundary */ -.comm mbd, 4 /* we will use this in i386_main */ -.comm magic, 4 /* we will use this in i386_main */ - +/* + * Kernel entry in assembly for i386 architecture. This handles saving off + * multiboot information, setting up initial page tables to map the kernel to + * where it is linked against, ensures segmentation is setup right (large segments + * over entire address range), and finally jumps to the kernel's main() function + * in C. + */ +.global start start: - movl $(stack + STACKSIZE), %esp /* set up the stack */ - movl %eax, magic /* Multiboot magic number */ - movl %ebx, mbd /* Multiboot data structure */ + movl %eax, virt_to_phys(magic) /* Save off multiboot magic number */ + movl %ebx, virt_to_phys(mbd) /* Save off multiboot data structure */ - call i386_main /* call kernel proper */ + movl $virt_to_phys(init_page_dir), %eax /* load page directory base register */ + movl %eax, %cr3 - cli + movl %cr4, %eax + orl $0x00000010, %eax /* set PSE bit for 4mb pages */ + movl %eax, %cr4 + + movl %cr0, %eax + orl $0x80000000, %eax /* set PG bit to enable paging */ + movl %eax, %cr0 + + lea paging_enabled, %eax + jmp *%eax +paging_enabled: + movl $0, init_page_dir /* unmap identity mapping and invalidate */ + invlpg 0 + + movl $(stack + STACK_SIZE), %esp /* set up the stack */ + + /* setup two large kernel segments over entire 4gb address range */ + pushl $initial_gdt /* setup the pointer to the GDT */ + pushw $(INITIAL_GDT_SIZE-1) + lgdt (%esp) + addl $6, %esp + ljmp $SEGMENT_SELECTOR_KERNEL_CS, $gdt_reload /* long jump to set %cs */ +gdt_reload: + + /* reset remaining segment registers */ + movl $SEGMENT_SELECTOR_KERNEL_DS, %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %fs + movl %eax, %gs + movl %eax, %ss + + call main /* call kernel proper */ + + cli hang: - hlt /* halt machine should kernel return */ - jmp hang + hlt /* halt machine should kernel return */ + jmp hang -.globl atags_ptr +.section .init + +/* Note: if the offsets into the GDT change, they must be updated in arch/segmentation.h */ +.set INITIAL_GDT_SIZE, 8*6 +initial_gdt: + .long 0x00000000 /* 0x00 intentionally empty */ + .long 0x00000000 + .long 0x00000000 /* 0x08 TODO TSS */ + .long 0x00000000 + .long 0x0000ffff /* 0x10 kernel code segment (%cs) */ + .long 0x00cf9a00 + .long 0x0000ffff /* 0x18 kernel data segment (%ds) */ + .long 0x00cf9200 + .long 0x0000ffff /* 0x20 userspace code segment (%cs) */ + .long 0x00cffa00 + .long 0x0000ffff /* 0x28 userspace data segment (%ds) */ + .long 0x00cff200 + +.align 0x1000 +init_page_dir: + /* This page directory entry identity-maps the first 4MB of the 32-bit physical address space. + All bits are clear except the following: + bit 7: PS The kernel page is 4MB. + bit 1: RW The kernel page is read/write. + bit 0: P The kernel page is present. + This entry must be here -- otherwise the kernel will crash immediately after paging is + enabled because it can't fetch the next instruction! It's ok to unmap this page later. */ + .long 0x00000083 + .rept (KERNEL_START_PAGE - 1) + .long 0 /* Pages before kernel space. */ + .endr + /* This page directory entry defines a 4MB page containing the kernel. */ + .long 0x00000083 + .rept (1024 - KERNEL_START_PAGE - 1) + .long 0 /* Pages after the kernel image. */ + .endr + +/* reserve initial kernel stack space */ +.align 32 +.lcomm stack, STACK_SIZE /* reserve stack space on a doubleword boundary */ + +/* place to store multiboot header information */ +.global mbd +.comm mbd, 4 /* we will use this in i386_main */ +.global magic +.comm magic, 4 /* we will use this in i386_main */ + +/* TODO FIXME remove this - needs fix to dependencies first */ +.global atags_ptr atags_ptr: - .word 0 + .long 0