more comments

This commit is contained in:
rsc 2006-09-07 14:12:30 +00:00
parent 7e019461c8
commit 31085bb416
13 changed files with 151 additions and 81 deletions

6
elf.h
View file

@ -1,9 +1,8 @@
// // Format of an ELF executable file
// format of an ELF executable file
//
#define ELF_MAGIC 0x464C457FU // "\x7FELF" in little endian #define ELF_MAGIC 0x464C457FU // "\x7FELF" in little endian
// File header
struct elfhdr { struct elfhdr {
uint magic; // must equal ELF_MAGIC uint magic; // must equal ELF_MAGIC
uchar elf[12]; uchar elf[12];
@ -22,6 +21,7 @@ struct elfhdr {
ushort shstrndx; ushort shstrndx;
}; };
// Program section header
struct proghdr { struct proghdr {
uint type; uint type;
uint offset; uint offset;

53
fs.h
View file

@ -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 #define BSIZE 512 // block size
// sector 1 (2nd sector) // File system super block
struct superblock{ struct superblock {
uint size; uint size; // Size of file system (bytes???) xxx
uint nblocks; uint nblocks; // Number of blocks
uint ninodes; uint ninodes; // Number of inodes.
}; };
#define NADDRS (NDIRECT+1) #define NADDRS (NDIRECT+1)
@ -15,24 +20,31 @@ struct superblock{
#define NINDIRECT (BSIZE / sizeof(uint)) #define NINDIRECT (BSIZE / sizeof(uint))
#define MAXFILE (NDIRECT + NINDIRECT) #define MAXFILE (NDIRECT + NINDIRECT)
// On-disk inode structure
struct dinode { struct dinode {
short type; short type; // File type
short major; short major; // Major device number (T_DEV only)
short minor; short minor; // Minor device number (T_DEV only)
short nlink; short nlink; // Number of links to inode in file system
uint size; uint size; // Size of file (bytes)
uint addrs[NADDRS]; uint addrs[NADDRS]; // Data block addresses
}; };
#define T_DIR 1 #define T_DIR 1 // Directory
#define T_FILE 2 #define T_FILE 2 // File
#define T_DEV 3 #define T_DEV 3 // Special device
// sector 0 is unused, sector 1 is superblock, inodes start at sector 2 // Inodes per block.
#define IPB (BSIZE / sizeof(struct dinode)) #define IPB (BSIZE / sizeof(struct dinode))
#define IBLOCK(inum) (inum / IPB + 2) // start of inode
#define BPB (BSIZE*8) // Block containing inode i
#define BBLOCK(b,ninodes) (b/BPB + (ninodes/IPB) + 3) // start of bitmap #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 #define DIRSIZ 14
@ -41,4 +53,5 @@ struct dirent {
char name[DIRSIZ]; char name[DIRSIZ];
}; };
extern uint rootdev; // Device number of root file system

View file

@ -40,6 +40,10 @@ kinit(void)
kfree(start, mem * PAGE); 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 void
kfree(char *cp, int len) kfree(char *cp, int len)
{ {

View file

@ -2,6 +2,7 @@
#include "stat.h" #include "stat.h"
#include "user.h" #include "user.h"
int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
int i; int i;

5
proc.c
View file

@ -136,7 +136,9 @@ copyproc(struct proc *p)
return np; return np;
} }
uint // Grow current process's memory by n bytes.
// Return old size on success, -1 on failure.
int
growproc(int n) growproc(int n)
{ {
struct proc *cp = curproc[cpu()]; struct proc *cp = curproc[cpu()];
@ -154,6 +156,7 @@ growproc(int n)
return cp->sz - n; return cp->sz - n;
} }
//PAGEBREAK: 42
// Per-CPU process scheduler. // Per-CPU process scheduler.
// Each CPU calls scheduler() after setting itself up. // Each CPU calls scheduler() after setting itself up.
// Scheduler never returns. It loops, doing: // Scheduler never returns. It loops, doing:

76
proc.h
View file

@ -1,18 +1,19 @@
// segments in proc->gdt // Segments in proc->gdt
#define SEG_KCODE 1 // kernel code #define SEG_KCODE 1 // kernel code
#define SEG_KDATA 2 // kernel data+stack #define SEG_KDATA 2 // kernel data+stack
#define SEG_UCODE 3 #define SEG_UCODE 3
#define SEG_UDATA 4 #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 #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 { 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 ebx;
int ecx; int ecx;
int edx; int edx;
@ -25,39 +26,42 @@ struct jmpbuf {
enum proc_state { UNUSED, EMBRYO, SLEEPING, RUNNABLE, RUNNING, ZOMBIE }; enum proc_state { UNUSED, EMBRYO, SLEEPING, RUNNABLE, RUNNING, ZOMBIE };
struct proc{ // Per-process state
char *mem; // start of process's memory (a kernel address) struct proc {
// process memory is laid out contiguously: char *mem; // Start of process memory (kernel address)
// text uint sz; // Size of process memory (bytes)
// original data and bss char *kstack; // Bottom of kernel stack for this process
// fixed-size stack enum proc_state state; // Process state
// expandable heap int pid; // Process ID
uint sz; // user memory size int ppid; // Parent pid
char *kstack; // kernel stack void *chan; // If non-zero, sleeping on chan
enum proc_state state; int killed; // If non-zero, have been killed
int pid; struct file *ofile[NOFILE]; // Open files
int ppid; struct inode *cwd; // Current directory
void *chan; // sleep struct jmpbuf jmpbuf; // Jump here to run process
int killed; struct trapframe *tf; // Trap frame for current interrupt
struct file *ofile[NOFILE];
struct inode *cwd;
struct jmpbuf jmpbuf;
struct trapframe *tf; // points into kstack, used to find user regs
}; };
// Process memory is laid out contiguously:
// text
// original data and bss
// fixed-size stack
// expandable heap
extern struct proc proc[]; 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 #define MPSTACK 512
// Per-CPU state
struct cpu { struct cpu {
uchar apicid; // Local APIC ID uchar apicid; // Local APIC ID
struct jmpbuf jmpbuf; struct jmpbuf jmpbuf; // Jump here to enter scheduler
struct taskstate ts; // only to give cpu address of kernel stack struct taskstate ts; // Used by x86 to find stack for interrupt
struct segdesc gdt[NSEGS]; struct segdesc gdt[NSEGS]; // x86 global descriptor table
char mpstack[MPSTACK]; // per-cpu start-up stack char mpstack[MPSTACK]; // Per-CPU startup stack
volatile int booted; volatile int booted; // Has the CPU started?
int nlock; // # of locks currently held int nlock; // Number of locks currently held
}; };
extern struct cpu cpus[NCPU]; extern struct cpu cpus[NCPU];

View file

@ -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 .globl setjmp
setjmp: setjmp:
movl 4(%esp), %eax movl 4(%esp), %eax
@ -9,10 +26,10 @@ setjmp:
movl %edi, 16(%eax) movl %edi, 16(%eax)
movl %esp, 20(%eax) movl %esp, 20(%eax)
movl %ebp, 24(%eax) movl %ebp, 24(%eax)
pushl 0(%esp) /* %eip */ pushl 0(%esp) # %eip
popl 28(%eax) popl 28(%eax)
movl $0, %eax /* return value */ movl $0, %eax # return value
ret ret
.globl longjmp .globl longjmp
@ -27,8 +44,8 @@ longjmp:
movl 20(%eax), %esp movl 20(%eax), %esp
movl 24(%eax), %ebp movl 24(%eax), %ebp
addl $4, %esp /* pop %eip into thin air */ addl $4, %esp # pop and discard %eip
pushl 28(%eax) /* push new %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 ret

View file

@ -1,3 +1,5 @@
// Mutual exclusion spin locks.
#include "types.h" #include "types.h"
#include "defs.h" #include "defs.h"
#include "x86.h" #include "x86.h"
@ -16,6 +18,7 @@ initlock(struct spinlock *lock, char *name)
lock->cpu = 0xffffffff; lock->cpu = 0xffffffff;
} }
// Record the current call stack in pcs[] by following the %ebp chain.
void void
getcallerpcs(void *v, uint pcs[]) getcallerpcs(void *v, uint pcs[])
{ {
@ -31,6 +34,10 @@ getcallerpcs(void *v, uint pcs[])
pcs[i] = 0; 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 void
acquire(struct spinlock *lock) acquire(struct spinlock *lock)
{ {
@ -44,10 +51,16 @@ acquire(struct spinlock *lock)
while(cmpxchg(0, 1, &lock->locked) == 1) while(cmpxchg(0, 1, &lock->locked) == 1)
; ;
cpuid(0, 0, 0, 0, 0); // memory barrier 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; lock->cpu = cpu() + 10;
getcallerpcs(&lock, lock->pcs);
} }
// Release the lock.
void void
release(struct spinlock *lock) release(struct spinlock *lock)
{ {
@ -63,6 +76,7 @@ release(struct spinlock *lock)
sti(); sti();
} }
// Check whether this cpu is holding the lock.
int int
holding(struct spinlock *lock) holding(struct spinlock *lock)
{ {

View file

@ -1,6 +1,10 @@
// Mutual exclusion lock.
struct spinlock { struct spinlock {
char *name; uint locked; // Is the lock held?
uint locked;
int cpu; // For debugging:
uint pcs[10]; 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.
}; };

10
stat.h
View file

@ -1,7 +1,7 @@
struct stat { struct stat {
int dev; int dev; // Device number
uint ino; uint ino; // Inode number on device
short type; short type; // Type of file
short nlink; short nlink; // Number of links to file
uint size; uint size; // Size of file in bytes
}; };

14
trap.c
View file

@ -7,11 +7,11 @@
#include "traps.h" #include "traps.h"
#include "syscall.h" #include "syscall.h"
// Interrupt descriptor table (shared by all CPUs).
struct gatedesc idt[256]; struct gatedesc idt[256];
extern uint vectors[]; // in vectors.S: array of 256 entry pointers extern uint vectors[]; // in vectors.S: array of 256 entry pointers
extern void trapenter(void);
extern void trapenter1(void);
void void
tvinit(void) tvinit(void)
@ -65,30 +65,34 @@ trap(struct trapframe *tf)
return; return;
} }
if(v == (IRQ_OFFSET + IRQ_IDE)){ if(v == IRQ_OFFSET + IRQ_IDE){
ide_intr(); ide_intr();
cli(); // prevent a waiting interrupt from overflowing stack cli(); // prevent a waiting interrupt from overflowing stack
lapic_eoi(); lapic_eoi();
return; return;
} }
if(v == (IRQ_OFFSET + IRQ_KBD)){ if(v == IRQ_OFFSET + IRQ_KBD){
kbd_intr(); kbd_intr();
cli(); // prevent a waiting interrupt from overflowing stack cli(); // prevent a waiting interrupt from overflowing stack
lapic_eoi(); lapic_eoi();
return; 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); cprintf("spurious interrupt from cpu %d eip %x\n", cpu(), tf->eip);
return; // no eoi for this one. return; // no eoi for this one.
} }
if(curproc[cpu()]) { 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", cprintf("pid %d: unhandled trap %d on cpu %d eip %x -- kill proc\n",
curproc[cpu()]->pid, v, cpu(), tf->eip); curproc[cpu()]->pid, v, cpu(), tf->eip);
proc_exit(); proc_exit();
} }
// otherwise it's our mistake
cprintf("unexpected trap %d from cpu %d eip %x\n", v, cpu(), tf->eip); cprintf("unexpected trap %d from cpu %d eip %x\n", v, cpu(), tf->eip);
panic("trap"); panic("trap");
} }

View file

@ -1,4 +1,6 @@
// system defined: // x86 trap and interrupt constants.
// Processor-defined:
#define T_DIVIDE 0 // divide error #define T_DIVIDE 0 // divide error
#define T_DEBUG 1 // debug exception #define T_DEBUG 1 // debug exception
#define T_NMI 2 // non-maskable interrupt #define T_NMI 2 // non-maskable interrupt

4
x86.h
View file

@ -1,3 +1,6 @@
// Special assembly routines to access x86-specific
// hardware instructions.
static __inline uchar static __inline uchar
inb(int port) inb(int port)
{ {
@ -124,6 +127,7 @@ sti(void)
__asm__ volatile("sti"); __asm__ volatile("sti");
} }
// Layout of the trap frame on the stack upon entry to trap.
struct trapframe { struct trapframe {
// registers as pushed by pusha // registers as pushed by pusha
uint edi; uint edi;