1.1 --- a/stage2/cpu.c Fri Feb 26 01:13:51 2016 +0100
1.2 +++ b/stage2/cpu.c Fri Feb 26 20:01:03 2016 +0100
1.3 @@ -173,12 +173,22 @@
1.4
1.5 void init_tlb(void)
1.6 {
1.7 + /* Wire in the kseg0 mapping and the page tables. */
1.8 +
1.9 asm volatile(
1.10 - "li $t1, 1\n" /* index of first randomly-replaced entry */
1.11 + "li $t1, 2\n" /* index of first randomly-replaced entry */
1.12 "mtc0 $t1, $6\n" /* CP0_WIRED */
1.13 + "mtc0 $zero, $4\n" /* CP0_CONTEXT */
1.14 + "mtc0 $zero, $10\n" /* CP0_ENTRYHI */
1.15 "nop\n");
1.16
1.17 + /* Map the code, making it globally available. */
1.18 +
1.19 map_page_index(0x80000000, 0x00000000, 16 * 1024 * 1024, 0x1f, 0, 0);
1.20 +
1.21 + /* Map the page tables. */
1.22 +
1.23 + map_page_index(page_table_start, page_table_start, 64 * 1024, 0x1f, 0, 1);
1.24 }
1.25
1.26 void map_page_index(u32 virtual, u32 physical, u32 pagesize, u8 flags, u8 asid, u32 index)
1.27 @@ -212,6 +222,58 @@
1.28 );
1.29 }
1.30
1.31 +void init_page_table(u32 page_table, u32 virtual, u32 physical, u32 pagesize, u8 flags, u8 asid)
1.32 +{
1.33 + u32 lower = ((physical & 0xfffff000) >> 6) | flags;
1.34 + u32 upper = (((physical + pagesize) & 0xfffff000) >> 6) | flags;
1.35 +
1.36 + /*
1.37 + With a complete address space mapping involving pairs of 4KB pages
1.38 + described by two values for each entry, there would be...
1.39 +
1.40 + an address space of 0x100000000 requiring...
1.41 +
1.42 + 0x100000000 / (8 * 1024) == 0x100000000 >> 13
1.43 + == 524288 entries
1.44 + == 0x80000 entries
1.45 +
1.46 + Thus, each task's entries would require...
1.47 +
1.48 + 0x80000 * 8 == 0x400000 bytes
1.49 +
1.50 + The kseg2 region thus permits 256 tasks occupying 0x40000000 bytes.
1.51 +
1.52 + However, for more modest address spaces occupying as much as 32MB there
1.53 + would be...
1.54 +
1.55 + an address space of 0x02000000 requiring...
1.56 +
1.57 + 0x02000000 / (8 * 1024) == 0x02000000 >> 13
1.58 + == 4096 entries
1.59 + == 0x1000 entries
1.60 +
1.61 + Thus, each task's entries would only require...
1.62 +
1.63 + 0x1000 * 8 == 0x8000 bytes
1.64 + */
1.65 +
1.66 + u32 base = page_table + page_table_task_size * asid;
1.67 +
1.68 + /* Each page table entry corresponds to a pair of 4KB pages and holds two values. */
1.69 +
1.70 + u32 entry = ((virtual & 0xffffe000) >> 13) * 8;
1.71 + u32 address = base + entry;
1.72 +
1.73 + /* The page tables should be permanently mapped to avoid hierarchical TLB miss handling. */
1.74 +
1.75 + asm volatile(
1.76 + "sw %1, 0(%0)\n"
1.77 + "sw %2, 4(%0)\n"
1.78 + :
1.79 + : "r" (address), "r" (lower), "r" (upper)
1.80 + );
1.81 +}
1.82 +
1.83 void map_page(u32 virtual, u32 physical, u32 pagesize, u8 flags, u8 asid)
1.84 {
1.85 u32 start = (virtual & 0xffffe000) | asid; /* VPN2 | ASID*/
2.1 --- a/stage2/cpu.h Fri Feb 26 01:13:51 2016 +0100
2.2 +++ b/stage2/cpu.h Fri Feb 26 20:01:03 2016 +0100
2.3 @@ -12,8 +12,13 @@
2.4 void init_interrupts(void);
2.5 void init_tlb(void);
2.6 void map_page(u32, u32, u32, u8, u8);
2.7 +void init_page_table(u32, u32, u32, u32, u8, u8);
2.8 void map_page_miss(u32, u32, u8);
2.9 void map_page_index(u32, u32, u32, u8, u8, u32);
2.10 void unmap_page(u32, u32, u32, u8, u8);
2.11
2.12 +#define page_table_start 0x00040000
2.13 +#define page_table_task_size 0x00008000
2.14 +#define page_table_task_size_log2 15
2.15 +
2.16 #endif /* __CPU_H__ */
3.1 --- a/stage2/entry.S Fri Feb 26 01:13:51 2016 +0100
3.2 +++ b/stage2/entry.S Fri Feb 26 20:01:03 2016 +0100
3.3 @@ -18,17 +18,86 @@
3.4 */
3.5
3.6 .text
3.7 -.extern tlb_handler
3.8 .extern interrupt_handler
3.9 .globl _tlb_entry
3.10 .globl _irq_entry
3.11 .globl _end_entries
3.12 .set noreorder
3.13
3.14 +/* NOTE: Duplicated from cpu.h. */
3.15 +
3.16 +#define page_table_start 0x00040000
3.17 +#define page_table_task_size 0x00008000
3.18 +#define page_table_task_size_log2 15
3.19 +
3.20 _tlb_entry:
3.21 + /* Get the bad address. */
3.22 +
3.23 + mfc0 $k0, $10 /* CP0_ENTRYHI */
3.24 + nop
3.25 + andi $k1, $k0, 0xff /* ASID */
3.26 +
3.27 + /* For ASID == 0... */
3.28 +
3.29 + beqz $k1, _tlb_entry_direct
3.30 + nop
3.31 +
3.32 + /* For addresses over 0x00080000... */
3.33 +
3.34 + li $k1, 0xfff80000
3.35 + and $k1, $k0, $k1
3.36 + bnez $k1, _tlb_entry_direct
3.37 + nop
3.38 +
3.39 + /* Otherwise, load the page table entries. */
3.40 +
3.41 + andi $k1, $k0, 0xff /* ASID */
3.42 + sll $k1, $k1, page_table_task_size_log2 /* [ASID] */
3.43 + li $k0, page_table_start /* page_table */
3.44 + add $k0, $k0, $k1 /* page_table[ASID] */
3.45 +
3.46 + mfc0 $k1, $4 /* CP0_CONTEXT */
3.47 + nop
3.48 + srl $k1, $k1, 1 /* use 8 byte - not 16 byte - entries */
3.49 + add $k0, $k0, $k1 /* page_table[ASID][entry] */
3.50 +
3.51 + lw $k1, 0($k0) /* page_table[ASID][entry][0] */
3.52 + mtc0 $k1, $2 /* CP0_ENTRYLO0 */
3.53 + lw $k1, 4($k0) /* page_table[ASID][entry][1] */
3.54 + mtc0 $k1, $3 /* CP0_ENTRYLO1 */
3.55 + /* page size is 4KB */
3.56 + mtc0 $zero, $5 /* CP0_PAGEMASK */
3.57 + nop
3.58 +
3.59 + tlbwr
3.60 + nop
3.61 + eret
3.62 + nop
3.63 +
3.64 +_tlb_entry_direct:
3.65 + /* Otherwise, just translate the address directly. */
3.66 +
3.67 + li $k1, 0xffffe000
3.68 + and $k0, $k0, $k1 /* VPN2 (8KB resolution) */
3.69 + srl $k0, $k0, 6 /* PFN (maintain 8KB resolution, bit 6 remaining zero) */
3.70 + ori $k0, $k0, 0x1e /* flags */
3.71 +
3.72 + mtc0 $k0, $2 /* CP0_ENTRYLO0 */
3.73 + ori $k0, $k0, 0x40 /* page size is 4KB (bit 6 set) */
3.74 + mtc0 $k0, $3 /* CP0_ENTRYLO1 */
3.75 + nop /* page size is 4KB */
3.76 + mtc0 $zero, $5 /* CP0_PAGEMASK */
3.77 + nop
3.78 +
3.79 + tlbwr
3.80 + nop
3.81 + eret
3.82 + nop
3.83 +
3.84 +_irq_entry:
3.85 /* Save the status. */
3.86
3.87 - mfc0 $k0, $12 /* CP0_STATUS */
3.88 + mfc0 $k0, $12 /* CP0_STATUS */
3.89 nop
3.90 sw $k0, -120($sp)
3.91
3.92 @@ -36,32 +105,7 @@
3.93
3.94 li $k1, 0xffff03ff
3.95 and $k1, $k0, $k1
3.96 - mtc0 $k1, $12
3.97 -
3.98 - /* Save registers that the assembler wants to trash. */
3.99 -
3.100 - sw $t9, -100($sp)
3.101 - sw $gp, -104($sp)
3.102 - sw $ra, -112($sp)
3.103 -
3.104 - lui $gp, %hi(_GLOBAL_OFFSET_TABLE_)
3.105 - ori $gp, $gp, %lo(_GLOBAL_OFFSET_TABLE_)
3.106 - la $k0, tlb_handler
3.107 - jr $k0
3.108 - nop
3.109 -
3.110 -_irq_entry:
3.111 - /* Save the status. */
3.112 -
3.113 - mfc0 $k0, $12 /* CP0_STATUS */
3.114 - nop
3.115 - sw $k0, -120($sp)
3.116 -
3.117 - /* Mask interrupts. */
3.118 -
3.119 - li $k1, 0xffff03ff
3.120 - and $k1, $k0, $k1
3.121 - mtc0 $k1, $12
3.122 + mtc0 $k1, $12 /* CP0_STATUS */
3.123
3.124 /* Save registers that the assembler wants to trash. */
3.125
4.1 --- a/stage2/handlers.S Fri Feb 26 01:13:51 2016 +0100
4.2 +++ b/stage2/handlers.S Fri Feb 26 20:01:03 2016 +0100
4.3 @@ -19,29 +19,13 @@
4.4 */
4.5
4.6 .text
4.7 -.extern tlb_handle
4.8 .extern irq_handle
4.9 .extern current_stack_pointer
4.10 .extern current_task
4.11 -.globl tlb_handler
4.12 .globl interrupt_handler
4.13 .set noreorder
4.14 .set noat
4.15
4.16 -tlb_handler:
4.17 - /* gp should have been set in the entrypoint. */
4.18 -
4.19 - jal save_state
4.20 - nop
4.21 -
4.22 - /* Invoke the handler. */
4.23 -
4.24 - jal tlb_handle
4.25 - nop
4.26 -
4.27 - j load_and_return
4.28 - nop
4.29 -
4.30 interrupt_handler:
4.31 /* gp should have been set in the entrypoint. */
4.32
5.1 --- a/stage2/irq.c Fri Feb 26 01:13:51 2016 +0100
5.2 +++ b/stage2/irq.c Fri Feb 26 20:01:03 2016 +0100
5.3 @@ -74,9 +74,9 @@
5.4
5.5 void irq_init()
5.6 {
5.7 + handle_error_level();
5.8 timer_init_irq();
5.9 init_interrupts();
5.10 - enable_interrupts();
5.11 }
5.12
5.13 void irq_handle()
5.14 @@ -106,51 +106,24 @@
5.15 }
5.16 }
5.17
5.18 -void tlb_handle()
5.19 -{
5.20 - u32 asid, virtual, physical, bottom, top;
5.21 -
5.22 - /* Obtain the bad virtual address. */
5.23 -
5.24 - asm volatile(
5.25 - "mfc0 %0, $10\n" /* CP0_ENTRYHI */
5.26 - "nop\n"
5.27 - : "=r" (virtual)
5.28 - );
5.29 -
5.30 - /* Obtain a virtual address region with 8KB resolution. */
5.31 -
5.32 - asid = virtual & 0xff;
5.33 - virtual = virtual & 0xffffe000;
5.34 -
5.35 - /* The appropriate physical address depends on the current task. */
5.36 -
5.37 - bottom = (stack_start - stack_size) & 0xffffe000;
5.38 - top = stack_start & 0xffffe000;
5.39 -
5.40 - if ((asid != 0) && (virtual >= bottom) && (virtual < top))
5.41 - physical = virtual + asid * stack_size;
5.42 - else
5.43 - physical = virtual;
5.44 -
5.45 - /*
5.46 - Request a physical region mapping two 4KB pages.
5.47 - Pages employ C=3, dirty, valid, with the task number as the ASID.
5.48 - */
5.49 -
5.50 - map_page_miss(physical, pagesize, 0x1e);
5.51 -}
5.52 -
5.53 void start_task(unsigned short task)
5.54 {
5.55 u32 args[] = {1, 0, (task - 1) * 120};
5.56 + u32 virtual, physical;
5.57
5.58 /*
5.59 Each task employs a stack at a multiple of the given start address in
5.60 physical memory, but at the same address in virtual memory.
5.61 */
5.62
5.63 - map_page(stack_start + stack_size * task - stack_size, stack_start + stack_size * task - stack_size, pagesize, 0x1e, 0);
5.64 + virtual = stack_start - pagesize * 2;
5.65 + physical = stack_start + stack_size * task - pagesize * 2;
5.66 +
5.67 + init_page_table(page_table_start, virtual, physical, pagesize, 0x1e, task);
5.68 +
5.69 + /* Map the page for initialisation. */
5.70 +
5.71 + map_page(physical, physical, pagesize, 0x1e, 0);
5.72
5.73 /*
5.74 Set the stack for the new task, initialising the global pointer and
5.75 @@ -190,7 +163,6 @@
5.76 {
5.77 current_task = task;
5.78 current_stack_pointer = stack_pointers[current_task];
5.79 - map_page(stack_start - stack_size, stack_start + stack_size * current_task - stack_size, pagesize, 0x1e, current_task);
5.80 set_task(current_task);
5.81
5.82 asm volatile(
6.1 --- a/stage2/stage2.c Fri Feb 26 01:13:51 2016 +0100
6.2 +++ b/stage2/stage2.c Fri Feb 26 20:01:03 2016 +0100
6.3 @@ -44,18 +44,16 @@
6.4 }
6.5
6.6 lcd_init();
6.7 - handle_error_level();
6.8 - set_task(0);
6.9 + irq_init();
6.10
6.11 /* Start the tasks. */
6.12
6.13 start_task(1);
6.14 start_task(2);
6.15 + invoke_task(1);
6.16
6.17 /* Now, wait for the tasks to be selected as interrupts occur. */
6.18
6.19 - irq_init();
6.20 - invoke_task(1);
6.21 -
6.22 + enable_interrupts();
6.23 while (1) asm volatile("wait");
6.24 }