paul@156 | 1 | /* |
paul@156 | 2 | * Task handling. |
paul@156 | 3 | * |
paul@156 | 4 | * Copyright (C) 2015, 2016 Paul Boddie <paul@boddie.org.uk> |
paul@156 | 5 | * |
paul@156 | 6 | * This program is free software: you can redistribute it and/or modify |
paul@156 | 7 | * it under the terms of the GNU General Public License as published by |
paul@156 | 8 | * the Free Software Foundation, either version 3 of the License, or |
paul@156 | 9 | * (at your option) any later version. |
paul@156 | 10 | * |
paul@156 | 11 | * This program is distributed in the hope that it will be useful, |
paul@156 | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
paul@156 | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
paul@156 | 14 | * GNU General Public License for more details. |
paul@156 | 15 | * |
paul@156 | 16 | * You should have received a copy of the GNU General Public License |
paul@156 | 17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
paul@156 | 18 | */ |
paul@156 | 19 | |
paul@156 | 20 | #include "cpu.h" |
paul@186 | 21 | #include "cpu_op.h" |
paul@194 | 22 | #include "memory.h" |
paul@193 | 23 | #include "mips.h" |
paul@156 | 24 | #include "paging.h" |
paul@159 | 25 | #include "tasks.h" |
paul@202 | 26 | #include "tasks/manifest.h" |
paul@156 | 27 | |
paul@156 | 28 | /* Task tables and data. */ |
paul@156 | 29 | |
paul@156 | 30 | static u32 stack_pointers[max_tasks]; |
paul@156 | 31 | static u32 registers[max_tasks][32]; |
paul@156 | 32 | |
paul@156 | 33 | u8 current_task; |
paul@156 | 34 | u32 *current_stack_pointer; |
paul@156 | 35 | u32 *current_registers; |
paul@156 | 36 | |
paul@179 | 37 | /* A reference to locations for the symbol tables. */ |
paul@179 | 38 | |
paul@179 | 39 | extern u32 _got_start, _got_copy_start, _got_copy_end; |
paul@156 | 40 | |
paul@189 | 41 | /* A reference to the start of the payload and end of memory locations. */ |
paul@179 | 42 | |
paul@189 | 43 | extern u32 _payload_start, _memory_end; |
paul@156 | 44 | |
paul@156 | 45 | /* Task management functions. */ |
paul@156 | 46 | |
paul@156 | 47 | void init_tasks() |
paul@156 | 48 | { |
paul@166 | 49 | current_task = 1; |
paul@160 | 50 | init_task(); |
paul@160 | 51 | } |
paul@160 | 52 | |
paul@160 | 53 | void init_task() |
paul@160 | 54 | { |
paul@156 | 55 | current_stack_pointer = &stack_pointers[current_task]; |
paul@156 | 56 | current_registers = registers[current_task]; |
paul@156 | 57 | } |
paul@156 | 58 | |
paul@202 | 59 | void start_tasks() |
paul@202 | 60 | { |
paul@202 | 61 | void (**starter)(unsigned short); |
paul@202 | 62 | int i; |
paul@202 | 63 | |
paul@202 | 64 | for (i = 1, starter = initial_tasks; *starter; i++, starter++) |
paul@202 | 65 | (*starter)(i); |
paul@202 | 66 | } |
paul@202 | 67 | |
paul@159 | 68 | void start_task(unsigned short task, void (*function)(), u32 args[], u8 nargs) |
paul@156 | 69 | { |
paul@179 | 70 | u32 virtual, physical, address; |
paul@156 | 71 | |
paul@156 | 72 | /* |
paul@156 | 73 | Each task employs a stack at a multiple of the given start address in |
paul@193 | 74 | physical memory, but at the same address in virtual memory. Task zero |
paul@193 | 75 | is never started. |
paul@156 | 76 | */ |
paul@156 | 77 | |
paul@204 | 78 | virtual = TASK_STACK_TOP; |
paul@204 | 79 | |
paul@204 | 80 | /* NOTE: Should allocate pages generally, not according to this simple calculation. */ |
paul@204 | 81 | |
paul@204 | 82 | physical = TASK_STACK_PHYSICAL - TASK_STACK_SIZE * task; |
paul@156 | 83 | |
paul@200 | 84 | init_page_table(STAGE2_PAGE_TABLE, |
paul@200 | 85 | previous_page(virtual, STAGE2_PAGESIZE), |
paul@200 | 86 | previous_page(physical, STAGE2_PAGESIZE), |
paul@200 | 87 | STAGE2_PAGESIZE, TLB_WRITE, task); |
paul@156 | 88 | |
paul@156 | 89 | /* |
paul@156 | 90 | Subtract from the stack pointer to prevent the called function from |
paul@156 | 91 | reaching into unmapped memory. |
paul@156 | 92 | */ |
paul@156 | 93 | |
paul@156 | 94 | stack_pointers[task] = virtual - 12; |
paul@156 | 95 | |
paul@156 | 96 | /* |
paul@156 | 97 | Set the registers for the new task, initialising the global pointer and |
paul@156 | 98 | return address. |
paul@156 | 99 | */ |
paul@156 | 100 | |
paul@173 | 101 | init_registers(registers[task], (u32) &_got_copy_start, function, args, nargs); |
paul@179 | 102 | |
paul@179 | 103 | /* Map the global object table for the task. */ |
paul@179 | 104 | |
paul@200 | 105 | init_page_table(STAGE2_PAGE_TABLE, |
paul@200 | 106 | user_address((u32) &_got_start), |
paul@200 | 107 | user_address((u32) &_got_copy_start), |
paul@200 | 108 | STAGE2_PAGESIZE, TLB_READ, task); |
paul@179 | 109 | |
paul@200 | 110 | /* Map all shared pages for the task. First, the read-only code region. */ |
paul@179 | 111 | |
paul@200 | 112 | for (address = (u32) &_payload_start; |
paul@200 | 113 | address < (u32) &_got_start; |
paul@200 | 114 | address = next_page(address, STAGE2_PAGESIZE)) |
paul@179 | 115 | { |
paul@200 | 116 | init_page_table(STAGE2_PAGE_TABLE, |
paul@200 | 117 | user_address(address), |
paul@200 | 118 | user_address(address), |
paul@200 | 119 | STAGE2_PAGESIZE, TLB_READ, task); |
paul@179 | 120 | } |
paul@179 | 121 | |
paul@200 | 122 | /* Make the pages after the code writable. */ |
paul@200 | 123 | |
paul@200 | 124 | for (address = next_page((u32) &_got_copy_end, STAGE2_PAGESIZE); |
paul@200 | 125 | address < (u32) &_memory_end; |
paul@200 | 126 | address = next_page(address, STAGE2_PAGESIZE)) |
paul@179 | 127 | { |
paul@200 | 128 | init_page_table(STAGE2_PAGE_TABLE, |
paul@200 | 129 | user_address(address), |
paul@200 | 130 | user_address(address), |
paul@200 | 131 | STAGE2_PAGESIZE, TLB_WRITE, task); |
paul@179 | 132 | } |
paul@156 | 133 | } |
paul@156 | 134 | |
paul@165 | 135 | void start_task_now() |
paul@165 | 136 | { |
paul@171 | 137 | invoke_task(current_task, current_registers, current_stack_pointer); |
paul@165 | 138 | } |
paul@165 | 139 | |
paul@156 | 140 | void switch_task() |
paul@156 | 141 | { |
paul@156 | 142 | /* Switch the current task. */ |
paul@156 | 143 | |
paul@156 | 144 | current_task++; |
paul@166 | 145 | if (current_task == max_tasks) current_task = 1; |
paul@160 | 146 | init_task(); |
paul@156 | 147 | } |