riscv-pke/kernel/process.c

212 lines
7.2 KiB
C

/*
* 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[];
// current points to the currently running user-mode application.
process* current = NULL;
// process pool
process procs[NPROC];
// start virtual address of our simple heap.
uint64 g_ufree_page = USER_FREE_ADDRESS_START;
//
// switch to a user-mode process
//
void switch_to(process* proc) {
assert(proc);
current = proc;
write_csr(stvec, (uint64)smode_trap_vector);
// set up trapframe values 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;
// set up the registers that strap_vector.S's sret will use
// to get to user space.
// set S Previous Privilege mode to User.
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_csr(sstatus, x);
// set S Exception Program Counter to the saved user pc.
write_csr(sepc, proc->trapframe->epc);
//make user page table
uint64 user_satp = MAKE_SATP(proc->pagetable);
// switch to user mode with sret.
return_to_user(proc->trapframe, user_satp);
}
//
// initialize process pool (the procs[] array)
//
void init_proc_pool() {
memset( procs, 0, sizeof(struct 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 its pid
//
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[0].va = USER_STACK_TOP - PGSIZE;
procs[i].mapped_info[0].npages = 1;
procs[i].mapped_info[0].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[1].va = (uint64)procs[i].trapframe;
procs[i].mapped_info[1].npages = 1;
procs[i].mapped_info[1].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[2].va = (uint64)trap_sec_start;
procs[i].mapped_info[2].npages = 1;
procs[i].mapped_info[2].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);
procs[i].total_mapped_region = 3;
// return after initialization.
return &procs[i];
}
//
// reclaim a process
//
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.
// 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[0].va),
(void*)lookup_pa(parent->pagetable, parent->mapped_info[i].va), PGSIZE );
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;
}