// cached copies of disk block contents. Caching disk blocks
// in memory reduces the number of disk reads and also provides
// a synchronization point for disk blocks used by multiple processes.
-//
+//
// Interface:
// * To get a buffer for a particular disk block, call bread.
// * After changing buffer data, call bwrite to write it to disk.
// * Do not use the buffer after calling brelse.
// * Only one process at a time can use a buffer,
// so do not keep them longer than necessary.
-//
+//
// The implementation uses three state flags internally:
// * B_BUSY: the block has been returned from bread
-// and has not been passed back to brelse.
+// and has not been passed back to brelse.
// * B_VALID: the buffer data has been read from the disk.
// * B_DIRTY: the buffer data has been modified
// and needs to be written to disk.
// Boot loader.
-//
+//
// Part of the boot block, along with bootasm.S, which calls bootmain().
// bootasm.S has put the processor into protected 32-bit mode.
// bootmain() loads an ELF kernel image from the disk starting at
{
int i;
uint pcs[10];
-
+
cli();
cons.locking = 0;
cprintf("cpu%d: panic: ", cpu->id);
cgaputc(int c)
{
int pos;
-
+
// Cursor position: col + 80*row.
outb(CRTPORT, 14);
pos = inb(CRTPORT+1) << 8;
if(pos < 0 || pos > 25*80)
panic("pos under/overflow");
-
+
if((pos/80) >= 24){ // Scroll up.
memmove(crt, crt+80, sizeof(crt[0])*23*80);
pos -= 80;
memset(crt+pos, 0, sizeof(crt[0])*(24*80 - pos));
}
-
+
outb(CRTPORT, 14);
outb(CRTPORT+1, pos>>8);
outb(CRTPORT, 15);
f->ref = 0;
f->type = FD_NONE;
release(&ftable.lock);
-
+
if(ff.type == FD_PIPE)
pipeclose(ff.pipe, ff.writable);
else if(ff.type == FD_INODE){
if(pid == 0)
exit();
}
-
+
if(n == N){
printf(1, "fork claimed to work N times!\n", N);
exit();
}
-
+
for(; n > 0; n--){
if(wait() < 0){
printf(1, "wait stopped early\n");
exit();
}
}
-
+
if(wait() != -1){
printf(1, "wait got too many\n");
exit();
}
-
+
printf(1, "fork test OK\n");
}
// + Directories: inode with special contents (list of other inodes!)
// + Names: paths like /usr/rtm/xv6/fs.c for convenient naming.
//
-// This file contains the low-level file system manipulation
+// This file contains the low-level file system manipulation
// routines. The (higher-level) system call implementations
// are in sysfile.c.
readsb(int dev, struct superblock *sb)
{
struct buf *bp;
-
+
bp = bread(dev, 1);
memmove(sb, bp->data, sizeof(*sb));
brelse(bp);
bzero(int dev, int bno)
{
struct buf *bp;
-
+
bp = bread(dev, bno);
memset(bp->data, 0, BSIZE);
log_write(bp);
brelse(bp);
}
-// Blocks.
+// Blocks.
// Allocate a zeroed disk block.
static uint
//
// The content (data) associated with each inode is stored
// in blocks on the disk. The first NDIRECT block numbers
-// are listed in ip->addrs[]. The next NINDIRECT blocks are
+// are listed in ip->addrs[]. The next NINDIRECT blocks are
// listed in block ip->addrs[NDIRECT].
// Return the disk block address of the nth block in inode ip.
ip->addrs[i] = 0;
}
}
-
+
if(ip->addrs[NDIRECT]){
bp = bread(ip->dev, ip->addrs[NDIRECT]);
a = (uint*)bp->data;
de.inum = inum;
if(writei(dp, (char*)&de, off, sizeof(de)) != sizeof(de))
panic("dirlink");
-
+
return 0;
}
-// On-disk file system format.
+// On-disk file system format.
// Both the kernel and user programs use this header file.
{
int n, m;
char *p, *q;
-
+
m = 0;
while((n = read(fd, buf+m, sizeof(buf)-m-1)) > 0){
m += n;
{
int fd, i;
char *pattern;
-
+
if(argc <= 1){
printf(2, "usage: grep pattern [file ...]\n");
exit();
}
pattern = argv[1];
-
+
if(argc <= 2){
grep(pattern, 0);
exit();
{
int r;
- while(((r = inb(0x1f7)) & (IDE_BSY|IDE_DRDY)) != IDE_DRDY)
+ while(((r = inb(0x1f7)) & (IDE_BSY|IDE_DRDY)) != IDE_DRDY)
;
if(checkerr && (r & (IDE_DF|IDE_ERR)) != 0)
return -1;
ideinit(void)
{
int i;
-
+
initlock(&idelock, "ide");
picenable(IRQ_IDE);
ioapicenable(IRQ_IDE, ncpu - 1);
idewait(0);
-
+
// Check if disk 1 is present
outb(0x1f6, 0xe0 | (1<<4));
for(i=0; i<1000; i++){
break;
}
}
-
+
// Switch back to disk 0.
outb(0x1f6, 0xe0 | (0<<4));
}
int sector = b->blockno * sector_per_block;
int read_cmd = (sector_per_block == 1) ? IDE_CMD_READ : IDE_CMD_RDMUL;
int write_cmd = (sector_per_block == 1) ? IDE_CMD_WRITE : IDE_CMD_WRMUL;
-
+
if (sector_per_block > 7) panic("idestart");
-
+
idewait(0);
outb(0x3f6, 0); // generate interrupt
outb(0x1f2, sector_per_block); // number of sectors
// Read data if needed.
if(!(b->flags & B_DIRTY) && idewait(1) >= 0)
insl(0x1f0, b->data, BSIZE/4);
-
+
// Wake process waiting for this buf.
b->flags |= B_VALID;
b->flags &= ~B_DIRTY;
wakeup(b);
-
+
// Start disk on next buf in queue.
if(idequeue != 0)
idestart(idequeue);
}
//PAGEBREAK!
-// Sync buf with disk.
+// Sync buf with disk.
// If B_DIRTY is set, write buf to disk, clear B_DIRTY, set B_VALID.
// Else if B_VALID is not set, read buf from disk, set B_VALID.
void
for(pp=&idequeue; *pp; pp=&(*pp)->qnext) //DOC:insert-queue
;
*pp = b;
-
+
// Start disk if necessary.
if(idequeue == b)
idestart(b);
-
+
// Wait for request to finish.
while((b->flags & (B_VALID|B_DIRTY)) != B_VALID){
sleep(b, &idelock);
#define REG_TABLE 0x10 // Redirection table base
// The redirection table starts at REG_TABLE and uses
-// two registers to configure each interrupt.
+// two registers to configure each interrupt.
// The first (low) register in a pair contains configuration bits.
// The second (high) register contains a bitmask telling which
// CPUs can serve that interrupt.
// Contents of the header block, used for both the on-disk header block
// and to keep track in memory of logged block# before commit.
struct logheader {
- int n;
+ int n;
int block[LOGSIZE];
};
}
// Copy committed blocks from log to their home location
-static void
+static void
install_trans(void)
{
int tail;
struct buf *dbuf = bread(log.dev, log.lh.block[tail]); // read dst
memmove(dbuf->data, lbuf->data, BSIZE); // copy block to dst
bwrite(dbuf); // write dst to disk
- brelse(lbuf);
+ brelse(lbuf);
brelse(dbuf);
}
}
static void
recover_from_log(void)
{
- read_head();
+ read_head();
install_trans(); // if committed, copy from log to disk
log.lh.n = 0;
write_head(); // clear the log
}
// Copy modified blocks from cache to log.
-static void
+static void
write_log(void)
{
int tail;
struct buf *from = bread(log.dev, log.lh.block[tail]); // cache block
memmove(to->data, from->data, BSIZE);
bwrite(to); // write the log
- brelse(from);
+ brelse(from);
brelse(to);
}
}
write_log(); // Write modified blocks from cache to log
write_head(); // Write header to disk -- the real commit
install_trans(); // Now install writes to home locations
- log.lh.n = 0;
+ log.lh.n = 0;
write_head(); // Erase the transaction from the log
}
}
{
static char buf[DIRSIZ+1];
char *p;
-
+
// Find first character after last slash.
for(p=path+strlen(path); p >= path && *p != '/'; p--)
;
p++;
-
+
// Return blank-padded name.
if(strlen(p) >= DIRSIZ)
return p;
int fd;
struct dirent de;
struct stat st;
-
+
if((fd = open(path, 0)) < 0){
printf(2, "ls: cannot open %s\n", path);
return;
}
-
+
if(fstat(fd, &st) < 0){
printf(2, "ls: cannot stat %s\n", path);
close(fd);
return;
}
-
+
switch(st.type){
case T_FILE:
printf(1, "%s %d %d %d\n", fmtname(path), st.type, st.ino, st.size);
break;
-
+
case T_DIR:
if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){
printf(1, "ls: path too long\n");
static void
mpenter(void)
{
- switchkvm();
+ switchkvm();
seginit();
lapicinit();
mpmain();
if(c == cpus+cpunum()) // We've started already.
continue;
- // Tell entryother.S what stack to use, where to enter, and what
+ // Tell entryother.S what stack to use, where to enter, and what
// pgdir to use. We cannot use kpgdir yet, because the AP processor
// is running in low memory, so we use entrypgdir for the APs too.
stack = kalloc();
// The boot page table used in entry.S and entryother.S.
// Page directories (and page tables) must start on page boundaries,
-// hence the __aligned__ attribute.
+// hence the __aligned__ attribute.
// PTE_PS in a page directory entry enables 4Mbyte pages.
__attribute__((__aligned__(PGSIZE)))
// no-op
}
-// Sync buf with disk.
+// Sync buf with disk.
// If B_DIRTY is set, write buf to disk, clear B_DIRTY, set B_VALID.
// Else if B_VALID is not set, read buf from disk, set B_VALID.
void
panic("iderw: block out of range");
p = memdisk + b->blockno*BSIZE;
-
+
if(b->flags & B_DIRTY){
b->flags &= ~B_DIRTY;
memmove(p, b->data, BSIZE);
int nbitmap = FSSIZE/(BSIZE*8) + 1;
int ninodeblocks = NINODES / IPB + 1;
-int nlog = LOGSIZE;
+int nlog = LOGSIZE;
int nmeta; // Number of meta blocks (boot, sb, nlog, inode, bitmap)
int nblocks; // Number of data blocks
perror(argv[i]);
exit(1);
}
-
+
// Skip leading _ in name when writing to file system.
// The binaries are named _rm, _cat, etc. to keep the
// build operating system from trying to execute them
-// This file contains definitions for the
+// This file contains definitions for the
// x86 memory management unit (MMU).
// Eflags register
// | Page Directory | Page Table | Offset within Page |
// | Index | Index | |
// +----------------+----------------+---------------------+
-// \--- PDX(va) --/ \--- PTX(va) --/
+// \--- PDX(va) --/ \--- PTX(va) --/
// page directory index
#define PDX(va) (((uint)(va) >> PDXSHIFT) & 0x3FF)
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;
{
struct proc *p;
extern char _binary_initcode_start[], _binary_initcode_size[];
-
+
acquire(&ptable.lock);
p = allocproc();
growproc(int n)
{
uint sz;
-
+
sz = proc->sz;
if(n > 0){
if((sz = allocuvm(proc->pgdir, sz, sz + n)) == 0)
np->cwd = idup(proc->cwd);
safestrcpy(np->name, proc->name, sizeof(proc->name));
-
+
pid = np->pid;
np->state = RUNNABLE;
release(&ptable.lock);
-
+
return pid;
}
if (first) {
// Some initialization functions must be run in the context
- // of a regular process (e.g., they call sleep), and thus cannot
+ // 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).
}
struct proc *p;
char *state;
uint pc[10];
-
+
for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){
if(p->state == UNUSED)
continue;
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.
if(cmd == 0)
exit();
-
+
switch(cmd->type){
default:
panic("runcmd");
wait();
wait();
break;
-
+
case BACK:
bcmd = (struct backcmd*)cmd;
if(fork1() == 0)
{
static char buf[100];
int fd;
-
+
// Ensure that three file descriptors are open.
while((fd = open("console", O_RDWR)) >= 0){
if(fd >= 3){
break;
}
}
-
+
// Read and run input commands.
while(getcmd(buf, sizeof(buf)) >= 0){
if(buf[0] == 'c' && buf[1] == 'd' && buf[2] == ' '){
fork1(void)
{
int pid;
-
+
pid = fork();
if(pid == -1)
panic("fork");
{
char *s;
int ret;
-
+
s = *ps;
while(s < es && strchr(whitespace, *s))
s++;
}
if(eq)
*eq = s;
-
+
while(s < es && strchr(whitespace, *s))
s++;
*ps = s;
peek(char **ps, char *es, char *toks)
{
char *s;
-
+
s = *ps;
while(s < es && strchr(whitespace, *s))
s++;
int tok, argc;
struct execcmd *cmd;
struct cmd *ret;
-
+
if(peek(ps, es, "("))
return parseblock(ps, es);
if(cmd == 0)
return 0;
-
+
switch(cmd->type){
case EXEC:
ecmd = (struct execcmd*)cmd;
nulterminate(pcmd->left);
nulterminate(pcmd->right);
break;
-
+
case LIST:
lcmd = (struct listcmd*)cmd;
nulterminate(lcmd->left);
{
uint *ebp;
int i;
-
+
ebp = (uint*)v - 2;
for(i = 0; i < 10; i++){
if(ebp == 0 || ebp < (uint*)KERNBASE || ebp == (uint*)0xffffffff)
pushcli(void)
{
int eflags;
-
+
eflags = readeflags();
cli();
if(cpu->ncli++ == 0)
// Mutual exclusion lock.
struct spinlock {
uint locked; // Is the lock held?
-
+
// For debugging:
char *name; // Name of lock.
struct cpu *cpu; // The cpu holding the lock.
close(fd);
wait();
-
+
exit();
}
memcmp(const void *v1, const void *v2, uint n)
{
const uchar *s1, *s2;
-
+
s1 = v1;
s2 = v2;
while(n-- > 0){
strncpy(char *s, const char *t, int n)
{
char *os;
-
+
os = s;
while(n-- > 0 && (*s++ = *t++) != 0)
;
safestrcpy(char *s, const char *t, int n)
{
char *os;
-
+
os = s;
if(n <= 0)
return os;
argptr(int n, char **pp, int size)
{
int i;
-
+
if(argint(n, &i) < 0)
return -1;
if((uint)i >= proc->sz || (uint)i+size > proc->sz)
{
int n;
uint ticks0;
-
+
if(argint(0, &n) < 0)
return -1;
acquire(&tickslock);
sys_uptime(void)
{
uint xticks;
-
+
acquire(&tickslock);
xticks = ticks;
release(&tickslock);
for(i = 0; i < 256; i++)
SETGATE(idt[i], 0, SEG_KCODE<<3, vectors[i], 0);
SETGATE(idt[T_SYSCALL], 1, SEG_KCODE<<3, vectors[T_SYSCALL], DPL_USER);
-
+
initlock(&tickslock, "time");
}
cpu->id, tf->cs, tf->eip);
lapiceoi();
break;
-
+
//PAGEBREAK: 13
default:
if(proc == 0 || (tf->cs&3) == 0){
// In user space, assume process misbehaved.
cprintf("pid %d %s: trap %d err %d on cpu %d "
"eip 0x%x addr 0x%x--kill proc\n",
- proc->pid, proc->name, tf->trapno, tf->err, cpu->id, tf->eip,
+ proc->pid, proc->name, tf->trapno, tf->err, cpu->id, tf->eip,
rcr2());
proc->killed = 1;
}
// Force process exit if it has been killed and is in user space.
- // (If it is still executing in the kernel, let it keep running
+ // (If it is still executing in the kernel, let it keep running
// until it gets to the regular system call return.)
if(proc && proc->killed && (tf->cs&3) == DPL_USER)
exit();
// Turn off the FIFO
outb(COM1+2, 0);
-
+
// 9600 baud, 8 data bits, 1 stop bit, parity off.
outb(COM1+3, 0x80); // Unlock divisor
outb(COM1+0, 115200/9600);
inb(COM1+0);
picenable(IRQ_COM1);
ioapicenable(IRQ_COM1, 0);
-
+
// Announce that we're here.
for(p="xv6...\n"; *p; p++)
uartputc(*p);
memmove(void *vdst, void *vsrc, int n)
{
char *dst, *src;
-
+
dst = vdst;
src = vsrc;
while(n-- > 0)
printf(1, "create failed\n");
exit();
}
-
+
memset(buf, '0'+pi, 512);
for(i = 0; i < 12; i++){
if((n = write(fd, buf, 500)) != 500){
if(pid)
wait();
- else
+ else
exit();
printf(1, "linkunlink ok\n");
}
write(fd, "ff", 2);
close(fd);
-
+
if(unlink("dd") >= 0){
printf(1, "unlink dd (non-empty dir) succeeded!\n");
exit();
if(pid == 0)
exit();
}
-
+
if(n == 1000){
printf(1, "fork claimed to work 1000 times!\n");
exit();
}
-
+
for(; n > 0; n--){
if(wait() < 0){
printf(1, "wait stopped early\n");
exit();
}
}
-
+
if(wait() != -1){
printf(1, "wait got too many\n");
exit();
}
-
+
printf(1, "fork test OK\n");
}
// can one sbrk() less than a page?
a = sbrk(0);
int i;
- for(i = 0; i < 5000; i++){
+ for(i = 0; i < 5000; i++){
b = sbrk(1);
if(b != a){
printf(stdout, "sbrk test failed %d %x %x\n", i, a, b);
a = sbrk(0);
amt = (BIG) - (uint)a;
p = sbrk(amt);
- if (p != a) {
+ if (p != a) {
printf(stdout, "sbrk test failed to grow big address space; enough phys mem?\n");
exit();
}
printf(stdout, "sbrk downsize failed, a %x c %x\n", a, c);
exit();
}
-
+
// can we read the kernel's memory?
for(a = (char*)(KERNBASE); a < (char*) (KERNBASE+2000000); a += 50000){
ppid = getpid();
lgdt(c->gdt, sizeof(c->gdt));
loadgs(SEG_KCPU << 3);
-
+
// Initialize cpu-local storage.
cpu = c;
proc = 0;
// Make sure all those PTE_P bits are zero.
memset(pgtab, 0, PGSIZE);
// The permissions here are overly generous, but they can
- // be further restricted by the permissions in the page table
+ // be further restricted by the permissions in the page table
// entries, if necessary.
*pde = V2P(pgtab) | PTE_P | PTE_W | PTE_U;
}
{
char *a, *last;
pte_t *pte;
-
+
a = (char*)PGROUNDDOWN((uint)va);
last = (char*)PGROUNDDOWN(((uint)va) + size - 1);
for(;;){
// current process's page table during system calls and interrupts;
// page protection bits prevent user code from using the kernel's
// mappings.
-//
+//
// setupkvm() and exec() set up every page table like this:
//
// 0..KERNBASE: user memory (text+data+stack+heap), mapped to
// KERNBASE..KERNBASE+EXTMEM: mapped to 0..EXTMEM (for I/O space)
// KERNBASE+EXTMEM..data: mapped to EXTMEM..V2P(data)
// for the kernel's instructions and r/o data
-// data..KERNBASE+PHYSTOP: mapped to V2P(data)..PHYSTOP,
+// data..KERNBASE+PHYSTOP: mapped to V2P(data)..PHYSTOP,
// rw data + free physical memory
// 0xfe000000..0: mapped direct (devices such as ioapic)
//
if (P2V(PHYSTOP) > (void*)DEVSPACE)
panic("PHYSTOP too high");
for(k = kmap; k < &kmap[NELEM(kmap)]; k++)
- if(mappages(pgdir, k->virt, k->phys_end - k->phys_start,
+ if(mappages(pgdir, k->virt, k->phys_end - k->phys_start,
(uint)k->phys_start, k->perm) < 0)
return 0;
return pgdir;
inituvm(pde_t *pgdir, char *init, uint sz)
{
char *mem;
-
+
if(sz >= PGSIZE)
panic("inituvm: more than a page");
mem = kalloc();
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) :
}
static inline void
-lcr3(uint val)
+lcr3(uint val)
{
asm volatile("movl %0,%%cr3" : : "r" (val));
}
-// Create a zombie process that
+// Create a zombie process that
// must be reparented at exit.
#include "types.h"