1
1
#include "kernel.h"
2
2
#include "common.h"
3
3
4
- extern char __bss [], __bss_end [], __stack_top [], __free_ram [], __free_ram_end [];
4
+ extern char __bss [], __bss_end [], __stack_top [], __free_ram [], __free_ram_end [], __kernel_base [] ;
5
5
6
6
struct process procs [PROCS_MAX ]; // Note: All elements are zero-initialized so the state is PROC_STATE_UNUSED
7
7
@@ -140,6 +140,43 @@ paddr_t alloc_pages(uint32_t const n) {
140
140
return paddr ;
141
141
}
142
142
143
+ // Register the mapping between the virtual address and the physical address in the page table
144
+ void map_page (uint32_t * table1 , uint32_t vaddr , paddr_t paddr , uint32_t flags ) {
145
+ // Sv32 is a two-phase lookup table. It means a tree structure where depth is 3.
146
+ // Depth-1 is VPN[1], depth-2 is VPN[0], depth-3 is the page offset in the 4KiB page.
147
+ //
148
+ // Example of Sv32 page mapping
149
+ // ┌──────────────────────────────────┐
150
+ // │ 8001123 │ Virtual address
151
+ // └──────────────────────────────────┘
152
+ // ┌──────────┬──────────┬────────────┐
153
+ // │0000100000│0000000001│000100100011│ Page table entry
154
+ // └──────────┴──────────┴────────────┘
155
+ // VPN[1] VPN[0] page offset
156
+ // (10 bits) (10 bits) (12 bits)
157
+ // =32 =1 =0x123
158
+
159
+ if (!is_aligned (vaddr , PAGE_SIZE )) {
160
+ PANIC ("unaligned vaddr %x" , vaddr );
161
+ }
162
+
163
+ if (!is_aligned (paddr , PAGE_SIZE )) {
164
+ PANIC ("unaligned paddr %x" , paddr );
165
+ }
166
+
167
+ uint32_t const vpn1 = (vaddr >> (10 + 12 )) & 0x3ff ; // Extract the first page table index
168
+ if ((table1 [vpn1 ] & PAGE_V ) == 0 ) {
169
+ // The second page table does not exist. Create it.
170
+ uint32_t const pt_paddr = alloc_pages (1 );
171
+ table1 [vpn1 ] = ((pt_paddr / PAGE_SIZE ) << 10 ) | PAGE_V ;
172
+ }
173
+
174
+ // Insert the new page entry to the second page table
175
+ uint32_t const vpn0 = (vaddr >> 12 ) & 0x3ff ; // Extract the second page table index
176
+ uint32_t * table0 = (uint32_t * )((table1 [vpn1 ] >> 10 ) * PAGE_SIZE );
177
+ table0 [vpn0 ] = ((paddr / PAGE_SIZE ) << 10 ) | flags | PAGE_V ;
178
+ }
179
+
143
180
__attribute__((naked )) void switch_context (uint32_t * prev_sp , uint32_t * next_sp ) {
144
181
__asm__ __volatile__(
145
182
// Save ra and saved registers on current process's stack
@@ -211,9 +248,17 @@ struct process *create_process(uint32_t const pc) {
211
248
* -- sp = 0 ; // s0
212
249
* -- sp = pc ; // ra
213
250
251
+ // Create kernel page mapping (from __kernel_base to __free_ram_end) so that kernel can access
252
+ // to both static area (e.g. .text) and dynamically allocated area by alloc_pages().
253
+ uint32_t * page_table = (uint32_t * )alloc_pages (1 );
254
+ for (paddr_t paddr = (paddr_t )__kernel_base ; paddr < (paddr_t )__free_ram_end ; paddr += PAGE_SIZE ) {
255
+ map_page (page_table , paddr , paddr , PAGE_R | PAGE_W | PAGE_X );
256
+ }
257
+
214
258
proc -> pid = i + 1 ; // PID starts from 1
215
259
proc -> state = PROC_STATE_RUNNABLE ;
216
260
proc -> sp = (uint32_t )sp ;
261
+ proc -> page_table = page_table ;
217
262
return proc ;
218
263
}
219
264
@@ -236,21 +281,26 @@ void yield(void) {
236
281
return ;
237
282
}
238
283
239
- // Save the next process's stack bottom address to sscratch register for exception handling.
240
284
__asm__ __volatile__(
285
+ // Switch the page table by registering VPN[1] to satp register.
286
+ // The sfence.vma instruction is a memory barrier to
287
+ // - ensure modifying the page table finishes
288
+ // - remove the cache of the page table entry (TLB)
289
+ "sfence.vma\n"
290
+ "csrw satp, %[satp]\n"
291
+ "sfence.vma\n"
292
+ // Save the next process's stack bottom address to sscratch register for exception handling.
241
293
"csrw sscratch, %[sscratch]\n"
242
294
:
243
- : [sscratch ] "r" ((uint32_t )& next -> stack [sizeof (next -> stack )]));
295
+ : [satp ] "r" (SATP_SV32 | ((uint32_t )next -> page_table / PAGE_SIZE )),
296
+ [sscratch ] "r" ((uint32_t )& next -> stack [sizeof (next -> stack )]));
244
297
245
298
// Switch context from the current process to the next runnable process
246
299
struct process * prev = current_proc ;
247
300
current_proc = next ;
248
301
switch_context (& prev -> sp , & next -> sp );
249
302
}
250
303
251
- struct process * proc_a ;
252
- struct process * proc_b ;
253
-
254
304
void kernel_main (void ) {
255
305
// There is no guarantee that bootloader initializes bss with zeros
256
306
memset (__bss , 0 , (size_t )__bss_end - (size_t )__bss );
0 commit comments