You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
253 lines
9.7 KiB
253 lines
9.7 KiB
/*
|
|
* Utility functions for process management.
|
|
*
|
|
* Note: in Lab1, only one process (i.e., our user application) exists. Therefore,
|
|
* PKE OS at this stage will set "current" to the loaded user application, and also
|
|
* switch to the old "current" process after trap handling.
|
|
*/
|
|
|
|
#include "riscv.h"
|
|
#include "strap.h"
|
|
#include "config.h"
|
|
#include "process.h"
|
|
#include "elf.h"
|
|
#include "string.h"
|
|
#include "vmm.h"
|
|
#include "pmm.h"
|
|
#include "memlayout.h"
|
|
#include "sched.h"
|
|
#include "spike_interface/spike_utils.h"
|
|
|
|
//Two functions defined in kernel/usertrap.S
|
|
extern char smode_trap_vector[];
|
|
extern void return_to_user(trapframe *, uint64 satp);
|
|
|
|
// trap_sec_start points to the beginning of S-mode trap segment (i.e., the entry point
|
|
// of S-mode trap vector).
|
|
extern char trap_sec_start[];
|
|
|
|
// process pool. added @lab3_1
|
|
process procs[NPROC];
|
|
|
|
// current points to the currently running user-mode application.
|
|
process* current = NULL;
|
|
|
|
//
|
|
// switch to a user-mode process
|
|
//
|
|
void switch_to(process* proc) {
|
|
assert(proc);
|
|
current = proc;
|
|
|
|
// write the smode_trap_vector (64-bit func. address) defined in kernel/strap_vector.S
|
|
// to the stvec privilege register, such that trap handler pointed by smode_trap_vector
|
|
// will be triggered when an interrupt occurs in S mode.
|
|
write_csr(stvec, (uint64)smode_trap_vector);
|
|
|
|
// set up trapframe values (in process structure) that smode_trap_vector will need when
|
|
// the process next re-enters the kernel.
|
|
proc->trapframe->kernel_sp = proc->kstack; // process's kernel stack
|
|
proc->trapframe->kernel_satp = read_csr(satp); // kernel page table
|
|
proc->trapframe->kernel_trap = (uint64)smode_trap_handler;
|
|
|
|
// SSTATUS_SPP and SSTATUS_SPIE are defined in kernel/riscv.h
|
|
// set S Previous Privilege mode (the SSTATUS_SPP bit in sstatus register) to User mode.
|
|
unsigned long x = read_csr(sstatus);
|
|
x &= ~SSTATUS_SPP; // clear SPP to 0 for user mode
|
|
x |= SSTATUS_SPIE; // enable interrupts in user mode
|
|
|
|
// write x back to 'sstatus' register to enable interrupts, and sret destination mode.
|
|
write_csr(sstatus, x);
|
|
|
|
// set S Exception Program Counter (sepc register) to the elf entry pc.
|
|
write_csr(sepc, proc->trapframe->epc);
|
|
|
|
// make user page table. macro MAKE_SATP is defined in kernel/riscv.h. added @lab2_1
|
|
uint64 user_satp = MAKE_SATP(proc->pagetable);
|
|
|
|
// return_to_user() is defined in kernel/strap_vector.S. switch to user mode with sret.
|
|
// note, return_to_user takes two parameters @ and after lab2_1.
|
|
return_to_user(proc->trapframe, user_satp);
|
|
}
|
|
|
|
//
|
|
// initialize process pool (the procs[] array). added @lab3_1
|
|
//
|
|
void init_proc_pool() {
|
|
memset( procs, 0, sizeof(process)*NPROC );
|
|
|
|
for (int i = 0; i < NPROC; ++i) {
|
|
procs[i].status = FREE;
|
|
procs[i].pid = i;
|
|
}
|
|
}
|
|
|
|
//
|
|
// allocate an empty process, init its vm space. returns the pointer to
|
|
// process strcuture. added @lab3_1
|
|
//
|
|
process* alloc_process() {
|
|
// locate the first usable process structure
|
|
int i;
|
|
|
|
for( i=0; i<NPROC; i++ )
|
|
if( procs[i].status == FREE ) break;
|
|
|
|
if( i>=NPROC ){
|
|
panic( "cannot find any free process structure.\n" );
|
|
return 0;
|
|
}
|
|
|
|
// init proc[i]'s vm space
|
|
procs[i].trapframe = (trapframe *)alloc_page(); //trapframe, used to save context
|
|
memset(procs[i].trapframe, 0, sizeof(trapframe));
|
|
|
|
// page directory
|
|
procs[i].pagetable = (pagetable_t)alloc_page();
|
|
memset((void *)procs[i].pagetable, 0, PGSIZE);
|
|
|
|
procs[i].kstack = (uint64)alloc_page() + PGSIZE; //user kernel stack top
|
|
uint64 user_stack = (uint64)alloc_page(); //phisical address of user stack bottom
|
|
procs[i].trapframe->regs.sp = USER_STACK_TOP; //virtual address of user stack top
|
|
|
|
// allocates a page to record memory regions (segments)
|
|
procs[i].mapped_info = (mapped_region*)alloc_page();
|
|
memset( procs[i].mapped_info, 0, PGSIZE );
|
|
|
|
// map user stack in userspace
|
|
user_vm_map((pagetable_t)procs[i].pagetable, USER_STACK_TOP - PGSIZE, PGSIZE,
|
|
user_stack, prot_to_type(PROT_WRITE | PROT_READ, 1));
|
|
procs[i].mapped_info[STACK_SEGMENT].va = USER_STACK_TOP - PGSIZE;
|
|
procs[i].mapped_info[STACK_SEGMENT].npages = 1;
|
|
procs[i].mapped_info[STACK_SEGMENT].seg_type = STACK_SEGMENT;
|
|
|
|
// map trapframe in user space (direct mapping as in kernel space).
|
|
user_vm_map((pagetable_t)procs[i].pagetable, (uint64)procs[i].trapframe, PGSIZE,
|
|
(uint64)procs[i].trapframe, prot_to_type(PROT_WRITE | PROT_READ, 0));
|
|
procs[i].mapped_info[CONTEXT_SEGMENT].va = (uint64)procs[i].trapframe;
|
|
procs[i].mapped_info[CONTEXT_SEGMENT].npages = 1;
|
|
procs[i].mapped_info[CONTEXT_SEGMENT].seg_type = CONTEXT_SEGMENT;
|
|
|
|
// map S-mode trap vector section in user space (direct mapping as in kernel space)
|
|
// we assume that the size of usertrap.S is smaller than a page.
|
|
user_vm_map((pagetable_t)procs[i].pagetable, (uint64)trap_sec_start, PGSIZE,
|
|
(uint64)trap_sec_start, prot_to_type(PROT_READ | PROT_EXEC, 0));
|
|
procs[i].mapped_info[SYSTEM_SEGMENT].va = (uint64)trap_sec_start;
|
|
procs[i].mapped_info[SYSTEM_SEGMENT].npages = 1;
|
|
procs[i].mapped_info[SYSTEM_SEGMENT].seg_type = SYSTEM_SEGMENT;
|
|
|
|
sprint("in alloc_proc. user frame 0x%lx, user stack 0x%lx, user kstack 0x%lx \n",
|
|
procs[i].trapframe, procs[i].trapframe->regs.sp, procs[i].kstack);
|
|
|
|
// initialize the process's heap manager
|
|
procs[i].user_heap.heap_top = USER_FREE_ADDRESS_START;
|
|
procs[i].user_heap.heap_bottom = USER_FREE_ADDRESS_START;
|
|
procs[i].user_heap.free_pages_count = 0;
|
|
|
|
// map user heap in userspace
|
|
procs[i].mapped_info[HEAP_SEGMENT].va = USER_FREE_ADDRESS_START;
|
|
procs[i].mapped_info[HEAP_SEGMENT].npages = 0; // no pages are mapped to heap yet.
|
|
procs[i].mapped_info[HEAP_SEGMENT].seg_type = HEAP_SEGMENT;
|
|
|
|
procs[i].total_mapped_region = 4;
|
|
|
|
// return after initialization.
|
|
return &procs[i];
|
|
}
|
|
|
|
//
|
|
// reclaim a process. added @lab3_1
|
|
//
|
|
int free_process( process* proc ) {
|
|
// we set the status to ZOMBIE, but cannot destruct its vm space immediately.
|
|
// since proc can be current process, and its user kernel stack is currently in use!
|
|
// but for proxy kernel, it (memory leaking) may NOT be a really serious issue,
|
|
// as it is different from regular OS, which needs to run 7x24.
|
|
proc->status = ZOMBIE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// implements fork syscal in kernel. added @lab3_1
|
|
// basic idea here is to first allocate an empty process (child), then duplicate the
|
|
// context and data segments of parent process to the child, and lastly, map other
|
|
// segments (code, system) of the parent to child. the stack segment remains unchanged
|
|
// for the child.
|
|
//
|
|
int do_fork( process* parent)
|
|
{
|
|
sprint( "will fork a child from parent %d.\n", parent->pid );
|
|
process* child = alloc_process();
|
|
|
|
for( int i=0; i<parent->total_mapped_region; i++ ){
|
|
// browse parent's vm space, and copy its trapframe and data segments,
|
|
// map its code segment.
|
|
switch( parent->mapped_info[i].seg_type ){
|
|
case CONTEXT_SEGMENT:
|
|
*child->trapframe = *parent->trapframe;
|
|
break;
|
|
case STACK_SEGMENT:
|
|
memcpy( (void*)lookup_pa(child->pagetable, child->mapped_info[STACK_SEGMENT].va),
|
|
(void*)lookup_pa(parent->pagetable, parent->mapped_info[i].va), PGSIZE );
|
|
break;
|
|
case HEAP_SEGMENT:
|
|
// build a same heap for child process.
|
|
|
|
// convert free_pages_address into a filter to skip reclaimed blocks in the heap
|
|
// when mapping the heap blocks
|
|
int free_block_filter[MAX_HEAP_PAGES];
|
|
memset(free_block_filter, 0, MAX_HEAP_PAGES);
|
|
uint64 heap_bottom = parent->user_heap.heap_bottom;
|
|
for (int i = 0; i < parent->user_heap.free_pages_count; i++) {
|
|
int index = (parent->user_heap.free_pages_address[i] - heap_bottom) / PGSIZE;
|
|
free_block_filter[index] = 1;
|
|
}
|
|
|
|
// copy and map the heap blocks
|
|
for (uint64 heap_block = current->user_heap.heap_bottom;
|
|
heap_block < current->user_heap.heap_top; heap_block += PGSIZE) {
|
|
if (free_block_filter[(heap_block - heap_bottom) / PGSIZE]) // skip free blocks
|
|
continue;
|
|
|
|
void* child_pa = alloc_page();
|
|
memcpy(child_pa, (void*)lookup_pa(parent->pagetable, heap_block), PGSIZE);
|
|
user_vm_map((pagetable_t)child->pagetable, heap_block, PGSIZE, (uint64)child_pa,
|
|
prot_to_type(PROT_WRITE | PROT_READ, 1));
|
|
}
|
|
|
|
child->mapped_info[HEAP_SEGMENT].npages = parent->mapped_info[HEAP_SEGMENT].npages;
|
|
|
|
// copy the heap manager from parent to child
|
|
memcpy((void*)&child->user_heap, (void*)&parent->user_heap, sizeof(parent->user_heap));
|
|
break;
|
|
case CODE_SEGMENT:
|
|
// TODO (lab3_1): implment the mapping of child code segment to parent's
|
|
// code segment.
|
|
// hint: the virtual address mapping of code segment is tracked in mapped_info
|
|
// page of parent's process structure. use the information in mapped_info to
|
|
// retrieve the virtual to physical mapping of code segment.
|
|
// after having the mapping information, just map the corresponding virtual
|
|
// address region of child to the physical pages that actually store the code
|
|
// segment of parent process.
|
|
// DO NOT COPY THE PHYSICAL PAGES, JUST MAP THEM.
|
|
panic( "You need to implement the code segment mapping of child in lab3_1.\n" );
|
|
|
|
// after mapping, register the vm region (do not delete codes below!)
|
|
child->mapped_info[child->total_mapped_region].va = parent->mapped_info[i].va;
|
|
child->mapped_info[child->total_mapped_region].npages =
|
|
parent->mapped_info[i].npages;
|
|
child->mapped_info[child->total_mapped_region].seg_type = CODE_SEGMENT;
|
|
child->total_mapped_region++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
child->status = READY;
|
|
child->trapframe->regs.a0 = 0;
|
|
child->parent = parent;
|
|
insert_to_ready_queue( child );
|
|
|
|
return child->pid;
|
|
}
|