mirror of
git://projects.qi-hardware.com/iris.git
synced 2025-04-21 12:27:27 +03:00
add license; reorganize arch
This commit is contained in:
59
mips/Makefile.arch
Normal file
59
mips/Makefile.arch
Normal file
@@ -0,0 +1,59 @@
|
||||
# Iris: micro-kernel for a capability-based operating system.
|
||||
# mips/Makefile.arch: mips-specific parts of the build rules
|
||||
# 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/>.
|
||||
|
||||
load = 0x80000000
|
||||
|
||||
ARCH_CXXFLAGS = -DNUM_THREADS=2
|
||||
ARCH_CPPFLAGS = -Imips -Wa,-mips32
|
||||
CROSS = mipsel-linux-gnu-
|
||||
OBJDUMP = $(CROSS)objdump
|
||||
junk = mdebug.abi32 reginfo comment pdr
|
||||
OBJCOPYFLAGS = $(addprefix --remove-section=.,$(junk))
|
||||
|
||||
arch_kernel_sources = mips/interrupts.cc mips/test.cc mips/arch.cc
|
||||
boot_sources = mips/init.cc
|
||||
BUILT_SOURCES = $(kernel_sources) $(boot_sources)
|
||||
arch_headers = mips/arch.hh
|
||||
boot_threads = thread0 thread1
|
||||
|
||||
uimage:
|
||||
|
||||
mips/entry.o: $(boot_threads)
|
||||
mips/init.o: TARGET_FLAGS = -I/usr/include
|
||||
$(boot_threads): TARGET_FLAGS = -I.
|
||||
|
||||
# Transform ':' into ';' so vim doesn't think there are errors.
|
||||
uimage: kernel.raw.gz Makefile mips/Makefile.arch
|
||||
mkimage -A MIPS -O Linux -C gzip -a $(load) -e 0x$(shell /bin/sh -c '$(OBJDUMP) -t kernel | grep __start$$ | cut -b-8') -n "Shevek's kernel" -d $< $@ | sed -e 's/:/;/g'
|
||||
|
||||
elf.h: /usr/include/elf.h
|
||||
ln -s $< $@
|
||||
|
||||
%.o:%.S Makefile mips/Makefile.arch mips/arch.hh
|
||||
$(CC) $(CPPFLAGS) $(TARGET_FLAGS) -DKERNEL_STACK_SIZE=0x2000 -c $< -o $@
|
||||
|
||||
# entry.o must be the first file. boot.o must be the first of the init objects (which can be freed after loading).
|
||||
kernel: mips/entry.o $(subst .cc,.o,$(kernel_sources)) mips/boot.o $(subst .cc,.o,$(boot_sources))
|
||||
$(LD) --omagic -Ttext $(load) $^ -o $@
|
||||
|
||||
%.raw: %
|
||||
$(OBJCOPY) -S $(addprefix --remove-section=.,$(junk)) -Obinary $< $@
|
||||
|
||||
%.gz: %
|
||||
gzip < $< > $@
|
||||
|
||||
ARCH_CLEAN_FILES = uimage kernel kernel.raw kernel.raw.gz elf.h $(boot_threads) mips/*.o
|
||||
379
mips/arch.ccp
Normal file
379
mips/arch.ccp
Normal file
@@ -0,0 +1,379 @@
|
||||
#pypp 0
|
||||
// Iris: micro-kernel for a capability-based operating system.
|
||||
// mips/arch.ccp: Most mips-specific parts.
|
||||
// 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/>.
|
||||
|
||||
#define ARCH
|
||||
#include "../kernel.hh"
|
||||
|
||||
void Thread_arch_init (Thread *thread):
|
||||
thread->arch.at = 0
|
||||
thread->arch.v0 = 0
|
||||
thread->arch.v1 = 0
|
||||
thread->arch.a0 = 0
|
||||
thread->arch.a1 = 0
|
||||
thread->arch.a2 = 0
|
||||
thread->arch.a3 = 0
|
||||
thread->arch.t0 = 0
|
||||
thread->arch.t1 = 0
|
||||
thread->arch.t2 = 0
|
||||
thread->arch.t3 = 0
|
||||
thread->arch.t4 = 0
|
||||
thread->arch.t5 = 0
|
||||
thread->arch.t6 = 0
|
||||
thread->arch.t7 = 0
|
||||
thread->arch.t8 = 0
|
||||
thread->arch.t9 = 0
|
||||
thread->arch.gp = 0
|
||||
thread->arch.fp = 0
|
||||
thread->arch.ra = 0
|
||||
thread->arch.hi = 0
|
||||
thread->arch.lo = 0
|
||||
thread->arch.k0 = 0
|
||||
thread->arch.k1 = 0
|
||||
|
||||
void Thread_arch_receive (Thread *thread, unsigned d[4], Capability *c[4]):
|
||||
thread->arch.a0 = (unsigned)c[0]
|
||||
thread->arch.a1 = (unsigned)c[1]
|
||||
thread->arch.a2 = (unsigned)c[2]
|
||||
thread->arch.a3 = (unsigned)c[3]
|
||||
thread->arch.t0 = d[0]
|
||||
thread->arch.t1 = d[1]
|
||||
thread->arch.t2 = d[2]
|
||||
thread->arch.t3 = d[3]
|
||||
thread->arch.v0 = 1
|
||||
|
||||
void Thread_arch_receive_fail (Thread *thread):
|
||||
thread->arch.v0 = 0
|
||||
|
||||
unsigned *Thread_arch_info (Thread *thread, unsigned num):
|
||||
switch num:
|
||||
case 1:
|
||||
return &thread->arch.at
|
||||
case 2:
|
||||
return &thread->arch.v0
|
||||
case 3:
|
||||
return &thread->arch.v1
|
||||
case 4:
|
||||
return &thread->arch.a0
|
||||
case 5:
|
||||
return &thread->arch.a1
|
||||
case 6:
|
||||
return &thread->arch.a2
|
||||
case 7:
|
||||
return &thread->arch.a3
|
||||
case 8:
|
||||
return &thread->arch.t0
|
||||
case 9:
|
||||
return &thread->arch.t1
|
||||
case 10:
|
||||
return &thread->arch.t2
|
||||
case 11:
|
||||
return &thread->arch.t3
|
||||
case 12:
|
||||
return &thread->arch.t4
|
||||
case 13:
|
||||
return &thread->arch.t5
|
||||
case 14:
|
||||
return &thread->arch.t6
|
||||
case 15:
|
||||
return &thread->arch.t7
|
||||
case 16:
|
||||
return &thread->arch.s0
|
||||
case 17:
|
||||
return &thread->arch.s1
|
||||
case 18:
|
||||
return &thread->arch.s2
|
||||
case 19:
|
||||
return &thread->arch.s3
|
||||
case 20:
|
||||
return &thread->arch.s4
|
||||
case 21:
|
||||
return &thread->arch.s5
|
||||
case 22:
|
||||
return &thread->arch.s6
|
||||
case 23:
|
||||
return &thread->arch.s7
|
||||
case 24:
|
||||
return &thread->arch.t8
|
||||
case 25:
|
||||
return &thread->arch.t9
|
||||
case 26:
|
||||
return &thread->arch.k0
|
||||
case 27:
|
||||
return &thread->arch.k1
|
||||
case 28:
|
||||
return &thread->arch.gp
|
||||
case 29:
|
||||
return &thread->sp
|
||||
case 30:
|
||||
return &thread->arch.fp
|
||||
case 31:
|
||||
return &thread->arch.ra
|
||||
default:
|
||||
return NULL
|
||||
|
||||
void Memory_arch_init (Memory *mem):
|
||||
mem->arch.asid = 1
|
||||
mem->arch.directory = NULL
|
||||
mem->arch.shadow = NULL
|
||||
|
||||
static 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")
|
||||
|
||||
void Memory_arch_free (Memory *mem):
|
||||
while mem->arch.first_page_table:
|
||||
mem->unmap (mem->arch.first_page_table->first_page->page, mem->arch.first_page_table->first_page->mapping)
|
||||
if (Memory *)asids[mem->arch.asid] == mem:
|
||||
flush_tlb (mem->arch.asid)
|
||||
asids[mem->arch.asid] = asids[0]
|
||||
asids[0] = mem->arch.asid
|
||||
mem->unuse ()
|
||||
mem->zfree ((unsigned)mem->arch.directory)
|
||||
|
||||
static arch_page_table *alloc_page_table (Memory *mem):
|
||||
arch_page_table *ret = (arch_page_table *)mem->search_free (sizeof (arch_page_table), (void **)&mem->arch.first_page_table)
|
||||
if !ret:
|
||||
return NULL
|
||||
ret->first_page = NULL
|
||||
return ret
|
||||
|
||||
static arch_page *alloc_page (Memory *mem, arch_page_table *t):
|
||||
arch_page *ret = (arch_page *)mem->search_free (sizeof (arch_page), (void **)&t->first_page)
|
||||
if !ret:
|
||||
return NULL
|
||||
ret->page = NULL
|
||||
ret->mapping = ~0
|
||||
ret->prev_mapped = NULL
|
||||
ret->next_mapped = NULL
|
||||
return ret
|
||||
|
||||
static void free_page_table (arch_page_table *t, unsigned idx):
|
||||
Memory *mem = t->address_space
|
||||
if t->next:
|
||||
t->next->prev = t->prev
|
||||
if t->prev:
|
||||
t->prev->next = t->next
|
||||
else:
|
||||
mem->arch.first_page_table = t->next
|
||||
mem->zfree ((unsigned)mem->arch.directory[idx])
|
||||
mem->arch.directory[idx] = NULL
|
||||
mem->arch.shadow[idx] = NULL
|
||||
mem->free_obj (t)
|
||||
if !mem->arch.first_page_table:
|
||||
mem->zfree ((unsigned)mem->arch.directory)
|
||||
mem->zfree ((unsigned)mem->arch.shadow)
|
||||
mem->arch.directory = NULL
|
||||
mem->arch.shadow = NULL
|
||||
|
||||
static void tlb_reset (unsigned address, unsigned asid, unsigned value):
|
||||
cp0_set (CP0_ENTRY_HI, address | asid)
|
||||
__asm__ volatile ("tlbp")
|
||||
unsigned idx
|
||||
cp0_get (CP0_INDEX, idx)
|
||||
if ~idx & 0x80000000:
|
||||
if address & (1 << PAGE_BITS):
|
||||
cp0_set (CP0_ENTRY_LO1, value)
|
||||
else:
|
||||
cp0_set (CP0_ENTRY_LO0, value)
|
||||
__asm__ volatile ("tlbwi")
|
||||
|
||||
static void free_page (arch_page_table *t, arch_page *p):
|
||||
if p->next:
|
||||
p->next->prev = p->prev
|
||||
if p->prev:
|
||||
p->prev->next = p->next
|
||||
else:
|
||||
t->first_page = p->next
|
||||
if p->prev_mapped:
|
||||
p->prev_mapped->next_mapped = p->next_mapped
|
||||
else:
|
||||
p->page->arch.first_mapped = p->next_mapped
|
||||
if p->next_mapped:
|
||||
p->next_mapped->prev_mapped = p->prev_mapped
|
||||
tlb_reset (p->mapping, p->address_space->arch.asid, 0)
|
||||
unsigned idx = p->mapping >> 21
|
||||
p->address_space->free_obj (p)
|
||||
if !t->first_page:
|
||||
free_page_table (t, idx)
|
||||
|
||||
static unsigned make_entry_lo (Page *page, bool write):
|
||||
if !page->data.frame:
|
||||
return 0
|
||||
unsigned flags
|
||||
if write:
|
||||
flags = 0x18 | 0x4 | 0x2
|
||||
else
|
||||
flags = 0x18 | 0x2
|
||||
return ((page->data.frame & ~0x80000000) >> 6) | flags
|
||||
|
||||
bool Memory_arch_map (Memory *mem, Page *page, unsigned address, bool write):
|
||||
if address >= 0x80000000:
|
||||
return false
|
||||
address &= PAGE_MASK
|
||||
if !mem->arch.directory:
|
||||
mem->arch.directory = (unsigned **)mem->zalloc ()
|
||||
if !mem->arch.directory:
|
||||
return false
|
||||
mem->arch.shadow = (arch_page_table **)mem->zalloc ()
|
||||
if !mem->arch.shadow:
|
||||
mem->zfree ((unsigned)mem->arch.directory)
|
||||
mem->arch.directory = NULL
|
||||
return false
|
||||
unsigned *table = mem->arch.directory[address >> 21]
|
||||
arch_page_table *t = mem->arch.shadow[address >> 21]
|
||||
if !table:
|
||||
table = (unsigned *)mem->zalloc ()
|
||||
if !table:
|
||||
if !mem->arch.first_page_table:
|
||||
mem->zfree ((unsigned)mem->arch.directory)
|
||||
mem->zfree ((unsigned)mem->arch.shadow)
|
||||
mem->arch.directory = NULL
|
||||
mem->arch.shadow = NULL
|
||||
return false
|
||||
t = alloc_page_table (mem)
|
||||
if !t:
|
||||
mem->zfree ((unsigned)table)
|
||||
if !mem->arch.first_page_table:
|
||||
mem->zfree ((unsigned)mem->arch.directory)
|
||||
mem->zfree ((unsigned)mem->arch.shadow)
|
||||
mem->arch.directory = NULL
|
||||
mem->arch.shadow = NULL
|
||||
return false
|
||||
mem->arch.directory[address >> 21] = table
|
||||
mem->arch.shadow[address >> 21] = t
|
||||
arch_page *p = alloc_page (mem, t)
|
||||
if !p:
|
||||
if !t->first_page:
|
||||
// This automatically cleans up the rest.
|
||||
free_page_table (t, address >> 21)
|
||||
return false
|
||||
unsigned idx = (address >> 12) & ((1 << 9) - 1)
|
||||
if table[idx]:
|
||||
mem->unmap ((Page *)table[idx + 0x200], address)
|
||||
table[idx] = make_entry_lo (page, write)
|
||||
table[idx + 0x200] = (unsigned)p
|
||||
p->mapping = address + write
|
||||
p->page = page
|
||||
p->next_mapped = page->arch.first_mapped
|
||||
if p->next_mapped:
|
||||
p->next_mapped->prev_mapped = p
|
||||
page->arch.first_mapped = p
|
||||
return true
|
||||
|
||||
void Memory_arch_unmap (Memory *mem, Page *page, unsigned address):
|
||||
unsigned didx = address >> 21
|
||||
unsigned tidx = (address >> 12) & ((1 << 9) - 1)
|
||||
unsigned *table = mem->arch.directory[didx]
|
||||
arch_page_table *t = mem->arch.shadow[didx]
|
||||
table[tidx] = 0
|
||||
arch_page *p = (arch_page *)table[tidx + 0x200]
|
||||
table[tidx + 0x200] = 0
|
||||
free_page (t, p)
|
||||
|
||||
Page *Memory_arch_get_mapping (Memory *mem, unsigned address, bool *writable):
|
||||
if address >= 0x80000000:
|
||||
return NULL
|
||||
unsigned *table = mem->arch.directory[address >> 21]
|
||||
unsigned idx = (address >> 12) & ((1 << 9) - 1)
|
||||
arch_page *page = (arch_page *)table[idx + 0x200]
|
||||
if writable:
|
||||
*writable = table[idx] & 4
|
||||
return page->page
|
||||
|
||||
void Page_arch_update_mapping (Page *page):
|
||||
if !page->arch.first_mapped:
|
||||
return
|
||||
Memory *as = page->address_space
|
||||
unsigned target = make_entry_lo (page, page->data.flags & PAGE_FLAG_WRITABLE)
|
||||
for arch_page *p = page->arch.first_mapped; p; p = p->next_mapped:
|
||||
unsigned de = p->mapping >> 21
|
||||
unsigned te = (p->mapping >> 12) & ((1 << 9) - 1)
|
||||
bool write = p->mapping & 1
|
||||
unsigned t
|
||||
if p->mapping & 1:
|
||||
t = target
|
||||
else:
|
||||
t = target & ~0x4
|
||||
as->arch.directory[de][te] = t
|
||||
tlb_reset (p->mapping & ~1, as->arch.asid, t)
|
||||
|
||||
void arch_invoke ():
|
||||
Capability *target, *c[4]
|
||||
bool wait, copy[4]
|
||||
Thread *caller = current
|
||||
target = caller->address_space->find_capability (current->arch.v0, &wait)
|
||||
if !target:
|
||||
// TODO: there must be no action here. This is just because the rest doesn't work yet.
|
||||
dbg_led (caller->arch.a0, caller->arch.a1, caller->arch.a2)
|
||||
dbg_sleep (1000)
|
||||
schedule ()
|
||||
// Calling an invalid capability always fails.
|
||||
caller->arch.v0 = 0
|
||||
else:
|
||||
if wait:
|
||||
caller->wait ()
|
||||
c[0] = caller->address_space->find_capability (caller->arch.a0, ©[0])
|
||||
c[1] = caller->address_space->find_capability (caller->arch.a1, ©[1])
|
||||
c[2] = caller->address_space->find_capability (caller->arch.a2, ©[2])
|
||||
c[3] = caller->address_space->find_capability (caller->arch.a3, ©[3])
|
||||
unsigned d[4]
|
||||
d[0] = caller->arch.t0
|
||||
d[1] = caller->arch.t1
|
||||
d[2] = caller->arch.t2
|
||||
d[3] = caller->arch.t3
|
||||
caller->arch.v0 = target->invoke (d, c, copy) ? 1 : 0
|
||||
if caller != current:
|
||||
if (Memory *)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
|
||||
unsigned status
|
||||
cp0_get (CP0_STATUS, status)
|
||||
status &= 0x0fffffff
|
||||
if current->flags & THREAD_FLAG_PRIV:
|
||||
status |= 0x10000000
|
||||
cp0_set (CP0_STATUS, status | 0x13)
|
||||
|
||||
void arch_register_interrupt (unsigned num, Receiver *r):
|
||||
arch_interrupt_receiver[num] = r
|
||||
unsigned status
|
||||
cp0_get (CP0_STATUS, status)
|
||||
// And enable or disable the interrupt.
|
||||
if r:
|
||||
status |= 1 << (num + 8)
|
||||
else:
|
||||
status &= ~(1 << (num + 8))
|
||||
cp0_set (CP0_STATUS, status)
|
||||
166
mips/arch.hhp
Normal file
166
mips/arch.hhp
Normal file
@@ -0,0 +1,166 @@
|
||||
#pypp 0
|
||||
// Iris: micro-kernel for a capability-based operating system.
|
||||
// mips/arch.hhp: mips-specific declarations and type definitions.
|
||||
// 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/>.
|
||||
|
||||
#ifndef _ARCH_HH
|
||||
#define _ARCH_HH
|
||||
|
||||
#ifdef ARCH
|
||||
#define reg_hack(x...) #x
|
||||
#define cp0_get(reg, target) do { __asm__ volatile ("mfc0 %0, $" reg_hack(reg) : "=r" (target)); } while (0)
|
||||
#define cp0_set(reg, value) do { __asm__ volatile ("mtc0 %0, $" reg_hack(reg) :: "r" (value)); } while (0)
|
||||
#define cp0_set0(reg) do { __asm__ volatile ("mtc0 $zero, $" reg_hack(reg)); } while (0)
|
||||
|
||||
// cp0 registers.
|
||||
#define CP0_INDEX 0
|
||||
#define CP0_RANDOM 1
|
||||
#define CP0_ENTRY_LO0 2
|
||||
#define CP0_ENTRY_LO1 3
|
||||
#define CP0_CONTEXT 4
|
||||
#define CP0_PAGE_MASK 5
|
||||
#define CP0_WIRED 6
|
||||
#define CP0_BAD_V_ADDR 8
|
||||
#define CP0_COUNT 9
|
||||
#define CP0_ENTRY_HI 10
|
||||
#define CP0_COMPARE 11
|
||||
#define CP0_STATUS 12
|
||||
#define CP0_CAUSE 13
|
||||
#define CP0_EPC 14
|
||||
#define CP0_P_R_ID 15
|
||||
#define CP0_CONFIG 16
|
||||
#define CP0_CONFIG1 16, 1
|
||||
#define CP0_CONFIG2 16, 2
|
||||
#define CP0_CONFIG3 16, 3
|
||||
#define CP0_L_L_ADDR 17
|
||||
#define CP0_WATCH_LO 18
|
||||
#define CP0_WATCH_HI 19
|
||||
#define CP0_DEBUG 23
|
||||
#define CP0_DEPC 24
|
||||
#define CP0_PERF_CNT 25
|
||||
#define CP0_ERR_CTL 26
|
||||
#define CP0_CACHE_ERR 27
|
||||
#define CP0_TAG_LO 28, 0
|
||||
#define CP0_DATA_LO 28, 1
|
||||
#define CP0_TAG_HI 29, 0
|
||||
#define CP0_DATA_HI 29, 1
|
||||
#define CP0_ERROR_EPC 30
|
||||
#define CP0_DESAVE 31
|
||||
#endif
|
||||
|
||||
#define PAGE_BITS (12)
|
||||
#define PAGE_SIZE (1 << PAGE_BITS)
|
||||
#define PAGE_MASK (~(PAGE_SIZE - 1))
|
||||
|
||||
// register save positions in Thread
|
||||
#define SAVE_PC (5 * 4)
|
||||
#define SAVE_SP (SAVE_PC + 4)
|
||||
#define SAVE_AT (SAVE_SP + 4)
|
||||
#define SAVE_V0 (SAVE_AT + 4)
|
||||
#define SAVE_V1 (SAVE_V0 + 4)
|
||||
#define SAVE_A0 (SAVE_V1 + 4)
|
||||
#define SAVE_A1 (SAVE_A0 + 4)
|
||||
#define SAVE_A2 (SAVE_A1 + 4)
|
||||
#define SAVE_A3 (SAVE_A2 + 4)
|
||||
#define SAVE_T0 (SAVE_A3 + 4)
|
||||
#define SAVE_T1 (SAVE_T0 + 4)
|
||||
#define SAVE_T2 (SAVE_T1 + 4)
|
||||
#define SAVE_T3 (SAVE_T2 + 4)
|
||||
#define SAVE_T4 (SAVE_T3 + 4)
|
||||
#define SAVE_T5 (SAVE_T4 + 4)
|
||||
#define SAVE_T6 (SAVE_T5 + 4)
|
||||
#define SAVE_T7 (SAVE_T6 + 4)
|
||||
#define SAVE_T8 (SAVE_T7 + 4)
|
||||
#define SAVE_T9 (SAVE_T8 + 4)
|
||||
#define SAVE_S0 (SAVE_T9 + 4)
|
||||
#define SAVE_S1 (SAVE_S0 + 4)
|
||||
#define SAVE_S2 (SAVE_S1 + 4)
|
||||
#define SAVE_S3 (SAVE_S2 + 4)
|
||||
#define SAVE_S4 (SAVE_S3 + 4)
|
||||
#define SAVE_S5 (SAVE_S4 + 4)
|
||||
#define SAVE_S6 (SAVE_S5 + 4)
|
||||
#define SAVE_S7 (SAVE_S6 + 4)
|
||||
#define SAVE_GP (SAVE_S7 + 4)
|
||||
#define SAVE_FP (SAVE_GP + 4)
|
||||
#define SAVE_RA (SAVE_FP + 4)
|
||||
#define SAVE_HI (SAVE_RA + 4)
|
||||
#define SAVE_LO (SAVE_HI + 4)
|
||||
#define SAVE_K0 (SAVE_LO + 4)
|
||||
#define SAVE_K1 (SAVE_K0 + 4)
|
||||
|
||||
#ifndef ASM
|
||||
|
||||
struct Thread_arch:
|
||||
unsigned at, v0, v1, a0, a1, a2, a3
|
||||
unsigned t0, t1, t2, t3, t4, t5, t6, t7, t8, t9
|
||||
unsigned s0, s1, s2, s3, s4, s5, s6, s7
|
||||
unsigned gp, fp, ra, hi, lo, k0, k1
|
||||
|
||||
// The following is used for page mapping.
|
||||
// Each Memory has a two directories with 0x400 entries,
|
||||
// page tables and mapping page tables. Mapping page tables are
|
||||
// pages which contain 0x200 EntryLo. values and 0x200 Page pointers.
|
||||
// For a virtual address, bits 0-11 are in the physical address,
|
||||
// bits 12-20 are an index in the page table, bits 21-30
|
||||
// are an index in the page directory and bit 31 is always 0.
|
||||
|
||||
struct arch_page : public Object <arch_page> :
|
||||
Page *page
|
||||
unsigned mapping
|
||||
arch_page *prev_mapped, *next_mapped
|
||||
|
||||
struct arch_page_table : public Object <arch_page_table> :
|
||||
arch_page *first_page
|
||||
|
||||
struct Page_arch:
|
||||
arch_page *first_mapped
|
||||
|
||||
struct Memory_arch:
|
||||
unsigned asid
|
||||
unsigned **directory
|
||||
arch_page_table **shadow
|
||||
arch_page_table *first_page_table
|
||||
|
||||
// Pointers to Memory when asid is taken, index of next free, or 0, if free.
|
||||
// asid[0] is used as index to first free asid.
|
||||
EXTERN unsigned asids[64]
|
||||
EXTERN Receiver *arch_interrupt_receiver[8]
|
||||
|
||||
// Functions which can be called from assembly must not be mangled.
|
||||
extern "C":
|
||||
// Kernel entry points, called from entry.S.
|
||||
Thread *interrupt ()
|
||||
Thread *cache_error ()
|
||||
Thread *exception ()
|
||||
Thread *tlb_refill ()
|
||||
|
||||
#ifdef INIT
|
||||
// Initialize most things (the rest is done in boot.S)
|
||||
void init ()
|
||||
// Start running the idle task for the first time.
|
||||
void run_idle (Thread *self)
|
||||
#endif
|
||||
|
||||
// These are "extern", not "EXTERN", because they really are defined elsewhere.
|
||||
#ifdef INIT
|
||||
extern unsigned thread_start[NUM_THREADS + 1]
|
||||
#endif
|
||||
// Fast pointer to page directory, for tlb miss events
|
||||
extern unsigned **directory
|
||||
|
||||
#endif // defined ASM
|
||||
|
||||
#endif
|
||||
68
mips/boot.S
Normal file
68
mips/boot.S
Normal file
@@ -0,0 +1,68 @@
|
||||
// Iris: micro-kernel for a capability-based operating system.
|
||||
// mips/boot.S: Kernel entry point, called by the boot loader.
|
||||
// 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/>.
|
||||
|
||||
// The kernel stack.
|
||||
.lcomm kernel_stack, KERNEL_STACK_SIZE
|
||||
|
||||
.globl __start
|
||||
.globl thread_start
|
||||
.set noreorder
|
||||
|
||||
#define Status 12
|
||||
#define Config 16
|
||||
|
||||
__start:
|
||||
bal 1f
|
||||
nop
|
||||
.word _gp
|
||||
// For some reason the disassembler considers everything
|
||||
// after __start non-code until the next label. So I add a label.
|
||||
start_hack_for_disassembler:
|
||||
1: lw $gp, 0($ra)
|
||||
|
||||
la $sp, kernel_stack + KERNEL_STACK_SIZE
|
||||
|
||||
// Disable interrupts during bootstrap.
|
||||
mtc0 $zero, $Status
|
||||
|
||||
// TODO: flush cache and optionally refill it.
|
||||
|
||||
// Set kseg0 cachable.
|
||||
li $k0, 0x3
|
||||
mtc0 $k0, $Config, 0
|
||||
|
||||
// Jump into cached code.
|
||||
la $t9, 1f
|
||||
jr $t9
|
||||
nop
|
||||
1:
|
||||
|
||||
// Clear .bss
|
||||
la $a0, _edata
|
||||
la $a1, _end
|
||||
1: sw $zero, 0($a0)
|
||||
bne $a1, $a0, 1b
|
||||
addu $a0, 4
|
||||
|
||||
la $t9, init
|
||||
jr $t9
|
||||
nop
|
||||
|
||||
thread_start:
|
||||
.word thread0
|
||||
.word thread1
|
||||
.word thread2
|
||||
229
mips/entry.S
Normal file
229
mips/entry.S
Normal file
@@ -0,0 +1,229 @@
|
||||
// Iris: micro-kernel for a capability-based operating system.
|
||||
// mips/entry.S: Routines which are entered from user space.
|
||||
// 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/>.
|
||||
|
||||
// The kernel stack.
|
||||
.lcomm kernel_stack, KERNEL_STACK_SIZE
|
||||
|
||||
.globl run_idle
|
||||
.globl directory
|
||||
.set noat
|
||||
.set noreorder
|
||||
|
||||
#define ARCH
|
||||
#define ASM
|
||||
#include "arch.hh"
|
||||
|
||||
addr_000:
|
||||
#if 0
|
||||
// TLB refill
|
||||
bne $zero, $k1, slow_refill
|
||||
nop
|
||||
bne $zero, $k0, slow_refill
|
||||
lw $k1, -0xd94($zero)
|
||||
mfc0 $k0, $CP0_ENTRY_HI
|
||||
srl $k0, $k0, 19
|
||||
and $k0, $k0, 0x3fc
|
||||
addu $k0, $k0, $k1
|
||||
beq $zero, $k0, zero_refill
|
||||
lw $k0, 0($k0)
|
||||
mfc0 $k1, $CP0_ENTRY_HI
|
||||
srl $k1, $k1, 10
|
||||
and $k1, $k1, 0x1f8
|
||||
add $k0, $k0, $k1
|
||||
lw $k1, 0($k0)
|
||||
mtc0 $k1, $CP0_ENTRY_LO0
|
||||
lw $k1, 4($k0)
|
||||
mtc0 $k1, $CP0_ENTRY_LO1
|
||||
1: tlbwr
|
||||
move $zero, $k0
|
||||
move $zero, $k1
|
||||
eret
|
||||
|
||||
zero_refill:
|
||||
mtc0 $zero, $CP_ENTRY_LO0
|
||||
b 1b
|
||||
mtc0 $zero, $CP_ENTRY_LO1
|
||||
|
||||
slow_refill:
|
||||
move $k1, $zero
|
||||
#endif
|
||||
sw $ra, -0xd88($zero)
|
||||
bal save_regs
|
||||
nop
|
||||
la $t9, tlb_refill
|
||||
jr $t9
|
||||
nop
|
||||
.fill 0x100 - (. - addr_000)
|
||||
addr_100:
|
||||
// Cache error
|
||||
sw $ra, -0xd88($zero)
|
||||
bal save_regs
|
||||
nop
|
||||
la $t9, cache_error
|
||||
jr $t9
|
||||
nop
|
||||
.fill 0x180 - (. - addr_000)
|
||||
addr_180:
|
||||
// General exception
|
||||
// Allow new exceptions to update EPC and friends.
|
||||
//mtc0 $zero, $CP0_STATUS
|
||||
sw $ra, -0xd88($zero)
|
||||
bal save_regs
|
||||
nop
|
||||
la $t9, exception
|
||||
jr $t9
|
||||
nop
|
||||
.fill 0x200 - (. - addr_000) - 8
|
||||
.word 0x0000001e // 1f8 EntryLo data for idle page.
|
||||
.word 0x80000000 // 1fc A pointer to the current page.
|
||||
addr_200:
|
||||
// Interrupt
|
||||
sw $ra, -0xd88($zero)
|
||||
bal save_regs
|
||||
nop
|
||||
la $t9, interrupt
|
||||
jr $t9
|
||||
nop
|
||||
.fill 0x280 - (. - addr_000) - 20
|
||||
|
||||
directory:
|
||||
.word 0 // -d94 == directory
|
||||
// space for save_regs
|
||||
.word 0 // -d90 == k0
|
||||
.word idle // -d8c == current
|
||||
.word 0 // -d88 == ra
|
||||
.word _gp // -d84 == gp
|
||||
|
||||
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.
|
||||
move $v0, $zero
|
||||
syscall
|
||||
nop
|
||||
1: wait
|
||||
b 1b
|
||||
nop
|
||||
|
||||
// TODO: save only fragile registers now, the rest on task switch.
|
||||
kernel_exit:
|
||||
lw $k0, SAVE_PC($v0)
|
||||
mtc0 $k0, $CP0_EPC
|
||||
lw $k0, SAVE_LO($v0)
|
||||
lw $k1, SAVE_HI($v0)
|
||||
mtlo $k0
|
||||
mthi $k1
|
||||
lw $v1, SAVE_V1($v0)
|
||||
lw $a0, SAVE_A0($v0)
|
||||
lw $a1, SAVE_A1($v0)
|
||||
lw $a2, SAVE_A2($v0)
|
||||
lw $a3, SAVE_A3($v0)
|
||||
lw $t0, SAVE_T0($v0)
|
||||
lw $t1, SAVE_T1($v0)
|
||||
lw $t2, SAVE_T2($v0)
|
||||
lw $t3, SAVE_T3($v0)
|
||||
lw $t4, SAVE_T4($v0)
|
||||
lw $t5, SAVE_T5($v0)
|
||||
lw $t6, SAVE_T6($v0)
|
||||
lw $t7, SAVE_T7($v0)
|
||||
lw $t8, SAVE_T8($v0)
|
||||
lw $t9, SAVE_T9($v0)
|
||||
lw $s0, SAVE_S0($v0)
|
||||
lw $s1, SAVE_S1($v0)
|
||||
lw $s2, SAVE_S2($v0)
|
||||
lw $s3, SAVE_S3($v0)
|
||||
lw $s4, SAVE_S4($v0)
|
||||
lw $s5, SAVE_S5($v0)
|
||||
lw $s6, SAVE_S6($v0)
|
||||
lw $s7, SAVE_S7($v0)
|
||||
lw $fp, SAVE_FP($v0)
|
||||
lw $ra, SAVE_RA($v0)
|
||||
lw $at, SAVE_AT($v0)
|
||||
lw $k0, SAVE_K0($v0)
|
||||
lw $k1, SAVE_V0($v0)
|
||||
sw $k1, -0xd90($zero)
|
||||
lw $k1, SAVE_K1($v0)
|
||||
sw $v0, -0xd8c($zero)
|
||||
|
||||
lw $sp, SAVE_SP($v0)
|
||||
lw $gp, SAVE_GP($v0)
|
||||
lw $v0, -0xd90($zero)
|
||||
eret
|
||||
|
||||
save_regs:
|
||||
sw $k0, -0xd90($zero)
|
||||
lw $k0, -0xd8c($zero)
|
||||
|
||||
sw $at, SAVE_AT($k0)
|
||||
sw $gp, SAVE_GP($k0)
|
||||
sw $sp, SAVE_SP($k0)
|
||||
sw $fp, SAVE_FP($k0)
|
||||
|
||||
sw $k1, SAVE_K1($k0)
|
||||
lw $k1, -0xd90($zero)
|
||||
sw $k1, SAVE_K0($k0)
|
||||
|
||||
lw $k1, -0xd88($zero)
|
||||
sw $k1, SAVE_RA($k0)
|
||||
sw $v0, SAVE_V0($k0)
|
||||
sw $v1, SAVE_V1($k0)
|
||||
sw $a0, SAVE_A0($k0)
|
||||
sw $a1, SAVE_A1($k0)
|
||||
sw $a2, SAVE_A2($k0)
|
||||
sw $a3, SAVE_A3($k0)
|
||||
sw $t0, SAVE_T0($k0)
|
||||
sw $t1, SAVE_T1($k0)
|
||||
sw $t2, SAVE_T2($k0)
|
||||
sw $t3, SAVE_T3($k0)
|
||||
sw $t4, SAVE_T4($k0)
|
||||
sw $t5, SAVE_T5($k0)
|
||||
sw $t6, SAVE_T6($k0)
|
||||
sw $t7, SAVE_T7($k0)
|
||||
sw $t8, SAVE_T8($k0)
|
||||
sw $t9, SAVE_T9($k0)
|
||||
sw $s0, SAVE_S0($k0)
|
||||
sw $s1, SAVE_S1($k0)
|
||||
sw $s2, SAVE_S2($k0)
|
||||
sw $s3, SAVE_S3($k0)
|
||||
sw $s4, SAVE_S4($k0)
|
||||
sw $s5, SAVE_S5($k0)
|
||||
sw $s6, SAVE_S6($k0)
|
||||
sw $s7, SAVE_S7($k0)
|
||||
mfhi $v0
|
||||
mflo $v1
|
||||
sw $v0, SAVE_HI($k0)
|
||||
sw $v1, SAVE_LO($k0)
|
||||
mfc0 $k1, $CP0_EPC
|
||||
sw $k1, SAVE_PC($k0)
|
||||
|
||||
lw $gp, -0xd84($zero)
|
||||
la $sp, kernel_stack + KERNEL_STACK_SIZE
|
||||
move $t9, $ra
|
||||
la $ra, kernel_exit
|
||||
jr $t9
|
||||
nop
|
||||
|
||||
.globl thread0
|
||||
.globl thread1
|
||||
.globl thread2
|
||||
.balign 0x1000
|
||||
thread0:
|
||||
.incbin "thread0"
|
||||
.balign 0x1000
|
||||
thread1:
|
||||
.incbin "thread1"
|
||||
thread2:
|
||||
240
mips/init.ccp
Normal file
240
mips/init.ccp
Normal file
@@ -0,0 +1,240 @@
|
||||
#pypp 0
|
||||
// Iris: micro-kernel for a capability-based operating system.
|
||||
// init.ccp: mips-specific boot code.
|
||||
// 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/>.
|
||||
|
||||
// Also declare things which only work during kernel init.
|
||||
#define INIT
|
||||
#define ARCH
|
||||
#include "../kernel.hh"
|
||||
#include <elf.h>
|
||||
|
||||
static void init_idle ():
|
||||
// initialize idle task as if it is currently running.
|
||||
idle.prev = NULL
|
||||
idle.next = NULL
|
||||
idle.schedule_prev = NULL
|
||||
idle.schedule_next = NULL
|
||||
idle.address_space = &idle_memory
|
||||
idle.refs = NULL
|
||||
idle.flags = THREAD_FLAG_RUNNING | THREAD_FLAG_PRIV
|
||||
// initialize idle_memory.
|
||||
idle_memory.prev = NULL
|
||||
idle_memory.next = NULL
|
||||
idle_memory.address_space = NULL
|
||||
idle_memory.refs = NULL
|
||||
idle_memory.pages = &idle_page
|
||||
idle_memory.threads = &idle
|
||||
idle_memory.memories = NULL
|
||||
idle_memory.limit = 0
|
||||
idle_memory.used = 0
|
||||
idle_memory.arch.directory = (unsigned **)0x80000000
|
||||
// Shadow is never used for the idle task.
|
||||
idle_memory.arch.shadow = NULL
|
||||
idle_memory.arch.asid = 0
|
||||
// initialize idle_page
|
||||
idle_page.prev = NULL
|
||||
idle_page.next = NULL
|
||||
idle_page.data.frame = 0x80000000
|
||||
idle_page.data.flags = PAGE_FLAG_WRITABLE | PAGE_FLAG_PAYING | PAGE_FLAG_FRAME
|
||||
idle_page.refs = NULL
|
||||
idle_page.address_space = NULL
|
||||
current = &idle
|
||||
directory = idle_memory.arch.directory
|
||||
|
||||
static void init_cp0 ():
|
||||
// Set timer to a defined value
|
||||
cp0_set (CP0_COMPARE, 1000000)
|
||||
// Reset timer
|
||||
cp0_set0 (CP0_COUNT)
|
||||
// Use the interrupt vector for interrupts
|
||||
cp0_set (CP0_CAUSE, 1 << 23)
|
||||
|
||||
// clear the tlb, hardwire page 0 to 0xffffffff
|
||||
// and soft-wire it to (0x294 << 20) + (0x290 << 10)
|
||||
// (for the idle task).
|
||||
|
||||
cp0_set (CP0_WIRED, 1)
|
||||
cp0_set0 (CP0_PAGE_MASK)
|
||||
cp0_set0 (CP0_ENTRY_LO0)
|
||||
cp0_set0 (CP0_ENTRY_LO1)
|
||||
// Get number of tlb entries (is 31).
|
||||
unsigned num
|
||||
cp0_get (CP0_CONFIG1, num)
|
||||
num >>= 25
|
||||
num &= 0x3f
|
||||
// Clear the tlb.
|
||||
for unsigned i = 1; i <= num; ++i:
|
||||
// with asid 0, no page faults will be triggered, so it's safe to map memory anywhere.
|
||||
cp0_set (CP0_ENTRY_HI, 0x2000 * i)
|
||||
cp0_set (CP0_INDEX, i)
|
||||
// write the data.
|
||||
__asm__ volatile ("tlbwi")
|
||||
// Fill the upper page in kseg3.
|
||||
cp0_set (CP0_ENTRY_HI, 0xffffe000)
|
||||
cp0_set (CP0_ENTRY_LO0, 0x1d)
|
||||
cp0_set (CP0_ENTRY_LO1, 0x1f)
|
||||
cp0_set0 (CP0_INDEX)
|
||||
__asm__ volatile ("tlbwi")
|
||||
// Fill the idle task's page in useg. Set it to non-cachable.
|
||||
// its directory entry is at 1fc, so it's number 7f (0fe00000).
|
||||
// its table entry is at 1f8, so it's number 7e (0007e000).
|
||||
// its address is 280 (00000280), used below for EPC.
|
||||
unsigned const idle_entry_hi = 0x0fe7e000
|
||||
cp0_set (CP0_ENTRY_HI, idle_entry_hi)
|
||||
cp0_set (CP0_ENTRY_LO0, 0x16)
|
||||
cp0_set (CP0_ENTRY_LO1, 0x14)
|
||||
__asm__ volatile ("tlbwr")
|
||||
// Allow eret to be used to jump to the idle task.
|
||||
cp0_set (CP0_EPC, (idle_entry_hi & PAGE_MASK) | 0x280)
|
||||
// Wait with initializing the status register until the last moment, so that
|
||||
// exceptions in the bootup code will fill EPC and friends.
|
||||
|
||||
// This returns unsigned, because the value is used to fill thread->arch.a*.
|
||||
static unsigned mkcap (Memory *mem, unsigned type, void *obj):
|
||||
return (unsigned)mem->alloc_capability ((Receiver *)type, NULL, &mem->capabilities, (unsigned)obj)
|
||||
|
||||
static void init_threads ():
|
||||
Thread *previous = NULL
|
||||
first_scheduled = NULL
|
||||
for unsigned i = 0; i < NUM_THREADS; ++i:
|
||||
Memory *mem = top_memory.alloc_memory ()
|
||||
Thread *thread = mem->alloc_thread ()
|
||||
Page **pages = (Page **)mem->zalloc ()
|
||||
Elf32_Ehdr *header = (Elf32_Ehdr *)thread_start[i]
|
||||
for unsigned j = 0; j < SELFMAG; ++j:
|
||||
if header->e_ident[j] != ELFMAG[j]:
|
||||
panic (i * 0x1000 + j, "invalid ELF magic")
|
||||
if header->e_ident[EI_CLASS] != ELFCLASS32:
|
||||
panic (i * 0x1000 + EI_CLASS, "invalid ELF class")
|
||||
if header->e_ident[EI_DATA] != ELFDATA2LSB:
|
||||
panic (i * 0x1000 + EI_DATA, "invalid ELF data")
|
||||
if header->e_ident[EI_VERSION] != EV_CURRENT:
|
||||
panic (i * 0x1000 + EI_VERSION, "invalid ELF version")
|
||||
if header->e_type != ET_EXEC:
|
||||
panic (i * 0x1000 + 0x10, "invalid ELF type")
|
||||
if header->e_machine != EM_MIPS_RS3_LE && header->e_machine != EM_MIPS:
|
||||
panic (i * 0x1000 + 0x12, "invalid ELF machine")
|
||||
thread->pc = header->e_entry
|
||||
thread->sp = 0x80000000
|
||||
for unsigned section = 0; section < header->e_shnum; ++section:
|
||||
Elf32_Shdr *shdr = (Elf32_Shdr *)(thread_start[i] + header->e_shoff + section * header->e_shentsize)
|
||||
if !(shdr->sh_flags & SHF_ALLOC):
|
||||
continue
|
||||
bool writable = shdr->sh_flags & SHF_WRITE
|
||||
//bool executable = shdr->sh_flags & SHF_EXEC_INSTR
|
||||
if shdr->sh_type != SHT_NOBITS:
|
||||
for unsigned p = (shdr->sh_addr & PAGE_MASK); p <= ((shdr->sh_addr + shdr->sh_size - 1) & PAGE_MASK); p += PAGE_SIZE:
|
||||
unsigned idx = (p - (shdr->sh_addr & PAGE_MASK)) >> PAGE_BITS
|
||||
if !pages[idx]:
|
||||
pages[idx] = mem->alloc_page ()
|
||||
pages[idx]->data.frame = thread_start[i] + (idx << PAGE_BITS)
|
||||
pages[idx]->data.flags = (writable ? PAGE_FLAG_WRITABLE : 0) | PAGE_FLAG_PAYING | PAGE_FLAG_FRAME
|
||||
++top_memory.limit
|
||||
if !mem->map (pages[idx], p, writable):
|
||||
panic (0x22446688, "unable to map initial page")
|
||||
else:
|
||||
for unsigned p = (shdr->sh_addr & PAGE_MASK); p <= ((shdr->sh_addr + shdr->sh_size - 1) & PAGE_MASK); p += PAGE_SIZE:
|
||||
bool write = false
|
||||
Page *page = mem->get_mapping (p, &write)
|
||||
if !page:
|
||||
page = mem->alloc_page ()
|
||||
if !page:
|
||||
panic (0x00220022, "out of memory")
|
||||
page->data.frame = mem->zalloc ()
|
||||
page->data.flags = (writable ? PAGE_FLAG_WRITABLE : 0) | PAGE_FLAG_PAYING | PAGE_FLAG_FRAME
|
||||
if !page->data.frame || !mem->map (page, p, true):
|
||||
panic (0x33557799, "unable to map initial bss page")
|
||||
else:
|
||||
if !write:
|
||||
panic (0x20203030, "bss section starts on read-only page")
|
||||
for unsigned a = p; a < p + PAGE_SIZE; a += 4:
|
||||
if a >= shdr->sh_addr + shdr->sh_size:
|
||||
break
|
||||
if a < shdr->sh_addr:
|
||||
continue
|
||||
((unsigned *)page->data.frame)[(a & ~PAGE_MASK) >> 2] = 0
|
||||
for unsigned p = 0; p <= ((thread_start[i + 1] - thread_start[i] - 1) >> PAGE_BITS); ++p:
|
||||
if pages[p]:
|
||||
continue
|
||||
++top_memory.limit
|
||||
top_memory.pfree (thread_start[i] + (p << PAGE_BITS))
|
||||
Page *stackpage = mem->alloc_page ()
|
||||
stackpage->data.frame = mem->zalloc ()
|
||||
stackpage->data.flags = PAGE_FLAG_WRITABLE | PAGE_FLAG_PAYING | PAGE_FLAG_FRAME
|
||||
if !stackpage || !mem->map (stackpage, 0x7ffff000, true):
|
||||
panic (0x13151719, "unable to map initial stack page")
|
||||
Receiver *recv = mem->alloc_receiver ()
|
||||
thread->arch.a0 = mkcap (mem, CAPTYPE_RECEIVER | CAP_RECEIVER_ALL_RIGHTS, recv)
|
||||
thread->arch.a1 = mkcap (mem, CAPTYPE_THREAD | CAP_THREAD_ALL_PRIV_RIGHTS, thread)
|
||||
thread->arch.a2 = mkcap (mem, CAPTYPE_MEMORY | CAP_MEMORY_ALL_RIGHTS, mem)
|
||||
thread->arch.a3 = mkcap (mem, CAPTYPE_RECEIVER | CAP_RECEIVER_CALL, recv)
|
||||
mem->pfree ((unsigned)pages)
|
||||
thread->flags = THREAD_FLAG_RUNNING | THREAD_FLAG_PRIV
|
||||
thread->schedule_next = NULL
|
||||
thread->schedule_prev = previous
|
||||
if previous:
|
||||
previous->schedule_next = thread
|
||||
else:
|
||||
first_scheduled = thread
|
||||
previous = thread
|
||||
|
||||
// Initialize the kernel, finish by falling into the idle task.
|
||||
extern unsigned _end
|
||||
void init ():
|
||||
// Disable interrupts and set interrupt vectors to normal.
|
||||
cp0_set0 (CP0_STATUS)
|
||||
// Initialize kernel variables to empty.
|
||||
sleepers = NULL
|
||||
runners = NULL
|
||||
zero_pages = NULL
|
||||
// Fill junk pages with all memory not currently used.
|
||||
junk_pages = (FreePage *)(((unsigned)&_end + ~PAGE_MASK) & PAGE_MASK)
|
||||
FreePage *p, *next
|
||||
unsigned count = 1
|
||||
for p = junk_pages, next = p; (unsigned)next - 0x80000000 < (1 << 27); p = next, next = (FreePage *)((unsigned)p + ~PAGE_MASK + 1):
|
||||
p->next = next
|
||||
++count
|
||||
p->next = NULL
|
||||
// initialize system control coprocessor.
|
||||
init_cp0 ()
|
||||
// initialize everything about the idle task.
|
||||
init_idle ()
|
||||
// initialize top_memory.
|
||||
top_memory.prev = NULL
|
||||
top_memory.next = NULL
|
||||
top_memory.address_space = NULL
|
||||
top_memory.refs = NULL
|
||||
top_memory.pages = NULL
|
||||
top_memory.threads = NULL
|
||||
top_memory.memories = NULL
|
||||
top_memory.limit = count
|
||||
top_memory.used = 0
|
||||
top_memory.arch.directory = NULL
|
||||
top_memory.arch.asid = 0
|
||||
|
||||
for unsigned i = 0; i < 63; ++i:
|
||||
asids[i] = i + 1
|
||||
asids[63] = 0
|
||||
|
||||
init_threads ()
|
||||
|
||||
// Say we're handling an exception. Don't enable interrupts; this will happen when handlers are registered.
|
||||
// Since we're going to enter the idle task, allow access to cp0.
|
||||
cp0_set (CP0_STATUS, 0x10000013)
|
||||
|
||||
// Done; return to user space (the idle task).
|
||||
__asm__ volatile ("eret")
|
||||
143
mips/interrupts.ccp
Normal file
143
mips/interrupts.ccp
Normal file
@@ -0,0 +1,143 @@
|
||||
#pypp 0
|
||||
// Iris: micro-kernel for a capability-based operating system.
|
||||
// interrupts.ccp: Functions called by mips/entry.S.
|
||||
// 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/>.
|
||||
|
||||
#define ARCH
|
||||
#include "../kernel.hh"
|
||||
|
||||
/// 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.
|
||||
Thread *tlb_refill ():
|
||||
//panic (0x88776655, "TLB refill")
|
||||
if !directory:
|
||||
panic (0x44449999, "No directory")
|
||||
unsigned EntryHi
|
||||
cp0_get (CP0_ENTRY_HI, EntryHi)
|
||||
unsigned *t = directory[EntryHi >> 21]
|
||||
if !t:
|
||||
panic (0x99992222, "No page table")
|
||||
// - 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")
|
||||
return current
|
||||
|
||||
/// An interrupt which is not an exception has occurred.
|
||||
Thread *interrupt ():
|
||||
panic (0x88877722, "Interrupt")
|
||||
unsigned cause, status
|
||||
cp0_get (CP0_CAUSE, cause)
|
||||
cp0_get (CP0_STATUS, status)
|
||||
for unsigned i = 0; i < 8; ++i:
|
||||
if cause & (1 << (i + 8)):
|
||||
// Disable the interrupt while handling it.
|
||||
status &= ~(1 << (i + 8))
|
||||
// Send message to interrupt handler.
|
||||
if arch_interrupt_receiver[i]:
|
||||
unsigned data[4] = {0, 0, 0, 0}
|
||||
Capability *cap[4] = {NULL, NULL, NULL, NULL}
|
||||
bool copy[4] = {false, false, false, false}
|
||||
arch_interrupt_receiver[i]->send_message (i, data, cap, copy)
|
||||
return current
|
||||
|
||||
/// A general exception has occurred.
|
||||
Thread *exception ():
|
||||
unsigned cause
|
||||
cp0_get (CP0_CAUSE, cause)
|
||||
//dbg_send (cause >> 2, 5)
|
||||
switch (cause >> 2) & 0x1f:
|
||||
case 0:
|
||||
// Interrupt. This shouldn't happen, since CAUSE[IV] == 1.
|
||||
panic (0x11223344, "Interrupt on exception vector.")
|
||||
case 1:
|
||||
// TLB modification.
|
||||
panic (0x21223344, "TLB modification.")
|
||||
case 2:
|
||||
// TLB load or instruction fetch.
|
||||
panic (0x31223344, "TLB load or instruction fetch.")
|
||||
case 3:
|
||||
// TLB store.
|
||||
panic (0x41223344, "TLB store.")
|
||||
case 4:
|
||||
// Address error load or instruction fetch.
|
||||
panic (0x51223344, "Address error load or instruction fetch.")
|
||||
case 5:
|
||||
// Address error store.
|
||||
panic (0x61223344, "Address error store.")
|
||||
case 6:
|
||||
// Bus error instruction fetch.
|
||||
panic (0x71223344, "Bus error instruction fetch.")
|
||||
case 7:
|
||||
// Bus error load or store.
|
||||
panic (0x81223344, "Bus error load or store.")
|
||||
case 8:
|
||||
// Syscall.
|
||||
arch_invoke ()
|
||||
break
|
||||
case 9:
|
||||
// Breakpoint.
|
||||
panic (0x91223344, "Breakpoint.")
|
||||
case 10:
|
||||
// Reserved instruction.
|
||||
panic (0xa1223344, "Reserved instruction.")
|
||||
case 11:
|
||||
// Coprocessor unusable.
|
||||
panic (0xb1223344, "Coprocessor unusable.")
|
||||
case 12:
|
||||
// Arithmetic overflow.
|
||||
panic (0xc1223344, "Arithmetic overflow.")
|
||||
case 13:
|
||||
// Trap.
|
||||
panic (0xe1223344, "Trap.")
|
||||
case 15:
|
||||
// Floating point exception.
|
||||
panic (0xf1223344, "Floating point exception.")
|
||||
case 23:
|
||||
// Reference to WatchHi/WatchLo address.
|
||||
panic (0xf2223344, "Reference to WatchHi/WatchLo address.")
|
||||
case 24:
|
||||
// Machine check.
|
||||
panic (0xf3223344, "Machine check.")
|
||||
case 30:
|
||||
// Cache error (EJTAG only).
|
||||
panic (0xf4223344, "Cache error (EJTAG only).")
|
||||
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")
|
||||
default:
|
||||
panic (0xf6223344, "Impossible exception code")
|
||||
return current
|
||||
|
||||
/// There's a cache error. Big trouble. Probably not worth trying to recover.
|
||||
Thread *cache_error ():
|
||||
panic (0x33333333, "cache error")
|
||||
return current
|
||||
114
mips/test.ccp
Normal file
114
mips/test.ccp
Normal file
@@ -0,0 +1,114 @@
|
||||
#pypp 0
|
||||
// Iris: micro-kernel for a capability-based operating system.
|
||||
// test.ccp: Telling the user things with LEDs.
|
||||
// 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/>.
|
||||
|
||||
#include "../kernel.hh"
|
||||
|
||||
#define REG32(addr) *((volatile unsigned int *)(addr))
|
||||
|
||||
#define GPIO_BASE 0xB0010000
|
||||
|
||||
#define __gpio_port_data(p) ( REG_GPIO_GPDR(p) )
|
||||
|
||||
#define GPIO_GPSLR(n) (GPIO_BASE + (0x10 + (n)*0x30))
|
||||
#define GPIO_GPSUR(n) (GPIO_BASE + (0x14 + (n)*0x30))
|
||||
#define GPIO_GPFLR(n) (GPIO_BASE + (0x18 + (n)*0x30))
|
||||
#define GPIO_GPFUR(n) (GPIO_BASE + (0x1c + (n)*0x30))
|
||||
|
||||
#define GPIO_GPDR(n) (GPIO_BASE + (0x00 + (n)*0x30))
|
||||
|
||||
#define REG_GPIO_GPSLR(n) REG32(GPIO_GPSLR((n)))
|
||||
#define REG_GPIO_GPSUR(n) REG32(GPIO_GPSUR((n)))
|
||||
#define REG_GPIO_GPFLR(n) REG32(GPIO_GPFLR((n)))
|
||||
#define REG_GPIO_GPFUR(n) REG32(GPIO_GPFUR((n)))
|
||||
|
||||
#define REG_GPIO_GPDR(n) REG32(GPIO_GPDR((n)))
|
||||
|
||||
static void __gpio_port_as_gpiofn (unsigned p, unsigned o, unsigned fn):
|
||||
unsigned int tmp
|
||||
if o < 16:
|
||||
tmp = REG_GPIO_GPSLR(p)
|
||||
tmp &= ~(3 << ((o) << 1))
|
||||
REG_GPIO_GPSLR(p) = tmp
|
||||
tmp = REG_GPIO_GPFLR(p)
|
||||
tmp &= ~(3 << ((o) << 1))
|
||||
tmp |= fn << ((o) << 1)
|
||||
REG_GPIO_GPFLR(p) = tmp
|
||||
else:
|
||||
tmp = REG_GPIO_GPSUR(p)
|
||||
tmp &= ~(3 << (((o) - 16) << 1))
|
||||
REG_GPIO_GPSUR(p) = tmp
|
||||
tmp = REG_GPIO_GPFUR(p)
|
||||
tmp &= ~(3 << (((o) - 16) << 1))
|
||||
tmp |= fn << (((o) - 16) << 1)
|
||||
REG_GPIO_GPFUR(p) = tmp
|
||||
|
||||
static void __gpio_port_as_output (unsigned p, unsigned o):
|
||||
__gpio_port_as_gpiofn (p, o, 1)
|
||||
static void __gpio_port_as_input (unsigned p, unsigned o):
|
||||
__gpio_port_as_gpiofn (p, o, 0)
|
||||
static void __gpio_as_output (unsigned n):
|
||||
__gpio_port_as_output(n / 32, n % 32)
|
||||
static void __gpio_as_input (unsigned n):
|
||||
__gpio_port_as_input(n / 32, n % 32)
|
||||
static void __gpio_set_pin (unsigned n):
|
||||
__gpio_port_data (n / 32) |= (1 << (n % 32))
|
||||
static void __gpio_clear_pin (unsigned n):
|
||||
__gpio_port_data (n / 32) &= ~(1 << (n % 32))
|
||||
|
||||
#define CAPSLOCKLED_IO 27
|
||||
#define NUMLOCKLED_IO 86
|
||||
#define NETWORK_IO 9
|
||||
#define LIGHT 105
|
||||
|
||||
void dbg_led (bool one, bool two, bool three):
|
||||
__gpio_as_output (CAPSLOCKLED_IO)
|
||||
__gpio_as_output (NUMLOCKLED_IO)
|
||||
__gpio_as_output (NETWORK_IO)
|
||||
if one:
|
||||
__gpio_clear_pin (NUMLOCKLED_IO)
|
||||
else:
|
||||
__gpio_set_pin (NUMLOCKLED_IO)
|
||||
if two:
|
||||
__gpio_clear_pin (CAPSLOCKLED_IO)
|
||||
else:
|
||||
__gpio_set_pin (CAPSLOCKLED_IO)
|
||||
if three:
|
||||
__gpio_clear_pin (NETWORK_IO)
|
||||
else:
|
||||
__gpio_set_pin (NETWORK_IO)
|
||||
|
||||
void dbg_sleep (unsigned ms):
|
||||
for unsigned i = 0; i < 2673 * ms; ++i:
|
||||
__gpio_as_output (CAPSLOCKLED_IO)
|
||||
|
||||
void dbg_send (unsigned code, unsigned bits):
|
||||
for int i = bits - 1; i >= 0; --i:
|
||||
bool on = code & (1 << i)
|
||||
dbg_led (false, false, false)
|
||||
dbg_sleep (200)
|
||||
if on:
|
||||
dbg_led (true, false, false)
|
||||
else:
|
||||
dbg_led (false, true, false)
|
||||
dbg_sleep (400)
|
||||
dbg_led (false, false, false)
|
||||
dbg_sleep (200)
|
||||
dbg_led (true, true, false)
|
||||
dbg_sleep (500)
|
||||
dbg_led (false, false, false)
|
||||
dbg_sleep (500)
|
||||
Reference in New Issue
Block a user