# HG changeset patch # User Paul Boddie # Date 1456513263 -3600 # Node ID d429015dc262836fd0c5500749bb6a5f2c97c883 # Parent 00392b6b6878bf4de5b64581ac0ef2ee47f66543 Introduced more immediate TLB miss handling in order to avoid stack usage and the potential for a TLB miss upon trying to save registers to the stack. Moved handle_error_level back into irq_init, separating out enable_interrupts instead. diff -r 00392b6b6878 -r d429015dc262 stage2/cpu.c --- a/stage2/cpu.c Fri Feb 26 01:13:51 2016 +0100 +++ b/stage2/cpu.c Fri Feb 26 20:01:03 2016 +0100 @@ -173,12 +173,22 @@ void init_tlb(void) { + /* Wire in the kseg0 mapping and the page tables. */ + asm volatile( - "li $t1, 1\n" /* index of first randomly-replaced entry */ + "li $t1, 2\n" /* index of first randomly-replaced entry */ "mtc0 $t1, $6\n" /* CP0_WIRED */ + "mtc0 $zero, $4\n" /* CP0_CONTEXT */ + "mtc0 $zero, $10\n" /* CP0_ENTRYHI */ "nop\n"); + /* Map the code, making it globally available. */ + map_page_index(0x80000000, 0x00000000, 16 * 1024 * 1024, 0x1f, 0, 0); + + /* Map the page tables. */ + + map_page_index(page_table_start, page_table_start, 64 * 1024, 0x1f, 0, 1); } void map_page_index(u32 virtual, u32 physical, u32 pagesize, u8 flags, u8 asid, u32 index) @@ -212,6 +222,58 @@ ); } +void init_page_table(u32 page_table, u32 virtual, u32 physical, u32 pagesize, u8 flags, u8 asid) +{ + u32 lower = ((physical & 0xfffff000) >> 6) | flags; + u32 upper = (((physical + pagesize) & 0xfffff000) >> 6) | flags; + + /* + With a complete address space mapping involving pairs of 4KB pages + described by two values for each entry, there would be... + + an address space of 0x100000000 requiring... + + 0x100000000 / (8 * 1024) == 0x100000000 >> 13 + == 524288 entries + == 0x80000 entries + + Thus, each task's entries would require... + + 0x80000 * 8 == 0x400000 bytes + + The kseg2 region thus permits 256 tasks occupying 0x40000000 bytes. + + However, for more modest address spaces occupying as much as 32MB there + would be... + + an address space of 0x02000000 requiring... + + 0x02000000 / (8 * 1024) == 0x02000000 >> 13 + == 4096 entries + == 0x1000 entries + + Thus, each task's entries would only require... + + 0x1000 * 8 == 0x8000 bytes + */ + + u32 base = page_table + page_table_task_size * asid; + + /* Each page table entry corresponds to a pair of 4KB pages and holds two values. */ + + u32 entry = ((virtual & 0xffffe000) >> 13) * 8; + u32 address = base + entry; + + /* The page tables should be permanently mapped to avoid hierarchical TLB miss handling. */ + + asm volatile( + "sw %1, 0(%0)\n" + "sw %2, 4(%0)\n" + : + : "r" (address), "r" (lower), "r" (upper) + ); +} + void map_page(u32 virtual, u32 physical, u32 pagesize, u8 flags, u8 asid) { u32 start = (virtual & 0xffffe000) | asid; /* VPN2 | ASID*/ diff -r 00392b6b6878 -r d429015dc262 stage2/cpu.h --- a/stage2/cpu.h Fri Feb 26 01:13:51 2016 +0100 +++ b/stage2/cpu.h Fri Feb 26 20:01:03 2016 +0100 @@ -12,8 +12,13 @@ void init_interrupts(void); void init_tlb(void); void map_page(u32, u32, u32, u8, u8); +void init_page_table(u32, u32, u32, u32, u8, u8); void map_page_miss(u32, u32, u8); void map_page_index(u32, u32, u32, u8, u8, u32); void unmap_page(u32, u32, u32, u8, u8); +#define page_table_start 0x00040000 +#define page_table_task_size 0x00008000 +#define page_table_task_size_log2 15 + #endif /* __CPU_H__ */ diff -r 00392b6b6878 -r d429015dc262 stage2/entry.S --- a/stage2/entry.S Fri Feb 26 01:13:51 2016 +0100 +++ b/stage2/entry.S Fri Feb 26 20:01:03 2016 +0100 @@ -18,17 +18,86 @@ */ .text -.extern tlb_handler .extern interrupt_handler .globl _tlb_entry .globl _irq_entry .globl _end_entries .set noreorder +/* NOTE: Duplicated from cpu.h. */ + +#define page_table_start 0x00040000 +#define page_table_task_size 0x00008000 +#define page_table_task_size_log2 15 + _tlb_entry: + /* Get the bad address. */ + + mfc0 $k0, $10 /* CP0_ENTRYHI */ + nop + andi $k1, $k0, 0xff /* ASID */ + + /* For ASID == 0... */ + + beqz $k1, _tlb_entry_direct + nop + + /* For addresses over 0x00080000... */ + + li $k1, 0xfff80000 + and $k1, $k0, $k1 + bnez $k1, _tlb_entry_direct + nop + + /* Otherwise, load the page table entries. */ + + andi $k1, $k0, 0xff /* ASID */ + sll $k1, $k1, page_table_task_size_log2 /* [ASID] */ + li $k0, page_table_start /* page_table */ + add $k0, $k0, $k1 /* page_table[ASID] */ + + mfc0 $k1, $4 /* CP0_CONTEXT */ + nop + srl $k1, $k1, 1 /* use 8 byte - not 16 byte - entries */ + add $k0, $k0, $k1 /* page_table[ASID][entry] */ + + lw $k1, 0($k0) /* page_table[ASID][entry][0] */ + mtc0 $k1, $2 /* CP0_ENTRYLO0 */ + lw $k1, 4($k0) /* page_table[ASID][entry][1] */ + mtc0 $k1, $3 /* CP0_ENTRYLO1 */ + /* page size is 4KB */ + mtc0 $zero, $5 /* CP0_PAGEMASK */ + nop + + tlbwr + nop + eret + nop + +_tlb_entry_direct: + /* Otherwise, just translate the address directly. */ + + li $k1, 0xffffe000 + and $k0, $k0, $k1 /* VPN2 (8KB resolution) */ + srl $k0, $k0, 6 /* PFN (maintain 8KB resolution, bit 6 remaining zero) */ + ori $k0, $k0, 0x1e /* flags */ + + mtc0 $k0, $2 /* CP0_ENTRYLO0 */ + ori $k0, $k0, 0x40 /* page size is 4KB (bit 6 set) */ + mtc0 $k0, $3 /* CP0_ENTRYLO1 */ + nop /* page size is 4KB */ + mtc0 $zero, $5 /* CP0_PAGEMASK */ + nop + + tlbwr + nop + eret + nop + +_irq_entry: /* Save the status. */ - mfc0 $k0, $12 /* CP0_STATUS */ + mfc0 $k0, $12 /* CP0_STATUS */ nop sw $k0, -120($sp) @@ -36,32 +105,7 @@ li $k1, 0xffff03ff and $k1, $k0, $k1 - mtc0 $k1, $12 - - /* Save registers that the assembler wants to trash. */ - - sw $t9, -100($sp) - sw $gp, -104($sp) - sw $ra, -112($sp) - - lui $gp, %hi(_GLOBAL_OFFSET_TABLE_) - ori $gp, $gp, %lo(_GLOBAL_OFFSET_TABLE_) - la $k0, tlb_handler - jr $k0 - nop - -_irq_entry: - /* Save the status. */ - - mfc0 $k0, $12 /* CP0_STATUS */ - nop - sw $k0, -120($sp) - - /* Mask interrupts. */ - - li $k1, 0xffff03ff - and $k1, $k0, $k1 - mtc0 $k1, $12 + mtc0 $k1, $12 /* CP0_STATUS */ /* Save registers that the assembler wants to trash. */ diff -r 00392b6b6878 -r d429015dc262 stage2/handlers.S --- a/stage2/handlers.S Fri Feb 26 01:13:51 2016 +0100 +++ b/stage2/handlers.S Fri Feb 26 20:01:03 2016 +0100 @@ -19,29 +19,13 @@ */ .text -.extern tlb_handle .extern irq_handle .extern current_stack_pointer .extern current_task -.globl tlb_handler .globl interrupt_handler .set noreorder .set noat -tlb_handler: - /* gp should have been set in the entrypoint. */ - - jal save_state - nop - - /* Invoke the handler. */ - - jal tlb_handle - nop - - j load_and_return - nop - interrupt_handler: /* gp should have been set in the entrypoint. */ diff -r 00392b6b6878 -r d429015dc262 stage2/irq.c --- a/stage2/irq.c Fri Feb 26 01:13:51 2016 +0100 +++ b/stage2/irq.c Fri Feb 26 20:01:03 2016 +0100 @@ -74,9 +74,9 @@ void irq_init() { + handle_error_level(); timer_init_irq(); init_interrupts(); - enable_interrupts(); } void irq_handle() @@ -106,51 +106,24 @@ } } -void tlb_handle() -{ - u32 asid, virtual, physical, bottom, top; - - /* Obtain the bad virtual address. */ - - asm volatile( - "mfc0 %0, $10\n" /* CP0_ENTRYHI */ - "nop\n" - : "=r" (virtual) - ); - - /* Obtain a virtual address region with 8KB resolution. */ - - asid = virtual & 0xff; - virtual = virtual & 0xffffe000; - - /* The appropriate physical address depends on the current task. */ - - bottom = (stack_start - stack_size) & 0xffffe000; - top = stack_start & 0xffffe000; - - if ((asid != 0) && (virtual >= bottom) && (virtual < top)) - physical = virtual + asid * stack_size; - else - physical = virtual; - - /* - Request a physical region mapping two 4KB pages. - Pages employ C=3, dirty, valid, with the task number as the ASID. - */ - - map_page_miss(physical, pagesize, 0x1e); -} - void start_task(unsigned short task) { u32 args[] = {1, 0, (task - 1) * 120}; + u32 virtual, physical; /* Each task employs a stack at a multiple of the given start address in physical memory, but at the same address in virtual memory. */ - map_page(stack_start + stack_size * task - stack_size, stack_start + stack_size * task - stack_size, pagesize, 0x1e, 0); + virtual = stack_start - pagesize * 2; + physical = stack_start + stack_size * task - pagesize * 2; + + init_page_table(page_table_start, virtual, physical, pagesize, 0x1e, task); + + /* Map the page for initialisation. */ + + map_page(physical, physical, pagesize, 0x1e, 0); /* Set the stack for the new task, initialising the global pointer and @@ -190,7 +163,6 @@ { current_task = task; current_stack_pointer = stack_pointers[current_task]; - map_page(stack_start - stack_size, stack_start + stack_size * current_task - stack_size, pagesize, 0x1e, current_task); set_task(current_task); asm volatile( diff -r 00392b6b6878 -r d429015dc262 stage2/stage2.c --- a/stage2/stage2.c Fri Feb 26 01:13:51 2016 +0100 +++ b/stage2/stage2.c Fri Feb 26 20:01:03 2016 +0100 @@ -44,18 +44,16 @@ } lcd_init(); - handle_error_level(); - set_task(0); + irq_init(); /* Start the tasks. */ start_task(1); start_task(2); + invoke_task(1); /* Now, wait for the tasks to be selected as interrupts occur. */ - irq_init(); - invoke_task(1); - + enable_interrupts(); while (1) asm volatile("wait"); }