diff --git a/elf.h b/elf.h index da18dca..da852a9 100644 --- a/elf.h +++ b/elf.h @@ -1,9 +1,8 @@ -// -// format of an ELF executable file -// +// Format of an ELF executable file #define ELF_MAGIC 0x464C457FU // "\x7FELF" in little endian +// File header struct elfhdr { uint magic; // must equal ELF_MAGIC uchar elf[12]; @@ -22,6 +21,7 @@ struct elfhdr { ushort shstrndx; }; +// Program section header struct proghdr { uint type; uint offset; diff --git a/fs.h b/fs.h index 04e0b67..8687d16 100644 --- a/fs.h +++ b/fs.h @@ -1,12 +1,17 @@ -// on-disk file system format +// On-disk file system format. +// This header is shared between kernel and user space. + +// Block 0 is unused. +// Block 1 is super block. +// Inodes start at block 2. #define BSIZE 512 // block size -// sector 1 (2nd sector) -struct superblock{ - uint size; - uint nblocks; - uint ninodes; +// File system super block +struct superblock { + uint size; // Size of file system (bytes???) xxx + uint nblocks; // Number of blocks + uint ninodes; // Number of inodes. }; #define NADDRS (NDIRECT+1) @@ -15,24 +20,31 @@ struct superblock{ #define NINDIRECT (BSIZE / sizeof(uint)) #define MAXFILE (NDIRECT + NINDIRECT) +// On-disk inode structure struct dinode { - short type; - short major; - short minor; - short nlink; - uint size; - uint addrs[NADDRS]; + short type; // File type + short major; // Major device number (T_DEV only) + short minor; // Minor device number (T_DEV only) + short nlink; // Number of links to inode in file system + uint size; // Size of file (bytes) + uint addrs[NADDRS]; // Data block addresses }; -#define T_DIR 1 -#define T_FILE 2 -#define T_DEV 3 +#define T_DIR 1 // Directory +#define T_FILE 2 // File +#define T_DEV 3 // Special device -// sector 0 is unused, sector 1 is superblock, inodes start at sector 2 -#define IPB (BSIZE / sizeof(struct dinode)) -#define IBLOCK(inum) (inum / IPB + 2) // start of inode -#define BPB (BSIZE*8) -#define BBLOCK(b,ninodes) (b/BPB + (ninodes/IPB) + 3) // start of bitmap +// Inodes per block. +#define IPB (BSIZE / sizeof(struct dinode)) + +// Block containing inode i +#define IBLOCK(i) ((i) / IPB + 2) + +// Bitmap bits per block +#define BPB (BSIZE*8) + +// Block containing bit for block b +#define BBLOCK(b, ninodes) (b/BPB + (ninodes)/IPB + 3) #define DIRSIZ 14 @@ -41,4 +53,5 @@ struct dirent { char name[DIRSIZ]; }; +extern uint rootdev; // Device number of root file system diff --git a/kalloc.c b/kalloc.c index 34a55eb..7d3ca0c 100644 --- a/kalloc.c +++ b/kalloc.c @@ -40,6 +40,10 @@ kinit(void) kfree(start, mem * PAGE); } +// Free the len bytes of memory pointed at by cp, +// which normally should have been returned by a +// call to kalloc(cp). (The exception is when +// initializing the allocator; see kinit above.) void kfree(char *cp, int len) { diff --git a/mkdir.c b/mkdir.c index ece7691..ac2c7a6 100644 --- a/mkdir.c +++ b/mkdir.c @@ -2,6 +2,7 @@ #include "stat.h" #include "user.h" +int main(int argc, char *argv[]) { int i; diff --git a/proc.c b/proc.c index 06ee2fd..3b7843f 100644 --- a/proc.c +++ b/proc.c @@ -136,7 +136,9 @@ copyproc(struct proc *p) return np; } -uint +// Grow current process's memory by n bytes. +// Return old size on success, -1 on failure. +int growproc(int n) { struct proc *cp = curproc[cpu()]; @@ -154,6 +156,7 @@ growproc(int n) return cp->sz - n; } +//PAGEBREAK: 42 // Per-CPU process scheduler. // Each CPU calls scheduler() after setting itself up. // Scheduler never returns. It loops, doing: diff --git a/proc.h b/proc.h index d29f765..3f0c006 100644 --- a/proc.h +++ b/proc.h @@ -1,18 +1,19 @@ -// segments in proc->gdt -#define SEG_KCODE 1 // kernel code -#define SEG_KDATA 2 // kernel data+stack +// Segments in proc->gdt +#define SEG_KCODE 1 // kernel code +#define SEG_KDATA 2 // kernel data+stack #define SEG_UCODE 3 #define SEG_UDATA 4 -#define SEG_TSS 5 // this process's task state +#define SEG_TSS 5 // this process's task state #define NSEGS 6 +// Saved registers for kernel context switches. +// Don't need to save all the %fs etc. segment registers, +// because they are constant across kernel contexts. +// Save all the regular registers so we don't need to care +// which are caller save. +// Don't save %eax, because that's the return register. +// The layout of jmpbuf is known to setjmp.S. struct jmpbuf { - // saved registers for kernel context switches - // don't need to save all the fs etc. registers because - // they are constant across kernel contexts - // save all the regular registers so we don't care which are caller save - // don't save eax because that's the return register - // layout known to setjmp.S int ebx; int ecx; int edx; @@ -25,39 +26,42 @@ struct jmpbuf { enum proc_state { UNUSED, EMBRYO, SLEEPING, RUNNABLE, RUNNING, ZOMBIE }; -struct proc{ - char *mem; // start of process's memory (a kernel address) - // process memory is laid out contiguously: - // text - // original data and bss - // fixed-size stack - // expandable heap - uint sz; // user memory size - char *kstack; // kernel stack - enum proc_state state; - int pid; - int ppid; - void *chan; // sleep - int killed; - struct file *ofile[NOFILE]; - struct inode *cwd; - struct jmpbuf jmpbuf; - struct trapframe *tf; // points into kstack, used to find user regs +// Per-process state +struct proc { + char *mem; // Start of process memory (kernel address) + uint sz; // Size of process memory (bytes) + char *kstack; // Bottom of kernel stack for this process + enum proc_state state; // Process state + int pid; // Process ID + int ppid; // Parent pid + 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 + struct jmpbuf jmpbuf; // Jump here to run process + struct trapframe *tf; // Trap frame for current interrupt }; +// Process memory is laid out contiguously: +// text +// original data and bss +// fixed-size stack +// expandable heap + extern struct proc proc[]; -extern struct proc *curproc[NCPU]; // can be NULL if no proc running. +extern struct proc *curproc[NCPU]; // Current (running) process per CPU #define MPSTACK 512 +// Per-CPU state struct cpu { - uchar apicid; // Local APIC ID - struct jmpbuf jmpbuf; - struct taskstate ts; // only to give cpu address of kernel stack - struct segdesc gdt[NSEGS]; - char mpstack[MPSTACK]; // per-cpu start-up stack - volatile int booted; - int nlock; // # of locks currently held + uchar apicid; // Local APIC ID + struct jmpbuf jmpbuf; // Jump here to enter scheduler + struct taskstate ts; // Used by x86 to find stack for interrupt + struct segdesc gdt[NSEGS]; // x86 global descriptor table + char mpstack[MPSTACK]; // Per-CPU startup stack + volatile int booted; // Has the CPU started? + int nlock; // Number of locks currently held }; extern struct cpu cpus[NCPU]; diff --git a/setjmp.S b/setjmp.S index aee5590..64c587a 100644 --- a/setjmp.S +++ b/setjmp.S @@ -1,3 +1,20 @@ +# int setjmp(struct jmpbuf *jmp); +# void longjmp(struct jmpbuf *jmp); +# +# Setjmp saves its stack environment in jmp +# for later use by longjmp. It returns 0. +# +# Longjmp restores the environment saved by +# the last call of setjmp. It then causes +# execution to continue as if the call of setjmp +# had just returned 1. +# +# The caller of setjmp must not itself have +# returned in the interim. All accessible data +# have values as of the time longjmp was called. +# +# [Description, but not code, borrowed from Plan 9.] + .globl setjmp setjmp: movl 4(%esp), %eax @@ -9,10 +26,10 @@ setjmp: movl %edi, 16(%eax) movl %esp, 20(%eax) movl %ebp, 24(%eax) - pushl 0(%esp) /* %eip */ + pushl 0(%esp) # %eip popl 28(%eax) - movl $0, %eax /* return value */ + movl $0, %eax # return value ret .globl longjmp @@ -27,8 +44,8 @@ longjmp: movl 20(%eax), %esp movl 24(%eax), %ebp - addl $4, %esp /* pop %eip into thin air */ - pushl 28(%eax) /* push new %eip */ + addl $4, %esp # pop and discard %eip + pushl 28(%eax) # push new %eip - movl $1, %eax /* return value (appears to come from setjmp!) */ + movl $1, %eax # return value (appears to come from setjmp!) ret diff --git a/spinlock.c b/spinlock.c index 1e68ae4..486c217 100644 --- a/spinlock.c +++ b/spinlock.c @@ -1,3 +1,5 @@ +// Mutual exclusion spin locks. + #include "types.h" #include "defs.h" #include "x86.h" @@ -16,6 +18,7 @@ initlock(struct spinlock *lock, char *name) lock->cpu = 0xffffffff; } +// Record the current call stack in pcs[] by following the %ebp chain. void getcallerpcs(void *v, uint pcs[]) { @@ -31,6 +34,10 @@ getcallerpcs(void *v, uint pcs[]) pcs[i] = 0; } +// Acquire the lock. +// Loops (spins) until the lock is acquired. +// (Because contention is handled by spinning, must not +// go to sleep holding any locks.) void acquire(struct spinlock *lock) { @@ -44,10 +51,16 @@ acquire(struct spinlock *lock) while(cmpxchg(0, 1, &lock->locked) == 1) ; cpuid(0, 0, 0, 0, 0); // memory barrier - getcallerpcs(&lock, lock->pcs); + + // Record info about lock acquisition for debugging. + // The +10 is only so that we can tell the difference + // between forgetting to initialize lock->cpu + // and holding a lock on cpu 0. lock->cpu = cpu() + 10; + getcallerpcs(&lock, lock->pcs); } +// Release the lock. void release(struct spinlock *lock) { @@ -63,6 +76,7 @@ release(struct spinlock *lock) sti(); } +// Check whether this cpu is holding the lock. int holding(struct spinlock *lock) { diff --git a/spinlock.h b/spinlock.h index ae5ad1f..1a706cf 100644 --- a/spinlock.h +++ b/spinlock.h @@ -1,6 +1,10 @@ +// Mutual exclusion lock. struct spinlock { - char *name; - uint locked; - int cpu; - uint pcs[10]; + uint locked; // Is the lock held? + + // For debugging: + char *name; // Name of lock. + int cpu; // The number of the cpu holding the lock. + uint pcs[10]; // The call stack (an array of program counters) + // that locked the lock. }; diff --git a/stat.h b/stat.h index 57a018c..bc3a06c 100644 --- a/stat.h +++ b/stat.h @@ -1,7 +1,7 @@ struct stat { - int dev; - uint ino; - short type; - short nlink; - uint size; + int dev; // Device number + uint ino; // Inode number on device + short type; // Type of file + short nlink; // Number of links to file + uint size; // Size of file in bytes }; diff --git a/trap.c b/trap.c index 4fb3039..b35107f 100644 --- a/trap.c +++ b/trap.c @@ -7,11 +7,11 @@ #include "traps.h" #include "syscall.h" +// Interrupt descriptor table (shared by all CPUs). struct gatedesc idt[256]; + extern uint vectors[]; // in vectors.S: array of 256 entry pointers -extern void trapenter(void); -extern void trapenter1(void); void tvinit(void) @@ -65,30 +65,34 @@ trap(struct trapframe *tf) return; } - if(v == (IRQ_OFFSET + IRQ_IDE)){ + if(v == IRQ_OFFSET + IRQ_IDE){ ide_intr(); cli(); // prevent a waiting interrupt from overflowing stack lapic_eoi(); return; } - if(v == (IRQ_OFFSET + IRQ_KBD)){ + if(v == IRQ_OFFSET + IRQ_KBD){ kbd_intr(); cli(); // prevent a waiting interrupt from overflowing stack lapic_eoi(); return; } - if(v == (IRQ_OFFSET + IRQ_SPURIOUS)){ + if(v == IRQ_OFFSET + IRQ_SPURIOUS){ cprintf("spurious interrupt from cpu %d eip %x\n", cpu(), tf->eip); return; // no eoi for this one. } if(curproc[cpu()]) { + // assume process caused unexpected trap, + // for example by dividing by zero or dereferencing a bad pointer cprintf("pid %d: unhandled trap %d on cpu %d eip %x -- kill proc\n", curproc[cpu()]->pid, v, cpu(), tf->eip); proc_exit(); } + + // otherwise it's our mistake cprintf("unexpected trap %d from cpu %d eip %x\n", v, cpu(), tf->eip); panic("trap"); } diff --git a/traps.h b/traps.h index 732d8e0..d4a6a3a 100644 --- a/traps.h +++ b/traps.h @@ -1,4 +1,6 @@ -// system defined: +// x86 trap and interrupt constants. + +// Processor-defined: #define T_DIVIDE 0 // divide error #define T_DEBUG 1 // debug exception #define T_NMI 2 // non-maskable interrupt diff --git a/x86.h b/x86.h index bb79d3e..ab7882a 100644 --- a/x86.h +++ b/x86.h @@ -1,3 +1,6 @@ +// Special assembly routines to access x86-specific +// hardware instructions. + static __inline uchar inb(int port) { @@ -124,6 +127,7 @@ sti(void) __asm__ volatile("sti"); } +// Layout of the trap frame on the stack upon entry to trap. struct trapframe { // registers as pushed by pusha uint edi;