#pypp 0 // Iris: micro-kernel for a capability-based operating system. // mips/interrupts.ccp: Functions called by mips/entry.S. // 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 ARCH #include "../kernel.hh" void arch_flush_cache (): __asm__ volatile ("\t.set noreorder\n" "\tlui $k0, 0x8000\n" "\tori $k1, $k0, 0x4000 - 0x20\n" "1:\tcache 0, 0($k0)\n" "\tcache 1, 0($k0)\n" "\tbne $k0, $k1, 1b\n" "\taddiu $k0, $k0, 0x20\n" "\t.set reorder\n") static kThread *handle_exit (): dbg_check () --dbg_code.h // Set must_wait to false, so random threads are not set to waiting when the kernel invokes something (such as a dbg_cap). must_wait = false if !current || (current == &idle): schedule () if !current: current = &idle if (current->flags & (Iris::Thread::RUNNING | Iris::Thread::WAITING)) != Iris::Thread::RUNNING: panic (current->flags, "non-scheduled thread running") if old_current == current: return current arch_flush_cache () if current != &idle: if (kMemory *)asids[current->address_space->arch.asid] != current->address_space: if asids[0]: current->address_space->arch.asid = asids[0] asids[0] = asids[asids[0]] else: static unsigned random = 1 current->address_space->arch.asid = random // Overwrite used asid, so flush those values from tlb. flush_tlb (random) ++random if random >= 64: random = 1 asids[current->address_space->arch.asid] = (unsigned)current->address_space cp0_set (CP0_ENTRY_HI, current->address_space->arch.asid) directory = current->address_space->arch.directory if current->flags & Iris::Thread::PRIV: cp0_set (CP0_STATUS, 0x1000ff13) else: cp0_set (CP0_STATUS, 0x0000ff13) dbg_push ((unsigned)current) dbg_push (current->pc) return current /// A TLB miss has occurred. This is the slow version. It is only used /// when k0 or k1 is not 0, or when an error occurs. /// Otherwise, the ultra-fast code in entry.S is used. kThread *tlb_refill (): ++dbg_code.h old_current = current if !directory: unsigned addr cp0_get (CP0_BAD_V_ADDR, addr) current->raise (Iris::ERR_NO_PAGE_DIRECTORY, addr) return handle_exit () unsigned EntryHi cp0_get (CP0_ENTRY_HI, EntryHi) Table *t = directory[EntryHi >> 21] if !t: unsigned addr cp0_get (CP0_BAD_V_ADDR, addr) current->raise (Iris::ERR_NO_PAGE_TABLE, addr) else: // - 2 instead of - 1 means reset bit 0 unsigned idx = (EntryHi >> 12) & ((1 << 9) - 2) cp0_set (CP0_ENTRY_LO0, t->entrylo[idx]) cp0_set (CP0_ENTRY_LO1, t->entrylo[idx + 1]) __asm__ volatile ("tlbwr") if dbg_code.l: kdebug ("tlb refill ") __asm__ volatile ("tlbp") unsigned index cp0_get (CP0_INDEX, index) kdebug_num (index, 2) kdebug ("|") kdebug_num (t->entrylo[idx]) kdebug (":") kdebug_num (t->entrylo[idx + 1]) kdebug (" for ") kdebug_num (EntryHi) kdebug ("\n") return handle_exit () /// An interrupt which is not an exception has occurred. kThread *interrupt (): ++dbg_code.h old_current = current unsigned ipr = INTC_IPR for unsigned i = 0; i < 32; ++i: if ipr & (1 << i): // Handle timer interrupts specially: don't disable them. if i == TIMER_INTERRUPT: continue //if i != 0x18: //kdebug ("interrupt: ") //kdebug_num (i, 2) //kdebug ("\n") // Disable the interrupt while handling it. intc_mask_irq (i) intc_ack_irq (i) // Send message to interrupt handler. if arch_interrupt_receiver[i]: kCapability::Context c for unsigned j = 0; j < 2; ++j: c.data[j] = 0 arch_interrupt_receiver[i]->send_message (i, &c) arch_interrupt_receiver[i] = NULL if ipr & (1 << TIMER_INTERRUPT): #if defined (TRENDTAC) ost_clear_uf (0) #elif defined (NANONOTE) tcu_clear_full_match_flag (0) #else #error unknown board #endif intc_ack_irq (TIMER_INTERRUPT) timer_interrupt () return handle_exit () void flush_tlb (unsigned asid): for unsigned tlb = 1; tlb < 32; ++tlb: cp0_set (CP0_INDEX, tlb) __asm__ volatile ("tlbr") unsigned hi cp0_get (CP0_ENTRY_HI, hi) if (hi & 0x1f) == asid: // Set asid to 0, which is only used by the idle task. cp0_set (CP0_ENTRY_HI, 0x2000 * tlb) __asm__ volatile ("tlbwi") static void arch_invoke (): kCapRef target = old_current->find_capability (old_current->arch.v[0], &must_wait) do_schedule = false kCapability::Context msg if must_wait: old_current->recv_reply = old_current->arch.t[2] old_current->recv_arg = old_current->arch.t[3] //kdebug_num (old_current->recv_reply) //kdebug ("/") //kdebug_num (old_current->recv_arg) //kdebug ("\n") if !target.valid (): if must_wait: old_current->wait () else: dpanic (target.index, "invalid target called") return msg.reply = old_current->find_capability (old_current->arch.t[0], &msg.copy[0]) msg.arg = old_current->find_capability (old_current->arch.t[1], &msg.copy[1]) msg.data[0] = Iris::Num (old_current->arch.a[0], old_current->arch.a[1]) msg.data[1] = Iris::Num (old_current->arch.a[2], old_current->arch.a[3]) target->invoke (&msg) if do_schedule && !must_wait: // If the call was to schedule without wait, it isn't done yet. schedule () else if old_current != current && old_current && (old_current->flags & (Iris::Thread::RUNNING | Iris::Thread::WAITING)) == Iris::Thread::RUNNING: // If the caller received an immediate reply from the kernel, it is no longer set as current. Don't let it lose its timeslice. current = old_current /// A general exception has occurred. kThread *exception (): ++dbg_code.h old_current = current unsigned cause cp0_get (CP0_CAUSE, cause) switch (cause >> 2) & 0x1f: case 0: // Interrupt. This shouldn't happen, since CAUSE[IV] == 1. panic (0, "Interrupt on exception vector.") break case 1: // TLB modification. unsigned addr cp0_get (CP0_BAD_V_ADDR, addr) current->raise (Iris::ERR_WRITE_DENIED, addr) break case 2: // TLB load or instruction fetch. unsigned addr cp0_get (CP0_BAD_V_ADDR, addr) current->raise (Iris::ERR_UNMAPPED_READ, addr) break case 3: // TLB store. unsigned addr cp0_get (CP0_BAD_V_ADDR, addr) current->raise (Iris::ERR_UNMAPPED_WRITE, addr) break case 4: // Address error load or instruction fetch. unsigned addr cp0_get (CP0_BAD_V_ADDR, addr) current->raise (Iris::ERR_INVALID_ADDRESS_READ, addr) break case 5: // Address error store. unsigned addr cp0_get (CP0_BAD_V_ADDR, addr) current->raise (Iris::ERR_INVALID_ADDRESS_WRITE, addr) break case 6: // Bus error instruction fetch. panic (0, "Bus error instruction fetch.") break case 7: // Bus error load or store. panic (0, "Bus error load or store.") break case 8: // Syscall. current->pc += 4 arch_invoke () //check (0x88392883, "check error") break case 9: // Breakpoint. #if 0 || defined (NDEBUG) //current->raise (Iris::ERR_BREAKPOINT, 0) #ifndef NDEBUG current->pc += 4 #endif #else unsigned opcode = *(unsigned *)current->pc if opcode == 0x0007000d: panic (0, "Division by zero (detected by compiler)") current->pc += 4 if current->arch.a[0]: if dbg_cap.valid (): dpanic (0, "Break instruction while log capability was already set") break bool dummy dbg_cap = current->find_capability (current->arch.a[1], &dummy) if !dbg_cap.valid (): dpanic (0, "no log capability provided") break kdebug ("log capability registered.\n") break kdebug (current->arch.a[1]) #endif break case 10: // Reserved instruction. current->raise (Iris::ERR_RESERVED_INSTRUCTION, 0) break case 11: // Coprocessor unusable. current->raise (Iris::ERR_COPROCESSOR_UNUSABLE, 0) break case 12: // Arithmetic overflow. current->raise (Iris::ERR_OVERFLOW, 0) break case 13: // Trap. current->raise (Iris::ERR_TRAP, 0) break case 15: // Floating point exception. panic (0xe1223344, "Floating point exception.") break case 23: // Reference to WatchHi/WatchLo address. cp0_set0 (CP0_WATCH_LO) current->raise (Iris::ERR_WATCHPOINT, 0) break case 24: // Machine check. panic (0xf3223344, "Machine check.") break case 30: // Cache error (EJTAG only). panic (0xf4223344, "Cache error (EJTAG only).") break case 14: case 16: case 17: case 18: case 19: case 20: case 21: case 22: case 25: case 26: case 27: case 28: case 29: case 31: // Reserved. panic (0xf5223344, "Reserved exception code") break default: panic (0xf6223344, "Impossible exception code") break return handle_exit () /// There's a cache error. Big trouble. Probably not worth trying to recover. kThread *cache_error (): ++dbg_code.h panic (0x33333333, "cache error") old_current = current return handle_exit ()