1
0
mirror of git://projects.qi-hardware.com/iris.git synced 2024-11-05 15:49:42 +02:00
iris/mips/init.ccp

316 lines
11 KiB
Plaintext
Raw Normal View History

2009-05-14 21:31:47 +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/init.ccp: mips-specific boot code.
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-18 10:30:27 +03:00
// Also declare things which only work during kernel init.
#define INIT
2009-05-22 23:48:49 +03:00
#define ARCH
2009-06-01 15:26:42 +03:00
#include "../kernel.hh"
#include <elf.h>
2009-05-18 10:30:27 +03:00
2010-01-24 22:34:24 +02:00
#define NUM_SLOTS 8
2010-01-16 17:13:54 +02:00
#define NUM_CAPS 32
2009-08-24 22:02:35 +03:00
2009-05-14 21:31:47 +03:00
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.reset ()
idle.flags = Iris::Thread::RUNNING | Iris::Thread::PRIV
2009-05-14 21:31:47 +03:00
// initialize idle_memory.
idle_memory.prev = NULL
idle_memory.next = NULL
2009-05-25 01:31:35 +03:00
idle_memory.address_space = NULL
idle_memory.refs.reset ()
2009-05-14 21:31:47 +03:00
idle_memory.pages = &idle_page
idle_memory.threads = &idle
idle_memory.memories = NULL
idle_memory.limit = 0
idle_memory.used = 0
2010-01-17 11:01:42 +02:00
idle_memory.arch.directory = (Table **)0x80000000
2009-05-27 15:38:52 +03:00
// Shadow is never used for the idle task.
idle_memory.arch.shadow = NULL
2009-05-19 00:18:23 +03:00
idle_memory.arch.asid = 0
2009-05-14 21:31:47 +03:00
// initialize idle_page
idle_page.prev = NULL
idle_page.next = NULL
idle_page.frame = 0x80000000
idle_page.flags = Iris::Page::PAYING | Iris::Page::FRAME
idle_page.refs.reset ()
2009-05-25 01:31:35 +03:00
idle_page.address_space = NULL
2009-05-23 21:55:31 +03:00
current = &idle
directory = idle_memory.arch.directory
2009-05-14 21:31:47 +03:00
static void init_cp0 ():
2009-09-30 00:48:33 +03:00
// Disable watchpoint interrupts.
cp0_set0 (CP0_WATCH_LO)
// Use the interrupt vector for interrupts; clear interrupt pending flags.
2009-05-19 00:18:23 +03:00
cp0_set (CP0_CAUSE, 1 << 23)
2009-09-30 00:48:33 +03:00
// Disable interrupts and set interrupt vectors to normal.
cp0_set0 (CP0_STATUS)
// Reset exception base address.
cp0_set0 (CP0_EBASE)
// Use non-vectored interrupts.
cp0_set0 (CP0_INT_CTL)
2009-05-14 21:31:47 +03:00
// clear the tlb, hardwire page 0 to 0xffffffff
// and soft-wire it to (0x294 << 20) + (0x290 << 10)
// (for the idle task).
2009-05-19 00:18:23 +03:00
cp0_set (CP0_WIRED, 1)
cp0_set0 (CP0_PAGE_MASK)
cp0_set0 (CP0_ENTRY_LO0)
cp0_set0 (CP0_ENTRY_LO1)
2009-05-14 21:31:47 +03:00
// Get number of tlb entries (is 31).
2009-05-22 23:48:49 +03:00
unsigned num
cp0_get (CP0_CONFIG1, num)
2009-05-14 21:31:47 +03:00
num >>= 25
num &= 0x3f
// Clear the tlb.
2009-05-22 23:48:49 +03:00
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)
2009-05-19 00:18:23 +03:00
cp0_set (CP0_INDEX, i)
2009-05-14 21:31:47 +03:00
// write the data.
__asm__ volatile ("tlbwi")
// Fill the upper page in kseg3.
2009-05-22 23:48:49 +03:00
cp0_set (CP0_ENTRY_HI, 0xffffe000)
2009-05-19 00:18:23 +03:00
cp0_set (CP0_ENTRY_LO0, 0x1d)
cp0_set (CP0_ENTRY_LO1, 0x1f)
cp0_set0 (CP0_INDEX)
2009-05-14 21:31:47 +03:00
__asm__ volatile ("tlbwi")
// Fill the idle task's page in useg. Set it to non-cachable.
2009-05-27 15:38:52 +03:00
// 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)
2009-05-19 00:18:23 +03:00
cp0_set (CP0_ENTRY_LO0, 0x16)
cp0_set (CP0_ENTRY_LO1, 0x14)
2009-05-14 21:31:47 +03:00
__asm__ volatile ("tlbwr")
// Allow eret to be used to jump to the idle task.
2009-05-27 15:38:52 +03:00
cp0_set (CP0_EPC, (idle_entry_hi & PAGE_MASK) | 0x280)
2009-05-23 21:55:31 +03:00
// Wait with initializing the status register until the last moment, so that
// exceptions in the bootup code will fill EPC and friends.
2009-05-18 10:30:27 +03:00
static void init_threads ():
kThread *previous = NULL
2009-05-23 21:55:31 +03:00
first_scheduled = NULL
2009-07-20 01:23:45 +03:00
first_alarm = NULL
kReceiver *init_receiver = NULL
2009-05-18 10:30:27 +03:00
for unsigned i = 0; i < NUM_THREADS; ++i:
2010-01-24 22:34:24 +02:00
kdebug ("Starting thread ")
kdebug_num (i, 2)
kdebug ("\n")
kMemory *mem = top_memory.alloc_memory ()
2009-07-20 01:23:45 +03:00
assert (mem)
2009-08-24 22:02:35 +03:00
kThread *thread = mem->alloc_thread (NUM_SLOTS)
2009-12-26 15:17:06 +02:00
#ifndef NDEBUG
thread->id = i
#endif
2009-05-22 23:48:49 +03:00
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")
2009-07-24 15:25:53 +03:00
return
2009-05-22 23:48:49 +03:00
if header->e_ident[EI_CLASS] != ELFCLASS32:
panic (i * 0x1000 + EI_CLASS, "invalid ELF class")
2009-07-24 15:25:53 +03:00
return
2009-05-22 23:48:49 +03:00
if header->e_ident[EI_DATA] != ELFDATA2LSB:
panic (i * 0x1000 + EI_DATA, "invalid ELF data")
2009-07-24 15:25:53 +03:00
return
2009-05-22 23:48:49 +03:00
if header->e_ident[EI_VERSION] != EV_CURRENT:
panic (i * 0x1000 + EI_VERSION, "invalid ELF version")
2009-07-24 15:25:53 +03:00
return
2009-05-22 23:48:49 +03:00
if header->e_type != ET_EXEC:
panic (i * 0x1000 + 0x10, "invalid ELF type")
2009-07-24 15:25:53 +03:00
return
2009-05-23 21:55:31 +03:00
if header->e_machine != EM_MIPS_RS3_LE && header->e_machine != EM_MIPS:
panic (i * 0x1000 + 0x12, "invalid ELF machine")
2009-07-24 15:25:53 +03:00
return
2009-05-22 23:48:49 +03:00
thread->pc = header->e_entry
thread->sp = 0x80000000
kPage **used = (kPage **)mem->zalloc ()
2009-05-22 23:48:49 +03:00
for unsigned section = 0; section < header->e_shnum; ++section:
Elf32_Shdr *shdr = (Elf32_Shdr *)(thread_start[i] + header->e_shoff + section * header->e_shentsize)
2009-07-20 01:23:45 +03:00
if ~shdr->sh_flags & SHF_ALLOC:
2009-05-22 23:48:49 +03:00
continue
bool readonly = !(shdr->sh_flags & SHF_WRITE)
2009-05-22 23:48:49 +03:00
//bool executable = shdr->sh_flags & SHF_EXEC_INSTR
if shdr->sh_type != SHT_NOBITS:
2009-07-20 01:23:45 +03:00
unsigned file_offset = shdr->sh_offset >> PAGE_BITS
2009-12-26 15:17:06 +02:00
if (file_offset + ((shdr->sh_size + PAGE_SIZE - 1) >> PAGE_BITS)) >= (PAGE_SIZE >> 2):
2009-07-21 13:17:52 +03:00
panic (0x87446809, "initial thread too large")
2009-07-24 15:25:53 +03:00
return
2009-07-20 01:23:45 +03:00
for unsigned p = (shdr->sh_addr & PAGE_MASK); p < shdr->sh_addr + shdr->sh_size; p += PAGE_SIZE:
unsigned section_offset = (p - (shdr->sh_addr & PAGE_MASK)) >> PAGE_BITS
unsigned idx = file_offset + section_offset
2010-01-17 11:01:42 +02:00
kPage *page = mem->get_mapping (p)
2009-12-26 15:17:06 +02:00
if page:
2010-01-17 11:01:42 +02:00
if page->frame != thread_start[i] + (idx << PAGE_BITS):
2009-12-26 15:17:06 +02:00
panic (0, "different pages mapped to one address in intitial file")
return
continue
2010-01-17 11:01:42 +02:00
page = mem->alloc_page ()
page->frame = thread_start[i] + (idx << PAGE_BITS)
page->flags = Iris::Page::PAYING | Iris::Page::FRAME
if used[idx]:
page->share_next = used[idx]
used[idx]->share_prev = page
used[idx]->flags |= Iris::Page::SHARED
used[idx] = page
page->flags |= Iris::Page::SHARED
else:
used[idx] = page
2010-01-17 11:01:42 +02:00
if readonly:
page->flags |= Iris::Page::MAPPED_READONLY
2010-01-17 11:01:42 +02:00
if !mem->map (page, p):
2009-05-22 23:48:49 +03:00
panic (0x22446688, "unable to map initial page")
2009-07-24 15:25:53 +03:00
return
2010-01-24 22:34:24 +02:00
//kdebug ("mapped page ")
2010-01-17 11:01:42 +02:00
//if readonly:
2010-01-24 22:34:24 +02:00
// kdebug ("as readonly ")
//kdebug ("at address ")
//kdebug_num (p)
//kdebug (" for ")
//kdebug_num (i, 1)
//kdebug ('\n')
2009-05-22 23:48:49 +03:00
else:
if readonly:
2009-07-20 01:23:45 +03:00
panic (0x33399993, "unwritable bss section")
2009-07-24 15:25:53 +03:00
return
2009-07-20 01:23:45 +03:00
for unsigned p = (shdr->sh_addr & PAGE_MASK); p < shdr->sh_addr + shdr->sh_size; p += PAGE_SIZE:
2010-01-17 11:01:42 +02:00
kPage *page = mem->get_mapping (p)
2009-05-22 23:48:49 +03:00
if !page:
page = mem->alloc_page ()
if !page:
panic (0x00220022, "out of memory")
2009-07-24 15:25:53 +03:00
return
page->frame = mem->zalloc ()
if !page->frame:
2009-07-20 01:23:45 +03:00
panic (0x02220022, "out of memory");
2009-07-24 15:25:53 +03:00
return
page->flags = Iris::Page::PAYING | Iris::Page::FRAME
2009-08-24 22:02:35 +03:00
if !mem->map (page, p):
2009-05-22 23:48:49 +03:00
panic (0x33557799, "unable to map initial bss page")
2009-07-24 15:25:53 +03:00
return
2010-01-24 22:34:24 +02:00
kdebug ("mapped bss page at address ")
kdebug_num (p)
kdebug (" for ")
kdebug_num (i, 1)
kdebug ('\n')
2009-05-22 23:48:49 +03:00
else:
if page->flags & Iris::Page::MAPPED_READONLY:
2009-05-24 13:22:22 +03:00
panic (0x20203030, "bss section starts on read-only page")
2009-07-24 15:25:53 +03:00
return
2009-07-20 01:23:45 +03:00
for unsigned a = p; a < ((p + PAGE_SIZE) & PAGE_MASK); a += 4:
2009-05-22 23:48:49 +03:00
if a >= shdr->sh_addr + shdr->sh_size:
break
if a < shdr->sh_addr:
continue
((unsigned *)page->frame)[(a & ~PAGE_MASK) >> 2] = 0
2009-05-22 23:48:49 +03:00
for unsigned p = 0; p <= ((thread_start[i + 1] - thread_start[i] - 1) >> PAGE_BITS); ++p:
++top_memory.limit
2010-01-17 11:01:42 +02:00
if used[p]:
mem->use ()
continue
2010-01-24 22:34:24 +02:00
//kdebug ("freeing unused page from initial file\n")
2009-05-24 13:22:22 +03:00
top_memory.pfree (thread_start[i] + (p << PAGE_BITS))
2010-01-24 22:34:24 +02:00
//kdebug ("freeing unused page list\n")
2010-01-17 11:01:42 +02:00
mem->pfree ((unsigned)used)
kPage *stackpage = mem->alloc_page ()
stackpage->frame = mem->zalloc ()
stackpage->flags = Iris::Page::PAYING | Iris::Page::FRAME
2009-08-24 22:02:35 +03:00
if !stackpage || !mem->map (stackpage, 0x7ffff000):
2009-05-22 23:48:49 +03:00
panic (0x13151719, "unable to map initial stack page")
2009-07-24 15:25:53 +03:00
return
2009-09-08 22:20:30 +03:00
thread->slot[0].caps = mem->alloc_caps (NUM_CAPS)
thread->slot[0].caps->first_slot.thread = thread
thread->slot[0].caps->first_slot.index = 0
2009-08-24 22:02:35 +03:00
thread->arch.a[0] = NUM_SLOTS
thread->arch.a[1] = NUM_CAPS
kReceiver *recv = mem->alloc_receiver ()
2009-06-08 14:46:13 +03:00
recv->owner = thread
thread->receivers = recv
thread->slot[0].caps->set (__caps_num, (kReceiverP)(CAPTYPE_CAPS | CAP_MASTER), Iris::Num ((unsigned)thread->slot[0].caps), kCapRef (), &thread->slot[0].caps->refs)
thread->slot[0].caps->set (__receiver_num, (kReceiverP)(CAPTYPE_RECEIVER | CAP_MASTER), Iris::Num ((unsigned)recv), kCapRef (), &recv->refs)
thread->slot[0].caps->set (__thread_num, (kReceiverP)(CAPTYPE_THREAD | CAP_MASTER), Iris::Num ((unsigned)thread), kCapRef (), &thread->refs)
thread->slot[0].caps->set (__memory_num, (kReceiverP)(CAPTYPE_MEMORY | CAP_MASTER), Iris::Num ((unsigned)mem), kCapRef (), &mem->refs)
thread->slot[0].caps->set (__call_num, (kReceiverP)(CAPTYPE_RECEIVER | Iris::Receiver::CALL), Iris::Num ((unsigned)recv), kCapRef (), &recv->refs)
thread->flags = Iris::Thread::RUNNING | Iris::Thread::PRIV
if !i:
2009-05-23 21:55:31 +03:00
first_scheduled = thread
init_receiver = recv
else:
2009-09-08 22:20:30 +03:00
thread->slot[0].caps->set (__parent_num, init_receiver, i, kCapRef (), &init_receiver->capabilities)
previous->schedule_next = thread
thread->schedule_prev = previous
2009-07-23 13:06:32 +03:00
thread->schedule_next = NULL
2009-05-23 21:55:31 +03:00
previous = thread
2009-05-14 21:31:47 +03:00
2009-05-22 23:48:49 +03:00
// Initialize the kernel, finish by falling into the idle task.
void init (unsigned mem):
2009-12-18 10:00:38 +02:00
// Initialize board-specific things.
board_init ()
2009-08-24 22:02:35 +03:00
must_wait = false
2009-05-14 21:31:47 +03:00
// Initialize kernel variables to empty.
unsigned count = init_memory (mem)
2010-01-24 22:34:24 +02:00
// Set up invoke system.
reply_caps.init (1)
replied_caps.init (1)
2009-05-14 21:31:47 +03:00
// 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
2009-05-25 01:31:35 +03:00
top_memory.address_space = NULL
top_memory.refs.reset ()
2009-05-14 21:31:47 +03:00
top_memory.pages = NULL
top_memory.threads = NULL
top_memory.memories = NULL
top_memory.limit = count
top_memory.used = 0
2009-05-19 00:18:23 +03:00
top_memory.arch.directory = NULL
top_memory.arch.asid = 0
2009-05-24 13:22:22 +03:00
2009-06-26 00:09:42 +03:00
// Record all asids as unused.
2009-05-24 13:22:22 +03:00
for unsigned i = 0; i < 63; ++i:
asids[i] = i + 1
asids[63] = 0
2009-06-26 00:09:42 +03:00
// Set up initial threads.
2009-05-18 10:30:27 +03:00
init_threads ()
2009-05-14 21:31:47 +03:00
2009-06-26 00:09:42 +03:00
// Unset all interrupt handlers.
for unsigned i = 0; i < 32; ++i:
arch_interrupt_receiver[i] = NULL
2009-12-18 10:00:38 +02:00
// Enable timer interrupts.
2009-09-30 00:48:33 +03:00
intc_unmask_irq (TIMER_INTERRUPT)
2009-06-26 00:09:42 +03:00
// Say we're handling an exception. Since we're going to enter the idle task, allow access to cp0.
// All interrupts enter the CPU through the interrupt controller at IP2, so enable that.
2009-07-04 17:21:28 +03:00
cp0_set (CP0_STATUS, 0x1000ff13)
2009-05-23 21:55:31 +03:00
2009-05-14 21:31:47 +03:00
// Done; return to user space (the idle task).
__asm__ volatile ("eret")