diff --git a/include/atags.h b/include/atags.h new file mode 100644 index 0000000..53b04ac --- /dev/null +++ b/include/atags.h @@ -0,0 +1,48 @@ +#ifndef ATAGS_H +#define ATAGS_H + +#include + +#define ATAG_NONE 0x00000000 +#define ATAG_CORE 0x54410001 +#define ATAG_MEM 0x54410002 +#define ATAG_VIDEOTEXT 0x54410003 +#define ATAG_RAMDISK 0x54410004 +#define ATAG_INITRD2 0x54420005 +#define ATAG_SERIAL 0x54410006 +#define ATAG_REVISION 0x54410007 +#define ATAG_VIDEOLFB 0x54410008 +#define ATAG_CMDLINE 0x54410009 + +struct atag_core { + uint32 flags; /* bit 0 = read-only */ + uint32 pagesize; /* systems page size (usually 4k) */ + uint32 rootdev; /* root device number */ +}; + +struct atag_mem { + uint32 size; /* size of the area */ + uint32 start; /* physical start address */ +}; + +struct atag { + uint32 size; /* Size of tag in words (includes entire struct) */ + uint32 tag; + union { + struct atag_core core; + struct atag_mem mem; +// struct atag_videotext videotext; +// struct atag_ramdisk ramdisk; +// struct atag_initrd2 initrd2; +// struct atag_serialnr serialnr; +// struct atag_revision revision; +// struct atag_videolfb videolfb; +// struct atag_cmdline cmdline; + } data; +}; + +#define atags_first_mem_region(atag) _atags_mem_region(atag, 1) +#define atags_next_mem_region(atag) _atags_mem_region(atag, 0) +int _atags_mem_region(struct atag **mem_header, int initialize); + +#endif /* ATAGS_H */ diff --git a/include/mmu.h b/include/mmu.h index 402b396..16a87df 100644 --- a/include/mmu.h +++ b/include/mmu.h @@ -1,8 +1,9 @@ #ifndef MMU_H #define MMU_H -extern unsigned int *kernel_start_phys, *kernel_start_virt, *kernel_end_phys, *kernel_end_virt; +#include void mmu_reinit(); +void declare_memory_region(void *lower, void *upper); #endif /* MMU_H */ diff --git a/include/types.h b/include/types.h new file mode 100644 index 0000000..4bf5fb1 --- /dev/null +++ b/include/types.h @@ -0,0 +1 @@ +typedef unsigned int uint32; diff --git a/kernel/Makefile.inc b/kernel/Makefile.inc index c4401b3..8870cbd 100644 --- a/kernel/Makefile.inc +++ b/kernel/Makefile.inc @@ -1,5 +1,6 @@ KERNEL_PREFIX = kernel +KOBJS += $(KERNEL_PREFIX)/atags.o KOBJS += $(KERNEL_PREFIX)/console.o KOBJS += $(KERNEL_PREFIX)/font.o KOBJS += $(KERNEL_PREFIX)/framebuffer.o diff --git a/kernel/atags.c b/kernel/atags.c new file mode 100644 index 0000000..7249c5f --- /dev/null +++ b/kernel/atags.c @@ -0,0 +1,42 @@ +#include + +extern uint32 atags_ptr; + +int atag_valid(struct atag *a) { + switch (a->tag) { + case ATAG_NONE: + return a->size == 2; + case ATAG_CORE: + return a->size == 2 || a->size == 5; + case ATAG_MEM: + case ATAG_INITRD2: + case ATAG_SERIAL: + return a->size == 4; + case ATAG_VIDEOTEXT: + case ATAG_RAMDISK: + return a->size == 5; + case ATAG_REVISION: + return a->size == 3; + case ATAG_VIDEOLFB: + return a->size == 8; + case ATAG_CMDLINE: + return a->size >= 3; + default: + return 0; + } +} + +int _atags_mem_region(struct atag **mem_header, int initialize) { + if (initialize) + *mem_header = (struct atag *)atags_ptr; + else + *mem_header = (struct atag *)((uint32 *)*mem_header + (*mem_header)->size); + + while (atag_valid(*mem_header) && (*mem_header)->tag != ATAG_NONE && (*mem_header)->tag != ATAG_MEM) { + *mem_header = (struct atag *)((uint32 *)*mem_header + (*mem_header)->size); + } + + if (!atag_valid(*mem_header) || (*mem_header)->tag == ATAG_NONE) + return -1; + return 0; +} diff --git a/kernel/mm.c b/kernel/mm.c index 232a6ae..b0d42e6 100644 --- a/kernel/mm.c +++ b/kernel/mm.c @@ -32,12 +32,13 @@ void mm_add_free_region(void *start, void *end) { void *page; //make sure both start and end address are aligned to the size of a page - if ((unsigned int)start & MM_PAGE_SIZE || (unsigned int)(end+1) & MM_PAGE_SIZE) { - print("Error: Supplied memory area(%x,%x) is not aligned to the page size (%d)\n", (unsigned int)start, (unsigned int)end, MM_PAGE_SIZE); - return; - } - if ((char *)end - (char *)start < MM_PAGE_SIZE<<1) { - print("Error: Supplied memory area(%x,%x) is not aligned to the page size (%d)\n", (unsigned int)start, (unsigned int)end, MM_PAGE_SIZE); + if ((unsigned int)start % MM_PAGE_SIZE != 0) + start = (char*)start + (MM_PAGE_SIZE - ((unsigned int)start % MM_PAGE_SIZE)); + if (((unsigned int)end + 1) % MM_PAGE_SIZE != 0) + end = (char*)end - ((unsigned int)end + 1) % MM_PAGE_SIZE; + + if ((char *)end + 1 - (char *)start < MM_PAGE_SIZE<<1) { + print("Error: Supplied memory area(%x,%x) is smaller than the page size (%d)\n", (unsigned int)start, (unsigned int)end, MM_PAGE_SIZE); return; } @@ -46,7 +47,7 @@ void mm_add_free_region(void *start, void *end) { //TODO we're potentially losing memory here because we're calculating //the number of page structs we need even for those pages that will contain only page structs - num_pages = ((char *)end - (char *)start) / MM_PAGE_SIZE; + num_pages = ((char *)end + 1 - (char *)start) / MM_PAGE_SIZE; usable_pages = num_pages - num_pages * sizeof(struct page) / MM_PAGE_SIZE; if (num_pages * sizeof(struct page) % MM_PAGE_SIZE) usable_pages--; diff --git a/kernel/mmu.c b/kernel/mmu.c index bde3c4b..437c334 100644 --- a/kernel/mmu.c +++ b/kernel/mmu.c @@ -1,4 +1,6 @@ #include +#include +#include #define SCTLR 15,0,1,0,0 #define TTBR0 15,0,2,0,0 @@ -11,23 +13,29 @@ #define cp_write(var, ...) _cp_write(var, __VA_ARGS__) #define TT_BASE_SIZE (1<<14) /* 16k */ +#define TT_SECTION_SIZE (1<<20) /* 1mb */ -unsigned int *kernel_start_phys, *kernel_start_virt, *kernel_end_phys, *kernel_end_virt; +uint32 *kernel_start_phys, *kernel_start_virt, *kernel_end_phys, *kernel_end_virt; + +void print_mapping(void *addr) { + extern uint32 tt_base_virtual; + print("%x: %x\n", addr, *(uint32 *)(tt_base_virtual + (((uint32)addr)>>18))); +} void mmu_reinit() { - extern unsigned int tt_base_virtual, tt_base_physical, start; - unsigned int curr_addr; - unsigned int *curr_tt_entry; + extern uint32 tt_base_virtual, tt_base_physical, start; + uint32 curr_addr; + uint32 *curr_tt_entry; int virt_phys_offset; virt_phys_offset = tt_base_virtual - tt_base_physical; kernel_start_virt = &start; kernel_start_phys = kernel_start_virt - virt_phys_offset/4; - kernel_end_virt = (unsigned int *)(tt_base_virtual + TT_BASE_SIZE); - kernel_end_phys = (unsigned int *)(tt_base_physical + TT_BASE_SIZE); + kernel_end_virt = (uint32 *)(tt_base_virtual + TT_BASE_SIZE); + kernel_end_phys = (uint32 *)(tt_base_physical + TT_BASE_SIZE); //get the current translation table base address - curr_tt_entry = (unsigned int *)tt_base_virtual; + curr_tt_entry = (uint32 *)tt_base_virtual; //do first loop iteration outside the loop, because we have to check against wrapping back around to know we're done *curr_tt_entry = 0xc02; /* 0xc02 means read/write at any priviledge level, and that it's a section w/o PXN bit set */ @@ -38,9 +46,9 @@ void mmu_reinit() { //memory, make sure we keep those mappings correct, and we'll actually //swap the twp mappings so all of memory is addressable. for (curr_addr = 0x00100000; curr_addr != 0; curr_addr += 0x00100000) { - if ((unsigned int *)curr_addr >= kernel_start_phys && (unsigned int *)curr_addr < kernel_end_phys) { + if ((uint32 *)curr_addr >= kernel_start_phys && (uint32 *)curr_addr < kernel_end_phys) { *curr_tt_entry = (curr_addr + virt_phys_offset) | 0xc02; - } else if ((unsigned int *)curr_addr >= kernel_start_virt && (unsigned int *)curr_addr < kernel_end_virt) { + } else if ((uint32 *)curr_addr >= kernel_start_virt && (uint32 *)curr_addr < kernel_end_virt) { *curr_tt_entry = (curr_addr - virt_phys_offset) | 0xc02; } else { *curr_tt_entry = curr_addr | 0xc02; @@ -48,3 +56,41 @@ void mmu_reinit() { curr_tt_entry++; } } + +int mmu_region_contains(void *lower_a, void *upper_a, void *lower_b, void *upper_b) { + return lower_b >= lower_a && upper_b <= upper_a; +} + +#define section_round_down(ptr) (((uint32)ptr) & ~(TT_SECTION_SIZE-1)) +#define section_round_up(ptr) (((((uint32)ptr) & ~1) + (TT_SECTION_SIZE-1) ) & ~(TT_SECTION_SIZE-1)) + +/* Called one per physical memory region by bootup code. This function is + * responsible for only adding (via mm_add_free_region()) those parts of the + * memory region which are still available (i.e. aren't in the kernel and + * haven't been remapped anywhere else. */ +void declare_memory_region(void *lower, void *upper) { + void *k_section_start_phys = (void *)section_round_down(kernel_start_phys); + void *k_section_end_phys = (void *)(section_round_up(kernel_end_phys) - 1); + void *k_section_start_virt = (void *)section_round_down(kernel_start_virt); + void *k_section_end_virt = (void *)(section_round_up(kernel_end_virt) - 1); + + if (upper - lower < 1) { + print("Warning: declare_memory_region() called with lower=%x, upper=%x. Ignoring.\n", lower, upper); + return; + } + + //TODO It's possible (though highly unlikely) that the kernel (virtual) + //is split across two different memory regions. We should probably + //handle this. + if (mmu_region_contains(lower, upper, k_section_start_phys, k_section_end_phys)) { + //Don't map any of the physical kernel's memory + declare_memory_region(lower, (void *) ((char *)k_section_start_phys - 1)); + declare_memory_region((void *) ((char *)k_section_end_phys + 1), upper); + mm_add_free_region(kernel_end_virt, k_section_end_virt); + } else if (mmu_region_contains(lower, upper, k_section_start_virt, k_section_end_virt)) { + declare_memory_region(lower, (void *) ((char *)k_section_start_virt - 1)); + declare_memory_region((void *) ((char *)k_section_end_virt + 1), upper); + } else { + mm_add_free_region(lower, upper); + } +} diff --git a/kernel/start_kernel.c b/kernel/start_kernel.c index c978642..49e8a63 100644 --- a/kernel/start_kernel.c +++ b/kernel/start_kernel.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -52,7 +53,8 @@ void test_memory() { } int main(void) { - char *lower; + char *lower, *upper; + struct atag *atags; //setup MMU mmu_reinit(); @@ -61,12 +63,17 @@ int main(void) { //setup memory mm_init(); - mm_add_free_region((void*)0x60000000, (void*)0x7FFFFFFF); - mm_add_free_region((void*)0x80000000, (void*)0x800FFFFF); - lower = (char*) &kernel_end_virt; - if ((unsigned int)lower % MM_PAGE_SIZE != 0) - lower += (MM_PAGE_SIZE - ((unsigned int)lower % MM_PAGE_SIZE)); - mm_add_free_region((void*)lower, (void*)0x9FFFFFFF); //subtract the memory used by the kernel + + if (atags_first_mem_region(&atags)) { + print("Error: atags must contain at least one memory region\n"); + return -1; + } + + do { + lower = (char *)atags->data.mem.start; + upper = lower + atags->data.mem.size - 1; + declare_memory_region(lower, upper); + } while (!atags_next_mem_region(&atags)); test_memory();