1
0
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:
Bas Wijnen
2009-06-01 14:26:42 +02:00
parent ef1b9bfe10
commit aa7263c565
26 changed files with 612 additions and 191 deletions

59
mips/Makefile.arch Normal file
View 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
View 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, &copy[0])
c[1] = caller->address_space->find_capability (caller->arch.a1, &copy[1])
c[2] = caller->address_space->find_capability (caller->arch.a2, &copy[2])
c[3] = caller->address_space->find_capability (caller->arch.a3, &copy[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
View 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
View 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
View 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
View 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
View 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
View 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)