Root/
| 1 | #pypp 0 |
| 2 | // vim: set filetype=cpp : // |
| 3 | // Iris: micro-kernel for a capability-based operating system. |
| 4 | // mips/init.ccp: mips-specific boot code. |
| 5 | // Copyright 2009 Bas Wijnen <wijnen@debian.org> |
| 6 | // |
| 7 | // This program is free software: you can redistribute it and/or modify |
| 8 | // it under the terms of the GNU General Public License as published by |
| 9 | // the Free Software Foundation, either version 3 of the License, or |
| 10 | // (at your option) any later version. |
| 11 | // |
| 12 | // This program is distributed in the hope that it will be useful, |
| 13 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | // GNU General Public License for more details. |
| 16 | // |
| 17 | // You should have received a copy of the GNU General Public License |
| 18 | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 19 | |
| 20 | // Also declare things which only work during kernel init. |
| 21 | #define INIT |
| 22 | #define ARCH |
| 23 | #include "kernel.hh" |
| 24 | #include <elf.h> |
| 25 | |
| 26 | #define NUM_SLOTS 8 |
| 27 | #define NUM_CAPS 32 |
| 28 | |
| 29 | static void init_idle (): |
| 30 | // initialize idle task as if it is currently running. |
| 31 | idle.prev = NULL |
| 32 | idle.next = NULL |
| 33 | idle.schedule_prev = NULL |
| 34 | idle.schedule_next = NULL |
| 35 | idle.address_space = &idle_memory |
| 36 | idle.refs.reset () |
| 37 | idle.flags = Iris::Thread::RUNNING | Iris::Thread::PRIV |
| 38 | // initialize idle_memory. |
| 39 | idle_memory.prev = NULL |
| 40 | idle_memory.next = NULL |
| 41 | idle_memory.address_space = NULL |
| 42 | idle_memory.refs.reset () |
| 43 | idle_memory.pages = &idle_page |
| 44 | idle_memory.threads = &idle |
| 45 | idle_memory.memories = NULL |
| 46 | idle_memory.limit = 0 |
| 47 | idle_memory.used = 0 |
| 48 | idle_memory.arch.directory = (Table **)0x80000000 |
| 49 | // Shadow is never used for the idle task. |
| 50 | idle_memory.arch.shadow = NULL |
| 51 | idle_memory.arch.asid = 0 |
| 52 | // initialize idle_page |
| 53 | idle_page.prev = NULL |
| 54 | idle_page.next = NULL |
| 55 | idle_page.frame = 0x80000000 |
| 56 | idle_page.flags = Iris::Page::PAYING | Iris::Page::FRAME |
| 57 | idle_page.refs.reset () |
| 58 | idle_page.address_space = NULL |
| 59 | current = &idle |
| 60 | directory = idle_memory.arch.directory |
| 61 | kdebug ("idle thread is ") |
| 62 | kdebug_num ((unsigned)&idle) |
| 63 | kdebug ("\n") |
| 64 | |
| 65 | static void init_cp0 (): |
| 66 | // Disable watchpoint interrupts. |
| 67 | cp0_set0 (CP0_WATCH_LO) |
| 68 | // Use the interrupt vector for interrupts; clear interrupt pending flags. |
| 69 | cp0_set (CP0_CAUSE, 1 << 23) |
| 70 | // Disable interrupts and set interrupt vectors to normal. |
| 71 | cp0_set0 (CP0_STATUS) |
| 72 | // Reset exception base address. |
| 73 | cp0_set0 (CP0_EBASE) |
| 74 | // Use non-vectored interrupts. |
| 75 | cp0_set0 (CP0_INT_CTL) |
| 76 | |
| 77 | // Get number of tlb entries (is 31). |
| 78 | unsigned num |
| 79 | cp0_get (CP0_CONFIG1, num) |
| 80 | num >>= 25 |
| 81 | num &= 0x3f |
| 82 | // Clear the tlb. |
| 83 | cp0_set (CP0_WIRED, 0) |
| 84 | cp0_set0 (CP0_PAGE_MASK) |
| 85 | cp0_set0 (CP0_ENTRY_LO0) |
| 86 | cp0_set0 (CP0_ENTRY_LO1) |
| 87 | for unsigned i = 0; i <= num; ++i: |
| 88 | // with asid 0, no page faults will be triggered, so it's safe to map memory anywhere. |
| 89 | // And it's set to invalid anyway. |
| 90 | cp0_set (CP0_ENTRY_HI, 0x2000 * (i + 1)) |
| 91 | cp0_set (CP0_INDEX, i) |
| 92 | // write the data. |
| 93 | __asm__ volatile ("tlbwi") |
| 94 | // Fill the idle task's page in useg. Set it to non-cachable. |
| 95 | // its directory entry is at 1fc, so it's number 7f (0fe00000). |
| 96 | // its table entry is at 1f8, so it's number 7e (0007e000). |
| 97 | // its address is 280 (00000280), used below for EPC. |
| 98 | unsigned const idle_entry_hi = 0x0fe7e000 |
| 99 | cp0_set (CP0_ENTRY_HI, idle_entry_hi) |
| 100 | cp0_set (CP0_ENTRY_LO0, 0x00000012) |
| 101 | // The following value doesn't make much sense, but it is what is |
| 102 | // filled in at run-time. So for robustness it's used here as well. |
| 103 | cp0_set (CP0_ENTRY_LO1, 0x80000000) |
| 104 | __asm__ volatile ("tlbwr") |
| 105 | // Allow eret to be used to jump to the idle task. |
| 106 | cp0_set (CP0_EPC, (idle_entry_hi & PAGE_MASK) | 0x280) |
| 107 | // Wait with initializing the status register until the last moment, so that |
| 108 | // exceptions in the bootup code will fill EPC and friends. |
| 109 | |
| 110 | static void init_threads (): |
| 111 | kThread *previous = NULL |
| 112 | first_scheduled = NULL |
| 113 | first_alarm = NULL |
| 114 | kReceiver *init_receiver = NULL |
| 115 | unsigned i = 0 |
| 116 | while thread_start[i + 1] != 0: |
| 117 | kMemory *mem = top_memory.alloc_memory () |
| 118 | assert (mem) |
| 119 | kThread *thread = mem->alloc_thread (NUM_SLOTS) |
| 120 | kdebug ("Starting thread ") |
| 121 | kdebug_num (i, 2) |
| 122 | kdebug (" at ") |
| 123 | kdebug_num ((unsigned)thread) |
| 124 | kdebug ("\n") |
| 125 | #ifndef NDEBUG |
| 126 | thread->id = i |
| 127 | #endif |
| 128 | Elf32_Ehdr *header = (Elf32_Ehdr *)thread_start[i] |
| 129 | for unsigned j = 0; j < SELFMAG; ++j: |
| 130 | if header->e_ident[j] != ELFMAG[j]: |
| 131 | panic (i * 0x1000 + j, "invalid ELF magic") |
| 132 | return |
| 133 | if header->e_ident[EI_CLASS] != ELFCLASS32: |
| 134 | panic (i * 0x1000 + EI_CLASS, "invalid ELF class") |
| 135 | return |
| 136 | if header->e_ident[EI_DATA] != ELFDATA2LSB: |
| 137 | panic (i * 0x1000 + EI_DATA, "invalid ELF data") |
| 138 | return |
| 139 | if header->e_ident[EI_VERSION] != EV_CURRENT: |
| 140 | panic (i * 0x1000 + EI_VERSION, "invalid ELF version") |
| 141 | return |
| 142 | if header->e_type != ET_EXEC: |
| 143 | panic (i * 0x1000 + 0x10, "invalid ELF type") |
| 144 | return |
| 145 | if header->e_machine != EM_MIPS_RS3_LE && header->e_machine != EM_MIPS: |
| 146 | panic (i * 0x1000 + 0x12, "invalid ELF machine") |
| 147 | return |
| 148 | thread->pc = header->e_entry |
| 149 | thread->sp = 0x80000000 |
| 150 | kPage **used = (kPage **)mem->zalloc () |
| 151 | for unsigned section = 0; section < header->e_shnum; ++section: |
| 152 | Elf32_Shdr *shdr = (Elf32_Shdr *)(thread_start[i] + header->e_shoff + section * header->e_shentsize) |
| 153 | if ~shdr->sh_flags & SHF_ALLOC: |
| 154 | continue |
| 155 | bool readonly = !(shdr->sh_flags & SHF_WRITE) |
| 156 | //bool executable = shdr->sh_flags & SHF_EXEC_INSTR |
| 157 | if shdr->sh_type != SHT_NOBITS: |
| 158 | unsigned file_offset = shdr->sh_offset >> PAGE_BITS |
| 159 | if (file_offset + ((shdr->sh_size + PAGE_SIZE - 1) >> PAGE_BITS)) >= (PAGE_SIZE >> 2): |
| 160 | panic (0x87446809, "initial thread too large") |
| 161 | return |
| 162 | for unsigned p = (shdr->sh_addr & PAGE_MASK); p < shdr->sh_addr + shdr->sh_size; p += PAGE_SIZE: |
| 163 | unsigned section_offset = (p - (shdr->sh_addr & PAGE_MASK)) >> PAGE_BITS |
| 164 | unsigned idx = file_offset + section_offset |
| 165 | kPage *page = mem->get_mapping (p) |
| 166 | if page: |
| 167 | if page->frame != thread_start[i] + (idx << PAGE_BITS): |
| 168 | panic (0, "different pages mapped to one address in intitial file") |
| 169 | return |
| 170 | continue |
| 171 | page = mem->alloc_page () |
| 172 | page->frame = thread_start[i] + (idx << PAGE_BITS) |
| 173 | page->flags = Iris::Page::PAYING | Iris::Page::FRAME |
| 174 | if used[idx]: |
| 175 | page->share_next = used[idx] |
| 176 | used[idx]->share_prev = page |
| 177 | used[idx]->flags |= Iris::Page::SHARED |
| 178 | used[idx] = page |
| 179 | page->flags |= Iris::Page::SHARED |
| 180 | else: |
| 181 | used[idx] = page |
| 182 | if readonly: |
| 183 | page->flags |= Iris::Page::MAPPED_READONLY |
| 184 | if !mem->map (page, p): |
| 185 | panic (0x22446688, "unable to map initial page") |
| 186 | return |
| 187 | //kdebug ("mapped page ") |
| 188 | //if readonly: |
| 189 | // kdebug ("as readonly ") |
| 190 | //kdebug ("at address ") |
| 191 | //kdebug_num (p) |
| 192 | //kdebug (" for ") |
| 193 | //kdebug_num (i, 1) |
| 194 | //kdebug ('\n') |
| 195 | else: |
| 196 | if readonly: |
| 197 | panic (0x33399993, "unwritable bss section") |
| 198 | return |
| 199 | for unsigned p = (shdr->sh_addr & PAGE_MASK); p < shdr->sh_addr + shdr->sh_size; p += PAGE_SIZE: |
| 200 | kPage *page = mem->get_mapping (p) |
| 201 | if !page: |
| 202 | page = mem->alloc_page () |
| 203 | if !page: |
| 204 | panic (0x00220022, "out of memory") |
| 205 | return |
| 206 | page->frame = mem->zalloc () |
| 207 | if !page->frame: |
| 208 | panic (0x02220022, "out of memory"); |
| 209 | return |
| 210 | page->flags = Iris::Page::PAYING | Iris::Page::FRAME |
| 211 | if !mem->map (page, p): |
| 212 | panic (0x33557799, "unable to map initial bss page") |
| 213 | return |
| 214 | kdebug ("mapped bss page at address ") |
| 215 | kdebug_num (p) |
| 216 | kdebug (" for ") |
| 217 | kdebug_num (i, 1) |
| 218 | kdebug ('\n') |
| 219 | else: |
| 220 | if page->flags & Iris::Page::MAPPED_READONLY: |
| 221 | panic (0x20203030, "bss section starts on read-only page") |
| 222 | return |
| 223 | for unsigned a = p; a < ((p + PAGE_SIZE) & PAGE_MASK); a += 4: |
| 224 | if a >= shdr->sh_addr + shdr->sh_size: |
| 225 | break |
| 226 | if a < shdr->sh_addr: |
| 227 | continue |
| 228 | ((unsigned *)page->frame)[(a & ~PAGE_MASK) >> 2] = 0 |
| 229 | for unsigned p = 0; p <= ((thread_start[i + 1] - thread_start[i] - 1) >> PAGE_BITS); ++p: |
| 230 | ++top_memory.limit |
| 231 | if used[p]: |
| 232 | mem->use () |
| 233 | continue |
| 234 | //kdebug ("freeing unused page from initial file\n") |
| 235 | top_memory.pfree (thread_start[i] + (p << PAGE_BITS)) |
| 236 | //kdebug ("freeing unused page list\n") |
| 237 | mem->pfree ((unsigned)used) |
| 238 | kPage *stackpage = mem->alloc_page () |
| 239 | stackpage->frame = mem->zalloc () |
| 240 | stackpage->flags = Iris::Page::PAYING | Iris::Page::FRAME |
| 241 | if !stackpage || !mem->map (stackpage, 0x7ffff000): |
| 242 | panic (0x13151719, "unable to map initial stack page") |
| 243 | return |
| 244 | thread->slot[0].caps = mem->alloc_caps (NUM_CAPS) |
| 245 | thread->slot[0].caps->first_slot.thread = thread |
| 246 | thread->slot[0].caps->first_slot.index = 0 |
| 247 | thread->arch.a[0] = NUM_SLOTS |
| 248 | thread->arch.a[1] = NUM_CAPS |
| 249 | kReceiver *recv = mem->alloc_receiver () |
| 250 | recv->owner = thread |
| 251 | thread->receivers = recv |
| 252 | 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) |
| 253 | thread->slot[0].caps->set (__receiver_num, (kReceiverP)(CAPTYPE_RECEIVER | CAP_MASTER), Iris::Num ((unsigned)recv), kCapRef (), &recv->refs) |
| 254 | thread->slot[0].caps->set (__thread_num, (kReceiverP)(CAPTYPE_THREAD | CAP_MASTER), Iris::Num ((unsigned)thread), kCapRef (), &thread->refs) |
| 255 | thread->slot[0].caps->set (__memory_num, (kReceiverP)(CAPTYPE_MEMORY | CAP_MASTER), Iris::Num ((unsigned)mem), kCapRef (), &mem->refs) |
| 256 | thread->slot[0].caps->set (__call_num, (kReceiverP)(CAPTYPE_RECEIVER | Iris::Receiver::CALL), Iris::Num ((unsigned)recv), kCapRef (), &recv->refs) |
| 257 | thread->flags = Iris::Thread::RUNNING | Iris::Thread::PRIV |
| 258 | if !i: |
| 259 | first_scheduled = thread |
| 260 | init_receiver = recv |
| 261 | else: |
| 262 | thread->slot[0].caps->set (__parent_num, init_receiver, i, kCapRef (), &init_receiver->capabilities) |
| 263 | previous->schedule_next = thread |
| 264 | thread->schedule_prev = previous |
| 265 | thread->schedule_next = NULL |
| 266 | previous = thread |
| 267 | ++i |
| 268 | |
| 269 | // Initialize the kernel, finish by falling into the idle task. |
| 270 | void init (unsigned mem): |
| 271 | // Initialize board-specific things. |
| 272 | board_init () |
| 273 | must_wait = false |
| 274 | // Initialize kernel variables to empty. |
| 275 | unsigned count = init_memory (mem) |
| 276 | // Set up invoke system. |
| 277 | reply_caps.init (1) |
| 278 | replied_caps.init (1) |
| 279 | // initialize system control coprocessor. |
| 280 | init_cp0 () |
| 281 | // initialize everything about the idle task. |
| 282 | init_idle () |
| 283 | // initialize top_memory. |
| 284 | top_memory.prev = NULL |
| 285 | top_memory.next = NULL |
| 286 | top_memory.address_space = NULL |
| 287 | top_memory.refs.reset () |
| 288 | top_memory.pages = NULL |
| 289 | top_memory.threads = NULL |
| 290 | top_memory.memories = NULL |
| 291 | top_memory.limit = count |
| 292 | top_memory.used = 0 |
| 293 | top_memory.arch.directory = NULL |
| 294 | top_memory.arch.asid = 0 |
| 295 | |
| 296 | // Record all asids as unused. |
| 297 | for unsigned i = 0; i < 63; ++i: |
| 298 | asids[i] = i + 1 |
| 299 | asids[63] = 0 |
| 300 | |
| 301 | // Set up initial threads. |
| 302 | init_threads () |
| 303 | |
| 304 | // Unset all interrupt handlers. |
| 305 | for unsigned i = 0; i < 32; ++i: |
| 306 | arch_interrupt_receiver[i] = NULL |
| 307 | |
| 308 | // Enable timer interrupts. |
| 309 | intc_unmask_irq (TIMER_INTERRUPT) |
| 310 | |
| 311 | // Say we're handling an exception. Since we're going to enter the idle task, allow access to cp0. |
| 312 | // All interrupts enter the CPU through the interrupt controller at IP2, so enable that. |
| 313 | cp0_set (CP0_STATUS, 0x10000413) |
| 314 | |
| 315 | for int a = 0; a < 0x300; a += 0x80: |
| 316 | kdebug("addr ") |
| 317 | kdebug_num(a) |
| 318 | kdebug(":") |
| 319 | for int b = 0; b < 0x10; b += 4: |
| 320 | kdebug(" ") |
| 321 | kdebug_num(*(unsigned *)(0x80000000 | (a + b))) |
| 322 | kdebug('\n') |
| 323 | |
| 324 | kdebug ("entering idle task\n") |
| 325 | // Done; return to user space (the idle task). |
| 326 | __asm__ volatile ("eret") |
| 327 |
Branches:
master
