@@ -114,8 +114,10 @@ const USERLAND_STACK_BOTTOM: VirtAddr = USERLAND_STACK_TOP.const_sub_u64(USERLAN
114
114
115
115
pub struct ArchTask {
116
116
context : Unique < Context > ,
117
+
117
118
address_space : AddressSpace ,
118
119
context_switch_rsp : VirtAddr ,
120
+ user : bool ,
119
121
120
122
fs_base : VirtAddr ,
121
123
gs_base : VirtAddr ,
@@ -130,29 +132,32 @@ impl ArchTask {
130
132
// Since the IDLE task is a special kernel task, we use the kernel's
131
133
// address space here and we also use the kernel privilage level here.
132
134
address_space : AddressSpace :: this ( ) ,
135
+ user : false ,
133
136
134
137
fs_base : VirtAddr :: zero ( ) ,
135
138
gs_base : VirtAddr :: zero ( ) ,
136
139
}
137
140
}
138
141
139
142
pub fn new_kernel ( entry_point : VirtAddr , enable_interrupts : bool ) -> Self {
143
+ let switch_stack = Self :: alloc_switch_stack ( ) . unwrap ( ) . as_mut_ptr :: < u8 > ( ) ;
144
+
140
145
let task_stack = unsafe {
141
146
let layout = Layout :: from_size_align_unchecked ( 4096 * 16 , 0x1000 ) ;
142
147
alloc_zeroed ( layout) . add ( layout. size ( ) )
143
148
} ;
144
149
145
150
let address_space = AddressSpace :: this ( ) ;
146
151
147
- let mut stack_ptr = task_stack as u64 ;
152
+ let mut stack_ptr = switch_stack as u64 ;
148
153
let mut stack = StackHelper :: new ( & mut stack_ptr) ;
149
154
150
155
let kframe = unsafe { stack. offset :: < InterruptErrorStack > ( ) } ;
151
156
152
157
kframe. stack . iret . ss = 0x10 ; // kernel stack segment
153
158
kframe. stack . iret . cs = 0x08 ; // kernel code segment
154
159
kframe. stack . iret . rip = entry_point. as_u64 ( ) ;
155
- kframe. stack . iret . rsp = unsafe { task_stack. sub ( 8 ) as u64 } ;
160
+ kframe. stack . iret . rsp = task_stack as u64 ;
156
161
kframe. stack . iret . rflags = if enable_interrupts { 0x200 } else { 0x00 } ;
157
162
158
163
extern "C" {
@@ -168,7 +173,8 @@ impl ArchTask {
168
173
Self {
169
174
context : unsafe { Unique :: new_unchecked ( context) } ,
170
175
address_space,
171
- context_switch_rsp : VirtAddr :: new ( task_stack as u64 ) ,
176
+ context_switch_rsp : VirtAddr :: new ( switch_stack as u64 ) ,
177
+ user : false ,
172
178
173
179
fs_base : VirtAddr :: zero ( ) ,
174
180
gs_base : VirtAddr :: zero ( ) ,
@@ -184,6 +190,8 @@ impl ArchTask {
184
190
}
185
191
186
192
pub fn fork ( & self ) -> Result < Self , MapToError < Size4KiB > > {
193
+ assert ! ( self . user, "cannot fork a kernel task" ) ;
194
+
187
195
let new_address_space = AddressSpace :: this ( ) . offset_page_table ( ) . fork ( ) ?;
188
196
189
197
// Since the fork function marks all of the userspace entries in both the forked
@@ -193,15 +201,7 @@ impl ArchTask {
193
201
asm ! ( "mov cr3, {}" , in( reg) controlregs:: read_cr3_raw( ) , options( nostack) ) ;
194
202
}
195
203
196
- let switch_stack = unsafe {
197
- let frame: PhysFrame = FRAME_ALLOCATOR . allocate_frame ( ) . unwrap ( ) ;
198
-
199
- frame
200
- . start_address ( )
201
- . as_hhdm_virt ( )
202
- . as_mut_ptr :: < u8 > ( )
203
- . add ( Size4KiB :: SIZE as usize )
204
- } ;
204
+ let switch_stack = Self :: alloc_switch_stack ( ) ?. as_mut_ptr :: < u8 > ( ) ;
205
205
206
206
let mut old_stack_ptr = self . context_switch_rsp . as_u64 ( ) ;
207
207
let mut old_stack = StackHelper :: new ( & mut old_stack_ptr) ;
@@ -232,6 +232,7 @@ impl ArchTask {
232
232
context : unsafe { Unique :: new_unchecked ( context) } ,
233
233
context_switch_rsp : VirtAddr :: new ( switch_stack as u64 ) ,
234
234
address_space : new_address_space,
235
+ user : false ,
235
236
236
237
// The FS and GS bases are inherited from the parent process.
237
238
fs_base : self . fs_base . clone ( ) ,
@@ -247,9 +248,18 @@ impl ArchTask {
247
248
argv : Option < ExecArgs > ,
248
249
envv : Option < ExecArgs > ,
249
250
) -> Result < ( ) , MapToError < Size4KiB > > {
250
- let address_space = AddressSpace :: new ( ) ?;
251
+ let address_space = if self . user {
252
+ self . unref_pt ( ) ;
253
+ AddressSpace :: new ( ) ?
254
+ } else {
255
+ AddressSpace :: new ( ) ?
256
+ } ;
257
+
251
258
let loaded_binary = vm. load_bin ( executable) . expect ( "exec: failed to load ELF" ) ;
252
259
260
+ // a kernel task can only execute a user executable
261
+ self . user = true ;
262
+
253
263
// mmap the userland stack...
254
264
vm. mmap (
255
265
USERLAND_STACK_BOTTOM ,
@@ -335,6 +345,49 @@ impl ArchTask {
335
345
Ok ( ( ) )
336
346
}
337
347
348
+ /// Allocates a new context switch stack for the process and returns the stack
349
+ /// top address. See the module level documentation for more information.
350
+ fn alloc_switch_stack ( ) -> Result < VirtAddr , MapToError < Size4KiB > > {
351
+ let frame: PhysFrame < Size4KiB > = FRAME_ALLOCATOR
352
+ . allocate_frame ( )
353
+ . ok_or ( MapToError :: FrameAllocationFailed ) ?;
354
+
355
+ Ok ( frame. start_address ( ) . as_hhdm_virt ( ) + Size4KiB :: SIZE )
356
+ }
357
+
358
+ fn unref_pt ( & mut self ) {
359
+ self . address_space
360
+ . offset_page_table ( )
361
+ . page_table ( )
362
+ . for_each_entry (
363
+ PageTableFlags :: PRESENT | PageTableFlags :: USER_ACCESSIBLE ,
364
+ |entry| {
365
+ entry. unref_vm_frame ( ) ;
366
+ entry. set_unused ( ) ;
367
+
368
+ Ok ( ( ) )
369
+ } ,
370
+ )
371
+ . expect ( "dealloc: failed to unref the page table" ) ;
372
+ }
373
+
374
+ /// Deallocates the architecture-specific task resources. This function is called
375
+ /// when the process is turned into a zombie.
376
+ pub fn dealloc ( & mut self ) {
377
+ if self . user {
378
+ self . unref_pt ( ) ;
379
+ }
380
+
381
+ // deallocate the switch stack
382
+ {
383
+ let frame: PhysFrame < Size4KiB > = PhysFrame :: containing_address (
384
+ ( self . context_switch_rsp - Size4KiB :: SIZE ) . as_hhdm_phys ( ) ,
385
+ ) ;
386
+
387
+ FRAME_ALLOCATOR . deallocate_frame ( frame) ;
388
+ }
389
+ }
390
+
338
391
/// Returns the saved GS base for this task.
339
392
pub fn get_gs_base ( & self ) -> VirtAddr {
340
393
self . gs_base
0 commit comments