212 lines
4.9 KiB
C
212 lines
4.9 KiB
C
#include "types.h"
|
|
#include "param.h"
|
|
#include "memlayout.h"
|
|
#include "riscv.h"
|
|
#include "spinlock.h"
|
|
#include "proc.h"
|
|
#include "defs.h"
|
|
|
|
struct proc proc[NPROC];
|
|
|
|
struct proc *initproc;
|
|
|
|
extern void forkret(void);
|
|
|
|
extern char trampoline[]; // trampoline.S
|
|
|
|
// helps ensure that wakeups of wait()ing
|
|
// parents are not lost. helps obey the
|
|
// memory model when using p->parent.
|
|
// must be acquired before any p->lock.
|
|
struct spinlock wait_lock;
|
|
|
|
// Allocate a page for each process's kernel stack.
|
|
// Map it high in memory, followed by an invalid
|
|
// guard page.
|
|
void
|
|
proc_mapstacks(pagetable_t kpgtbl)
|
|
{
|
|
struct proc *p;
|
|
|
|
for(p = proc; p < &proc[NPROC]; p++) {
|
|
char *pa = kalloc();
|
|
if(pa == 0)
|
|
panic("kalloc");
|
|
uint64 va = KSTACK((int) (p - proc));
|
|
kvmmap(kpgtbl, va, (uint64)pa, PGSIZE, PTE_R | PTE_W);
|
|
}
|
|
}
|
|
|
|
// initialize the proc table.
|
|
void
|
|
procinit(void)
|
|
{
|
|
struct proc *p;
|
|
|
|
initlock(&wait_lock, "wait_lock");
|
|
for(p = proc; p < &proc[NPROC]; p++) {
|
|
initlock(&p->lock, "proc");
|
|
p->state = UNUSED;
|
|
p->kstack = KSTACK((int) (p - proc));
|
|
}
|
|
}
|
|
|
|
// Look in the process table for an UNUSED proc.
|
|
// If found, initialize state required to run in the kernel,
|
|
// and return with p->lock held.
|
|
// If there are no free procs, or a memory allocation fails, return 0.
|
|
struct proc *allocproc(void);
|
|
|
|
// Create a user page table for a given process, with no user memory,
|
|
// but with trampoline and trapframe pages.
|
|
pagetable_t proc_pagetable(struct proc *p);
|
|
|
|
// Free a process's page table, and free the
|
|
// physical memory it refers to.
|
|
void proc_freepagetable(pagetable_t pagetable, uint64 sz);
|
|
|
|
// a user program that calls exec("/init")
|
|
// assembled from ../user/initcode.S
|
|
// od -t xC ../user/initcode
|
|
uchar initcode[] = {
|
|
0x17, 0x05, 0x00, 0x00, 0x13, 0x05, 0x45, 0x02,
|
|
0x97, 0x05, 0x00, 0x00, 0x93, 0x85, 0x35, 0x02,
|
|
0x93, 0x08, 0x70, 0x00, 0x73, 0x00, 0x00, 0x00,
|
|
0x93, 0x08, 0x20, 0x00, 0x73, 0x00, 0x00, 0x00,
|
|
0xef, 0xf0, 0x9f, 0xff, 0x2f, 0x69, 0x6e, 0x69,
|
|
0x74, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00
|
|
};
|
|
|
|
// Set up first user process.
|
|
void
|
|
userinit(void)
|
|
{
|
|
struct proc *p;
|
|
|
|
p = allocproc();
|
|
initproc = p;
|
|
|
|
// allocate one user page and copy initcode's instructions
|
|
// and data into it.
|
|
uvmfirst(p->pagetable, initcode, sizeof(initcode));
|
|
p->sz = PGSIZE;
|
|
|
|
// prepare for the very first "return" from kernel to user.
|
|
p->trapframe->epc = 0; // user program counter
|
|
p->trapframe->sp = PGSIZE; // user stack pointer
|
|
|
|
safestrcpy(p->name, "initcode", sizeof(p->name));
|
|
p->cwd = namei("/");
|
|
|
|
p->state = RUNNABLE;
|
|
|
|
release(&p->lock);
|
|
}
|
|
|
|
// A fork child's very first scheduling by scheduler()
|
|
// will swtch to forkret.
|
|
void
|
|
forkret(void)
|
|
{
|
|
static int first = 1;
|
|
|
|
// Still holding p->lock from scheduler.
|
|
release(&myproc()->lock);
|
|
|
|
if (first) {
|
|
// File system initialization must be run in the context of a
|
|
// regular process (e.g., because it calls sleep), and thus cannot
|
|
// be run from main().
|
|
first = 0;
|
|
fsinit(ROOTDEV);
|
|
}
|
|
|
|
usertrapret();
|
|
}
|
|
|
|
// Atomically release lock and sleep on chan.
|
|
// Reacquires lock when awakened.
|
|
void sleep_lock(void *chan, struct spinlock *lk);
|
|
|
|
// Wake up all processes sleeping on chan.
|
|
// Must be called without any p->lock.
|
|
void wakeup(void *chan)
|
|
{
|
|
struct proc *p;
|
|
|
|
for(p = proc; p < &proc[NPROC]; p++) {
|
|
if(p != myproc()){
|
|
acquire(&p->lock);
|
|
if(p->state == SLEEPING && p->chan == chan) {
|
|
p->state = RUNNABLE;
|
|
}
|
|
release(&p->lock);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy to either a user address, or kernel address,
|
|
// depending on usr_dst.
|
|
// Returns 0 on success, -1 on error.
|
|
int
|
|
either_copyout(int user_dst, uint64 dst, void *src, uint64 len)
|
|
{
|
|
struct proc *p = myproc();
|
|
if(user_dst){
|
|
return copyout(p->pagetable, dst, src, len);
|
|
} else {
|
|
memmove((char *)dst, src, len);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Copy from either a user address, or kernel address,
|
|
// depending on usr_src.
|
|
// Returns 0 on success, -1 on error.
|
|
int
|
|
either_copyin(void *dst, int user_src, uint64 src, uint64 len)
|
|
{
|
|
struct proc *p = myproc();
|
|
if(user_src){
|
|
return copyin(p->pagetable, dst, src, len);
|
|
} else {
|
|
memmove(dst, (char*)src, len);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Print a process listing to console. For debugging.
|
|
// Runs when user types ^P on console.
|
|
// No lock to avoid wedging a stuck machine further.
|
|
void procdump(void)
|
|
{
|
|
static char *states[] = {
|
|
[UNUSED] "unused",
|
|
[USED] "used",
|
|
[SLEEPING] "sleep ",
|
|
[RUNNABLE] "runble",
|
|
[RUNNING] "run ",
|
|
[ZOMBIE] "zombie"
|
|
};
|
|
struct proc *p;
|
|
char *state;
|
|
|
|
printstr("\n");
|
|
for(p = proc; p < &proc[NPROC]; p++){
|
|
if(p->state == UNUSED)
|
|
continue;
|
|
if(p->state >= 0 && p->state < NELEM(states) && states[p->state])
|
|
state = states[p->state];
|
|
else
|
|
state = "???";
|
|
|
|
printint(p->pid);
|
|
printstr(" ");
|
|
printstr(state);
|
|
printstr(" ");
|
|
printstr(p->name);
|
|
printstr("\n");
|
|
}
|
|
}
|