2009-05-11 01:28:12 +03:00
|
|
|
#pypp 0
|
2009-06-01 15:26:42 +03:00
|
|
|
// Iris: micro-kernel for a capability-based operating system.
|
2009-06-24 01:47:13 +03:00
|
|
|
// mips/interrupts.ccp: Functions called by mips/entry.S.
|
2009-06-01 15:26:42 +03:00
|
|
|
// Copyright 2009 Bas Wijnen <wijnen@debian.org>
|
|
|
|
//
|
|
|
|
// 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 <http://www.gnu.org/licenses/>.
|
|
|
|
|
2009-05-22 23:48:49 +03:00
|
|
|
#define ARCH
|
2009-06-01 15:26:42 +03:00
|
|
|
#include "../kernel.hh"
|
2009-05-11 01:28:12 +03:00
|
|
|
|
2009-07-21 13:17:52 +03:00
|
|
|
typedef unsigned cacheline[8]
|
|
|
|
void arch_flush_cache ():
|
|
|
|
for cacheline *line = (cacheline *)0x80000000; line < (cacheline *)0x80008000; ++line:
|
|
|
|
__asm__ volatile ("lw $k0, %0; cache 0, 0($k0); cache 1, 0($k0)" :: "m"(line))
|
|
|
|
|
2009-07-24 15:25:53 +03:00
|
|
|
static void handle_exit ():
|
2009-07-20 01:23:45 +03:00
|
|
|
if !current || (current == &idle):
|
2009-07-05 11:52:44 +03:00
|
|
|
schedule ()
|
|
|
|
if !current:
|
|
|
|
current = &idle
|
2009-08-18 00:11:15 +03:00
|
|
|
if (current->flags & (Thread::RUNNING | Thread::WAITING)) != Thread::RUNNING:
|
2009-07-25 01:54:12 +03:00
|
|
|
panic (current->flags, "non-scheduled thread running")
|
2009-07-24 15:25:53 +03:00
|
|
|
if !current:
|
|
|
|
current = &idle
|
2009-07-05 11:52:44 +03:00
|
|
|
if old_current == current:
|
|
|
|
return
|
2009-07-21 13:17:52 +03:00
|
|
|
arch_flush_cache ()
|
2009-07-05 11:52:44 +03:00
|
|
|
if current != &idle:
|
2009-08-18 00:11:15 +03:00
|
|
|
if (kMemory *)asids[current->address_space->arch.asid] != current->address_space:
|
2009-07-05 11:52:44 +03:00
|
|
|
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
|
2009-08-18 00:11:15 +03:00
|
|
|
if current->flags & Thread::PRIV:
|
2009-07-05 11:52:44 +03:00
|
|
|
cp0_set (CP0_STATUS, 0x1000ff13)
|
|
|
|
else:
|
|
|
|
cp0_set (CP0_STATUS, 0x0000ff13)
|
|
|
|
|
2009-05-26 01:42:47 +03:00
|
|
|
/// 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.
|
2009-08-18 00:11:15 +03:00
|
|
|
kThread *tlb_refill ():
|
2009-07-24 15:25:53 +03:00
|
|
|
old_current = current
|
2009-05-26 01:42:47 +03:00
|
|
|
if !directory:
|
2009-07-25 01:54:12 +03:00
|
|
|
unsigned addr
|
|
|
|
cp0_get (CP0_BAD_V_ADDR, addr)
|
|
|
|
current->raise (ERR_NO_PAGE_DIRECTORY, addr)
|
2009-07-24 15:25:53 +03:00
|
|
|
handle_exit ()
|
|
|
|
return current
|
2009-05-23 21:55:31 +03:00
|
|
|
unsigned EntryHi
|
|
|
|
cp0_get (CP0_ENTRY_HI, EntryHi)
|
2009-05-26 01:42:47 +03:00
|
|
|
unsigned *t = directory[EntryHi >> 21]
|
|
|
|
if !t:
|
2009-07-25 01:54:12 +03:00
|
|
|
unsigned addr
|
|
|
|
cp0_get (CP0_BAD_V_ADDR, addr)
|
|
|
|
current->raise (ERR_NO_PAGE_TABLE, addr)
|
2009-07-24 15:25:53 +03:00
|
|
|
else:
|
|
|
|
// - 2 instead of - 1 means reset bit 0
|
|
|
|
unsigned idx = (EntryHi >> 12) & ((1 << 9) - 2)
|
|
|
|
cp0_set (CP0_ENTRY_LO0, t[idx])
|
|
|
|
cp0_set (CP0_ENTRY_LO1, t[idx + 1])
|
|
|
|
__asm__ volatile ("tlbwr")
|
|
|
|
handle_exit ()
|
2009-05-11 01:28:12 +03:00
|
|
|
return current
|
|
|
|
|
|
|
|
/// An interrupt which is not an exception has occurred.
|
2009-08-18 00:11:15 +03:00
|
|
|
kThread *interrupt ():
|
2009-07-24 15:25:53 +03:00
|
|
|
old_current = current
|
2009-06-26 00:09:42 +03:00
|
|
|
unsigned ipr = INTC_IPR
|
2009-07-04 17:21:28 +03:00
|
|
|
for unsigned i = 0; i < 32; ++i:
|
2009-06-26 00:09:42 +03:00
|
|
|
if ipr & (1 << i):
|
2009-07-04 17:21:28 +03:00
|
|
|
// Handle timer interrupts specially: don't disable them.
|
|
|
|
if i == IRQ_OST0:
|
|
|
|
continue
|
2009-05-27 19:33:05 +03:00
|
|
|
// Disable the interrupt while handling it.
|
2009-06-26 00:09:42 +03:00
|
|
|
intc_mask_irq (i)
|
2009-07-04 17:21:28 +03:00
|
|
|
intc_ack_irq (i)
|
2009-05-27 19:33:05 +03:00
|
|
|
// Send message to interrupt handler.
|
|
|
|
if arch_interrupt_receiver[i]:
|
2009-08-18 00:11:15 +03:00
|
|
|
kCapability::Context c
|
|
|
|
for unsigned j = 0; j < 2; ++j:
|
2009-06-08 14:46:13 +03:00
|
|
|
c.data[j] = 0
|
2009-08-18 00:11:15 +03:00
|
|
|
c.caps = NULL
|
2009-06-08 14:46:13 +03:00
|
|
|
arch_interrupt_receiver[i]->send_message (i, &c)
|
2009-07-23 13:06:32 +03:00
|
|
|
arch_interrupt_receiver[i] = NULL
|
2009-07-04 17:21:28 +03:00
|
|
|
if ipr & (1 << IRQ_OST0):
|
2009-06-26 00:09:42 +03:00
|
|
|
ost_clear_uf (0)
|
2009-07-04 17:21:28 +03:00
|
|
|
intc_ack_irq (IRQ_OST0)
|
2009-06-26 00:09:42 +03:00
|
|
|
timer_interrupt ()
|
2009-07-24 15:25:53 +03:00
|
|
|
handle_exit ()
|
2009-05-11 01:28:12 +03:00
|
|
|
return current
|
|
|
|
|
2009-06-26 00:09:42 +03:00
|
|
|
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 ():
|
2009-08-18 00:11:15 +03:00
|
|
|
kCapRef target
|
2009-06-26 00:09:42 +03:00
|
|
|
bool wait
|
2009-08-05 11:16:24 +03:00
|
|
|
target = old_current->find_capability (old_current->arch.v0, &wait)
|
2009-07-20 01:23:45 +03:00
|
|
|
do_schedule = false
|
2009-08-18 00:11:15 +03:00
|
|
|
kCapability::Context msg
|
|
|
|
unsigned num = old_current->arch.s2
|
|
|
|
unsigned first = old_current->arch.s3
|
|
|
|
if num:
|
|
|
|
if num > 10:
|
|
|
|
num = 10
|
|
|
|
bool copy
|
2009-08-19 02:00:05 +03:00
|
|
|
if old_current->arch.s1 < old_current->slots:
|
|
|
|
msg.caps = old_current->caps[old_current->arch.s1]
|
2009-08-18 00:11:15 +03:00
|
|
|
if msg.caps && first < msg.caps->size:
|
|
|
|
for unsigned i = first; i < num && i < msg.caps->size; ++i:
|
|
|
|
msg.caps->cap (i)->invalidate ()
|
|
|
|
kCapRef t = old_current->find_capability ((&old_current->arch.t0)[i], ©)
|
|
|
|
if t:
|
|
|
|
msg.caps->clone (i, t, copy)
|
|
|
|
else:
|
|
|
|
msg.caps = NULL
|
|
|
|
else:
|
|
|
|
msg.caps = NULL
|
2009-07-20 01:23:45 +03:00
|
|
|
if wait:
|
2009-08-19 02:00:05 +03:00
|
|
|
old_current->recv_slot = old_current->arch.s0
|
2009-08-18 00:11:15 +03:00
|
|
|
old_current->wait ()
|
2009-06-26 00:09:42 +03:00
|
|
|
if !target:
|
2009-08-18 00:11:15 +03:00
|
|
|
if (old_current->arch.v0 & ~CAP_COPY) != ~CAP_COPY:
|
2009-08-13 18:28:36 +03:00
|
|
|
panic (old_current->arch.v0, "debug")
|
2009-07-05 11:52:44 +03:00
|
|
|
// There must be no action here.
|
2009-08-13 18:28:36 +03:00
|
|
|
return
|
2009-08-18 00:11:15 +03:00
|
|
|
msg.data[0] = Num (old_current->arch.a0, old_current->arch.a1)
|
|
|
|
msg.data[1] = Num (old_current->arch.a2, old_current->arch.a3)
|
|
|
|
target->invoke (&msg)
|
2009-08-13 18:28:36 +03:00
|
|
|
if do_schedule && !wait:
|
|
|
|
// If the call was to schedule without wait, it isn't done yet.
|
|
|
|
schedule ()
|
2009-08-18 00:11:15 +03:00
|
|
|
else if old_current != current && (old_current->flags & (Thread::RUNNING | Thread::WAITING)) == Thread::RUNNING:
|
2009-08-13 18:28:36 +03:00
|
|
|
// 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
|
2009-06-26 00:09:42 +03:00
|
|
|
|
2009-05-11 01:28:12 +03:00
|
|
|
/// A general exception has occurred.
|
2009-08-18 00:11:15 +03:00
|
|
|
kThread *exception ():
|
2009-07-24 15:25:53 +03:00
|
|
|
old_current = current
|
2009-05-20 23:07:56 +03:00
|
|
|
unsigned cause
|
2009-05-23 21:55:31 +03:00
|
|
|
cp0_get (CP0_CAUSE, cause)
|
2009-05-20 23:07:56 +03:00
|
|
|
switch (cause >> 2) & 0x1f:
|
|
|
|
case 0:
|
2009-05-25 01:31:35 +03:00
|
|
|
// Interrupt. This shouldn't happen, since CAUSE[IV] == 1.
|
2009-07-25 01:54:12 +03:00
|
|
|
panic (0, "Interrupt on exception vector.")
|
2009-07-24 15:25:53 +03:00
|
|
|
break
|
2009-05-20 23:07:56 +03:00
|
|
|
case 1:
|
|
|
|
// TLB modification.
|
2009-07-25 01:54:12 +03:00
|
|
|
unsigned addr
|
|
|
|
cp0_get (CP0_BAD_V_ADDR, addr)
|
|
|
|
current->raise (ERR_WRITE_DENIED, addr)
|
2009-07-24 15:25:53 +03:00
|
|
|
break
|
2009-05-20 23:07:56 +03:00
|
|
|
case 2:
|
|
|
|
// TLB load or instruction fetch.
|
2009-07-25 01:54:12 +03:00
|
|
|
unsigned addr
|
|
|
|
cp0_get (CP0_BAD_V_ADDR, addr)
|
|
|
|
current->raise (ERR_UNMAPPED_READ, addr)
|
2009-07-24 15:25:53 +03:00
|
|
|
break
|
2009-05-20 23:07:56 +03:00
|
|
|
case 3:
|
|
|
|
// TLB store.
|
2009-07-25 01:54:12 +03:00
|
|
|
unsigned addr
|
|
|
|
cp0_get (CP0_BAD_V_ADDR, addr)
|
|
|
|
current->raise (ERR_UNMAPPED_WRITE, addr)
|
2009-07-24 15:25:53 +03:00
|
|
|
break
|
2009-05-20 23:07:56 +03:00
|
|
|
case 4:
|
|
|
|
// Address error load or instruction fetch.
|
2009-07-25 01:54:12 +03:00
|
|
|
unsigned addr
|
|
|
|
cp0_get (CP0_BAD_V_ADDR, addr)
|
|
|
|
current->raise (ERR_INVALID_ADDRESS_READ, addr)
|
2009-07-24 15:25:53 +03:00
|
|
|
break
|
2009-05-20 23:07:56 +03:00
|
|
|
case 5:
|
|
|
|
// Address error store.
|
2009-07-25 01:54:12 +03:00
|
|
|
unsigned addr
|
|
|
|
cp0_get (CP0_BAD_V_ADDR, addr)
|
|
|
|
current->raise (ERR_INVALID_ADDRESS_WRITE, addr)
|
2009-07-24 15:25:53 +03:00
|
|
|
break
|
2009-05-20 23:07:56 +03:00
|
|
|
case 6:
|
|
|
|
// Bus error instruction fetch.
|
2009-07-25 01:54:12 +03:00
|
|
|
panic (0, "Bus error instruction fetch.")
|
2009-07-24 15:25:53 +03:00
|
|
|
break
|
2009-05-20 23:07:56 +03:00
|
|
|
case 7:
|
|
|
|
// Bus error load or store.
|
2009-07-25 01:54:12 +03:00
|
|
|
panic (0, "Bus error load or store.")
|
2009-07-24 15:25:53 +03:00
|
|
|
break
|
2009-05-20 23:07:56 +03:00
|
|
|
case 8:
|
|
|
|
// Syscall.
|
2009-06-08 14:46:13 +03:00
|
|
|
current->pc += 4
|
2009-05-23 21:55:31 +03:00
|
|
|
arch_invoke ()
|
2009-05-27 15:38:52 +03:00
|
|
|
break
|
2009-05-20 23:07:56 +03:00
|
|
|
case 9:
|
|
|
|
// Breakpoint.
|
2009-08-13 18:28:36 +03:00
|
|
|
#if 0
|
2009-07-25 01:54:12 +03:00
|
|
|
current->raise (ERR_BREAKPOINT, 0)
|
|
|
|
#else
|
2009-07-20 01:23:45 +03:00
|
|
|
current->pc += 4
|
2009-07-23 13:06:32 +03:00
|
|
|
if current->arch.a0:
|
|
|
|
if dbg_cap:
|
2009-07-25 01:54:12 +03:00
|
|
|
panic (0, "Break instruction while log capability was already set")
|
2009-07-24 15:25:53 +03:00
|
|
|
break
|
2009-07-23 13:06:32 +03:00
|
|
|
bool dummy
|
2009-08-05 11:16:24 +03:00
|
|
|
dbg_cap = current->find_capability (current->arch.a1, &dummy)
|
2009-07-23 13:06:32 +03:00
|
|
|
if !dbg_cap:
|
2009-07-25 01:54:12 +03:00
|
|
|
panic (0, "no log capability provided")
|
2009-07-24 15:25:53 +03:00
|
|
|
break
|
2009-07-23 13:06:32 +03:00
|
|
|
break
|
|
|
|
if dbg_cap:
|
|
|
|
dbg_log_char (current->arch.a1)
|
|
|
|
break
|
2009-07-20 01:23:45 +03:00
|
|
|
break
|
2009-07-25 01:54:12 +03:00
|
|
|
#endif
|
2009-05-20 23:07:56 +03:00
|
|
|
case 10:
|
|
|
|
// Reserved instruction.
|
2009-07-25 01:54:12 +03:00
|
|
|
current->raise (ERR_RESERVED_INSTRUCTION, 0)
|
2009-07-24 15:25:53 +03:00
|
|
|
break
|
2009-05-20 23:07:56 +03:00
|
|
|
case 11:
|
|
|
|
// Coprocessor unusable.
|
2009-07-25 01:54:12 +03:00
|
|
|
current->raise (ERR_COPROCESSOR_UNUSABLE, 0)
|
2009-07-24 15:25:53 +03:00
|
|
|
break
|
2009-05-20 23:07:56 +03:00
|
|
|
case 12:
|
|
|
|
// Arithmetic overflow.
|
2009-07-25 01:54:12 +03:00
|
|
|
current->raise (ERR_OVERFLOW, 0)
|
2009-07-24 15:25:53 +03:00
|
|
|
break
|
2009-05-20 23:07:56 +03:00
|
|
|
case 13:
|
|
|
|
// Trap.
|
2009-07-25 01:54:12 +03:00
|
|
|
current->raise (ERR_TRAP, 0)
|
2009-07-24 15:25:53 +03:00
|
|
|
break
|
2009-05-20 23:07:56 +03:00
|
|
|
case 15:
|
|
|
|
// Floating point exception.
|
2009-07-21 13:17:52 +03:00
|
|
|
panic (0xe1223344, "Floating point exception.")
|
2009-07-24 15:25:53 +03:00
|
|
|
break
|
2009-05-20 23:07:56 +03:00
|
|
|
case 23:
|
|
|
|
// Reference to WatchHi/WatchLo address.
|
2009-07-25 01:54:12 +03:00
|
|
|
current->raise (ERR_WATCHPOINT, 0)
|
2009-07-24 15:25:53 +03:00
|
|
|
break
|
2009-05-20 23:07:56 +03:00
|
|
|
case 24:
|
|
|
|
// Machine check.
|
2009-05-22 23:48:49 +03:00
|
|
|
panic (0xf3223344, "Machine check.")
|
2009-07-24 15:25:53 +03:00
|
|
|
break
|
2009-05-20 23:07:56 +03:00
|
|
|
case 30:
|
|
|
|
// Cache error (EJTAG only).
|
2009-05-22 23:48:49 +03:00
|
|
|
panic (0xf4223344, "Cache error (EJTAG only).")
|
2009-07-24 15:25:53 +03:00
|
|
|
break
|
2009-05-20 23:07:56 +03:00
|
|
|
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.
|
2009-05-23 21:55:31 +03:00
|
|
|
panic (0xf5223344, "Reserved exception code")
|
2009-07-24 15:25:53 +03:00
|
|
|
break
|
2009-05-23 21:55:31 +03:00
|
|
|
default:
|
|
|
|
panic (0xf6223344, "Impossible exception code")
|
2009-07-24 15:25:53 +03:00
|
|
|
break
|
|
|
|
handle_exit ()
|
2009-05-11 01:28:12 +03:00
|
|
|
return current
|
|
|
|
|
|
|
|
/// There's a cache error. Big trouble. Probably not worth trying to recover.
|
2009-08-18 00:11:15 +03:00
|
|
|
kThread *cache_error ():
|
2009-05-11 01:28:12 +03:00
|
|
|
panic (0x33333333, "cache error")
|
2009-07-24 15:25:53 +03:00
|
|
|
old_current = current
|
|
|
|
handle_exit ()
|
2009-05-11 01:28:12 +03:00
|
|
|
return current
|