// Iris: micro-kernel for a capability-based operating system. // mips/entry.S: Routines which are entered from user space. // Copyright 2009 Bas Wijnen // // 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 3 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, see . #define KERNEL_STACK_SIZE 0x1000 // The kernel stack. .bss .lcomm kernel_stack, KERNEL_STACK_SIZE .globl run_idle .globl directory .set noat .set noreorder .text #define ARCH #define ASM #define __KERNEL__ #include "arch.hh" .org 0x000 addr_000: #if 1 // TLB refill lui $k0, 0x8000 lw $k1, 0x174($k0) // directory mfc0 $k0, $CP0_ENTRY_HI // Entry high is address: 12 bit offset in page; 1 bit even/odd; 9 bit which page pair; 10 bit which table. srl $k0, $k0, 19 andi $k0, $k0, 0xffc // Bits 30:21 remain: which page table to get from directory. addu $k0, $k0, $k1 lw $k0, 0($k0) // k0 is the page table. beq $zero, $k0, zero_refill mfc0 $k1, $CP0_ENTRY_HI srl $k1, $k1, 10 // Bits 20:13 remain: which page pair to get from page table. // (Only the lower half is used for this; the upper half contains pointers to the kPages.) andi $k1, $k1, 0x7f8 addu $k0, $k0, $k1 lw $k1, 0($k0) mtc0 $k1, $CP0_ENTRY_LO0 lw $k1, 4($k0) mtc0 $k1, $CP0_ENTRY_LO1 1: tlbwr move $k0, $zero move $k1, $zero eret zero_refill: mtc0 $zero, $CP0_ENTRY_LO0 b 1b mtc0 $zero, $CP0_ENTRY_LO1 #else // Slow refill: use C code (which calls kdebug etc) move $k1, $ra bal save_regs nop la $t9, tlb_refill jr $t9 nop #endif .org 0x100 addr_100: // Cache error move $k1, $ra bal save_regs nop la $t9, cache_error jr $t9 nop .org 0x180 - 3 * 4 directory: // 0x174 .word 0 current: // 0x178 .word idle // 0x17c .word _gp addr_180: // General exception move $k1, $ra bal save_regs nop la $t9, exception jr $t9 nop .org 0x200 - 2 * 4 .word 0x00000012 // 1f8 EntryLo data for idle page. .word 0x80000000 // 1fc A pointer to the current page. addr_200: // Interrupt move $k1, $ra bal save_regs nop la $t9, interrupt jr $t9 nop .org 0x280 start_idle: // 280 // Wait for the next interrupt, then the first thread will be scheduled. // It is impractical to try to call schedule, because for that the // idle task would need to own capabilities. 1: wait b 1b nop // TODO: save only fragile registers now, the rest on task switch. kernel_exit: move $k0, $v0 lui $k1, 0x8000 sw $v0, 0x178($k1) // current lw $k1, SAVE_PC($k0) mtc0 $k1, $CP0_EPC lw $k1, SAVE_LO($k0) mtlo $k1 lw $k1, SAVE_HI($k0) mthi $k1 lw $v0, SAVE_V + 0 * 4($k0) lw $v1, SAVE_V + 1 * 4($k0) lw $a0, SAVE_A + 0 * 4($k0) lw $a1, SAVE_A + 1 * 4($k0) lw $a2, SAVE_A + 2 * 4($k0) lw $a3, SAVE_A + 3 * 4($k0) lw $t0, SAVE_T + 0 * 4($k0) lw $t1, SAVE_T + 1 * 4($k0) lw $t2, SAVE_T + 2 * 4($k0) lw $t3, SAVE_T + 3 * 4($k0) lw $t4, SAVE_T + 4 * 4($k0) lw $t5, SAVE_T + 5 * 4($k0) lw $t6, SAVE_T + 6 * 4($k0) lw $t7, SAVE_T + 7 * 4($k0) lw $t8, SAVE_T + 8 * 4($k0) lw $t9, SAVE_T + 9 * 4($k0) lw $s0, SAVE_S + 0 * 4($k0) lw $s1, SAVE_S + 1 * 4($k0) lw $s2, SAVE_S + 2 * 4($k0) lw $s3, SAVE_S + 3 * 4($k0) lw $s4, SAVE_S + 4 * 4($k0) lw $s5, SAVE_S + 5 * 4($k0) lw $s6, SAVE_S + 6 * 4($k0) lw $s7, SAVE_S + 7 * 4($k0) lw $fp, SAVE_FP($k0) lw $at, SAVE_AT($k0) lw $sp, SAVE_SP($k0) lw $gp, SAVE_GP($k0) lw $ra, SAVE_RA($k0) #ifndef NDEBUG // Exceptions were enabled in the kernel; set them to usermode setting again. mfc0 $k1, $CP0_STATUS ori $k1, $k1, 0xff13 mtc0 $k1, $CP0_STATUS #endif move $k0, $zero move $k1, $zero eret save_regs: lui $k0, 0x8000 lw $k0, 0x178($k0) // current sw $k1, SAVE_RA($k0) sw $gp, SAVE_GP($k0) sw $sp, SAVE_SP($k0) sw $at, SAVE_AT($k0) sw $fp, SAVE_FP($k0) sw $s7, SAVE_S + 7 * 4($k0) sw $s6, SAVE_S + 6 * 4($k0) sw $s5, SAVE_S + 5 * 4($k0) sw $s4, SAVE_S + 4 * 4($k0) sw $s3, SAVE_S + 3 * 4($k0) sw $s2, SAVE_S + 2 * 4($k0) sw $s1, SAVE_S + 1 * 4($k0) sw $s0, SAVE_S + 0 * 4($k0) sw $t9, SAVE_T + 9 * 4($k0) sw $t8, SAVE_T + 8 * 4($k0) sw $t7, SAVE_T + 7 * 4($k0) sw $t6, SAVE_T + 6 * 4($k0) sw $t5, SAVE_T + 5 * 4($k0) sw $t4, SAVE_T + 4 * 4($k0) sw $t3, SAVE_T + 3 * 4($k0) sw $t2, SAVE_T + 2 * 4($k0) sw $t1, SAVE_T + 1 * 4($k0) sw $t0, SAVE_T + 0 * 4($k0) sw $a3, SAVE_A + 3 * 4($k0) sw $a2, SAVE_A + 2 * 4($k0) sw $a1, SAVE_A + 1 * 4($k0) sw $a0, SAVE_A + 0 * 4($k0) sw $v1, SAVE_V + 1 * 4($k0) sw $v0, SAVE_V + 0 * 4($k0) mfhi $k1 sw $k1, SAVE_HI($k0) mflo $k1 sw $k1, SAVE_LO($k0) mfc0 $k1, $CP0_EPC sw $k1, SAVE_PC($k0) #ifndef NDEBUG // Allow kernel bugs to set EPC and friends. mfc0 $k0, $CP0_STATUS lui $k1, 0x1000 and $k0, $k0, $k1 mtc0 $k0, $CP0_STATUS #endif lui $k0, 0x8000 lw $gp, 0x17c($k0) la $sp, kernel_stack + KERNEL_STACK_SIZE move $t9, $ra la $ra, kernel_exit jr $t9 nop .set at .set reorder