Academic Integrity: tutoring, explanations, and feedback — we don’t complete graded work or submit on a student’s behalf.

I have this code that is supposed to run an MLFQ scheduler in xv6 but when I try

ID: 3593294 • Letter: I

Question

I have this code that is supposed to run an MLFQ scheduler in xv6 but when I try to run it, it get's stuck saying starting cpu0 and cpu1 any ideas why the code is doing this?

Proc.c

#include "types.h"

#include "defs.h"

#include "param.h"

#include "memlayout.h"

#include "mmu.h"

#include "x86.h"

#include "proc.h"

#include "spinlock.h"

#include "pstat.h"

#define NULL 0

struct proc* q0[64];

struct proc* q1[64];

struct proc* q2[64];

struct proc* q3[64];

int c0 =-1;

int c1=-1;

int c2=-1;

int c3=-1;

int clkPerPrio[4] ={1,2,4,8};

struct pstat pstat_var;

struct {

struct spinlock lock;

struct proc proc[NPROC];

} ptable;

static struct proc *initproc;

int nextpid = 1;

extern void forkret(void);

extern void trapret(void);

static void wakeup1(void *chan);

void

pinit(void)

{

initlock(&ptable.lock, "ptable");

}

//PAGEBREAK: 32

// Look in the process table for an UNUSED proc.

// If found, change state to EMBRYO and initialize

// state required to run in the kernel.

// Otherwise return 0.

static struct proc*

allocproc(void)

{

struct proc *p;

char *sp;

acquire(&ptable.lock);

for(p = ptable.proc; p < &ptable.proc[NPROC]; p++)

if(p->state == UNUSED)

goto found;

release(&ptable.lock);

return 0;

found:

p->state = EMBRYO;

p->pid = nextpid++;

release(&ptable.lock);

// Allocate kernel stack.

if((p->kstack = kalloc()) == 0){

p->state = UNUSED;

return 0;

}

sp = p->kstack + KSTACKSIZE;

  

// Leave room for trap frame.

sp -= sizeof *p->tf;

p->tf = (struct trapframe*)sp;

  

// Set up new context to start executing at forkret,

// which returns to trapret.

sp -= 4;

*(uint*)sp = (uint)trapret;

sp -= sizeof *p->context;

p->context = (struct context*)sp;

memset(p->context, 0, sizeof *p->context);

p->context->eip = (uint)forkret;

return p;

}

//PAGEBREAK: 32

// Set up first user process.

void

userinit(void)

{

struct proc *p;

extern char _binary_initcode_start[], _binary_initcode_size[];

  

p = allocproc();

initproc = p;

if((p->pgdir = setupkvm()) == 0)

panic("userinit: out of memory?");

inituvm(p->pgdir, _binary_initcode_start, (int)_binary_initcode_size);

p->sz = PGSIZE;

memset(p->tf, 0, sizeof(*p->tf));

p->tf->cs = (SEG_UCODE << 3) | DPL_USER;

p->tf->ds = (SEG_UDATA << 3) | DPL_USER;

p->tf->es = p->tf->ds;

p->tf->ss = p->tf->ds;

p->tf->eflags = FL_IF;

p->tf->esp = PGSIZE;

p->tf->eip = 0; // beginning of initcode.S

safestrcpy(p->name, "initcode", sizeof(p->name));

p->cwd = namei("/");

p->state = RUNNABLE;

}

// Grow current process's memory by n bytes.

// Return 0 on success, -1 on failure.

int

growproc(int n)

{

uint sz;

  

sz = proc->sz;

if(n > 0){

if((sz = allocuvm(proc->pgdir, sz, sz + n)) == 0)

return -1;

} else if(n < 0){

if((sz = deallocuvm(proc->pgdir, sz, sz + n)) == 0)

return -1;

}

proc->sz = sz;

switchuvm(proc);

return 0;

}

// Create a new process copying p as the parent.

// Sets up stack to return as if from system call.

// Caller must set state of returned proc to RUNNABLE.

int

fork(void)

{

int i, pid;

struct proc *np;

// Allocate process.

if((np = allocproc()) == 0)

return -1;

// Copy process state from p.

if((np->pgdir = copyuvm(proc->pgdir, proc->sz)) == 0){

kfree(np->kstack);

np->kstack = 0;

np->state = UNUSED;

return -1;

}

np->sz = proc->sz;

np->parent = proc;

*np->tf = *proc->tf;

// Clear %eax so that fork returns 0 in the child.

np->tf->eax = 0;

for(i = 0; i < NOFILE; i++)

if(proc->ofile[i])

np->ofile[i] = filedup(proc->ofile[i]);

np->cwd = idup(proc->cwd);

safestrcpy(np->name, proc->name, sizeof(proc->name));

pid = np->pid;

// lock to force the compiler to emit the np->state write last.

acquire(&ptable.lock);

np->state = RUNNABLE;

release(&ptable.lock);

  

return pid;

}

// Exit the current process. Does not return.

// An exited process remains in the zombie state

// until its parent calls wait() to find out it exited.

void

exit(void)

{

struct proc *p;

int fd;

if(proc == initproc)

panic("init exiting");

// Close all open files.

for(fd = 0; fd < NOFILE; fd++){

if(proc->ofile[fd]){

fileclose(proc->ofile[fd]);

proc->ofile[fd] = 0;

}

}

begin_op();

iput(proc->cwd);

end_op();

proc->cwd = 0;

acquire(&ptable.lock);

// Parent might be sleeping in wait().

wakeup1(proc->parent);

// Pass abandoned children to init.

for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){

if(p->parent == proc){

p->parent = initproc;

if(p->state == ZOMBIE)

wakeup1(initproc);

}

}

// Jump into the scheduler, never to return.

proc->state = ZOMBIE;

sched();

panic("zombie exit");

}

// Wait for a child process to exit and return its pid.

// Return -1 if this process has no children.

int

wait(void)

{

struct proc *p;

int havekids, pid;

acquire(&ptable.lock);

for(;;){

// Scan through table looking for zombie children.

havekids = 0;

for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){

if(p->parent != proc)

continue;

havekids = 1;

if(p->state == ZOMBIE){

// Found one.

pid = p->pid;

kfree(p->kstack);

p->kstack = 0;

freevm(p->pgdir);

p->state = UNUSED;

p->pid = 0;

p->parent = 0;

p->name[0] = 0;

p->killed = 0;

release(&ptable.lock);

return pid;

}

}

// No point waiting if we don't have any children.

if(!havekids || proc->killed){

release(&ptable.lock);

return -1;

}

// Wait for children to exit. (See wakeup1 call in proc_exit.)

sleep(proc, &ptable.lock); //DOC: wait-sleep

}

}

//PAGEBREAK: 42

// Per-CPU process scheduler.

// Each CPU calls scheduler() after setting itself up.

// Scheduler never returns. It loops, doing:

// - choose a process to run

// - swtch to start running that process

// - eventually that process transfers control

// via swtch back to the scheduler.

void

scheduler (void)

{

struct proc *p;

int i;

int j;

for(;;){

// Enable interrupts on this processor.

sti();

// Loop over process table looking for process to run.

acquire(&ptable.lock);

if(c0!=-1){

for(i=0;i<=c0;i++){

if(q0[i]->state != RUNNABLE)

continue;

p=q0[i];

proc = q0[i];

p->clicks++;

switchuvm(p);

p->state = RUNNING;

swtch(&cpu->scheduler, proc->context);

switchkvm();

pstat_var.ticks[p->pid][0]=p->clicks;

if(p->clicks ==clkPerPrio[0]){

/*copy proc to lower priority queue*/

c1++;

proc->priority=proc->priority+1;

pstat_var.priority[proc->pid] = proc->priority;

q1[c1] = proc;

/*delete proc from q0*/

q0[i]=NULL;

for(j=i;j<=c0-1;j++)

q0[j] = q0[j+1];

q0[c0] = NULL;

proc->clicks = 0;

c0--;

}

proc = 0;

}

}

if(c1!=-1){

for(i=0;i<=c1;i++){

if(q1[i]->state != RUNNABLE)

continue;

p=q1[i];

proc = q1[i];

proc->clicks++;

switchuvm(p);

p->state = RUNNING;

swtch(&cpu->scheduler, proc->context);

switchkvm();

pstat_var.ticks[p->pid][1]=p->clicks;;

if(p->clicks ==clkPerPrio[1]){

/*copy proc to lower priority queue*/

c2++;

proc->priority=proc->priority+1;

pstat_var.priority[proc->pid] = proc->priority;

q2[c2] = proc;

/*delete proc from q0*/

q1[i]=NULL;

for(j=i;j<=c1-1;j++)

q1[j] = q1[j+1];

q1[c1] = NULL;

proc->clicks = 0;

c1--;

}

proc = 0;

}

}

if(c2!=-1){

for(i=0;i<=c2;i++){

if(q2[i]->state != RUNNABLE)

continue;

p=q2[i];

proc = q2[i];

proc->clicks++;

switchuvm(p);

p->state = RUNNING;

swtch(&cpu->scheduler, proc->context);

switchkvm();

pstat_var.ticks[p->pid][2]=p->clicks;;

if(p->clicks ==clkPerPrio[2]){

/*copy proc to lower priority queue*/

c3++;

proc->priority=proc->priority+1;

pstat_var.priority[p->pid] = p->priority;

q3[c3] = proc;

/*delete proc from q0*/

q2[i]=NULL;

for(j=i;j<=c2-1;j++)

q2[j] = q2[j+1];

q2[c2] =NULL;

proc->clicks = 0;

c2--;

}

proc = 0;

}

}

if(c3!=-1){

for(i=0;i<=c3;i++){

if(q3[i]->state != RUNNABLE)

continue;

p=q3[i];

proc = q3[i];

proc->clicks++;

switchuvm(p);

p->state = RUNNING;

swtch(&cpu->scheduler, proc->context);

switchkvm();

pstat_var.priority[p->pid] = p->priority;

pstat_var.ticks[p->pid][3]=p->clicks;;

/*move process to end of its own queue */

q3[i]=NULL;

for(j=i;j<=c3-1;j++)

q3[j] = q3[j+1];

q3[c3] = proc;

proc = 0;

}

}

release(&ptable.lock);

}

}

// Enter scheduler. Must hold only ptable.lock

// and have changed proc->state.

void

sched(void)

{

int intena;

if(!holding(&ptable.lock))

panic("sched ptable.lock");

if(cpu->ncli != 1)

panic("sched locks");

if(proc->state == RUNNING)

panic("sched running");

if(readeflags()&FL_IF)

panic("sched interruptible");

intena = cpu->intena;

swtch(&proc->context, cpu->scheduler);

cpu->intena = intena;

}

// Give up the CPU for one scheduling round.

void

yield(void)

{

acquire(&ptable.lock); //DOC: yieldlock

proc->state = RUNNABLE;

sched();

release(&ptable.lock);

}

// A fork child's very first scheduling by scheduler()

// will swtch here. "Return" to user space.

void

forkret(void)

{

static int first = 1;

// Still holding ptable.lock from scheduler.

release(&ptable.lock);

if (first) {

// Some initialization functions must be run in the context

// of a regular process (e.g., they call sleep), and thus cannot

// be run from main().

first = 0;

iinit(ROOTDEV);

initlog(ROOTDEV);

}

  

// Return to "caller", actually trapret (see allocproc).

}

// Atomically release lock and sleep on chan.

// Reacquires lock when awakened.

void

sleep(void *chan, struct spinlock *lk)

{

if(proc == 0)

panic("sleep");

if(lk == 0)

panic("sleep without lk");

// Must acquire ptable.lock in order to

// change p->state and then call sched.

// Once we hold ptable.lock, we can be

// guaranteed that we won't miss any wakeup

// (wakeup runs with ptable.lock locked),

// so it's okay to release lk.

if(lk != &ptable.lock){ //DOC: sleeplock0

acquire(&ptable.lock); //DOC: sleeplock1

release(lk);

}

// Go to sleep.

proc->chan = chan;

proc->state = SLEEPING;

sched();

// Tidy up.

proc->chan = 0;

// Reacquire original lock.

if(lk != &ptable.lock){ //DOC: sleeplock2

release(&ptable.lock);

acquire(lk);

}

}

//PAGEBREAK!

// Wake up all processes sleeping on chan.

// The ptable lock must be held.

static void

wakeup1(void *chan)

{

struct proc *p;

for(p = ptable.proc; p < &ptable.proc[NPROC]; p++)

if(p->state == SLEEPING && p->chan == chan)

p->state = RUNNABLE;

}

// Wake up all processes sleeping on chan.

void

wakeup(void *chan)

{

acquire(&ptable.lock);

wakeup1(chan);

release(&ptable.lock);

}

// Kill the process with the given pid.

// Process won't exit until it returns

// to user space (see trap in trap.c).

int

kill(int pid)

{

struct proc *p;

acquire(&ptable.lock);

for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){

if(p->pid == pid){

p->killed = 1;

// Wake process from sleep if necessary.

if(p->state == SLEEPING)

p->state = RUNNABLE;

release(&ptable.lock);

return 0;

}

}

release(&ptable.lock);

return -1;

}

//PAGEBREAK: 36

// 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",

[EMBRYO] "embryo",

[SLEEPING] "sleep ",

[RUNNABLE] "runble",

[RUNNING] "run ",

[ZOMBIE] "zombie"

};

int i;

struct proc *p;

char *state;

uint pc[10];

  

for(p = ptable.proc; p < &ptable.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 = "???";

cprintf("%d %s %s", p->pid, state, p->name);

if(p->state == SLEEPING){

getcallerpcs((uint*)p->context->ebp+2, pc);

for(i=0; i<10 && pc[i] != 0; i++)

cprintf(" %p", pc[i]);

}

cprintf(" ");

}

}

proc.h

// Segments in proc->gdt.

#define NSEGS 7

// Per-CPU state

struct cpu {

uchar id; // Local APIC ID; index into cpus[] below

struct context *scheduler; // swtch() here to enter scheduler

struct taskstate ts; // Used by x86 to find stack for interrupt

struct segdesc gdt[NSEGS]; // x86 global descriptor table

volatile uint started; // Has the CPU started?

int ncli; // Depth of pushcli nesting.

int intena; // Were interrupts enabled before pushcli?

  

// Cpu-local storage variables; see below

struct cpu *cpu;

struct proc *proc; // The currently-running process.

};

extern struct cpu cpus[NCPU];

extern int ncpu;

// Per-CPU variables, holding pointers to the

// current cpu and to the current process.

// The asm suffix tells gcc to use "%gs:0" to refer to cpu

// and "%gs:4" to refer to proc. seginit sets up the

// %gs segment register so that %gs refers to the memory

// holding those two variables in the local cpu's struct cpu.

// This is similar to how thread-local variables are implemented

// in thread libraries such as Linux pthreads.

extern struct cpu *cpu asm("%gs:0"); // &cpus[cpunum()]

extern struct proc *proc asm("%gs:4"); // cpus[cpunum()].proc

//PAGEBREAK: 17

// Saved registers for kernel context switches.

// Don't need to save all the segment registers (%cs, etc),

// because they are constant across kernel contexts.

// Don't need to save %eax, %ecx, %edx, because the

// x86 convention is that the caller has saved them.

// Contexts are stored at the bottom of the stack they

// describe; the stack pointer is the address of the context.

// The layout of the context matches the layout of the stack in swtch.S

// at the "Switch stacks" comment. Switch doesn't save eip explicitly,

// but it is on the stack and allocproc() manipulates it.

struct context {

uint edi;

uint esi;

uint ebx;

uint ebp;

uint eip;

};

extern struct proc* q0[64];

extern struct proc* q1[64];

extern struct proc* q2[64];

extern struct proc* q3[64];

extern int c0;

extern int c1;

extern int c2;

extern int c3;

extern struct pstat pstat_var;

enum procstate { UNUSED, EMBRYO, SLEEPING, RUNNABLE, RUNNING, ZOMBIE };

// Per-process state

struct proc {

uint sz; // Size of process memory (bytes)

pde_t* pgdir; // Page table

char *kstack; // Bottom of kernel stack for this process

enum procstate state; // Process state

int pid; // Process ID

struct proc *parent; // Parent process

struct trapframe *tf; // Trap frame for current syscall

struct context *context; // swtch() here to run process

void *chan; // If non-zero, sleeping on chan

int killed; // If non-zero, have been killed

struct file *ofile[NOFILE]; // Open files

struct inode *cwd; // Current directory

char name[16]; // Process name (debugging)

int clicks; //number of timer clicks the process has run for

int priority; //current priority of process

};

// Process memory is laid out contiguously, low addresses first:

// text

// original data and bss

// fixed-size stack

// expandable heap

pstat.h

#ifndef _PSTAT_H_
#define _PSTAT_H_

#include "param.h"

struct pstat {
int inuse[NPROC]; // whether this slot of the process table is in use (1 or 0)
int pid[NPROC]; // PID of each process
int priority[NPROC]; // current priority level of each process (0-3)
int ticks[NPROC][4]; // number of ticks each process has accumulated at each of 4 priorities
};


#endif // _PSTAT_H_

types.h

typedef unsigned int uint;
typedef unsigned short ushort;
typedef unsigned char uchar;
typedef uint pde_t;

defs.h

struct buf;
struct context;
struct file;
struct inode;
struct pipe;
struct proc;
struct rtcdate;
struct spinlock;
struct stat;
struct superblock;

// bio.c
void binit(void);
struct buf* bread(uint, uint);
void brelse(struct buf*);
void bwrite(struct buf*);

// console.c
void consoleinit(void);
void cprintf(char*, ...);
void consoleintr(int(*)(void));
void panic(char*) __attribute__((noreturn));

// exec.c
int exec(char*, char**);

// file.c
struct file* filealloc(void);
void fileclose(struct file*);
struct file* filedup(struct file*);
void fileinit(void);
int fileread(struct file*, char*, int n);
int filestat(struct file*, struct stat*);
int filewrite(struct file*, char*, int n);

// fs.c
void readsb(int dev, struct superblock *sb);
int dirlink(struct inode*, char*, uint);
struct inode* dirlookup(struct inode*, char*, uint*);
struct inode* ialloc(uint, short);
struct inode* idup(struct inode*);
void iinit(int dev);
void ilock(struct inode*);
void iput(struct inode*);
void iunlock(struct inode*);
void iunlockput(struct inode*);
void iupdate(struct inode*);
int namecmp(const char*, const char*);
struct inode* namei(char*);
struct inode* nameiparent(char*, char*);
int readi(struct inode*, char*, uint, uint);
void stati(struct inode*, struct stat*);
int writei(struct inode*, char*, uint, uint);

// ide.c
void ideinit(void);
void ideintr(void);
void iderw(struct buf*);

// ioapic.c
void ioapicenable(int irq, int cpu);
extern uchar ioapicid;
void ioapicinit(void);

// kalloc.c
char* kalloc(void);
void kfree(char*);
void kinit1(void*, void*);
void kinit2(void*, void*);

// kbd.c
void kbdintr(void);

// lapic.c
void cmostime(struct rtcdate *r);
int cpunum(void);
extern volatile uint* lapic;
void lapiceoi(void);
void lapicinit(void);
void lapicstartap(uchar, uint);
void microdelay(int);

// log.c
void initlog(int dev);
void log_write(struct buf*);
void begin_op();
void end_op();

// mp.c
extern int ismp;
int mpbcpu(void);
void mpinit(void);
void mpstartthem(void);

// picirq.c
void picenable(int);
void picinit(void);

// pipe.c
int pipealloc(struct file**, struct file**);
void pipeclose(struct pipe*, int);
int piperead(struct pipe*, char*, int);
int pipewrite(struct pipe*, char*, int);

//PAGEBREAK: 16
// proc.c
struct proc* copyproc(struct proc*);
void exit(void);
int fork(void);
int growproc(int);
int kill(int);
void pinit(void);
void procdump(void);
void scheduler(void) __attribute__((noreturn));
void sched(void);
void sleep(void*, struct spinlock*);
void userinit(void);
int wait(void);
void wakeup(void*);
void yield(void);

// swtch.S
void swtch(struct context**, struct context*);

// spinlock.c
void acquire(struct spinlock*);
void getcallerpcs(void*, uint*);
int holding(struct spinlock*);
void initlock(struct spinlock*, char*);
void release(struct spinlock*);
void pushcli(void);
void popcli(void);

// string.c
int memcmp(const void*, const void*, uint);
void* memmove(void*, const void*, uint);
void* memset(void*, int, uint);
char* safestrcpy(char*, const char*, int);
int strlen(const char*);
int strncmp(const char*, const char*, uint);
char* strncpy(char*, const char*, int);

// syscall.c
int argint(int, int*);
int argptr(int, char**, int);
int argstr(int, char**);
int fetchint(uint, int*);
int fetchstr(uint, char**);
void syscall(void);

// timer.c
void timerinit(void);

// trap.c
void idtinit(void);
extern uint ticks;
void tvinit(void);
extern struct spinlock tickslock;

// uart.c
void uartinit(void);
void uartintr(void);
void uartputc(int);

// vm.c
void seginit(void);
void kvmalloc(void);
void vmenable(void);
pde_t* setupkvm(void);
char* uva2ka(pde_t*, char*);
int allocuvm(pde_t*, uint, uint);
int deallocuvm(pde_t*, uint, uint);
void freevm(pde_t*);
void inituvm(pde_t*, char*, uint);
int loaduvm(pde_t*, char*, struct inode*, uint, uint);
pde_t* copyuvm(pde_t*, uint);
void switchuvm(struct proc*);
void switchkvm(void);
int copyout(pde_t*, uint, void*, uint);
void clearpteu(pde_t *pgdir, char *uva);

// number of elements in fixed-size array
#define NELEM(x) (sizeof(x)/sizeof((x)[0]))

param.h

#define NPROC 64 // maximum number of processes
#define KSTACKSIZE 4096 // size of per-process kernel stack
#define NCPU 8 // maximum number of CPUs
#define NOFILE 16 // open files per process
#define NFILE 100 // open files per system
#define NINODE 50 // maximum number of active i-nodes
#define NDEV 10 // maximum major device number
#define ROOTDEV 1 // device number of file system root disk
#define MAXARG 32 // max exec arguments
#define MAXOPBLOCKS 10 // max # of blocks any FS op writes
#define LOGSIZE (MAXOPBLOCKS*3) // max data blocks in on-disk log
#define NBUF (MAXOPBLOCKS*3) // size of disk block cache
#define FSSIZE 1000 // size of file system in blocks

memlayout.h

// Memory layout

#define EXTMEM 0x100000 // Start of extended memory
#define PHYSTOP 0xE000000 // Top physical memory
#define DEVSPACE 0xFE000000 // Other devices are at high addresses

// Key addresses for address space layout (see kmap in vm.c for layout)
#define KERNBASE 0x80000000 // First kernel virtual address
#define KERNLINK (KERNBASE+EXTMEM) // Address where kernel is linked

#ifndef __ASSEMBLER__

static inline uint v2p(void *a) { return ((uint) (a)) - KERNBASE; }
static inline void *p2v(uint a) { return (void *) ((a) + KERNBASE); }

#endif

#define V2P(a) (((uint) (a)) - KERNBASE)
#define P2V(a) (((void *) (a)) + KERNBASE)

#define V2P_WO(x) ((x) - KERNBASE) // same as V2P, but without casts
#define P2V_WO(x) ((x) + KERNBASE) // same as P2V, but without casts

mmu.h

// This file contains definitions for the
// x86 memory management unit (MMU).

// Eflags register
#define FL_CF 0x00000001 // Carry Flag
#define FL_PF 0x00000004 // Parity Flag
#define FL_AF 0x00000010 // Auxiliary carry Flag
#define FL_ZF 0x00000040 // Zero Flag
#define FL_SF 0x00000080 // Sign Flag
#define FL_TF 0x00000100 // Trap Flag
#define FL_IF 0x00000200 // Interrupt Enable
#define FL_DF 0x00000400 // Direction Flag
#define FL_OF 0x00000800 // Overflow Flag
#define FL_IOPL_MASK 0x00003000 // I/O Privilege Level bitmask
#define FL_IOPL_0 0x00000000 // IOPL == 0
#define FL_IOPL_1 0x00001000 // IOPL == 1
#define FL_IOPL_2 0x00002000 // IOPL == 2
#define FL_IOPL_3 0x00003000 // IOPL == 3
#define FL_NT 0x00004000 // Nested Task
#define FL_RF 0x00010000 // Resume Flag
#define FL_VM 0x00020000 // Virtual 8086 mode
#define FL_AC 0x00040000 // Alignment Check
#define FL_VIF 0x00080000 // Virtual Interrupt Flag
#define FL_VIP 0x00100000 // Virtual Interrupt Pending
#define FL_ID 0x00200000 // ID flag

// Control Register flags
#define CR0_PE 0x00000001 // Protection Enable
#define CR0_MP 0x00000002 // Monitor coProcessor
#define CR0_EM 0x00000004 // Emulation
#define CR0_TS 0x00000008 // Task Switched
#define CR0_ET 0x00000010 // Extension Type
#define CR0_NE 0x00000020 // Numeric Errror
#define CR0_WP 0x00010000 // Write Protect
#define CR0_AM 0x00040000 // Alignment Mask
#define CR0_NW 0x20000000 // Not Writethrough
#define CR0_CD 0x40000000 // Cache Disable
#define CR0_PG 0x80000000 // Paging

#define CR4_PSE 0x00000010 // Page size extension

#define SEG_KCODE 1 // kernel code
#define SEG_KDATA 2 // kernel data+stack
#define SEG_KCPU 3 // kernel per-cpu data
#define SEG_UCODE 4 // user code
#define SEG_UDATA 5 // user data+stack
#define SEG_TSS 6 // this process's task state

//PAGEBREAK!
#ifndef __ASSEMBLER__
// Segment Descriptor
struct segdesc {
uint lim_15_0 : 16; // Low bits of segment limit
uint base_15_0 : 16; // Low bits of segment base address
uint base_23_16 : 8; // Middle bits of segment base address
uint type : 4; // Segment type (see STS_ constants)
uint s : 1; // 0 = system, 1 = application
uint dpl : 2; // Descriptor Privilege Level
uint p : 1; // Present
uint lim_19_16 : 4; // High bits of segment limit
uint avl : 1; // Unused (available for software use)
uint rsv1 : 1; // Reserved
uint db : 1; // 0 = 16-bit segment, 1 = 32-bit segment
uint g : 1; // Granularity: limit scaled by 4K when set
uint base_31_24 : 8; // High bits of segment base address
};

// Normal segment
#define SEG(type, base, lim, dpl) (struct segdesc)
{ ((lim) >> 12) & 0xffff, (uint)(base) & 0xffff,
((uint)(base) >> 16) & 0xff, type, 1, dpl, 1,
(uint)(lim) >> 28, 0, 0, 1, 1, (uint)(base) >> 24 }
#define SEG16(type, base, lim, dpl) (struct segdesc)
{ (lim) & 0xffff, (uint)(base) & 0xffff,
((uint)(base) >> 16) & 0xff, type, 1, dpl, 1,
(uint)(lim) >> 16, 0, 0, 1, 0, (uint)(base) >> 24 }
#endif

#define DPL_USER 0x3 // User DPL

// Application segment type bits
#define STA_X 0x8 // Executable segment
#define STA_E 0x4 // Expand down (non-executable segments)
#define STA_C 0x4 // Conforming code segment (executable only)
#define STA_W 0x2 // Writeable (non-executable segments)
#define STA_R 0x2 // Readable (executable segments)
#define STA_A 0x1 // Accessed

// System segment type bits
#define STS_T16A 0x1 // Available 16-bit TSS
#define STS_LDT 0x2 // Local Descriptor Table
#define STS_T16B 0x3 // Busy 16-bit TSS
#define STS_CG16 0x4 // 16-bit Call Gate
#define STS_TG 0x5 // Task Gate / Coum Transmitions
#define STS_IG16 0x6 // 16-bit Interrupt Gate
#define STS_TG16 0x7 // 16-bit Trap Gate
#define STS_T32A 0x9 // Available 32-bit TSS
#define STS_T32B 0xB // Busy 32-bit TSS
#define STS_CG32 0xC // 32-bit Call Gate
#define STS_IG32 0xE // 32-bit Interrupt Gate
#define STS_TG32 0xF // 32-bit Trap Gate

// A virtual address 'la' has a three-part structure as follows:
//
// +--------10------+-------10-------+---------12----------+
// | Page Directory | Page Table | Offset within Page |
// | Index | Index | |
// +----------------+----------------+---------------------+
// --- PDX(va) --/ --- PTX(va) --/

// page directory index
#define PDX(va) (((uint)(va) >> PDXSHIFT) & 0x3FF)

// page table index
#define PTX(va) (((uint)(va) >> PTXSHIFT) & 0x3FF)

// construct virtual address from indexes and offset
#define PGADDR(d, t, o) ((uint)((d) << PDXSHIFT | (t) << PTXSHIFT | (o)))

// Page directory and page table constants.
#define NPDENTRIES 1024 // # directory entries per page directory
#define NPTENTRIES 1024 // # PTEs per page table
#define PGSIZE 4096 // bytes mapped by a page

#define PGSHIFT 12 // log2(PGSIZE)
#define PTXSHIFT 12 // offset of PTX in a linear address
#define PDXSHIFT 22 // offset of PDX in a linear address

#define PGROUNDUP(sz) (((sz)+PGSIZE-1) & ~(PGSIZE-1))
#define PGROUNDDOWN(a) (((a)) & ~(PGSIZE-1))

// Page table/directory entry flags.
#define PTE_P 0x001 // Present
#define PTE_W 0x002 // Writeable
#define PTE_U 0x004 // User
#define PTE_PWT 0x008 // Write-Through
#define PTE_PCD 0x010 // Cache-Disable
#define PTE_A 0x020 // Accessed
#define PTE_D 0x040 // Dirty
#define PTE_PS 0x080 // Page Size
#define PTE_MBZ 0x180 // Bits must be zero

// Address in page table or page directory entry
#define PTE_ADDR(pte) ((uint)(pte) & ~0xFFF)
#define PTE_FLAGS(pte) ((uint)(pte) & 0xFFF)

#ifndef __ASSEMBLER__
typedef uint pte_t;

// Task state segment format
struct taskstate {
uint link; // Old ts selector
uint esp0; // Stack pointers and segment selectors
ushort ss0; // after an increase in privilege level
ushort padding1;
uint *esp1;
ushort ss1;
ushort padding2;
uint *esp2;
ushort ss2;
ushort padding3;
void *cr3; // Page directory base
uint *eip; // Saved state from last task switch
uint eflags;
uint eax; // More saved state (registers)
uint ecx;
uint edx;
uint ebx;
uint *esp;
uint *ebp;
uint esi;
uint edi;
ushort es; // Even more saved state (segment selectors)
ushort padding4;
ushort cs;
ushort padding5;
ushort ss;
ushort padding6;
ushort ds;
ushort padding7;
ushort fs;
ushort padding8;
ushort gs;
ushort padding9;
ushort ldt;
ushort padding10;
ushort t; // Trap on task switch
ushort iomb; // I/O map base address
};

// PAGEBREAK: 12
// Gate descriptors for interrupts and traps
struct gatedesc {
uint off_15_0 : 16; // low 16 bits of offset in segment
uint cs : 16; // code segment selector
uint args : 5; // # args, 0 for interrupt/trap gates
uint rsv1 : 3; // reserved(should be zero I guess)
uint type : 4; // type(STS_{TG,IG32,TG32})
uint s : 1; // must be 0 (system)
uint dpl : 2; // descriptor(meaning new) privilege level
uint p : 1; // Present
uint off_31_16 : 16; // high bits of offset in segment
};

// Set up a normal interrupt/trap gate descriptor.
// - istrap: 1 for a trap (= exception) gate, 0 for an interrupt gate.
// interrupt gate clears FL_IF, trap gate leaves FL_IF alone
// - sel: Code segment selector for interrupt/trap handler
// - off: Offset in code segment for interrupt/trap handler
// - dpl: Descriptor Privilege Level -
// the privilege level required for software to invoke
// this interrupt/trap gate explicitly using an int instruction.
#define SETGATE(gate, istrap, sel, off, d)
{
(gate).off_15_0 = (uint)(off) & 0xffff;
(gate).cs = (sel);
(gate).args = 0;
(gate).rsv1 = 0;
(gate).type = (istrap) ? STS_TG32 : STS_IG32;
(gate).s = 0;
(gate).dpl = (d);
(gate).p = 1;
(gate).off_31_16 = (uint)(off) >> 16;
}

#endif

x86.h

// Routines to let C code use special x86 instructions.

static inline uchar
inb(ushort port)
{
uchar data;

asm volatile("in %1,%0" : "=a" (data) : "d" (port));
return data;
}

static inline void
insl(int port, void *addr, int cnt)
{
asm volatile("cld; rep insl" :
"=D" (addr), "=c" (cnt) :
"d" (port), "0" (addr), "1" (cnt) :
"memory", "cc");
}

static inline void
outb(ushort port, uchar data)
{
asm volatile("out %0,%1" : : "a" (data), "d" (port));
}

static inline void
outw(ushort port, ushort data)
{
asm volatile("out %0,%1" : : "a" (data), "d" (port));
}

static inline void
outsl(int port, const void *addr, int cnt)
{
asm volatile("cld; rep outsl" :
"=S" (addr), "=c" (cnt) :
"d" (port), "0" (addr), "1" (cnt) :
"cc");
}

static inline void
stosb(void *addr, int data, int cnt)
{
asm volatile("cld; rep stosb" :
"=D" (addr), "=c" (cnt) :
"0" (addr), "1" (cnt), "a" (data) :
"memory", "cc");
}

static inline void
stosl(void *addr, int data, int cnt)
{
asm volatile("cld; rep stosl" :
"=D" (addr), "=c" (cnt) :
"0" (addr), "1" (cnt), "a" (data) :
"memory", "cc");
}

struct segdesc;

static inline void
lgdt(struct segdesc *p, int size)
{
volatile ushort pd[3];

pd[0] = size-1;
pd[1] = (uint)p;
pd[2] = (uint)p >> 16;

asm volatile("lgdt (%0)" : : "r" (pd));
}

struct gatedesc;

static inline void
lidt(struct gatedesc *p, int size)
{
volatile ushort pd[3];

pd[0] = size-1;
pd[1] = (uint)p;
pd[2] = (uint)p >> 16;

asm volatile("lidt (%0)" : : "r" (pd));
}

static inline void
ltr(ushort sel)
{
asm volatile("ltr %0" : : "r" (sel));
}

static inline uint
readeflags(void)
{
uint eflags;
asm volatile("pushfl; popl %0" : "=r" (eflags));
return eflags;
}

static inline void
loadgs(ushort v)
{
asm volatile("movw %0, %%gs" : : "r" (v));
}

static inline void
cli(void)
{
asm volatile("cli");
}

static inline void
sti(void)
{
asm volatile("sti");
}

static inline uint
xchg(volatile uint *addr, uint newval)
{
uint result;
  
// The + in "+m" denotes a read-modify-write operand.
asm volatile("lock; xchgl %0, %1" :
"+m" (*addr), "=a" (result) :
"1" (newval) :
"cc");
return result;
}

static inline uint
rcr2(void)
{
uint val;
asm volatile("movl %%cr2,%0" : "=r" (val));
return val;
}

static inline void
lcr3(uint val)
{
asm volatile("movl %0,%%cr3" : : "r" (val));
}

//PAGEBREAK: 36
// Layout of the trap frame built on the stack by the
// hardware and by trapasm.S, and passed to trap().
struct trapframe {
// registers as pushed by pusha
uint edi;
uint esi;
uint ebp;
uint oesp; // useless & ignored
uint ebx;
uint edx;
uint ecx;
uint eax;

// rest of trap frame
ushort gs;
ushort padding1;
ushort fs;
ushort padding2;
ushort es;
ushort padding3;
ushort ds;
ushort padding4;
uint trapno;

// below here defined by x86 hardware
uint err;
uint eip;
ushort cs;
ushort padding5;
uint eflags;

// below here only when crossing rings, such as from user to kernel
uint esp;
ushort ss;
ushort padding6;
};

spinlock.h

// Mutual exclusion spin locks.

#include "types.h"
#include "defs.h"
#include "param.h"
#include "x86.h"
#include "memlayout.h"
#include "mmu.h"
#include "proc.h"
#include "spinlock.h"

void
initlock(struct spinlock *lk, char *name)
{
lk->name = name;
lk->locked = 0;
lk->cpu = 0;
}

// Acquire the lock.
// Loops (spins) until the lock is acquired.
// Holding a lock for a long time may cause
// other CPUs to waste time spinning to acquire it.
void
acquire(struct spinlock *lk)
{
pushcli(); // disable interrupts to avoid deadlock.
if(holding(lk))
panic("acquire");

// The xchg is atomic.
// It also serializes, so that reads after acquire are not
// reordered before it.
while(xchg(&lk->locked, 1) != 0)
;

// Record info about lock acquisition for debugging.
lk->cpu = cpu;
getcallerpcs(&lk, lk->pcs);
}

// Release the lock.
void
release(struct spinlock *lk)
{
if(!holding(lk))
panic("release");

lk->pcs[0] = 0;
lk->cpu = 0;

// The xchg serializes, so that reads before release are
// not reordered after it. The 1996 PentiumPro manual (Volume 3,
// 7.2) says reads can be carried out speculatively and in
// any order, which implies we need to serialize here.
// But the 2007 Intel 64 Architecture Memory Ordering White
// Paper says that Intel 64 and IA-32 will not move a load
// after a store. So lock->locked = 0 would work here.
// The xchg being asm volatile ensures gcc emits it after
// the above assignments (and after the critical section).
xchg(&lk->locked, 0);

popcli();
}

// Record the current call stack in pcs[] by following the %ebp chain.
void
getcallerpcs(void *v, uint pcs[])
{
uint *ebp;
int i;
  
ebp = (uint*)v - 2;
for(i = 0; i < 10; i++){
if(ebp == 0 || ebp < (uint*)KERNBASE || ebp == (uint*)0xffffffff)
break;
pcs[i] = ebp[1]; // saved %eip
ebp = (uint*)ebp[0]; // saved %ebp
}
for(; i < 10; i++)
pcs[i] = 0;
}

// Check whether this cpu is holding the lock.
int
holding(struct spinlock *lock)
{
return lock->locked && lock->cpu == cpu;
}


// Pushcli/popcli are like cli/sti except that they are matched:
// it takes two popcli to undo two pushcli. Also, if interrupts
// are off, then pushcli, popcli leaves them off.

void
pushcli(void)
{
int eflags;
  
eflags = readeflags();
cli();
if(cpu->ncli++ == 0)
cpu->intena = eflags & FL_IF;
}

void
popcli(void)
{
if(readeflags()&FL_IF)
panic("popcli - interruptible");
if(--cpu->ncli < 0)
panic("popcli");
if(cpu->ncli == 0 && cpu->intena)
sti();
}

Explanation / Answer

#include "types.h"

#include "defs.h"

#include "param.h"

#include "memlayout.h"

#include "mmu.h"

#include "x86.h"

#include "proc.h"

#include "spinlock.h"

#include "pstat.h"

#define NULL 0

struct proc* q0[64];

struct proc* q1[64];

struct proc* q2[64];

struct proc* q3[64];

int c0 =-1;

int c1=-1;

int c2=-1;

int c3=-1;

int clkPerPrio[4] ={1,2,4,8};

struct pstat pstat_var;

struct {

struct spinlock lock;

struct proc proc[NPROC];

} ptable;

static struct proc *initproc;

int nextpid = 1;

extern void forkret(void);

extern void trapret(void);

static void wakeup1(void *chan);

void

pinit(void)

{

initlock(&ptable.lock, "ptable");

}

//PAGEBREAK: 32

// Look in the process table for an UNUSED proc.

// If found, change state to EMBRYO and initialize

// state required to run in the kernel.

// Otherwise return 0.

static struct proc*

allocproc(void)

{

struct proc *p;

char *sp;

acquire(&ptable.lock);

for(p = ptable.proc; p < &ptable.proc[NPROC]; p++)

if(p->state == UNUSED)

goto found;

release(&ptable.lock);

return 0;

found:

p->state = EMBRYO;

p->pid = nextpid++;

release(&ptable.lock);

// Allocate kernel stack.

if((p->kstack = kalloc()) == 0){

p->state = UNUSED;

return 0;

}

sp = p->kstack + KSTACKSIZE;

  

// Leave room for trap frame.

sp -= sizeof *p->tf;

p->tf = (struct trapframe*)sp;

  

// Set up new context to start executing at forkret,

// which returns to trapret.

sp -= 4;

*(uint*)sp = (uint)trapret;

sp -= sizeof *p->context;

p->context = (struct context*)sp;

memset(p->context, 0, sizeof *p->context);

p->context->eip = (uint)forkret;

return p;

}

//PAGEBREAK: 32

// Set up first user process.

void

userinit(void)

{

struct proc *p;

extern char _binary_initcode_start[], _binary_initcode_size[];

  

p = allocproc();

initproc = p;

if((p->pgdir = setupkvm()) == 0)

panic("userinit: out of memory?");

inituvm(p->pgdir, _binary_initcode_start, (int)_binary_initcode_size);

p->sz = PGSIZE;

memset(p->tf, 0, sizeof(*p->tf));

p->tf->cs = (SEG_UCODE << 3) | DPL_USER;

p->tf->ds = (SEG_UDATA << 3) | DPL_USER;

p->tf->es = p->tf->ds;

p->tf->ss = p->tf->ds;

p->tf->eflags = FL_IF;

p->tf->esp = PGSIZE;

p->tf->eip = 0; // beginning of initcode.S

safestrcpy(p->name, "initcode", sizeof(p->name));

p->cwd = namei("/");

p->state = RUNNABLE;

}

// Grow current process's memory by n bytes.

// Return 0 on success, -1 on failure.

int

growproc(int n)

{

uint sz;

  

sz = proc->sz;

if(n > 0){

if((sz = allocuvm(proc->pgdir, sz, sz + n)) == 0)

return -1;

} else if(n < 0){

if((sz = deallocuvm(proc->pgdir, sz, sz + n)) == 0)

return -1;

}

proc->sz = sz;

switchuvm(proc);

return 0;

}

// Create a new process copying p as the parent.

// Sets up stack to return as if from system call.

// Caller must set state of returned proc to RUNNABLE.

int

fork(void)

{

int i, pid;

struct proc *np;

// Allocate process.

if((np = allocproc()) == 0)

return -1;

// Copy process state from p.

if((np->pgdir = copyuvm(proc->pgdir, proc->sz)) == 0){

kfree(np->kstack);

np->kstack = 0;

np->state = UNUSED;

return -1;

}

np->sz = proc->sz;

np->parent = proc;

*np->tf = *proc->tf;

// Clear %eax so that fork returns 0 in the child.

np->tf->eax = 0;

for(i = 0; i < NOFILE; i++)

if(proc->ofile[i])

np->ofile[i] = filedup(proc->ofile[i]);

np->cwd = idup(proc->cwd);

safestrcpy(np->name, proc->name, sizeof(proc->name));

pid = np->pid;

// lock to force the compiler to emit the np->state write last.

acquire(&ptable.lock);

np->state = RUNNABLE;

release(&ptable.lock);

  

return pid;

}

// Exit the current process. Does not return.

// An exited process remains in the zombie state

// until its parent calls wait() to find out it exited.

void

exit(void)

{

struct proc *p;

int fd;

if(proc == initproc)

panic("init exiting");

// Close all open files.

for(fd = 0; fd < NOFILE; fd++){

if(proc->ofile[fd]){

fileclose(proc->ofile[fd]);

proc->ofile[fd] = 0;

}

}

begin_op();

iput(proc->cwd);

end_op();

proc->cwd = 0;

acquire(&ptable.lock);

// Parent might be sleeping in wait().

wakeup1(proc->parent);

// Pass abandoned children to init.

for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){

if(p->parent == proc){

p->parent = initproc;

if(p->state == ZOMBIE)

wakeup1(initproc);

}

}

// Jump into the scheduler, never to return.

proc->state = ZOMBIE;

sched();

panic("zombie exit");

}

// Wait for a child process to exit and return its pid.

// Return -1 if this process has no children.

int

wait(void)

{

struct proc *p;

int havekids, pid;

acquire(&ptable.lock);

for(;;){

// Scan through table looking for zombie children.

havekids = 0;

for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){

if(p->parent != proc)

continue;

havekids = 1;

if(p->state == ZOMBIE){

// Found one.

pid = p->pid;

kfree(p->kstack);

p->kstack = 0;

freevm(p->pgdir);

p->state = UNUSED;

p->pid = 0;

p->parent = 0;

p->name[0] = 0;

p->killed = 0;

release(&ptable.lock);

return pid;

}

}

// No point waiting if we don't have any children.

if(!havekids || proc->killed){

release(&ptable.lock);

return -1;

}

// Wait for children to exit. (See wakeup1 call in proc_exit.)

sleep(proc, &ptable.lock); //DOC: wait-sleep

}

}

//PAGEBREAK: 42

// Per-CPU process scheduler.

// Each CPU calls scheduler() after setting itself up.

// Scheduler never returns. It loops, doing:

// - choose a process to run

// - swtch to start running that process

// - eventually that process transfers control

// via swtch back to the scheduler.

void

scheduler (void)

{

struct proc *p;

int i;

int j;

for(;;){

// Enable interrupts on this processor.

sti();

// Loop over process table looking for process to run.

acquire(&ptable.lock);

if(c0!=-1){

for(i=0;i<=c0;i++){

if(q0[i]->state != RUNNABLE)

continue;

p=q0[i];

proc = q0[i];

p->clicks++;

switchuvm(p);

p->state = RUNNING;

swtch(&cpu->scheduler, proc->context);

switchkvm();

pstat_var.ticks[p->pid][0]=p->clicks;

if(p->clicks ==clkPerPrio[0]){

/*copy proc to lower priority queue*/

c1++;

proc->priority=proc->priority+1;

pstat_var.priority[proc->pid] = proc->priority;

q1[c1] = proc;

/*delete proc from q0*/

q0[i]=NULL;

for(j=i;j<=c0-1;j++)

q0[j] = q0[j+1];

q0[c0] = NULL;

proc->clicks = 0;

c0--;

}

proc = 0;

}

}

if(c1!=-1){

for(i=0;i<=c1;i++){

if(q1[i]->state != RUNNABLE)

continue;

p=q1[i];

proc = q1[i];

proc->clicks++;

switchuvm(p);

p->state = RUNNING;

swtch(&cpu->scheduler, proc->context);

switchkvm();

pstat_var.ticks[p->pid][1]=p->clicks;;

if(p->clicks ==clkPerPrio[1]){

/*copy proc to lower priority queue*/

c2++;

proc->priority=proc->priority+1;

pstat_var.priority[proc->pid] = proc->priority;

q2[c2] = proc;

/*delete proc from q0*/

q1[i]=NULL;

for(j=i;j<=c1-1;j++)

q1[j] = q1[j+1];

q1[c1] = NULL;

proc->clicks = 0;

c1--;

}

proc = 0;

}

}

if(c2!=-1){

for(i=0;i<=c2;i++){

if(q2[i]->state != RUNNABLE)

continue;

p=q2[i];

proc = q2[i];

proc->clicks++;

switchuvm(p);

p->state = RUNNING;

swtch(&cpu->scheduler, proc->context);

switchkvm();

pstat_var.ticks[p->pid][2]=p->clicks;;

if(p->clicks ==clkPerPrio[2]){

/*copy proc to lower priority queue*/

c3++;

proc->priority=proc->priority+1;

pstat_var.priority[p->pid] = p->priority;

q3[c3] = proc;

/*delete proc from q0*/

q2[i]=NULL;

for(j=i;j<=c2-1;j++)

q2[j] = q2[j+1];

q2[c2] =NULL;

proc->clicks = 0;

c2--;

}

proc = 0;

}

}

if(c3!=-1){

for(i=0;i<=c3;i++){

if(q3[i]->state != RUNNABLE)

continue;

p=q3[i];

proc = q3[i];

proc->clicks++;

switchuvm(p);

p->state = RUNNING;

swtch(&cpu->scheduler, proc->context);

switchkvm();

pstat_var.priority[p->pid] = p->priority;

pstat_var.ticks[p->pid][3]=p->clicks;;

/*move process to end of its own queue */

q3[i]=NULL;

for(j=i;j<=c3-1;j++)

q3[j] = q3[j+1];

q3[c3] = proc;

proc = 0;

}

}

release(&ptable.lock);

}

}

// Enter scheduler. Must hold only ptable.lock

// and have changed proc->state.

void

sched(void)

{

int intena;

if(!holding(&ptable.lock))

panic("sched ptable.lock");

if(cpu->ncli != 1)

panic("sched locks");

if(proc->state == RUNNING)

panic("sched running");

if(readeflags()&FL_IF)

panic("sched interruptible");

intena = cpu->intena;

swtch(&proc->context, cpu->scheduler);

cpu->intena = intena;

}

// Give up the CPU for one scheduling round.

void

yield(void)

{

acquire(&ptable.lock); //DOC: yieldlock

proc->state = RUNNABLE;

sched();

release(&ptable.lock);

}

// A fork child's very first scheduling by scheduler()

// will swtch here. "Return" to user space.

void

forkret(void)

{

static int first = 1;

// Still holding ptable.lock from scheduler.

release(&ptable.lock);

if (first) {

// Some initialization functions must be run in the context

// of a regular process (e.g., they call sleep), and thus cannot

// be run from main().

first = 0;

iinit(ROOTDEV);

initlog(ROOTDEV);

}

  

// Return to "caller", actually trapret (see allocproc).

}

// Atomically release lock and sleep on chan.

// Reacquires lock when awakened.

void

sleep(void *chan, struct spinlock *lk)

{

if(proc == 0)

panic("sleep");

if(lk == 0)

panic("sleep without lk");

// Must acquire ptable.lock in order to

// change p->state and then call sched.

// Once we hold ptable.lock, we can be

// guaranteed that we won't miss any wakeup

// (wakeup runs with ptable.lock locked),

// so it's okay to release lk.

if(lk != &ptable.lock){ //DOC: sleeplock0

acquire(&ptable.lock); //DOC: sleeplock1

release(lk);

}

// Go to sleep.

proc->chan = chan;

proc->state = SLEEPING;

sched();

// Tidy up.

proc->chan = 0;

// Reacquire original lock.

if(lk != &ptable.lock){ //DOC: sleeplock2

release(&ptable.lock);

acquire(lk);

}

}

//PAGEBREAK!

// Wake up all processes sleeping on chan.

// The ptable lock must be held.

static void

wakeup1(void *chan)

{

struct proc *p;

for(p = ptable.proc; p < &ptable.proc[NPROC]; p++)

if(p->state == SLEEPING && p->chan == chan)

p->state = RUNNABLE;

}

// Wake up all processes sleeping on chan.

void

wakeup(void *chan)

{

acquire(&ptable.lock);

wakeup1(chan);

release(&ptable.lock);

}

// Kill the process with the given pid.

// Process won't exit until it returns

// to user space (see trap in trap.c).

int

kill(int pid)

{

struct proc *p;

acquire(&ptable.lock);

for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){

if(p->pid == pid){

p->killed = 1;

// Wake process from sleep if necessary.

if(p->state == SLEEPING)

p->state = RUNNABLE;

release(&ptable.lock);

return 0;

}

}

release(&ptable.lock);

return -1;

}

//PAGEBREAK: 36

// 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",

[EMBRYO] "embryo",

[SLEEPING] "sleep ",

[RUNNABLE] "runble",

[RUNNING] "run ",

[ZOMBIE] "zombie"

};

int i;

struct proc *p;

char *state;

uint pc[10];

  

for(p = ptable.proc; p < &ptable.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 = "???";

cprintf("%d %s %s", p->pid, state, p->name);

if(p->state == SLEEPING){

getcallerpcs((uint*)p->context->ebp+2, pc);

for(i=0; i<10 && pc[i] != 0; i++)

cprintf(" %p", pc[i]);

}

cprintf(" ");

}

}

proc.h

// Segments in proc->gdt.

#define NSEGS 7

// Per-CPU state

struct cpu {

uchar id; // Local APIC ID; index into cpus[] below

struct context *scheduler; // swtch() here to enter scheduler

struct taskstate ts; // Used by x86 to find stack for interrupt

struct segdesc gdt[NSEGS]; // x86 global descriptor table

volatile uint started; // Has the CPU started?

int ncli; // Depth of pushcli nesting.

int intena; // Were interrupts enabled before pushcli?

  

// Cpu-local storage variables; see below

struct cpu *cpu;

struct proc *proc; // The currently-running process.

};

extern struct cpu cpus[NCPU];

extern int ncpu;

// Per-CPU variables, holding pointers to the

// current cpu and to the current process.

// The asm suffix tells gcc to use "%gs:0" to refer to cpu

// and "%gs:4" to refer to proc. seginit sets up the

// %gs segment register so that %gs refers to the memory

// holding those two variables in the local cpu's struct cpu.

// This is similar to how thread-local variables are implemented

// in thread libraries such as Linux pthreads.

extern struct cpu *cpu asm("%gs:0"); // &cpus[cpunum()]

extern struct proc *proc asm("%gs:4"); // cpus[cpunum()].proc

//PAGEBREAK: 17

// Saved registers for kernel context switches.

// Don't need to save all the segment registers (%cs, etc),

// because they are constant across kernel contexts.

// Don't need to save %eax, %ecx, %edx, because the

// x86 convention is that the caller has saved them.

// Contexts are stored at the bottom of the stack they

// describe; the stack pointer is the address of the context.

// The layout of the context matches the layout of the stack in swtch.S

// at the "Switch stacks" comment. Switch doesn't save eip explicitly,

// but it is on the stack and allocproc() manipulates it.

struct context {

uint edi;

uint esi;

uint ebx;

uint ebp;

uint eip;

};

extern struct proc* q0[64];

extern struct proc* q1[64];

extern struct proc* q2[64];

extern struct proc* q3[64];

extern int c0;

extern int c1;

extern int c2;

extern int c3;

extern struct pstat pstat_var;

enum procstate { UNUSED, EMBRYO, SLEEPING, RUNNABLE, RUNNING, ZOMBIE };

// Per-process state

struct proc {

uint sz; // Size of process memory (bytes)

pde_t* pgdir; // Page table

char *kstack; // Bottom of kernel stack for this process

enum procstate state; // Process state

int pid; // Process ID

struct proc *parent; // Parent process

struct trapframe *tf; // Trap frame for current syscall

struct context *context; // swtch() here to run process

void *chan; // If non-zero, sleeping on chan

int killed; // If non-zero, have been killed

struct file *ofile[NOFILE]; // Open files

struct inode *cwd; // Current directory

char name[16]; // Process name (debugging)

int clicks; //number of timer clicks the process has run for

int priority; //current priority of process

};

// Process memory is laid out contiguously, low addresses first:

// text

// original data and bss

// fixed-size stack

// expandable heap

pstat.h

#ifndef _PSTAT_H_
#define _PSTAT_H_

#include "param.h"

struct pstat {
int inuse[NPROC]; // whether this slot of the process table is in use (1 or 0)
int pid[NPROC]; // PID of each process
int priority[NPROC]; // current priority level of each process (0-3)
int ticks[NPROC][4]; // number of ticks each process has accumulated at each of 4 priorities
};


#endif // _PSTAT_H_

types.h

typedef unsigned int uint;
typedef unsigned short ushort;
typedef unsigned char uchar;
typedef uint pde_t;

defs.h

struct buf;
struct context;
struct file;
struct inode;
struct pipe;
struct proc;
struct rtcdate;
struct spinlock;
struct stat;
struct superblock;

// bio.c
void binit(void);
struct buf* bread(uint, uint);
void brelse(struct buf*);
void bwrite(struct buf*);

// console.c
void consoleinit(void);
void cprintf(char*, ...);
void consoleintr(int(*)(void));
void panic(char*) __attribute__((noreturn));

// exec.c
int exec(char*, char**);

// file.c
struct file* filealloc(void);
void fileclose(struct file*);
struct file* filedup(struct file*);
void fileinit(void);
int fileread(struct file*, char*, int n);
int filestat(struct file*, struct stat*);
int filewrite(struct file*, char*, int n);

// fs.c
void readsb(int dev, struct superblock *sb);
int dirlink(struct inode*, char*, uint);
struct inode* dirlookup(struct inode*, char*, uint*);
struct inode* ialloc(uint, short);
struct inode* idup(struct inode*);
void iinit(int dev);
void ilock(struct inode*);
void iput(struct inode*);
void iunlock(struct inode*);
void iunlockput(struct inode*);
void iupdate(struct inode*);
int namecmp(const char*, const char*);
struct inode* namei(char*);
struct inode* nameiparent(char*, char*);
int readi(struct inode*, char*, uint, uint);
void stati(struct inode*, struct stat*);
int writei(struct inode*, char*, uint, uint);

// ide.c
void ideinit(void);
void ideintr(void);
void iderw(struct buf*);

// ioapic.c
void ioapicenable(int irq, int cpu);
extern uchar ioapicid;
void ioapicinit(void);

// kalloc.c
char* kalloc(void);
void kfree(char*);
void kinit1(void*, void*);
void kinit2(void*, void*);

// kbd.c
void kbdintr(void);

// lapic.c
void cmostime(struct rtcdate *r);
int cpunum(void);
extern volatile uint* lapic;
void lapiceoi(void);
void lapicinit(void);
void lapicstartap(uchar, uint);
void microdelay(int);

// log.c
void initlog(int dev);
void log_write(struct buf*);
void begin_op();
void end_op();

// mp.c
extern int ismp;
int mpbcpu(void);
void mpinit(void);
void mpstartthem(void);

// picirq.c
void picenable(int);
void picinit(void);

// pipe.c
int pipealloc(struct file**, struct file**);
void pipeclose(struct pipe*, int);
int piperead(struct pipe*, char*, int);
int pipewrite(struct pipe*, char*, int);

//PAGEBREAK: 16
// proc.c
struct proc* copyproc(struct proc*);
void exit(void);
int fork(void);
int growproc(int);
int kill(int);
void pinit(void);
void procdump(void);
void scheduler(void) __attribute__((noreturn));
void sched(void);
void sleep(void*, struct spinlock*);
void userinit(void);
int wait(void);
void wakeup(void*);
void yield(void);

// swtch.S
void swtch(struct context**, struct context*);

// spinlock.c
void acquire(struct spinlock*);
void getcallerpcs(void*, uint*);
int holding(struct spinlock*);
void initlock(struct spinlock*, char*);
void release(struct spinlock*);
void pushcli(void);
void popcli(void);

// string.c
int memcmp(const void*, const void*, uint);
void* memmove(void*, const void*, uint);
void* memset(void*, int, uint);
char* safestrcpy(char*, const char*, int);
int strlen(const char*);
int strncmp(const char*, const char*, uint);
char* strncpy(char*, const char*, int);

// syscall.c
int argint(int, int*);
int argptr(int, char**, int);
int argstr(int, char**);
int fetchint(uint, int*);
int fetchstr(uint, char**);
void syscall(void);

// timer.c
void timerinit(void);

// trap.c
void idtinit(void);
extern uint ticks;
void tvinit(void);
extern struct spinlock tickslock;

// uart.c
void uartinit(void);
void uartintr(void);
void uartputc(int);

// vm.c
void seginit(void);
void kvmalloc(void);
void vmenable(void);
pde_t* setupkvm(void);
char* uva2ka(pde_t*, char*);
int allocuvm(pde_t*, uint, uint);
int deallocuvm(pde_t*, uint, uint);
void freevm(pde_t*);
void inituvm(pde_t*, char*, uint);
int loaduvm(pde_t*, char*, struct inode*, uint, uint);
pde_t* copyuvm(pde_t*, uint);
void switchuvm(struct proc*);
void switchkvm(void);
int copyout(pde_t*, uint, void*, uint);
void clearpteu(pde_t *pgdir, char *uva);

// number of elements in fixed-size array
#define NELEM(x) (sizeof(x)/sizeof((x)[0]))

param.h

#define NPROC 64 // maximum number of processes
#define KSTACKSIZE 4096 // size of per-process kernel stack
#define NCPU 8 // maximum number of CPUs
#define NOFILE 16 // open files per process
#define NFILE 100 // open files per system
#define NINODE 50 // maximum number of active i-nodes
#define NDEV 10 // maximum major device number
#define ROOTDEV 1 // device number of file system root disk
#define MAXARG 32 // max exec arguments
#define MAXOPBLOCKS 10 // max # of blocks any FS op writes
#define LOGSIZE (MAXOPBLOCKS*3) // max data blocks in on-disk log
#define NBUF (MAXOPBLOCKS*3) // size of disk block cache
#define FSSIZE 1000 // size of file system in blocks

memlayout.h

// Memory layout

#define EXTMEM 0x100000 // Start of extended memory
#define PHYSTOP 0xE000000 // Top physical memory
#define DEVSPACE 0xFE000000 // Other devices are at high addresses

// Key addresses for address space layout (see kmap in vm.c for layout)
#define KERNBASE 0x80000000 // First kernel virtual address
#define KERNLINK (KERNBASE+EXTMEM) // Address where kernel is linked

#ifndef __ASSEMBLER__

static inline uint v2p(void *a) { return ((uint) (a)) - KERNBASE; }
static inline void *p2v(uint a) { return (void *) ((a) + KERNBASE); }

#endif

#define V2P(a) (((uint) (a)) - KERNBASE)
#define P2V(a) (((void *) (a)) + KERNBASE)

#define V2P_WO(x) ((x) - KERNBASE) // same as V2P, but without casts
#define P2V_WO(x) ((x) + KERNBASE) // same as P2V, but without casts

mmu.h

// This file contains definitions for the
// x86 memory management unit (MMU).

// Eflags register
#define FL_CF 0x00000001 // Carry Flag
#define FL_PF 0x00000004 // Parity Flag
#define FL_AF 0x00000010 // Auxiliary carry Flag
#define FL_ZF 0x00000040 // Zero Flag
#define FL_SF 0x00000080 // Sign Flag
#define FL_TF 0x00000100 // Trap Flag
#define FL_IF 0x00000200 // Interrupt Enable
#define FL_DF 0x00000400 // Direction Flag
#define FL_OF 0x00000800 // Overflow Flag
#define FL_IOPL_MASK 0x00003000 // I/O Privilege Level bitmask
#define FL_IOPL_0 0x00000000 // IOPL == 0
#define FL_IOPL_1 0x00001000 // IOPL == 1
#define FL_IOPL_2 0x00002000 // IOPL == 2
#define FL_IOPL_3 0x00003000 // IOPL == 3
#define FL_NT 0x00004000 // Nested Task
#define FL_RF 0x00010000 // Resume Flag
#define FL_VM 0x00020000 // Virtual 8086 mode
#define FL_AC 0x00040000 // Alignment Check
#define FL_VIF 0x00080000 // Virtual Interrupt Flag
#define FL_VIP 0x00100000 // Virtual Interrupt Pending
#define FL_ID 0x00200000 // ID flag

// Control Register flags
#define CR0_PE 0x00000001 // Protection Enable
#define CR0_MP 0x00000002 // Monitor coProcessor
#define CR0_EM 0x00000004 // Emulation
#define CR0_TS 0x00000008 // Task Switched
#define CR0_ET 0x00000010 // Extension Type
#define CR0_NE 0x00000020 // Numeric Errror
#define CR0_WP 0x00010000 // Write Protect
#define CR0_AM 0x00040000 // Alignment Mask
#define CR0_NW 0x20000000 // Not Writethrough
#define CR0_CD 0x40000000 // Cache Disable
#define CR0_PG 0x80000000 // Paging

#define CR4_PSE 0x00000010 // Page size extension

#define SEG_KCODE 1 // kernel code
#define SEG_KDATA 2 // kernel data+stack
#define SEG_KCPU 3 // kernel per-cpu data
#define SEG_UCODE 4 // user code
#define SEG_UDATA 5 // user data+stack
#define SEG_TSS 6 // this process's task state

//PAGEBREAK!
#ifndef __ASSEMBLER__
// Segment Descriptor
struct segdesc {
uint lim_15_0 : 16; // Low bits of segment limit
uint base_15_0 : 16; // Low bits of segment base address
uint base_23_16 : 8; // Middle bits of segment base address
uint type : 4; // Segment type (see STS_ constants)
uint s : 1; // 0 = system, 1 = application
uint dpl : 2; // Descriptor Privilege Level
uint p : 1; // Present
uint lim_19_16 : 4; // High bits of segment limit
uint avl : 1; // Unused (available for software use)
uint rsv1 : 1; // Reserved
uint db : 1; // 0 = 16-bit segment, 1 = 32-bit segment
uint g : 1; // Granularity: limit scaled by 4K when set
uint base_31_24 : 8; // High bits of segment base address
};

// Normal segment
#define SEG(type, base, lim, dpl) (struct segdesc)
{ ((lim) >> 12) & 0xffff, (uint)(base) & 0xffff,
((uint)(base) >> 16) & 0xff, type, 1, dpl, 1,
(uint)(lim) >> 28, 0, 0, 1, 1, (uint)(base) >> 24 }
#define SEG16(type, base, lim, dpl) (struct segdesc)
{ (lim) & 0xffff, (uint)(base) & 0xffff,
((uint)(base) >> 16) & 0xff, type, 1, dpl, 1,
(uint)(lim) >> 16, 0, 0, 1, 0, (uint)(base) >> 24 }
#endif

#define DPL_USER 0x3 // User DPL

// Application segment type bits
#define STA_X 0x8 // Executable segment
#define STA_E 0x4 // Expand down (non-executable segments)
#define STA_C 0x4 // Conforming code segment (executable only)
#define STA_W 0x2 // Writeable (non-executable segments)
#define STA_R 0x2 // Readable (executable segments)
#define STA_A 0x1 // Accessed

// System segment type bits
#define STS_T16A 0x1 // Available 16-bit TSS
#define STS_LDT 0x2 // Local Descriptor Table
#define STS_T16B 0x3 // Busy 16-bit TSS
#define STS_CG16 0x4 // 16-bit Call Gate
#define STS_TG 0x5 // Task Gate / Coum Transmitions
#define STS_IG16 0x6 // 16-bit Interrupt Gate
#define STS_TG16 0x7 // 16-bit Trap Gate
#define STS_T32A 0x9 // Available 32-bit TSS
#define STS_T32B 0xB // Busy 32-bit TSS
#define STS_CG32 0xC // 32-bit Call Gate
#define STS_IG32 0xE // 32-bit Interrupt Gate
#define STS_TG32 0xF // 32-bit Trap Gate

// A virtual address 'la' has a three-part structure as follows:
//
// +--------10------+-------10-------+---------12----------+
// | Page Directory | Page Table | Offset within Page |
// | Index | Index | |
// +----------------+----------------+---------------------+
// --- PDX(va) --/ --- PTX(va) --/

// page directory index
#define PDX(va) (((uint)(va) >> PDXSHIFT) & 0x3FF)

// page table index
#define PTX(va) (((uint)(va) >> PTXSHIFT) & 0x3FF)

// construct virtual address from indexes and offset
#define PGADDR(d, t, o) ((uint)((d) << PDXSHIFT | (t) << PTXSHIFT | (o)))

// Page directory and page table constants.
#define NPDENTRIES 1024 // # directory entries per page directory
#define NPTENTRIES 1024 // # PTEs per page table
#define PGSIZE 4096 // bytes mapped by a page

#define PGSHIFT 12 // log2(PGSIZE)
#define PTXSHIFT 12 // offset of PTX in a linear address
#define PDXSHIFT 22 // offset of PDX in a linear address

#define PGROUNDUP(sz) (((sz)+PGSIZE-1) & ~(PGSIZE-1))
#define PGROUNDDOWN(a) (((a)) & ~(PGSIZE-1))

// Page table/directory entry flags.
#define PTE_P 0x001 // Present
#define PTE_W 0x002 // Writeable
#define PTE_U 0x004 // User
#define PTE_PWT 0x008 // Write-Through
#define PTE_PCD 0x010 // Cache-Disable
#define PTE_A 0x020 // Accessed
#define PTE_D 0x040 // Dirty
#define PTE_PS 0x080 // Page Size
#define PTE_MBZ 0x180 // Bits must be zero

// Address in page table or page directory entry
#define PTE_ADDR(pte) ((uint)(pte) & ~0xFFF)
#define PTE_FLAGS(pte) ((uint)(pte) & 0xFFF)

#ifndef __ASSEMBLER__
typedef uint pte_t;

// Task state segment format
struct taskstate {
uint link; // Old ts selector
uint esp0; // Stack pointers and segment selectors
ushort ss0; // after an increase in privilege level
ushort padding1;
uint *esp1;
ushort ss1;
ushort padding2;
uint *esp2;
ushort ss2;
ushort padding3;
void *cr3; // Page directory base
uint *eip; // Saved state from last task switch
uint eflags;
uint eax; // More saved state (registers)
uint ecx;
uint edx;
uint ebx;
uint *esp;
uint *ebp;
uint esi;
uint edi;
ushort es; // Even more saved state (segment selectors)
ushort padding4;
ushort cs;
ushort padding5;
ushort ss;
ushort padding6;
ushort ds;
ushort padding7;
ushort fs;
ushort padding8;
ushort gs;
ushort padding9;
ushort ldt;
ushort padding10;
ushort t; // Trap on task switch
ushort iomb; // I/O map base address
};

// PAGEBREAK: 12
// Gate descriptors for interrupts and traps
struct gatedesc {
uint off_15_0 : 16; // low 16 bits of offset in segment
uint cs : 16; // code segment selector
uint args : 5; // # args, 0 for interrupt/trap gates
uint rsv1 : 3; // reserved(should be zero I guess)
uint type : 4; // type(STS_{TG,IG32,TG32})
uint s : 1; // must be 0 (system)
uint dpl : 2; // descriptor(meaning new) privilege level
uint p : 1; // Present
uint off_31_16 : 16; // high bits of offset in segment
};

// Set up a normal interrupt/trap gate descriptor.
// - istrap: 1 for a trap (= exception) gate, 0 for an interrupt gate.
// interrupt gate clears FL_IF, trap gate leaves FL_IF alone
// - sel: Code segment selector for interrupt/trap handler
// - off: Offset in code segment for interrupt/trap handler
// - dpl: Descriptor Privilege Level -
// the privilege level required for software to invoke
// this interrupt/trap gate explicitly using an int instruction.
#define SETGATE(gate, istrap, sel, off, d)
{
(gate).off_15_0 = (uint)(off) & 0xffff;
(gate).cs = (sel);
(gate).args = 0;
(gate).rsv1 = 0;
(gate).type = (istrap) ? STS_TG32 : STS_IG32;
(gate).s = 0;
(gate).dpl = (d);
(gate).p = 1;
(gate).off_31_16 = (uint)(off) >> 16;
}

#endif

x86.h

// Routines to let C code use special x86 instructions.

static inline uchar
inb(ushort port)
{
uchar data;

asm volatile("in %1,%0" : "=a" (data) : "d" (port));
return data;
}

static inline void
insl(int port, void *addr, int cnt)
{
asm volatile("cld; rep insl" :
"=D" (addr), "=c" (cnt) :
"d" (port), "0" (addr), "1" (cnt) :
"memory", "cc");
}

static inline void
outb(ushort port, uchar data)
{
asm volatile("out %0,%1" : : "a" (data), "d" (port));
}

static inline void
outw(ushort port, ushort data)
{
asm volatile("out %0,%1" : : "a" (data), "d" (port));
}

static inline void
outsl(int port, const void *addr, int cnt)
{
asm volatile("cld; rep outsl" :
"=S" (addr), "=c" (cnt) :
"d" (port), "0" (addr), "1" (cnt) :
"cc");
}

static inline void
stosb(void *addr, int data, int cnt)
{
asm volatile("cld; rep stosb" :
"=D" (addr), "=c" (cnt) :
"0" (addr), "1" (cnt), "a" (data) :
"memory", "cc");
}

static inline void
stosl(void *addr, int data, int cnt)
{
asm volatile("cld; rep stosl" :
"=D" (addr), "=c" (cnt) :
"0" (addr), "1" (cnt), "a" (data) :
"memory", "cc");
}

struct segdesc;

static inline void
lgdt(struct segdesc *p, int size)
{
volatile ushort pd[3];

pd[0] = size-1;
pd[1] = (uint)p;
pd[2] = (uint)p >> 16;

asm volatile("lgdt (%0)" : : "r" (pd));
}

struct gatedesc;

static inline void
lidt(struct gatedesc *p, int size)
{
volatile ushort pd[3];

pd[0] = size-1;
pd[1] = (uint)p;
pd[2] = (uint)p >> 16;

asm volatile("lidt (%0)" : : "r" (pd));
}

static inline void
ltr(ushort sel)
{
asm volatile("ltr %0" : : "r" (sel));
}

static inline uint
readeflags(void)
{
uint eflags;
asm volatile("pushfl; popl %0" : "=r" (eflags));
return eflags;
}

static inline void
loadgs(ushort v)
{
asm volatile("movw %0, %%gs" : : "r" (v));
}

static inline void
cli(void)
{
asm volatile("cli");
}

static inline void
sti(void)
{
asm volatile("sti");
}

static inline uint
xchg(volatile uint *addr, uint newval)
{
uint result;
  
// The + in "+m" denotes a read-modify-write operand.
asm volatile("lock; xchgl %0, %1" :
"+m" (*addr), "=a" (result) :
"1" (newval) :
"cc");
return result;
}

static inline uint
rcr2(void)
{
uint val;
asm volatile("movl %%cr2,%0" : "=r" (val));
return val;
}

static inline void
lcr3(uint val)
{
asm volatile("movl %0,%%cr3" : : "r" (val));
}

//PAGEBREAK: 36
// Layout of the trap frame built on the stack by the
// hardware and by trapasm.S, and passed to trap().
struct trapframe {
// registers as pushed by pusha
uint edi;
uint esi;
uint ebp;
uint oesp; // useless & ignored
uint ebx;
uint edx;
uint ecx;
uint eax;

// rest of trap frame
ushort gs;
ushort padding1;
ushort fs;
ushort padding2;
ushort es;
ushort padding3;
ushort ds;
ushort padding4;
uint trapno;

// below here defined by x86 hardware
uint err;
uint eip;
ushort cs;
ushort padding5;
uint eflags;

// below here only when crossing rings, such as from user to kernel
uint esp;
ushort ss;
ushort padding6;
};

spinlock.h

// Mutual exclusion spin locks.

#include "types.h"
#include "defs.h"
#include "param.h"
#include "x86.h"
#include "memlayout.h"
#include "mmu.h"
#include "proc.h"
#include "spinlock.h"

void
initlock(struct spinlock *lk, char *name)
{
lk->name = name;
lk->locked = 0;
lk->cpu = 0;
}

// Acquire the lock.
// Loops (spins) until the lock is acquired.
// Holding a lock for a long time may cause
// other CPUs to waste time spinning to acquire it.
void
acquire(struct spinlock *lk)
{
pushcli(); // disable interrupts to avoid deadlock.
if(holding(lk))
panic("acquire");

// The xchg is atomic.
// It also serializes, so that reads after acquire are not
// reordered before it.
while(xchg(&lk->locked, 1) != 0)
;

// Record info about lock acquisition for debugging.
lk->cpu = cpu;
getcallerpcs(&lk, lk->pcs);
}

// Release the lock.
void
release(struct spinlock *lk)
{
if(!holding(lk))
panic("release");

lk->pcs[0] = 0;
lk->cpu = 0;

// The xchg serializes, so that reads before release are
// not reordered after it. The 1996 PentiumPro manual (Volume 3,
// 7.2) says reads can be carried out speculatively and in
// any order, which implies we need to serialize here.
// But the 2007 Intel 64 Architecture Memory Ordering White
// Paper says that Intel 64 and IA-32 will not move a load
// after a store. So lock->locked = 0 would work here.
// The xchg being asm volatile ensures gcc emits it after
// the above assignments (and after the critical section).
xchg(&lk->locked, 0);

popcli();
}

// Record the current call stack in pcs[] by following the %ebp chain.
void
getcallerpcs(void *v, uint pcs[])
{
uint *ebp;
int i;
  
ebp = (uint*)v - 2;
for(i = 0; i < 10; i++){
if(ebp == 0 || ebp < (uint*)KERNBASE || ebp == (uint*)0xffffffff)
break;
pcs[i] = ebp[1]; // saved %eip
ebp = (uint*)ebp[0]; // saved %ebp
}
for(; i < 10; i++)
pcs[i] = 0;
}

// Check whether this cpu is holding the lock.
int
holding(struct spinlock *lock)
{
return lock->locked && lock->cpu == cpu;
}


// Pushcli/popcli are like cli/sti except that they are matched:
// it takes two popcli to undo two pushcli. Also, if interrupts
// are off, then pushcli, popcli leaves them off.

void
pushcli(void)
{
int eflags;
  
eflags = readeflags();
cli();
if(cpu->ncli++ == 0)
cpu->intena = eflags & FL_IF;
}

void
popcli(void)
{
if(readeflags()&FL_IF)
panic("popcli - interruptible");
if(--cpu->ncli < 0)
panic("popcli");
if(cpu->ncli == 0 && cpu->intena)
sti();
}