Initial commit

This commit is contained in:
kayomn 2023-01-04 16:04:31 +00:00
commit 7424a59fa1
88 changed files with 10528 additions and 0 deletions

16
.cvsignore Normal file
View File

@ -0,0 +1,16 @@
*.asm
*.d
*.sym
_*
kernel
user1
userfs
usertests
xv6.img
vectors.S
bochsout.txt
bootblock
bootother
bootother.out
parport.out
fmt

4
.dir-locals.el Normal file
View File

@ -0,0 +1,4 @@
((c-mode
(indent-tabs-mode . nil)
(c-file-style . "bsd")
(c-basic-offset . 2)))

27
.gdbinit.tmpl Normal file
View File

@ -0,0 +1,27 @@
set $lastcs = -1
define hook-stop
# There doesn't seem to be a good way to detect if we're in 16- or
# 32-bit mode, but in 32-bit mode we always run with CS == 8 in the
# kernel and CS == 35 in user space
if $cs == 8 || $cs == 35
if $lastcs != 8 && $lastcs != 35
set architecture i386
end
x/i $pc
else
if $lastcs == -1 || $lastcs == 8 || $lastcs == 35
set architecture i8086
end
# Translate the segment:offset into a physical address
printf "[%4x:%4x] ", $cs, $eip
x/i $cs*16+$eip
end
set $lastcs = $cs
end
echo + target remote localhost:1234\n
target remote localhost:1234
echo + symbol-file kernel\n
symbol-file kernel

19
.gitignore vendored Normal file
View File

@ -0,0 +1,19 @@
*~
_*
*.o
*.d
*.asm
*.sym
*.img
vectors.S
bootblock
entryother
initcode
initcode.out
kernel
kernelmemfs
mkfs
.gdbinit
syscalltable.h
syscall.h
usys.S

28
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,28 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/kernel",
"args": [],
"stopAtEntry": true,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"miDebuggerPath": "/usr/bin/gdb"
}
]
}

35
.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,35 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "Build Xv6 operating system",
"type": "shell",
"command": "make",
"problemMatcher": [],
"group": {
"kind": "build",
"isDefault": true
}
},
{
"label": "Run Xv6 under QEMU",
"type": "shell",
"command": "make qemu",
"problemMatcher": []
},
{
"label": "Run Xv6 under QEMU for debugging",
"type": "shell",
"command": "make qemu-gdb",
"problemMatcher": []
},
{
"label": "Clean non-source files",
"type": "shell",
"command": "make clean",
"problemMatcher": []
}
]
}

24
LICENSE Normal file
View File

@ -0,0 +1,24 @@
The xv6 software is:
Copyright (c) 2006-2018 Frans Kaashoek, Robert Morris, Russ Cox,
Massachusetts Institute of Technology
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

246
Makefile Normal file
View File

@ -0,0 +1,246 @@
OBJS = \
bio.o\
console.o\
exec.o\
file.o\
fs.o\
ide.o\
ioapic.o\
kalloc.o\
kbd.o\
lapic.o\
log.o\
main.o\
mp.o\
picirq.o\
pipe.o\
proc.o\
sleeplock.o\
spinlock.o\
string.o\
swtch.o\
syscall.o\
sysfile.o\
sysproc.o\
trapasm.o\
trap.o\
uart.o\
vectors.o\
vm.o\
# Cross-compiling (e.g., on Mac OS X)
# TOOLPREFIX = i386-jos-elf
# Using native tools (e.g., on X86 Linux)
#TOOLPREFIX =
# Try to infer the correct TOOLPREFIX if not set
ifndef TOOLPREFIX
TOOLPREFIX := $(shell if i386-jos-elf-objdump -i 2>&1 | grep '^elf32-i386$$' >/dev/null 2>&1; \
then echo 'i386-jos-elf-'; \
elif objdump -i 2>&1 | grep 'elf32-i386' >/dev/null 2>&1; \
then echo ''; \
else echo "***" 1>&2; \
echo "*** Error: Couldn't find an i386-*-elf version of GCC/binutils." 1>&2; \
echo "*** Is the directory with i386-jos-elf-gcc in your PATH?" 1>&2; \
echo "*** If your i386-*-elf toolchain is installed with a command" 1>&2; \
echo "*** prefix other than 'i386-jos-elf-', set your TOOLPREFIX" 1>&2; \
echo "*** environment variable to that prefix and run 'make' again." 1>&2; \
echo "*** To turn off this error, run 'gmake TOOLPREFIX= ...'." 1>&2; \
echo "***" 1>&2; exit 1; fi)
endif
# If the makefile can't find QEMU, specify its path here
# QEMU = qemu-system-i386
# Try to infer the correct QEMU
ifndef QEMU
QEMU = $(shell if which qemu > /dev/null; \
then echo qemu; exit; \
elif which qemu-system-i386 > /dev/null; \
then echo qemu-system-i386; exit; \
elif which qemu-system-x86_64 > /dev/null; \
then echo qemu-system-x86_64; exit; \
else \
qemu=/Applications/Q.app/Contents/MacOS/i386-softmmu.app/Contents/MacOS/i386-softmmu; \
if test -x $$qemu; then echo $$qemu; exit; fi; fi; \
echo "***" 1>&2; \
echo "*** Error: Couldn't find a working QEMU executable." 1>&2; \
echo "*** Is the directory containing the qemu binary in your PATH" 1>&2; \
echo "*** or have you tried setting the QEMU variable in Makefile?" 1>&2; \
echo "***" 1>&2; exit 1)
endif
CC = $(TOOLPREFIX)gcc
AS = $(TOOLPREFIX)gas
LD = $(TOOLPREFIX)ld
OBJCOPY = $(TOOLPREFIX)objcopy
OBJDUMP = $(TOOLPREFIX)objdump
CFLAGS = -fno-pic -static -fno-builtin -fno-strict-aliasing -O2 -Wall -MD -ggdb -m32 -Werror -fno-omit-frame-pointer
CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector)
ASFLAGS = -m32 -gdwarf-2 -Wa,-divide
# FreeBSD ld wants ``elf_i386_fbsd''
LDFLAGS += -m $(shell $(LD) -V | grep elf_i386 2>/dev/null | head -n 1)
# Disable PIE when possible (for Ubuntu 16.10 toolchain)
ifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]no-pie'),)
CFLAGS += -fno-pie -no-pie
endif
ifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]nopie'),)
CFLAGS += -fno-pie -nopie
endif
xv6.img: bootblock kernel
dd if=/dev/zero of=xv6.img count=10000
dd if=bootblock of=xv6.img conv=notrunc
dd if=kernel of=xv6.img seek=1 conv=notrunc
xv6memfs.img: bootblock kernelmemfs
dd if=/dev/zero of=xv6memfs.img count=10000
dd if=bootblock of=xv6memfs.img conv=notrunc
dd if=kernelmemfs of=xv6memfs.img seek=1 conv=notrunc
bootblock: bootasm.S bootmain.c
$(CC) $(CFLAGS) -fno-pic -O -nostdinc -I. -c bootmain.c
$(CC) $(CFLAGS) -fno-pic -nostdinc -I. -c bootasm.S
$(LD) $(LDFLAGS) -N -e start -Ttext 0x7C00 -o bootblock.o bootasm.o bootmain.o
$(OBJDUMP) -S bootblock.o > bootblock.asm
$(OBJCOPY) -S -O binary -j .text bootblock.o bootblock
# The following line is here since it has been noticed that if you use Explorer to
# copy folders on wsl, sometimees the execute permissions can be removed from perl scripts.
# Uncomment if needed, but it will flag as a change for git.
# chmod +x sign.pl
./sign.pl bootblock
syscall.h: gensyscalls.pl
./gensyscalls.pl -h > syscall.h
syscalltable.h: gensyscalls.pl
./gensyscalls.pl -c > syscalltable.h
usys.S: gensyscalls.pl
./gensyscalls.pl -a > usys.S
entryother: entryother.S
$(CC) $(CFLAGS) -fno-pic -nostdinc -I. -c entryother.S
$(LD) $(LDFLAGS) -N -e start -Ttext 0x7000 -o bootblockother.o entryother.o
$(OBJCOPY) -S -O binary -j .text bootblockother.o entryother
$(OBJDUMP) -S bootblockother.o > entryother.asm
initcode: initcode.S
$(CC) $(CFLAGS) -nostdinc -I. -c initcode.S
$(LD) $(LDFLAGS) -N -e start -Ttext 0 -o initcode.out initcode.o
$(OBJCOPY) -S -O binary initcode.out initcode
$(OBJDUMP) -S initcode.o > initcode.asm
kernel: syscall.h syscalltable.h $(OBJS) entry.o entryother initcode kernel.ld
$(LD) $(LDFLAGS) -T kernel.ld -o kernel entry.o $(OBJS) -b binary initcode entryother
$(OBJDUMP) -S kernel > kernel.asm
$(OBJDUMP) -t kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernel.sym
# kernelmemfs is a copy of kernel that maintains the
# disk image in memory instead of writing to a disk.
# This is not so useful for testing persistent storage or
# exploring disk buffering implementations, but it is
# great for testing the kernel on real hardware without
# needing a scratch disk.
MEMFSOBJS = $(filter-out ide.o,$(OBJS)) memide.o
kernelmemfs: $(MEMFSOBJS) entry.o entryother initcode kernel.ld fs.img
$(LD) $(LDFLAGS) -T kernel.ld -o kernelmemfs entry.o $(MEMFSOBJS) -b binary initcode entryother fs.img
$(OBJDUMP) -S kernelmemfs > kernelmemfs.asm
$(OBJDUMP) -t kernelmemfs | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernelmemfs.sym
tags: $(OBJS) entryother.S _init
etags *.S *.c
vectors.S: vectors.pl
# chmod +x vectors.pl
./vectors.pl > vectors.S
ULIB = ulib.o usys.o printf.o umalloc.o
_%: %.o $(ULIB)
$(LD) $(LDFLAGS) -N -e _init -Ttext 0 -o $@ $^
$(OBJDUMP) -S $@ > $*.asm
$(OBJDUMP) -t $@ | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $*.sym
_forktest: forktest.o $(ULIB)
# forktest has less library code linked in - needs to be small
# in order to be able to max out the proc table.
$(LD) $(LDFLAGS) -N -e _init -Ttext 0 -o _forktest forktest.o ulib.o usys.o
$(OBJDUMP) -S _forktest > forktest.asm
mkfs: mkfs.c fs.h
gcc -Werror -Wall -o mkfs mkfs.c
# Prevent deletion of intermediate files, e.g. cat.o, after first build, so
# that disk image changes after first build are persistent until clean. More
# details:
# http://www.gnu.org/software/make/manual/html_node/Chained-Rules.html
.PRECIOUS: %.o
UPROGS=\
_cat\
_echo\
_forktest\
_grep\
_init\
_kill\
_ln\
_ls\
_mkdir\
_rm\
_sh\
_stressfs\
_usertests\
_wc\
_zombie\
fs.img: mkfs $(UPROGS)
./mkfs fs.img $(UPROGS)
-include *.d
clean:
rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg \
*.o *.d *.asm *.sym vectors.S bootblock entryother \
initcode initcode.out kernel xv6.img fs.img kernelmemfs \
xv6memfs.img mkfs .gdbinit \
syscall.h syscalltable.h usys.S \
$(UPROGS)
# run in emulators
# try to generate a unique GDB port
GDBPORT = $(shell expr `id -u` % 5000 + 25000)
# QEMU's gdb stub command line changed in 0.11
QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \
then echo "-gdb tcp::$(GDBPORT)"; \
else echo "-s -p $(GDBPORT)"; fi)
ifndef CPUS
CPUS := 2
endif
QEMUOPTS = -drive file=fs.img,index=1,media=disk,format=raw -drive file=xv6.img,index=0,media=disk,format=raw -smp $(CPUS) -m 512 $(QEMUEXTRA)
qemu: fs.img xv6.img
$(QEMU) -vga std -serial mon:stdio $(QEMUOPTS)
qemu-memfs: xv6memfs.img
$(QEMU) -vga std -drive file=xv6memfs.img,index=0,media=disk,format=raw -smp $(CPUS) -m 256
qemu-nox: fs.img xv6.img
$(QEMU) -nographic $(QEMUOPTS)
qemu-curses: fs.img xv6.img
$(QEMU) -curses $(QEMUOPTS)
.gdbinit: .gdbinit.tmpl
sed "s/localhost:1234/localhost:$(GDBPORT)/" < $^ > $@
qemu-gdb: fs.img xv6.img .gdbinit
@echo "*** Now run 'gdb'." 1>&2
$(QEMU) -vga std -serial mon:stdio $(QEMUOPTS) -S $(QEMUGDB)
qemu-nox-gdb: fs.img xv6.img .gdbinit
@echo "*** Now run 'gdb'." 1>&2
$(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB)

18
asm.h Normal file
View File

@ -0,0 +1,18 @@
//
// assembler macros to create x86 segments
//
#define SEG_NULLASM \
.word 0, 0; \
.byte 0, 0, 0, 0
// The 0xC0 means the limit is in 4096-byte units
// and (for executable segments) 32-bit mode.
#define SEG_ASM(type, base, lim) \
.word(((lim) >> 12) & 0xffff), ((base) & 0xffff); \
.byte(((base) >> 16) & 0xff), (0x90 | (type)), \
(0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff)
#define STA_X 0x8 // Executable segment
#define STA_W 0x2 // Writeable (non-executable segments)
#define STA_R 0x2 // Readable (executable segments)

140
bio.c Normal file
View File

@ -0,0 +1,140 @@
// Buffer cache.
//
// The buffer cache is a linked list of buf structures holding
// 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.
// * When done with the buffer, call brelse.
// * 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 two state flags internally:
// * 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.
#include "types.h"
#include "defs.h"
#include "param.h"
#include "spinlock.h"
#include "sleeplock.h"
#include "fs.h"
#include "buf.h"
struct {
struct spinlock lock;
struct buf buf[NBUF];
// Linked list of all buffers, through prev/next.
// head.next is most recently used.
struct buf head;
} bcache;
void binit(void) {
struct buf *b;
initlock(&bcache.lock, "bcache");
// Create linked list of buffers
bcache.head.prev = &bcache.head;
bcache.head.next = &bcache.head;
for (b = bcache.buf; b < bcache.buf + NBUF; b++) {
b->next = bcache.head.next;
b->prev = &bcache.head;
initsleeplock(&b->lock, "buffer");
bcache.head.next->prev = b;
bcache.head.next = b;
}
}
// Look through buffer cache for block on device dev.
// If not found, allocate a buffer.
// In either case, return locked buffer.
static struct buf* bget(uint dev, uint blockno) {
struct buf *b;
acquire(&bcache.lock);
// Is the block already cached?
for (b = bcache.head.next; b != &bcache.head; b = b->next) {
if (b->dev == dev && b->blockno == blockno) {
b->refcnt++;
release(&bcache.lock);
acquiresleep(&b->lock);
return b;
}
}
// Not cached; recycle an unused buffer.
// Even if refcnt==0, B_DIRTY indicates a buffer is in use
// because log.c has modified it but not yet committed it.
for (b = bcache.head.prev; b != &bcache.head; b = b->prev) {
if (b->refcnt == 0 && (b->flags & B_DIRTY) == 0) {
b->dev = dev;
b->blockno = blockno;
b->flags = 0;
b->refcnt = 1;
release(&bcache.lock);
acquiresleep(&b->lock);
return b;
}
}
panic("bget: no buffers");
}
// Return a locked buf with the contents of the indicated block.
struct buf*bread(uint dev, uint blockno) {
struct buf *b;
b = bget(dev, blockno);
if ((b->flags & B_VALID) == 0) {
iderw(b);
}
return b;
}
// Write b's contents to disk. Must be locked.
void bwrite(struct buf *b) {
if (!holdingsleep(&b->lock)) {
panic("bwrite");
}
b->flags |= B_DIRTY;
iderw(b);
}
// Release a locked buffer.
// Move to the head of the MRU list.
void brelse(struct buf *b) {
if (!holdingsleep(&b->lock)) {
panic("brelse");
}
releasesleep(&b->lock);
acquire(&bcache.lock);
b->refcnt--;
if (b->refcnt == 0) {
// no one is waiting for it.
b->next->prev = b->prev;
b->prev->next = b->next;
b->next = bcache.head.next;
b->prev = &bcache.head;
bcache.head.next->prev = b;
bcache.head.next = b;
}
release(&bcache.lock);
}

81
bootasm.S Normal file
View File

@ -0,0 +1,81 @@
#include "asm.h"
#include "memlayout.h"
#include "mmu.h"
# Start the first CPU: switch to 32-bit protected mode, jump into C.
# The BIOS loads this code from the first sector of the hard disk into
# memory at physical address 0x7c00 and starts executing in real mode
# with %cs=0 %ip=7c00.
.code16 # Assemble for 16-bit mode
.globl start
start:
cli # BIOS enabled interrupts; disable
# Zero data segment registers DS, ES, and SS.
xorw %ax,%ax # Set %ax to zero
movw %ax,%ds # -> Data Segment
movw %ax,%es # -> Extra Segment
movw %ax,%ss # -> Stack Segment
# Physical address line A20 is tied to zero so that the first PCs
# with 2 MB would run software that assumed 1 MB. Undo that.
seta20.1:
inb $0x64,%al # Wait for not busy
testb $0x2,%al
jnz seta20.1
movb $0xd1,%al # 0xd1 -> port 0x64
outb %al,$0x64
seta20.2:
inb $0x64,%al # Wait for not busy
testb $0x2,%al
jnz seta20.2
movb $0xdf,%al # 0xdf -> port 0x60
outb %al,$0x60
# Switch from real to protected mode. Use a bootstrap GDT that makes
# virtual addresses map directly to physical addresses so that the
# effective memory map doesn't change during the transition.
lgdt gdtdesc
movl %cr0, %eax
orl $CR0_PE, %eax
movl %eax, %cr0
# Complete the transition to 32-bit protected mode by using a long jmp
# to reload %cs and %eip. The segment descriptors are set up with no
# translation, so that the mapping is still the identity mapping.
ljmp $(SEG_KCODE<<3), $start32
.code32 # Tell assembler to generate 32-bit code now.
start32:
# Set up the protected-mode data segment registers
movw $(SEG_KDATA<<3), %ax # Our data segment selector
movw %ax, %ds # -> DS: Data Segment
movw %ax, %es # -> ES: Extra Segment
movw %ax, %ss # -> SS: Stack Segment
movw $0, %ax # Zero segments not ready for use
movw %ax, %fs # -> FS
movw %ax, %gs # -> GS
# Set up the stack pointer and call into C.
movl $start, %esp
call bootmain
# If bootmain returns (it shouldn't), loop.
spin:
jmp spin
# Bootstrap GDT
.p2align 2 # force 4 byte alignment
gdt:
SEG_NULLASM # null seg
SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg
SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg
gdtdesc:
.word (gdtdesc - gdt - 1) # sizeof(gdt) - 1
.long gdt # address gdt

93
bootmain.c Normal file
View File

@ -0,0 +1,93 @@
// 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
// sector 1 and then jumps to the kernel entry routine.
#include "types.h"
#include "elf.h"
#include "x86.h"
#include "memlayout.h"
#define SECTSIZE 512
void readseg(uchar*, uint, uint);
void bootmain(void) {
struct elfhdr *elf;
struct proghdr *ph, *eph;
void (*entry)(void);
uchar* pa;
elf = (struct elfhdr*)0x10000; // scratch space
// Read 1st page off disk
readseg((uchar*)elf, 4096, 0);
// Is this an ELF executable?
if (elf->magic != ELF_MAGIC) {
return; // let bootasm.S handle error
}
// Load each program segment (ignores ph flags).
ph = (struct proghdr*)((uchar*)elf + elf->phoff);
eph = ph + elf->phnum;
for (; ph < eph; ph++) {
pa = (uchar*)ph->paddr;
readseg(pa, ph->filesz, ph->off);
if (ph->memsz > ph->filesz) {
stosb(pa + ph->filesz, 0, ph->memsz - ph->filesz);
}
}
// Call the entry point from the ELF header.
// Does not return!
entry = (void (*)(void))(elf->entry);
entry();
}
void waitdisk(void) {
// Wait for disk ready.
while ((inb(0x1F7) & 0xC0) != 0x40) {
;
}
}
// Read a single sector at offset into dst.
void readsect(void *dst, uint offset) {
// Issue command.
waitdisk();
outb(0x1F2, 1); // count = 1
outb(0x1F3, offset);
outb(0x1F4, offset >> 8);
outb(0x1F5, offset >> 16);
outb(0x1F6, (offset >> 24) | 0xE0);
outb(0x1F7, 0x20); // cmd 0x20 - read sectors
// Read data.
waitdisk();
insl(0x1F0, dst, SECTSIZE / 4);
}
// Read 'count' bytes at 'offset' from kernel into physical address 'pa'.
// Might copy more than asked.
void readseg(uchar* pa, uint count, uint offset) {
uchar* epa;
epa = pa + count;
// Round down to sector boundary.
pa -= offset % SECTSIZE;
// Translate from bytes to sectors; kernel starts at sector 1.
offset = (offset / SECTSIZE) + 1;
// If this is too slow, we could read lots of sectors at a time.
// We'd write more to memory than asked, but it doesn't matter --
// we load in increasing order.
for (; pa < epa; pa += SECTSIZE, offset++) {
readsect(pa, offset);
}
}

14
buf.h Normal file
View File

@ -0,0 +1,14 @@
struct buf {
int flags;
uint dev;
uint blockno;
struct sleeplock lock;
uint refcnt;
struct buf *prev; // LRU cache list
struct buf *next;
struct buf *qnext; // disk queue
uchar data[BSIZE];
};
#define B_VALID 0x2 // buffer has been read from disk
#define B_DIRTY 0x4 // buffer needs to be written to disk

39
cat.c Normal file
View File

@ -0,0 +1,39 @@
#include "types.h"
#include "stat.h"
#include "user.h"
char buf[512];
void cat(int fd) {
int n;
while ((n = read(fd, buf, sizeof(buf))) > 0) {
if (write(1, buf, n) != n) {
printf(1, "cat: write error\n");
exit();
}
}
if (n < 0) {
printf(1, "cat: read error\n");
exit();
}
}
int main(int argc, char *argv[]) {
int fd, i;
if (argc <= 1) {
cat(0);
exit();
}
for (i = 1; i < argc; i++) {
if ((fd = open(argv[i], 0)) < 0) {
printf(1, "cat: cannot open %s\n", argv[i]);
exit();
}
cat(fd);
close(fd);
}
exit();
}

2
config.xlaunch Normal file
View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<XLaunch WindowMode="MultiWindow" ClientMode="NoClient" LocalClient="False" Display="-1" LocalProgram="xcalc" RemoteProgram="xterm" RemotePassword="" PrivateKey="" RemoteHost="" RemoteUser="" XDMCPHost="" XDMCPBroadcast="False" XDMCPIndirect="False" Clipboard="True" ClipboardPrimary="True" ExtraParams="" Wgl="True" DisableAC="True" XDMCPTerminate="False"/>

332
console.c Normal file
View File

@ -0,0 +1,332 @@
// Console input and output.
// Input is from the keyboard or serial port.
// Output is written to the screen and serial port.
#include "types.h"
#include "defs.h"
#include "param.h"
#include "traps.h"
#include "spinlock.h"
#include "sleeplock.h"
#include "fs.h"
#include "file.h"
#include "memlayout.h"
#include "mmu.h"
#include "proc.h"
#include "x86.h"
#define INPUT_BUF 128
struct kbdbuffer {
char buf[INPUT_BUF];
uint r; // Read index
uint w; // Write index
uint e; // Edit index
};
struct kbdbuffer inputBuffer;
struct kbdbuffer * input = 0;
#define C(x) ((x) - '@') // Control-x
static void consputc(int);
static int panicked = 0;
static struct {
struct spinlock lock;
int locking;
} cons;
static void printint(int xx, int base, int sign) {
static char digits[] = "0123456789abcdef";
char buf[16];
int i;
uint x;
if (sign && (sign = xx < 0)) {
x = -xx;
}
else {
x = xx;
}
i = 0;
do {
buf[i++] = digits[x % base];
}
while ((x /= base) != 0);
if (sign) {
buf[i++] = '-';
}
while (--i >= 0) {
consputc(buf[i]);
}
}
// Print to the console. only understands %d, %x, %p, %s.
void cprintf(char *fmt, ...) {
int i, c, locking;
uint *argp;
char *s;
locking = cons.locking;
if (locking) {
acquire(&cons.lock);
}
if (fmt == 0) {
panic("null fmt");
}
argp = (uint*)(void*)(&fmt + 1);
for (i = 0; (c = fmt[i] & 0xff) != 0; i++) {
if (c != '%') {
consputc(c);
continue;
}
c = fmt[++i] & 0xff;
if (c == 0) {
break;
}
switch (c) {
case 'd':
printint(*argp++, 10, 1);
break;
case 'x':
case 'p':
printint(*argp++, 16, 0);
break;
case 's':
if ((s = (char*)*argp++) == 0) {
s = "(null)";
}
for (; *s; s++) {
consputc(*s);
}
break;
case '%':
consputc('%');
break;
default:
// Print unknown % sequence to draw attention.
consputc('%');
consputc(c);
break;
}
}
if (locking) {
release(&cons.lock);
}
}
void panic(char *s) {
int i;
uint pcs[10];
cli();
cons.locking = 0;
// use lapiccpunum so that we can call panic from mycpu()
cprintf("lapicid %d: panic: ", lapicid());
cprintf(s);
cprintf("\n");
getcallerpcs(&s, pcs);
for (i = 0; i < 10; i++) {
cprintf(" %p", pcs[i]);
}
panicked = 1; // freeze other CPU
for (;;) {
;
}
}
#define BACKSPACE 0x100
#define CRTPORT 0x3d4
static ushort *crt = (ushort*)P2V(0xb8000); // CGA memory
static void cgaputc(int c) {
int pos;
// Cursor position: col + 80*row.
outb(CRTPORT, 14);
pos = inb(CRTPORT + 1) << 8;
outb(CRTPORT, 15);
pos |= inb(CRTPORT + 1);
if (c == '\n') {
pos += 80 - pos % 80;
}
else if (c == BACKSPACE) {
if (pos > 0) {
--pos;
}
}
else {
crt[pos++] = (c & 0xff) | 0x0700; // black on white
}
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);
outb(CRTPORT + 1, pos);
crt[pos] = ' ' | 0x0700;
}
void consputc(int c) {
if (panicked) {
cli();
for (;;) {
;
}
}
if (c == BACKSPACE) {
uartputc('\b');
uartputc(' ');
uartputc('\b');
}
else {
uartputc(c);
}
cgaputc(c);
}
int consoleget(void) {
int c;
acquire(&cons.lock);
while ((c = kbdgetc()) <= 0) {
if (c == 0) {
c = kbdgetc();
}
}
release(&cons.lock);
return c;
}
void consoleintr(int (*getc)(void)) {
int c, doprocdump = 0;
acquire(&cons.lock);
while ((c = getc()) >= 0) {
switch (c) {
case C('P'): // Process listing.
// procdump() locks cons.lock indirectly; invoke later
doprocdump = 1;
break;
case C('U'): // Kill line.
while (input->e != input->w &&
input->buf[(input->e - 1) % INPUT_BUF] != '\n') {
input->e--;
consputc(BACKSPACE);
}
break;
case C('H'):
case '\x7f': // Backspace
if (input->e != input->w) {
input->e--;
consputc(BACKSPACE);
}
break;
default:
if (c != 0 && input->e - input->r < INPUT_BUF) {
c = (c == '\r') ? '\n' : c;
input->buf[input->e++ % INPUT_BUF] = c;
consputc(c);
if (c == '\n' || c == C('D') || input->e == input->r + INPUT_BUF) {
input->w = input->e;
wakeup(&(input->r));
}
}
break;
}
}
release(&cons.lock);
if (doprocdump) {
procdump(); // now call procdump() wo. cons.lock held
}
}
int consoleread(struct inode *ip, char *dst, int n) {
uint target;
int c;
iunlock(ip);
target = n;
acquire(&cons.lock);
while (n > 0) {
while (input->r == input->w) {
if (myproc()->killed) {
release(&cons.lock);
ilock(ip);
return -1;
}
sleep(&(input->r), &cons.lock);
}
c = input->buf[input->r++ % INPUT_BUF];
if (c == C('D')) { // EOF
if (n < target) {
// Save ^D for next time, to make sure
// caller gets a 0-byte result.
input->r--;
}
break;
}
*dst++ = c;
--n;
if (c == '\n') {
break;
}
}
release(&cons.lock);
ilock(ip);
return target - n;
}
int consolewrite(struct inode *ip, char *buf, int n) {
int i;
iunlock(ip);
acquire(&cons.lock);
for (i = 0; i < n; i++) {
consputc(buf[i] & 0xff);
}
release(&cons.lock);
ilock(ip);
return n;
}
void consoleinit(void) {
initlock(&cons.lock, "console");
// Initialise pointer to point to our console input buffer
input = &inputBuffer;
devsw[CONSOLE].write = consolewrite;
devsw[CONSOLE].read = consoleread;
cons.locking = 1;
ioapicenable(IRQ_KBD, 0);
}

48
cuth Normal file
View File

@ -0,0 +1,48 @@
#!/usr/bin/perl
$| = 1;
sub writefile($@){
my ($file, @lines) = @_;
sleep(1);
open(F, ">$file") || die "open >$file: $!";
print F @lines;
close(F);
}
# Cut out #include lines that don't contribute anything.
for($i=0; $i<@ARGV; $i++){
$file = $ARGV[$i];
if(!open(F, $file)){
print STDERR "open $file: $!\n";
next;
}
@lines = <F>;
close(F);
$obj = "$file.o";
$obj =~ s/\.c\.o$/.o/;
system("touch $file");
if(system("make CC='gcc -Werror' $obj >/dev/null 2>\&1") != 0){
print STDERR "make $obj failed: $rv\n";
next;
}
system("cp $file =$file");
for($j=@lines-1; $j>=0; $j--){
if($lines[$j] =~ /^#include/){
$old = $lines[$j];
$lines[$j] = "/* CUT-H */\n";
writefile($file, @lines);
if(system("make CC='gcc -Werror' $obj >/dev/null 2>\&1") != 0){
$lines[$j] = $old;
}else{
print STDERR "$file $old";
}
}
}
writefile($file, grep {!/CUT-H/} @lines);
system("rm =$file");
}

8
date.h Normal file
View File

@ -0,0 +1,8 @@
struct rtcdate {
uint second;
uint minute;
uint hour;
uint day;
uint month;
uint year;
};

191
defs.h Normal file
View File

@ -0,0 +1,191 @@
struct buf;
struct context;
struct file;
struct inode;
struct pipe;
struct proc;
struct rtcdate;
struct spinlock;
struct sleeplock;
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));
int consoleget(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);
int kbdgetc(void);
// lapic.c
void cmostime(struct rtcdate *r);
int lapicid(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;
void mpinit(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);
// proc.c
int cpuid(void);
void exit(void);
int fork(void);
int growproc(int);
int kill(int);
struct cpu* mycpu(void);
struct proc* myproc();
void pinit(void);
void procdump(void);
void scheduler(void) __attribute__((noreturn));
void sched(void);
void setproc(struct proc*);
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);
// sleeplock.c
void acquiresleep(struct sleeplock*);
void releasesleep(struct sleeplock*);
int holdingsleep(struct sleeplock*);
void initsleeplock(struct sleeplock*, char*);
// 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);
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]))

12
echo.c Normal file
View File

@ -0,0 +1,12 @@
#include "types.h"
#include "stat.h"
#include "user.h"
int main(int argc, char *argv[]) {
int i;
for (i = 1; i < argc; i++) {
printf(1, "%s%s", argv[i], i + 1 < argc ? " " : "\n");
}
exit();
}

42
elf.h Normal file
View File

@ -0,0 +1,42 @@
// 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];
ushort type;
ushort machine;
uint version;
uint entry;
uint phoff;
uint shoff;
uint flags;
ushort ehsize;
ushort phentsize;
ushort phnum;
ushort shentsize;
ushort shnum;
ushort shstrndx;
};
// Program section header
struct proghdr {
uint type;
uint off;
uint vaddr;
uint paddr;
uint filesz;
uint memsz;
uint flags;
uint align;
};
// Values for Proghdr type
#define ELF_PROG_LOAD 1
// Flag bits for Proghdr flags
#define ELF_PROG_FLAG_EXEC 1
#define ELF_PROG_FLAG_WRITE 2
#define ELF_PROG_FLAG_READ 4

68
entry.S Normal file
View File

@ -0,0 +1,68 @@
# The xv6 kernel starts executing in this file. This file is linked with
# the kernel C code, so it can refer to kernel symbols such as main().
# The boot block (bootasm.S and bootmain.c) jumps to entry below.
# Multiboot header, for multiboot boot loaders like GNU Grub.
# http://www.gnu.org/software/grub/manual/multiboot/multiboot.html
#
# Using GRUB 2, you can boot xv6 from a file stored in a
# Linux file system by copying kernel or kernelmemfs to /boot
# and then adding this menu entry:
#
# menuentry "xv6" {
# insmod ext2
# set root='(hd0,msdos1)'
# set kernel='/boot/kernel'
# echo "Loading ${kernel}..."
# multiboot ${kernel} ${kernel}
# boot
# }
#include "asm.h"
#include "memlayout.h"
#include "mmu.h"
#include "param.h"
# Multiboot header. Data to direct multiboot loader.
.p2align 2
.text
.globl multiboot_header
multiboot_header:
#define magic 0x1badb002
#define flags 0
.long magic
.long flags
.long (-magic-flags)
# By convention, the _start symbol specifies the ELF entry point.
# Since we haven't set up virtual memory yet, our entry point is
# the physical address of 'entry'.
.globl _start
_start = V2P_WO(entry)
# Entering xv6 on boot processor, with paging off.
.globl entry
entry:
# Turn on page size extension for 4Mbyte pages
movl %cr4, %eax
orl $(CR4_PSE), %eax
movl %eax, %cr4
# Set page directory
movl $(V2P_WO(entrypgdir)), %eax
movl %eax, %cr3
# Turn on paging.
movl %cr0, %eax
orl $(CR0_PG|CR0_WP), %eax
movl %eax, %cr0
# Set up the stack pointer.
movl $(stack + KSTACKSIZE), %esp
# Jump to main(), and switch to executing at
# high addresses. The indirect call is needed because
# the assembler produces a PC-relative instruction
# for a direct jump.
mov $main, %eax
jmp *%eax
.comm stack, KSTACKSIZE

89
entryother.S Normal file
View File

@ -0,0 +1,89 @@
#include "asm.h"
#include "memlayout.h"
#include "mmu.h"
# Each non-boot CPU ("AP") is started up in response to a STARTUP
# IPI from the boot CPU. Section B.4.2 of the Multi-Processor
# Specification says that the AP will start in real mode with CS:IP
# set to XY00:0000, where XY is an 8-bit value sent with the
# STARTUP. Thus this code must start at a 4096-byte boundary.
#
# Because this code sets DS to zero, it must sit
# at an address in the low 2^16 bytes.
#
# Startothers (in main.c) sends the STARTUPs one at a time.
# It copies this code (start) at 0x7000. It puts the address of
# a newly allocated per-core stack in start-4,the address of the
# place to jump to (mpenter) in start-8, and the physical address
# of entrypgdir in start-12.
#
# This code combines elements of bootasm.S and entry.S.
.code16
.globl start
start:
cli
# Zero data segment registers DS, ES, and SS.
xorw %ax,%ax
movw %ax,%ds
movw %ax,%es
movw %ax,%ss
# Switch from real to protected mode. Use a bootstrap GDT that makes
# virtual addresses map directly to physical addresses so that the
# effective memory map doesn't change during the transition.
lgdt gdtdesc
movl %cr0, %eax
orl $CR0_PE, %eax
movl %eax, %cr0
# Complete the transition to 32-bit protected mode by using a long jmp
# to reload %cs and %eip. The segment descriptors are set up with no
# translation, so that the mapping is still the identity mapping.
ljmpl $(SEG_KCODE<<3), $(start32)
.code32 # Tell assembler to generate 32-bit code now.
start32:
# Set up the protected-mode data segment registers
movw $(SEG_KDATA<<3), %ax # Our data segment selector
movw %ax, %ds # -> DS: Data Segment
movw %ax, %es # -> ES: Extra Segment
movw %ax, %ss # -> SS: Stack Segment
movw $0, %ax # Zero segments not ready for use
movw %ax, %fs # -> FS
movw %ax, %gs # -> GS
# Turn on page size extension for 4Mbyte pages
movl %cr4, %eax
orl $(CR4_PSE), %eax
movl %eax, %cr4
# Use entrypgdir as our initial page table
movl (start-12), %eax
movl %eax, %cr3
# Turn on paging.
movl %cr0, %eax
orl $(CR0_PE|CR0_PG|CR0_WP), %eax
movl %eax, %cr0
# Switch to the stack allocated by startothers()
movl (start-4), %esp
# Call mpenter()
call *(start-8)
# We should never return.
spin:
jmp spin
.p2align 2
gdt:
SEG_NULLASM
SEG_ASM(STA_X|STA_R, 0, 0xffffffff)
SEG_ASM(STA_W, 0, 0xffffffff)
gdtdesc:
.word (gdtdesc - gdt - 1)
.long gdt

142
exec.c Normal file
View File

@ -0,0 +1,142 @@
#include "types.h"
#include "param.h"
#include "memlayout.h"
#include "mmu.h"
#include "proc.h"
#include "defs.h"
#include "x86.h"
#include "elf.h"
void cleanupexec(pde_t * pgdir, struct inode *ip) {
if (pgdir) {
freevm(pgdir);
}
if (ip) {
iunlockput(ip);
end_op();
}
}
int exec(char *path, char **argv) {
char *s, *last;
int i, off;
uint argc, sz, sp, ustack[3 + MAXARG + 1];
struct elfhdr elf;
struct inode *ip;
struct proghdr ph;
pde_t *pgdir, *oldpgdir;
struct proc *curproc = myproc();
begin_op();
if ((ip = namei(path)) == 0) {
end_op();
cprintf("exec: fail\n");
return -1;
}
ilock(ip);
pgdir = 0;
// Check ELF header
if (readi(ip, (char*)&elf, 0, sizeof(elf)) != sizeof(elf)) {
cleanupexec(pgdir, ip);
return -1;
}
if (elf.magic != ELF_MAGIC) {
cleanupexec(pgdir, ip);
return -1;
}
if ((pgdir = setupkvm()) == 0) {
cleanupexec(pgdir, ip);
return -1;
}
// Load program into memory.
sz = 0;
for (i = 0, off = elf.phoff; i < elf.phnum; i++, off += sizeof(ph)) {
if (readi(ip, (char*)&ph, off, sizeof(ph)) != sizeof(ph)) {
cleanupexec(pgdir, ip);
return -1;
}
if (ph.type != ELF_PROG_LOAD) {
continue;
}
if (ph.memsz < ph.filesz) {
cleanupexec(pgdir, ip);
return -1;
}
if (ph.vaddr + ph.memsz < ph.vaddr) {
cleanupexec(pgdir, ip);
return -1;
}
if ((sz = allocuvm(pgdir, sz, ph.vaddr + ph.memsz)) == 0) {
cleanupexec(pgdir, ip);
return -1;
}
if (ph.vaddr % PGSIZE != 0) {
cleanupexec(pgdir, ip);
return -1;
}
if (loaduvm(pgdir, (char*)ph.vaddr, ip, ph.off, ph.filesz) < 0) {
cleanupexec(pgdir, ip);
return -1;
}
}
iunlockput(ip);
end_op();
ip = 0;
// Allocate two pages at the next page boundary.
// Make the first inaccessible. Use the second as the user stack.
sz = PGROUNDUP(sz);
if ((sz = allocuvm(pgdir, sz, sz + 2 * PGSIZE)) == 0) {
cleanupexec(pgdir, ip);
return -1;
}
clearpteu(pgdir, (char*)(sz - 2 * PGSIZE));
sp = sz;
// Push argument strings, prepare rest of stack in ustack.
for (argc = 0; argv[argc]; argc++) {
if (argc >= MAXARG) {
cleanupexec(pgdir, ip);
return -1;
}
sp = (sp - (strlen(argv[argc]) + 1)) & ~3;
if (copyout(pgdir, sp, argv[argc], strlen(argv[argc]) + 1) < 0) {
cleanupexec(pgdir, ip);
return -1;
}
ustack[3 + argc] = sp;
}
ustack[3 + argc] = 0;
ustack[0] = 0xffffffff; // fake return PC
ustack[1] = argc;
ustack[2] = sp - (argc + 1) * 4; // argv pointer
sp -= (3 + argc + 1) * 4;
if (copyout(pgdir, sp, ustack, (3 + argc + 1) * 4) < 0) {
cleanupexec(pgdir, ip);
return -1;
}
// Save program name for debugging.
for (last = s = path; *s; s++) {
if (*s == '/') {
last = s + 1;
}
}
safestrcpy(curproc->name, last, sizeof(curproc->name));
// Commit to the user image.
oldpgdir = curproc->pgdir;
curproc->pgdir = pgdir;
curproc->sz = sz;
curproc->tf->eip = elf.entry; // main
curproc->tf->esp = sp;
switchuvm(curproc);
freevm(oldpgdir);
return 0;
}

4
fcntl.h Normal file
View File

@ -0,0 +1,4 @@
#define O_RDONLY 0x000
#define O_WRONLY 0x001
#define O_RDWR 0x002
#define O_CREATE 0x200

155
file.c Normal file
View File

@ -0,0 +1,155 @@
//
// File descriptors
//
#include "types.h"
#include "defs.h"
#include "param.h"
#include "fs.h"
#include "spinlock.h"
#include "sleeplock.h"
#include "file.h"
struct devsw devsw[NDEV];
struct {
struct spinlock lock;
struct file file[NFILE];
} ftable;
void fileinit(void) {
initlock(&ftable.lock, "ftable");
}
// Allocate a file structure.
struct file* filealloc(void) {
struct file *f;
acquire(&ftable.lock);
for (f = ftable.file; f < ftable.file + NFILE; f++) {
if (f->ref == 0) {
f->ref = 1;
release(&ftable.lock);
return f;
}
}
release(&ftable.lock);
return 0;
}
// Increment ref count for file f.
struct file* filedup(struct file *f) {
acquire(&ftable.lock);
if (f->ref < 1) {
panic("filedup");
}
f->ref++;
release(&ftable.lock);
return f;
}
// Close file f. (Decrement ref count, close when reaches 0.)
void fileclose(struct file *f) {
struct file ff;
acquire(&ftable.lock);
if (f->ref < 1) {
panic("fileclose");
}
if (--f->ref > 0) {
release(&ftable.lock);
return;
}
ff = *f;
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) {
begin_op();
iput(ff.ip);
end_op();
}
}
// Get metadata about file f.
int filestat(struct file *f, struct stat *st) {
if (f->type == FD_INODE) {
ilock(f->ip);
stati(f->ip, st);
iunlock(f->ip);
return 0;
}
return -1;
}
// Read from file f.
int fileread(struct file *f, char *addr, int n) {
int r;
if (f->readable == 0) {
return -1;
}
if (f->type == FD_PIPE) {
return piperead(f->pipe, addr, n);
}
if (f->type == FD_INODE) {
ilock(f->ip);
if ((r = readi(f->ip, addr, f->off, n)) > 0) {
f->off += r;
}
iunlock(f->ip);
return r;
}
panic("fileread");
}
// Write to file f.
int filewrite(struct file *f, char *addr, int n) {
int r;
if (f->writable == 0) {
return -1;
}
if (f->type == FD_PIPE) {
return pipewrite(f->pipe, addr, n);
}
if (f->type == FD_INODE) {
// write a few blocks at a time to avoid exceeding
// the maximum log transaction size, including
// i-node, indirect block, allocation blocks,
// and 2 blocks of slop for non-aligned writes.
// this really belongs lower down, since writei()
// might be writing a device like the console.
int max = ((MAXOPBLOCKS - 1 - 1 - 2) / 2) * 512;
int i = 0;
while (i < n) {
int n1 = n - i;
if (n1 > max) {
n1 = max;
}
begin_op();
ilock(f->ip);
if ((r = writei(f->ip, addr + i, f->off, n1)) > 0) {
f->off += r;
}
iunlock(f->ip);
end_op();
if (r < 0) {
break;
}
if (r != n1) {
panic("short filewrite");
}
i += r;
}
return i == n ? n : -1;
}
panic("filewrite");
}

37
file.h Normal file
View File

@ -0,0 +1,37 @@
struct file {
enum { FD_NONE, FD_PIPE, FD_INODE } type;
int ref; // reference count
char readable;
char writable;
struct pipe *pipe;
struct inode *ip;
uint off;
};
// in-memory copy of an inode
struct inode {
uint dev; // Device number
uint inum; // Inode number
int ref; // Reference count
struct sleeplock lock; // protects everything below here
int valid; // inode has been read from disk?
short type; // copy of disk inode
short major;
short minor;
short nlink;
uint size;
uint addrs[NDIRECT + 1];
};
// table mapping major device number to
// device functions
struct devsw {
int (*read)(struct inode*, char*, int);
int (*write)(struct inode*, char*, int);
};
extern struct devsw devsw[];
#define CONSOLE 1

52
forktest.c Normal file
View File

@ -0,0 +1,52 @@
// Test that fork fails gracefully.
// Tiny executable so that the limit can be filling the proc table.
#include "types.h"
#include "stat.h"
#include "user.h"
#define N 1000
void printf(int fd, const char *s, ...) {
write(fd, s, strlen(s));
}
void forktest(void) {
int n, pid;
printf(1, "fork test\n");
for (n = 0; n < N; n++) {
pid = fork();
if (pid < 0) {
break;
}
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");
}
int main(int argc, char* argv[]) {
forktest();
exit();
}

649
fs.c Normal file
View File

@ -0,0 +1,649 @@
// File system implementation. Five layers:
// + Blocks: allocator for raw disk blocks.
// + Log: crash recovery for multi-step updates.
// + Files: inode allocator, reading, writing, metadata.
// + 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
// routines. The (higher-level) system call implementations
// are in sysfile.c.
#include "types.h"
#include "defs.h"
#include "param.h"
#include "stat.h"
#include "mmu.h"
#include "proc.h"
#include "spinlock.h"
#include "sleeplock.h"
#include "fs.h"
#include "buf.h"
#include "file.h"
#define min(a, b) ((a) < (b) ? (a) : (b))
static void itrunc(struct inode*);
// there should be one superblock per disk device, but we run with
// only one device
struct superblock sb;
// Read the super block.
void readsb(int dev, struct superblock *sb) {
struct buf *bp;
bp = bread(dev, 1);
memmove(sb, bp->data, sizeof(*sb));
brelse(bp);
}
// Zero a block.
static void bzero(int dev, int bno) {
struct buf *bp;
bp = bread(dev, bno);
memset(bp->data, 0, BSIZE);
log_write(bp);
brelse(bp);
}
// Blocks.
// Allocate a zeroed disk block.
static uint balloc(uint dev) {
int b, bi, m;
struct buf *bp;
bp = 0;
for (b = 0; b < sb.size; b += BPB) {
bp = bread(dev, BBLOCK(b, sb));
for (bi = 0; bi < BPB && b + bi < sb.size; bi++) {
m = 1 << (bi % 8);
if ((bp->data[bi / 8] & m) == 0) { // Is block free?
bp->data[bi / 8] |= m; // Mark block in use.
log_write(bp);
brelse(bp);
bzero(dev, b + bi);
return b + bi;
}
}
brelse(bp);
}
panic("balloc: out of blocks");
}
// Free a disk block.
static void bfree(int dev, uint b) {
struct buf *bp;
int bi, m;
bp = bread(dev, BBLOCK(b, sb));
bi = b % BPB;
m = 1 << (bi % 8);
if ((bp->data[bi / 8] & m) == 0) {
panic("freeing free block");
}
bp->data[bi / 8] &= ~m;
log_write(bp);
brelse(bp);
}
// Inodes.
//
// An inode describes a single unnamed file.
// The inode disk structure holds metadata: the file's type,
// its size, the number of links referring to it, and the
// list of blocks holding the file's content.
//
// The inodes are laid out sequentially on disk at
// sb.startinode. Each inode has a number, indicating its
// position on the disk.
//
// The kernel keeps a cache of in-use inodes in memory
// to provide a place for synchronizing access
// to inodes used by multiple processes. The cached
// inodes include book-keeping information that is
// not stored on disk: ip->ref and ip->valid.
//
// An inode and its in-memory representation go through a
// sequence of states before they can be used by the
// rest of the file system code.
//
// * Allocation: an inode is allocated if its type (on disk)
// is non-zero. ialloc() allocates, and iput() frees if
// the reference and link counts have fallen to zero.
//
// * Referencing in cache: an entry in the inode cache
// is free if ip->ref is zero. Otherwise ip->ref tracks
// the number of in-memory pointers to the entry (open
// files and current directories). iget() finds or
// creates a cache entry and increments its ref; iput()
// decrements ref.
//
// * Valid: the information (type, size, &c) in an inode
// cache entry is only correct when ip->valid is 1.
// ilock() reads the inode from
// the disk and sets ip->valid, while iput() clears
// ip->valid if ip->ref has fallen to zero.
//
// * Locked: file system code may only examine and modify
// the information in an inode and its content if it
// has first locked the inode.
//
// Thus a typical sequence is:
// ip = iget(dev, inum)
// ilock(ip)
// ... examine and modify ip->xxx ...
// iunlock(ip)
// iput(ip)
//
// ilock() is separate from iget() so that system calls can
// get a long-term reference to an inode (as for an open file)
// and only lock it for short periods (e.g., in read()).
// The separation also helps avoid deadlock and races during
// pathname lookup. iget() increments ip->ref so that the inode
// stays cached and pointers to it remain valid.
//
// Many internal file system functions expect the caller to
// have locked the inodes involved; this lets callers create
// multi-step atomic operations.
//
// The icache.lock spin-lock protects the allocation of icache
// entries. Since ip->ref indicates whether an entry is free,
// and ip->dev and ip->inum indicate which i-node an entry
// holds, one must hold icache.lock while using any of those fields.
//
// An ip->lock sleep-lock protects all ip-> fields other than ref,
// dev, and inum. One must hold ip->lock in order to
// read or write that inode's ip->valid, ip->size, ip->type, &c.
struct {
struct spinlock lock;
struct inode inode[NINODE];
} icache;
void iinit(int dev) {
int i = 0;
initlock(&icache.lock, "icache");
for (i = 0; i < NINODE; i++) {
initsleeplock(&icache.inode[i].lock, "inode");
}
readsb(dev, &sb);
cprintf("sb: size %d nblocks %d ninodes %d nlog %d logstart %d\
inodestart %d bmap start %d\n", sb.size, sb.nblocks,
sb.ninodes, sb.nlog, sb.logstart, sb.inodestart,
sb.bmapstart);
}
static struct inode* iget(uint dev, uint inum);
// Allocate an inode on device dev.
// Mark it as allocated by giving it type type.
// Returns an unlocked but allocated and referenced inode.
struct inode* ialloc(uint dev, short type) {
int inum;
struct buf *bp;
struct dinode *dip;
for (inum = 1; inum < sb.ninodes; inum++) {
bp = bread(dev, IBLOCK(inum, sb));
dip = (struct dinode*)bp->data + inum % IPB;
if (dip->type == 0) { // a free inode
memset(dip, 0, sizeof(*dip));
dip->type = type;
log_write(bp); // mark it allocated on the disk
brelse(bp);
return iget(dev, inum);
}
brelse(bp);
}
panic("ialloc: no inodes");
}
// Copy a modified in-memory inode to disk.
// Must be called after every change to an ip->xxx field
// that lives on disk, since i-node cache is write-through.
// Caller must hold ip->lock.
void iupdate(struct inode *ip) {
struct buf *bp;
struct dinode *dip;
bp = bread(ip->dev, IBLOCK(ip->inum, sb));
dip = (struct dinode*)bp->data + ip->inum % IPB;
dip->type = ip->type;
dip->major = ip->major;
dip->minor = ip->minor;
dip->nlink = ip->nlink;
dip->size = ip->size;
memmove(dip->addrs, ip->addrs, sizeof(ip->addrs));
log_write(bp);
brelse(bp);
}
// Find the inode with number inum on device dev
// and return the in-memory copy. Does not lock
// the inode and does not read it from disk.
static struct inode* iget(uint dev, uint inum) {
struct inode *ip, *empty;
acquire(&icache.lock);
// Is the inode already cached?
empty = 0;
for (ip = &icache.inode[0]; ip < &icache.inode[NINODE]; ip++) {
if (ip->ref > 0 && ip->dev == dev && ip->inum == inum) {
ip->ref++;
release(&icache.lock);
return ip;
}
if (empty == 0 && ip->ref == 0) { // Remember empty slot.
empty = ip;
}
}
// Recycle an inode cache entry.
if (empty == 0) {
panic("iget: no inodes");
}
ip = empty;
ip->dev = dev;
ip->inum = inum;
ip->ref = 1;
ip->valid = 0;
release(&icache.lock);
return ip;
}
// Increment reference count for ip.
// Returns ip to enable ip = idup(ip1) idiom.
struct inode* idup(struct inode *ip) {
acquire(&icache.lock);
ip->ref++;
release(&icache.lock);
return ip;
}
// Lock the given inode.
// Reads the inode from disk if necessary.
void ilock(struct inode *ip) {
struct buf *bp;
struct dinode *dip;
if (ip == 0 || ip->ref < 1) {
panic("ilock");
}
acquiresleep(&ip->lock);
if (ip->valid == 0) {
bp = bread(ip->dev, IBLOCK(ip->inum, sb));
dip = (struct dinode*)bp->data + ip->inum % IPB;
ip->type = dip->type;
ip->major = dip->major;
ip->minor = dip->minor;
ip->nlink = dip->nlink;
ip->size = dip->size;
memmove(ip->addrs, dip->addrs, sizeof(ip->addrs));
brelse(bp);
ip->valid = 1;
if (ip->type == 0) {
panic("ilock: no type");
}
}
}
// Unlock the given inode.
void iunlock(struct inode *ip) {
if (ip == 0 || !holdingsleep(&ip->lock) || ip->ref < 1) {
panic("iunlock");
}
releasesleep(&ip->lock);
}
// Drop a reference to an in-memory inode.
// If that was the last reference, the inode cache entry can
// be recycled.
// If that was the last reference and the inode has no links
// to it, free the inode (and its content) on disk.
// All calls to iput() must be inside a transaction in
// case it has to free the inode.
void iput(struct inode *ip) {
acquiresleep(&ip->lock);
if (ip->valid && ip->nlink == 0) {
acquire(&icache.lock);
int r = ip->ref;
release(&icache.lock);
if (r == 1) {
// inode has no links and no other references: truncate and free.
itrunc(ip);
ip->type = 0;
iupdate(ip);
ip->valid = 0;
}
}
releasesleep(&ip->lock);
acquire(&icache.lock);
ip->ref--;
release(&icache.lock);
}
// Common idiom: unlock, then put.
void iunlockput(struct inode *ip) {
iunlock(ip);
iput(ip);
}
// Inode content
//
// 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
// listed in block ip->addrs[NDIRECT].
// Return the disk block address of the nth block in inode ip.
// If there is no such block, bmap allocates one.
static uint bmap(struct inode *ip, uint bn) {
uint addr, *a;
struct buf *bp;
if (bn < NDIRECT) {
if ((addr = ip->addrs[bn]) == 0) {
ip->addrs[bn] = addr = balloc(ip->dev);
}
return addr;
}
bn -= NDIRECT;
if (bn < NINDIRECT) {
// Load indirect block, allocating if necessary.
if ((addr = ip->addrs[NDIRECT]) == 0) {
ip->addrs[NDIRECT] = addr = balloc(ip->dev);
}
bp = bread(ip->dev, addr);
a = (uint*)bp->data;
if ((addr = a[bn]) == 0) {
a[bn] = addr = balloc(ip->dev);
log_write(bp);
}
brelse(bp);
return addr;
}
panic("bmap: out of range");
}
// Truncate inode (discard contents).
// Only called when the inode has no links
// to it (no directory entries referring to it)
// and has no in-memory reference to it (is
// not an open file or current directory).
static void itrunc(struct inode *ip) {
int i, j;
struct buf *bp;
uint *a;
for (i = 0; i < NDIRECT; i++) {
if (ip->addrs[i]) {
bfree(ip->dev, ip->addrs[i]);
ip->addrs[i] = 0;
}
}
if (ip->addrs[NDIRECT]) {
bp = bread(ip->dev, ip->addrs[NDIRECT]);
a = (uint*)bp->data;
for (j = 0; j < NINDIRECT; j++) {
if (a[j]) {
bfree(ip->dev, a[j]);
}
}
brelse(bp);
bfree(ip->dev, ip->addrs[NDIRECT]);
ip->addrs[NDIRECT] = 0;
}
ip->size = 0;
iupdate(ip);
}
// Copy stat information from inode.
// Caller must hold ip->lock.
void stati(struct inode *ip, struct stat *st) {
st->dev = ip->dev;
st->ino = ip->inum;
st->type = ip->type;
st->nlink = ip->nlink;
st->size = ip->size;
}
// Read data from inode.
// Caller must hold ip->lock.
int readi(struct inode *ip, char *dst, uint off, uint n) {
uint tot, m;
struct buf *bp;
if (ip->type == T_DEV) {
if (ip->major < 0 || ip->major >= NDEV || !devsw[ip->major].read) {
return -1;
}
return devsw[ip->major].read(ip, dst, n);
}
if (off > ip->size || off + n < off) {
return -1;
}
if (off + n > ip->size) {
n = ip->size - off;
}
for (tot = 0; tot < n; tot += m, off += m, dst += m) {
bp = bread(ip->dev, bmap(ip, off / BSIZE));
m = min(n - tot, BSIZE - off % BSIZE);
memmove(dst, bp->data + off % BSIZE, m);
brelse(bp);
}
return n;
}
// Write data to inode.
// Caller must hold ip->lock.
int writei(struct inode *ip, char *src, uint off, uint n) {
uint tot, m;
struct buf *bp;
if (ip->type == T_DEV) {
if (ip->major < 0 || ip->major >= NDEV || !devsw[ip->major].write) {
return -1;
}
return devsw[ip->major].write(ip, src, n);
}
if (off > ip->size || off + n < off) {
return -1;
}
if (off + n > MAXFILE * BSIZE) {
return -1;
}
for (tot = 0; tot < n; tot += m, off += m, src += m) {
bp = bread(ip->dev, bmap(ip, off / BSIZE));
m = min(n - tot, BSIZE - off % BSIZE);
memmove(bp->data + off % BSIZE, src, m);
log_write(bp);
brelse(bp);
}
if (n > 0 && off > ip->size) {
ip->size = off;
iupdate(ip);
}
return n;
}
// Directories
int namecmp(const char *s, const char *t) {
return strncmp(s, t, DIRSIZ);
}
// Look for a directory entry in a directory.
// If found, set *poff to byte offset of entry.
struct inode* dirlookup(struct inode *dp, char *name, uint *poff) {
uint off, inum;
struct dirent de;
if (dp->type != T_DIR) {
panic("dirlookup not DIR");
}
for (off = 0; off < dp->size; off += sizeof(de)) {
if (readi(dp, (char*)&de, off, sizeof(de)) != sizeof(de)) {
panic("dirlookup read");
}
if (de.inum == 0) {
continue;
}
if (namecmp(name, de.name) == 0) {
// entry matches path element
if (poff) {
*poff = off;
}
inum = de.inum;
return iget(dp->dev, inum);
}
}
return 0;
}
// Write a new directory entry (name, inum) into the directory dp.
int dirlink(struct inode *dp, char *name, uint inum) {
int off;
struct dirent de;
struct inode *ip;
// Check that name is not present.
if ((ip = dirlookup(dp, name, 0)) != 0) {
iput(ip);
return -1;
}
// Look for an empty dirent.
for (off = 0; off < dp->size; off += sizeof(de)) {
if (readi(dp, (char*)&de, off, sizeof(de)) != sizeof(de)) {
panic("dirlink read");
}
if (de.inum == 0) {
break;
}
}
strncpy(de.name, name, DIRSIZ);
de.inum = inum;
if (writei(dp, (char*)&de, off, sizeof(de)) != sizeof(de)) {
panic("dirlink");
}
return 0;
}
// Paths
// Copy the next path element from path into name.
// Return a pointer to the element following the copied one.
// The returned path has no leading slashes,
// so the caller can check *path=='\0' to see if the name is the last one.
// If no name to remove, return 0.
//
// Examples:
// skipelem("a/bb/c", name) = "bb/c", setting name = "a"
// skipelem("///a//bb", name) = "bb", setting name = "a"
// skipelem("a", name) = "", setting name = "a"
// skipelem("", name) = skipelem("////", name) = 0
//
static char* skipelem(char *path, char *name) {
char *s;
int len;
while (*path == '/') {
path++;
}
if (*path == 0) {
return 0;
}
s = path;
while (*path != '/' && *path != 0) {
path++;
}
len = path - s;
if (len >= DIRSIZ) {
memmove(name, s, DIRSIZ);
}
else {
memmove(name, s, len);
name[len] = 0;
}
while (*path == '/') {
path++;
}
return path;
}
// Look up and return the inode for a path name.
// If parent != 0, return the inode for the parent and copy the final
// path element into name, which must have room for DIRSIZ bytes.
// Must be called inside a transaction since it calls iput().
static struct inode* namex(char *path, int nameiparent, char *name) {
struct inode *ip, *next;
if (*path == '/') {
ip = iget(ROOTDEV, ROOTINO);
}
else {
ip = idup(myproc()->cwd);
}
while ((path = skipelem(path, name)) != 0) {
ilock(ip);
if (ip->type != T_DIR) {
iunlockput(ip);
return 0;
}
if (nameiparent && *path == '\0') {
// Stop one level early.
iunlock(ip);
return ip;
}
if ((next = dirlookup(ip, name, 0)) == 0) {
iunlockput(ip);
return 0;
}
iunlockput(ip);
ip = next;
}
if (nameiparent) {
iput(ip);
return 0;
}
return ip;
}
struct inode* namei(char *path) {
char name[DIRSIZ];
return namex(path, 0, name);
}
struct inode*nameiparent(char *path, char *name) {
return namex(path, 1, name);
}

57
fs.h Normal file
View File

@ -0,0 +1,57 @@
// On-disk file system format.
// Both the kernel and user programs use this header file.
#define ROOTINO 1 // root i-number
#define BSIZE 512 // block size
// Disk layout:
// [ boot block | super block | log | inode blocks |
// free bit map | data blocks]
//
// mkfs computes the super block and builds an initial file system. The
// super block describes the disk layout:
struct superblock {
uint size; // Size of file system image (blocks)
uint nblocks; // Number of data blocks
uint ninodes; // Number of inodes.
uint nlog; // Number of log blocks
uint logstart; // Block number of first log block
uint inodestart; // Block number of first inode block
uint bmapstart; // Block number of first free map block
};
#define NDIRECT 12
#define NINDIRECT (BSIZE / sizeof(uint))
#define MAXFILE (NDIRECT + NINDIRECT)
// On-disk inode structure
struct dinode {
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[NDIRECT + 1]; // Data block addresses
};
// Inodes per block.
#define IPB (BSIZE / sizeof(struct dinode))
// Block containing inode i
#define IBLOCK(i, sb) ((i) / IPB + sb.inodestart)
// Bitmap bits per block
#define BPB (BSIZE * 8)
// Block of free map containing bit for block b
#define BBLOCK(b, sb) (b / BPB + sb.bmapstart)
// Directory is a file containing a sequence of dirent structures.
#define DIRSIZ 14
struct dirent {
ushort inum;
char name[DIRSIZ];
};

291
gdbutil Normal file
View File

@ -0,0 +1,291 @@
# -*- gdb-script -*-
# Utility functions to pretty-print x86 segment/interrupt descriptors.
# To load this file, run "source gdbutil" in gdb.
# printdesc and printdescs are the main entry points.
# IA32 2007, Volume 3A, Table 3-2
set $STS_T16A = 0x1
set $STS_LDT = 0x2
set $STS_T16B = 0x3
set $STS_CG16 = 0x4
set $STS_TG = 0x5
set $STS_IG16 = 0x6
set $STS_TG16 = 0x7
set $STS_T32A = 0x9
set $STS_T32B = 0xB
set $STS_CG32 = 0xC
set $STS_IG32 = 0xE
set $STS_TG32 = 0xF
define outputsts
while 1
if $arg0 == $STS_T16A
echo STS_T16A
loop_break
end
if $arg0 == $STS_LDT
echo STS_LDT\
loop_break
end
if $arg0 == $STS_T16B
echo STS_T16B
loop_break
end
if $arg0 == $STS_CG16
echo STS_CG16
loop_break
end
if $arg0 == $STS_TG
echo STS_TG\ \
loop_break
end
if $arg0 == $STS_IG16
echo STS_IG16
loop_break
end
if $arg0 == $STS_TG16
echo STS_TG16
loop_break
end
if $arg0 == $STS_T32A
echo STS_T32A
loop_break
end
if $arg0 == $STS_T32B
echo STS_T32B
loop_break
end
if $arg0 == $STS_CG32
echo STS_CG32
loop_break
end
if $arg0 == $STS_IG32
echo STS_IG32
loop_break
end
if $arg0 == $STS_TG32
echo STS_TG32
loop_break
end
echo Reserved
loop_break
end
end
# IA32 2007, Volume 3A, Table 3-1
set $STA_X = 0x8
set $STA_E = 0x4
set $STA_C = 0x4
set $STA_W = 0x2
set $STA_R = 0x2
set $STA_A = 0x1
define outputsta
if $arg0 & $STA_X
# Code segment
echo code
if $arg0 & $STA_C
echo |STA_C
end
if $arg0 & $STA_R
echo |STA_R
end
else
# Data segment
echo data
if $arg0 & $STA_E
echo |STA_E
end
if $arg0 & $STA_W
echo |STA_W
end
end
if $arg0 & $STA_A
echo |STA_A
else
printf " "
end
end
# xv6-specific
set $SEG_KCODE = 1
set $SEG_KDATA = 2
set $SEG_KCPU = 3
set $SEG_UCODE = 4
set $SEG_UDATA = 5
set $SEG_TSS = 6
define outputcs
if ($arg0 & 4) == 0
if $arg0 >> 3 == $SEG_KCODE
printf "SEG_KCODE<<3"
end
if $arg0 >> 3 == $SEG_KDATA
printf "SEG_KDATA<<3"
end
if $arg0 >> 3 == $SEG_KCPU
printf "SEG_KCPU<<3"
end
if $arg0 >> 3 == $SEG_UCODE
printf "SEG_UCODE<<3"
end
if $arg0 >> 3 == $SEG_UDATA
printf "SEG_UDATA<<3"
end
if $arg0 >> 3 == $SEG_TSS
printf "SEG_TSS<<3"
end
if ($arg0 >> 3 < 1) + ($arg0 >> 3 > 6)
printf "GDT[%d]", $arg0 >> 3
end
else
printf "LDT[%d]", $arg0 >> 3
end
if ($arg0 & 3) > 0
printf "|"
outputdpl ($arg0&3)
end
end
define outputdpl
if $arg0 == 0
printf "DPL_KERN"
else
if $arg0 == 3
printf "DPL_USER"
else
printf "DPL%d", $arg0
end
end
end
define printdesc
if $argc != 1
echo Usage: printdesc expr
else
_printdesc ((uint*)&($arg0))[0] ((uint*)&($arg0))[1]
printf "\n"
end
end
document printdesc
Print an x86 segment or gate descriptor.
printdesc EXPR
EXPR must evaluate to a descriptor value. It can be of any C type.
end
define _printdesc
_printdesc1 $arg0 $arg1 ($arg1>>15&1) ($arg1>>13&3) ($arg1>>12&1) ($arg1>>8&15)
end
define _printdesc1
# 2:P 3:DPL 4:S 5:Type
if $arg2 == 0
printf "P = 0 (Not present)"
else
printf "type = "
if $arg4 == 0
# System segment
outputsts $arg5
printf " (0x%x) ", $arg5
_printsysdesc $arg0 $arg1 $arg5
else
# Code/data segment
outputsta $arg5
printf " "
_printsegdesc $arg0 $arg1
end
printf " DPL = "
outputdpl $arg3
printf " (%d)", $arg3
end
end
define _printsysdesc
# 2:Type
# GDB's || is buggy
if ($arg2 == $STS_TG) + (($arg2&7) == $STS_IG16) + (($arg2&7) == $STS_TG16)
# Gate descriptor
_printgate $arg2 ($arg0>>16) ($arg0&0xFFFF) ($arg1>>16)
else
# System segment descriptor
_printsegdesc $arg0 $arg1
end
end
define _printgate
# IA32 2007, Voume 3A, Figure 5-2
# 0:Type 1:CS 2:Offset 15..0 3:Offset 31..16
printf "CS = "
outputcs $arg1
printf " (%d)", $arg1
if (($arg0&7) == $STS_IG16) + (($arg0&7) == $STS_TG16)
printf " Offset = "
output/a $arg3 << 16 | $arg2
end
end
define _printsegdesc
# IA32 20007, Volume 3A, Figure 3-8 and Figure 4-1
_printsegdesc1 ($arg0>>16) ($arg1&0xFF) ($arg1>>24) ($arg0&0xFFFF) ($arg1>>16&15) ($arg1>>23&1)
if ($arg1>>12&1) == 1
printf " AVL = %d", $arg1>>20&1
if ($arg1>>11&1) == 0
# Data segment
if ($arg1>>22&1) == 0
printf " B = small (0) "
else
printf " B = big (1) "
end
else
# Code segment
printf " D = "
if ($arg1>>22&1) == 0
printf "16-bit (0)"
else
printf "32-bit (1)"
end
end
end
end
define _printsegdesc1
# 0:Base 0..15 1:Base 16..23 2:Base 24..32 3:Limit 0..15 4:Limit 16..19 5:G
printf "base = 0x%08x", $arg0 | ($arg1<<16) | ($arg2<<24)
printf " limit = 0x"
if $arg5 == 0
printf "%08x", $arg3 | ($arg4<<16)
else
printf "%08x", (($arg3 | ($arg4<<16)) << 12) | 0xFFF
end
end
define printdescs
if $argc < 1 || $argc > 2
echo Usage: printdescs expr [count]
else
if $argc == 1
_printdescs ($arg0) (sizeof($arg0)/sizeof(($arg0)[0]))
else
_printdescs ($arg0) ($arg1)
end
end
end
document printdescs
Print an array of x86 segment or gate descriptors.
printdescs EXPR [COUNT]
EXPR must evaluate to an array of descriptors.
end
define _printdescs
set $i = 0
while $i < $arg1
printf "[%d] ", $i
printdesc $arg0[$i]
set $i = $i + 1
end
end

102
gensyscalls.pl Executable file
View File

@ -0,0 +1,102 @@
#!/usr/bin/perl -w
# Generate syscall.h, syscalltable.h or usys.S. These are the header and assembly
# files for system calls.
#
# Generating these files from one script avoids them getting out of sync.
#
# Specify an argument of -h to generate syscall.h
# Specify an argument of -c to generate syscalltable.h
# Specify an argument of -a to generate usys.S
#
# Note that you also need to update user.h with the declarations for these functions that
# user programs will use. This ensures that the C compiler generates the correct code to
# push the parameters on to the stack.
my @syscalls = (
"fork",
"exit",
"wait",
"pipe",
"read",
"kill",
"exec",
"fstat",
"chdir",
"dup",
"getpid",
"sbrk",
"sleep",
"uptime",
"open",
"write",
"mknod",
"unlink",
"link",
"mkdir",
"close",
"getch"
);
my $i;
if ($#ARGV == -1)
{
print 'Error: No argument supplied to gensyscalls.pl';
exit(1);
}
if (($ARGV[0] ne '-h') && ($ARGV[0] ne '-a') && ($ARGV[0] ne '-c'))
{
print 'Error: Invalid argument to gensyscalls.pl';
exit(1);
}
if ($ARGV[0] eq '-h'|| $ARGV[0] eq '-c')
{
print "// Generated by gensyscalls.pl. Do not edit.\n";
print "// To change syscall numbers or add new syscalls, edit gensyscalls.pl\n";
print "\n";
}
else
{
print "# Generated by gensyscalls.pl. Do not edit.\n";
print "# To change syscall numbers or add new syscalls, edit gensyscalls.pl\n";
print "\n";
}
for ($i = 0; $i < scalar(@syscalls); $i++)
{
my $index = $i + 1;
if ($ARGV[0] eq '-h')
{
print "#define SYS_$syscalls[$i]\t\t$index\n";
}
elsif ($ARGV[0] eq '-c')
{
print "extern int sys_$syscalls[$i](void);\n";
}
}
if ($ARGV[0] eq '-a')
{
print "#include \"syscall.h\"\n";
print "#include \"traps.h\"\n";
print "\n";
print "#define SYSCALL(name) \\\n";
print ".globl name; \\\n";
print "name: \\\n";
print "\tmovl\t\$SYS_ ## name, \%eax; \\\n";
print "\tint\t\$T_SYSCALL; \\\n";
print "\tret\n";
print "\n";
for ($i = 0; $i < scalar(@syscalls); $i++)
{
print "SYSCALL($syscalls[$i])\n";
}
}
elsif ($ARGV[0] eq '-c')
{
print "\n";
print "static int(*syscalls[])(void) = {\n";
for ($i = 0; $i < scalar(@syscalls); $i++)
{
print "[SYS_$syscalls[$i]]\tsys_$syscalls[$i],\n";
}
print "};\n"
}

109
grep.c Normal file
View File

@ -0,0 +1,109 @@
// Simple grep. Only supports ^ . * $ operators.
#include "types.h"
#include "stat.h"
#include "user.h"
char buf[1024];
int match(char*, char*);
void grep(char *pattern, int fd) {
int n, m;
char *p, *q;
m = 0;
while ((n = read(fd, buf + m, sizeof(buf) - m - 1)) > 0) {
m += n;
buf[m] = '\0';
p = buf;
while ((q = strchr(p, '\n')) != 0) {
*q = 0;
if (match(pattern, p)) {
*q = '\n';
write(1, p, q + 1 - p);
}
p = q + 1;
}
if (p == buf) {
m = 0;
}
if (m > 0) {
m -= p - buf;
memmove(buf, p, m);
}
}
}
int main(int argc, char *argv[]) {
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();
}
for (i = 2; i < argc; i++) {
if ((fd = open(argv[i], 0)) < 0) {
printf(1, "grep: cannot open %s\n", argv[i]);
exit();
}
grep(pattern, fd);
close(fd);
}
exit();
}
// Regexp matcher from Kernighan & Pike,
// The Practice of Programming, Chapter 9.
int matchhere(char*, char*);
int matchstar(int, char*, char*);
int match(char *re, char *text) {
if (re[0] == '^') {
return matchhere(re + 1, text);
}
do { // must look at empty string
if (matchhere(re, text)) {
return 1;
}
}
while (*text++ != '\0');
return 0;
}
// matchhere: search for re at beginning of text
int matchhere(char *re, char *text){
if (re[0] == '\0') {
return 1;
}
if (re[1] == '*') {
return matchstar(re[0], re + 2, text);
}
if (re[0] == '$' && re[1] == '\0') {
return *text == '\0';
}
if (*text != '\0' && (re[0] == '.' || re[0] == *text)) {
return matchhere(re + 1, text + 1);
}
return 0;
}
// matchstar: search for c*re at beginning of text
int matchstar(int c, char *re, char *text) {
do { // a * matches zero or more instances
if (matchhere(re, text)) {
return 1;
}
}
while (*text != '\0' && (*text++ == c || c == '.'));
return 0;
}

171
ide.c Normal file
View File

@ -0,0 +1,171 @@
// Simple PIO-based (non-DMA) IDE driver code.
#include "types.h"
#include "defs.h"
#include "param.h"
#include "memlayout.h"
#include "mmu.h"
#include "proc.h"
#include "x86.h"
#include "traps.h"
#include "spinlock.h"
#include "sleeplock.h"
#include "fs.h"
#include "buf.h"
#define SECTOR_SIZE 512
#define IDE_BSY 0x80
#define IDE_DRDY 0x40
#define IDE_DF 0x20
#define IDE_ERR 0x01
#define IDE_CMD_READ 0x20
#define IDE_CMD_WRITE 0x30
#define IDE_CMD_RDMUL 0xc4
#define IDE_CMD_WRMUL 0xc5
// idequeue points to the buf now being read/written to the disk.
// idequeue->qnext points to the next buf to be processed.
// You must hold idelock while manipulating queue.
static struct spinlock idelock;
static struct buf *idequeue;
static int havedisk1;
static void idestart(struct buf*);
// Wait for IDE disk to become ready.
static int idewait(int checkerr) {
int r;
while (((r = inb(0x1f7)) & (IDE_BSY | IDE_DRDY)) != IDE_DRDY) {
;
}
if (checkerr && (r & (IDE_DF | IDE_ERR)) != 0) {
return -1;
}
return 0;
}
void ideinit(void) {
int i;
initlock(&idelock, "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++) {
if (inb(0x1f7) != 0) {
havedisk1 = 1;
break;
}
}
// Switch back to disk 0.
outb(0x1f6, 0xe0 | (0 << 4));
}
// Start the request for b. Caller must hold idelock.
static void idestart(struct buf *b) {
if (b == 0) {
panic("idestart");
}
if (b->blockno >= FSSIZE) {
panic("incorrect blockno");
}
int sector_per_block = BSIZE / SECTOR_SIZE;
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
outb(0x1f3, sector & 0xff);
outb(0x1f4, (sector >> 8) & 0xff);
outb(0x1f5, (sector >> 16) & 0xff);
outb(0x1f6, 0xe0 | ((b->dev & 1) << 4) | ((sector >> 24) & 0x0f));
if (b->flags & B_DIRTY) {
outb(0x1f7, write_cmd);
outsl(0x1f0, b->data, BSIZE / 4);
}
else {
outb(0x1f7, read_cmd);
}
}
// Interrupt handler.
void ideintr(void) {
struct buf *b;
// First queued buffer is the active request.
acquire(&idelock);
if ((b = idequeue) == 0) {
release(&idelock);
return;
}
idequeue = b->qnext;
// 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);
}
release(&idelock);
}
// 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 iderw(struct buf *b) {
struct buf **pp;
if (!holdingsleep(&b->lock)) {
panic("iderw: buf not locked");
}
if ((b->flags & (B_VALID | B_DIRTY)) == B_VALID) {
panic("iderw: nothing to do");
}
if (b->dev != 0 && !havedisk1) {
panic("iderw: ide disk 1 not present");
}
acquire(&idelock); //DOC:acquire-lock
// Append b to idequeue.
b->qnext = 0;
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);
}
release(&idelock);
}

35
init.c Normal file
View File

@ -0,0 +1,35 @@
// init: The initial user-level program
#include "types.h"
#include "stat.h"
#include "user.h"
#include "fcntl.h"
char *shell_argv[] = { "sh", 0 };
int main(int argc, char* argv[]) {
int pid, wpid;
if (open("console", O_RDWR) < 0) {
mknod("console", 1, 1);
open("console", O_RDWR);
}
dup(0); // stdout
dup(0); // stderr
for (;;) {
printf(1, "init: starting sh\n");
pid = fork();
if (pid < 0) {
printf(1, "init: fork failed\n");
exit();
}
if (pid == 0) {
exec("sh", shell_argv);
printf(1, "init: exec sh failed\n");
exit();
}
while ((wpid = wait()) >= 0 && wpid != pid) {
}
}
}

32
initcode.S Normal file
View File

@ -0,0 +1,32 @@
# Initial process execs /init.
# This code runs in user space.
#include "syscall.h"
#include "traps.h"
# exec(init, argv)
.globl start
start:
pushl $argv
pushl $init
pushl $0 // where caller pc would be
movl $SYS_exec, %eax
int $T_SYSCALL
# for(;;) exit();
exit:
movl $SYS_exit, %eax
int $T_SYSCALL
jmp exit
# char init[] = "/init\0";
init:
.string "/init\0"
# char *argv[] = { init, 0 };
.p2align 2
argv:
.long init
.long 0

68
ioapic.c Normal file
View File

@ -0,0 +1,68 @@
// The I/O APIC manages hardware interrupts for an SMP system.
// http://www.intel.com/design/chipsets/datashts/29056601.pdf
// See also picirq.c.
#include "types.h"
#include "defs.h"
#include "traps.h"
#define IOAPIC 0xFEC00000 // Default physical address of IO APIC
#define REG_ID 0x00 // Register index: ID
#define REG_VER 0x01 // Register index: version
#define REG_TABLE 0x10 // Redirection table base
// The redirection table starts at REG_TABLE and uses
// 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.
#define INT_DISABLED 0x00010000 // Interrupt disabled
#define INT_LEVEL 0x00008000 // Level-triggered (vs edge-)
#define INT_ACTIVELOW 0x00002000 // Active low (vs high)
#define INT_LOGICAL 0x00000800 // Destination is CPU id (vs APIC ID)
volatile struct ioapic *ioapic;
// IO APIC MMIO structure: write reg, then read or write data.
struct ioapic {
uint reg;
uint pad[3];
uint data;
};
static uint ioapicread(int reg) {
ioapic->reg = reg;
return ioapic->data;
}
static void ioapicwrite(int reg, uint data) {
ioapic->reg = reg;
ioapic->data = data;
}
void ioapicinit(void) {
int i, id, maxintr;
ioapic = (volatile struct ioapic*)IOAPIC;
maxintr = (ioapicread(REG_VER) >> 16) & 0xFF;
id = ioapicread(REG_ID) >> 24;
if (id != ioapicid) {
cprintf("ioapicinit: id isn't equal to ioapicid; not a MP\n");
}
// Mark all interrupts edge-triggered, active high, disabled,
// and not routed to any CPUs.
for (i = 0; i <= maxintr; i++) {
ioapicwrite(REG_TABLE + 2 * i, INT_DISABLED | (T_IRQ0 + i));
ioapicwrite(REG_TABLE + 2 * i + 1, 0);
}
}
void ioapicenable(int irq, int cpunum) {
// Mark interrupt edge-triggered, active high,
// enabled, and routed to the given cpunum,
// which happens to be that cpu's APIC ID.
ioapicwrite(REG_TABLE + 2 * irq, T_IRQ0 + irq);
ioapicwrite(REG_TABLE + 2 * irq + 1, cpunum << 24);
}

93
kalloc.c Normal file
View File

@ -0,0 +1,93 @@
// Physical memory allocator, intended to allocate
// memory for user processes, kernel stacks, page table pages,
// and pipe buffers. Allocates 4096-byte pages.
#include "types.h"
#include "defs.h"
#include "param.h"
#include "memlayout.h"
#include "mmu.h"
#include "spinlock.h"
void freerange(void *vstart, void *vend);
extern char end[]; // first address after kernel loaded from ELF file
// defined by the kernel linker script in kernel.ld
struct run {
struct run *next;
};
struct {
struct spinlock lock;
int use_lock;
struct run *freelist;
} kmem;
// Initialization happens in two phases.
// 1. main() calls kinit1() while still using entrypgdir to place just
// the pages mapped by entrypgdir on free list.
// 2. main() calls kinit2() with the rest of the physical pages
// after installing a full page table that maps them on all cores.
void kinit1(void *vstart, void *vend) {
initlock(&kmem.lock, "kmem");
kmem.use_lock = 0;
freerange(vstart, vend);
}
void kinit2(void *vstart, void *vend) {
freerange(vstart, vend);
kmem.use_lock = 1;
}
void freerange(void *vstart, void *vend) {
char *p;
p = (char*)PGROUNDUP((uint)vstart);
for (; p + PGSIZE <= (char*)vend; p += PGSIZE) {
kfree(p);
}
}
// Free the page of physical memory pointed at by v,
// which normally should have been returned by a
// call to kalloc(). (The exception is when
// initializing the allocator; see kinit above.)
void kfree(char *v) {
struct run *r;
if ((uint)v % PGSIZE || v < end || V2P(v) >= PHYSTOP) {
panic("kfree");
}
// Fill with junk to catch dangling refs.
memset(v, 1, PGSIZE);
if (kmem.use_lock) {
acquire(&kmem.lock);
}
r = (struct run*)v;
r->next = kmem.freelist;
kmem.freelist = r;
if (kmem.use_lock) {
release(&kmem.lock);
}
}
// Allocate one 4096-byte page of physical memory.
// Returns a pointer that the kernel can use.
// Returns 0 if the memory cannot be allocated.
char* kalloc(void) {
struct run *r;
if (kmem.use_lock) {
acquire(&kmem.lock);
}
r = kmem.freelist;
if (r) {
kmem.freelist = r->next;
}
if (kmem.use_lock) {
release(&kmem.lock);
}
return (char*)r;
}

51
kbd.c Normal file
View File

@ -0,0 +1,51 @@
#include "types.h"
#include "x86.h"
#include "defs.h"
#include "kbd.h"
int kbdgetc(void) {
static uint shift;
static uchar *charcode[4] = {
normalmap, shiftmap, ctlmap, ctlmap
};
uint st, data, c;
st = inb(KBSTATP);
if ((st & KBS_DIB) == 0) {
return -1;
}
data = inb(KBDATAP);
if (data == 0xE0) {
shift |= E0ESC;
return 0;
}
else if (data & 0x80) {
// Key released
data = (shift & E0ESC ? data : data & 0x7F);
shift &= ~(shiftcode[data] | E0ESC);
return 0;
}
else if (shift & E0ESC) {
// Last character was an E0 escape; or with 0x80
data |= 0x80;
shift &= ~E0ESC;
}
shift |= shiftcode[data];
shift ^= togglecode[data];
c = charcode[shift & (CTL | SHIFT)][data];
if (shift & CAPSLOCK) {
if ('a' <= c && c <= 'z') {
c += 'A' - 'a';
}
else if ('A' <= c && c <= 'Z') {
c += 'a' - 'A';
}
}
return c;
}
void kbdintr(void) {
consoleintr(kbdgetc);
}

112
kbd.h Normal file
View File

@ -0,0 +1,112 @@
// PC keyboard interface constants
#define KBSTATP 0x64 // kbd controller status port(I)
#define KBS_DIB 0x01 // kbd data in buffer
#define KBDATAP 0x60 // kbd data port(I)
#define NO 0
#define SHIFT (1 << 0)
#define CTL (1 << 1)
#define ALT (1 << 2)
#define CAPSLOCK (1 << 3)
#define NUMLOCK (1 << 4)
#define SCROLLLOCK (1 << 5)
#define E0ESC (1 << 6)
// Special keycodes
#define KEY_HOME 0xE0
#define KEY_END 0xE1
#define KEY_UP 0xE2
#define KEY_DN 0xE3
#define KEY_LF 0xE4
#define KEY_RT 0xE5
#define KEY_PGUP 0xE6
#define KEY_PGDN 0xE7
#define KEY_INS 0xE8
#define KEY_DEL 0xE9
// C('A') == Control-A
#define C(x) (x - '@')
static uchar shiftcode[256] =
{
[0x1D] CTL,
[0x2A] SHIFT,
[0x36] SHIFT,
[0x38] ALT,
[0x9D] CTL,
[0xB8] ALT
};
static uchar togglecode[256] =
{
[0x3A] CAPSLOCK,
[0x45] NUMLOCK,
[0x46] SCROLLLOCK
};
static uchar normalmap[256] =
{
NO, 0x1B, '1', '2', '3', '4', '5', '6', // 0x00
'7', '8', '9', '0', '-', '=', '\b', '\t',
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', // 0x10
'o', 'p', '[', ']', '\n', NO, 'a', 's',
'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', // 0x20
'\'', '`', NO, '\\', 'z', 'x', 'c', 'v',
'b', 'n', 'm', ',', '.', '/', NO, '*', // 0x30
NO, ' ', NO, NO, NO, NO, NO, NO,
NO, NO, NO, NO, NO, NO, NO, '7', // 0x40
'8', '9', '-', '4', '5', '6', '+', '1',
'2', '3', '0', '.', NO, NO, NO, NO, // 0x50
[0x9C] '\n', // KP_Enter
[0xB5] '/', // KP_Div
[0xC8] KEY_UP, [0xD0] KEY_DN,
[0xC9] KEY_PGUP, [0xD1] KEY_PGDN,
[0xCB] KEY_LF, [0xCD] KEY_RT,
[0x97] KEY_HOME, [0xCF] KEY_END,
[0xD2] KEY_INS, [0xD3] KEY_DEL
};
static uchar shiftmap[256] =
{
NO, 033, '!', '@', '#', '$', '%', '^', // 0x00
'&', '*', '(', ')', '_', '+', '\b', '\t',
'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', // 0x10
'O', 'P', '{', '}', '\n', NO, 'A', 'S',
'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', // 0x20
'"', '~', NO, '|', 'Z', 'X', 'C', 'V',
'B', 'N', 'M', '<', '>', '?', NO, '*', // 0x30
NO, ' ', NO, NO, NO, NO, NO, NO,
NO, NO, NO, NO, NO, NO, NO, '7', // 0x40
'8', '9', '-', '4', '5', '6', '+', '1',
'2', '3', '0', '.', NO, NO, NO, NO, // 0x50
[0x9C] '\n', // KP_Enter
[0xB5] '/', // KP_Div
[0xC8] KEY_UP, [0xD0] KEY_DN,
[0xC9] KEY_PGUP, [0xD1] KEY_PGDN,
[0xCB] KEY_LF, [0xCD] KEY_RT,
[0x97] KEY_HOME, [0xCF] KEY_END,
[0xD2] KEY_INS, [0xD3] KEY_DEL
};
static uchar ctlmap[256] =
{
NO, NO, NO, NO, NO, NO, NO, NO,
NO, NO, NO, NO, NO, NO, NO, NO,
C('Q'), C('W'), C('E'), C('R'), C('T'), C('Y'), C('U'), C('I'),
C('O'), C('P'), NO, NO, '\r', NO, C('A'), C('S'),
C('D'), C('F'), C('G'), C('H'), C('J'), C('K'), C('L'), NO,
NO, NO, NO, C('\\'), C('Z'), C('X'), C('C'), C('V'),
C('B'), C('N'), C('M'), NO, NO, C('/'), NO, NO,
[0x9C] '\r', // KP_Enter
[0xB5] C('/'), // KP_Div
[0xC8] KEY_UP, [0xD0] KEY_DN,
[0xC9] KEY_PGUP, [0xD1] KEY_PGDN,
[0xCB] KEY_LF, [0xCD] KEY_RT,
[0x97] KEY_HOME, [0xCF] KEY_END,
[0xD2] KEY_INS, [0xD3] KEY_DEL
};

64
kernel.ld Normal file
View File

@ -0,0 +1,64 @@
/* Simple linker script for the JOS kernel.
See the GNU ld 'info' manual ("info ld") to learn the syntax. */
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
OUTPUT_ARCH(i386)
ENTRY(_start)
SECTIONS
{
/* Link the kernel at this address: "." means the current address */
/* Must be equal to KERNLINK */
. = 0x80100000;
.text : AT(0x100000) {
*(.text .stub .text.* .gnu.linkonce.t.*)
}
PROVIDE(etext = .); /* Define the 'etext' symbol to this value */
.rodata : {
*(.rodata .rodata.* .gnu.linkonce.r.*)
}
/* Include debugging information in kernel memory */
.stab : {
PROVIDE(__STAB_BEGIN__ = .);
*(.stab);
PROVIDE(__STAB_END__ = .);
}
.stabstr : {
PROVIDE(__STABSTR_BEGIN__ = .);
*(.stabstr);
PROVIDE(__STABSTR_END__ = .);
}
/* Adjust the address for the data segment to the next page */
. = ALIGN(0x1000);
/* Conventionally, Unix linkers provide pseudo-symbols
* etext, edata, and end, at the end of the text, data, and bss.
* For the kernel mapping, we need the address at the beginning
* of the data section, but that's not one of the conventional
* symbols, because the convention started before there was a
* read-only rodata section between text and data. */
PROVIDE(data = .);
/* The data segment */
.data : {
*(.data)
}
PROVIDE(edata = .);
.bss : {
*(.bss)
}
PROVIDE(end = .);
/DISCARD/ : {
*(.eh_frame .note.GNU-stack)
}
}

16
kill.c Normal file
View File

@ -0,0 +1,16 @@
#include "types.h"
#include "stat.h"
#include "user.h"
int main(int argc, char **argv) {
int i;
if (argc < 2) {
printf(2, "usage: kill pid...\n");
exit();
}
for (i = 1; i < argc; i++) {
kill(atoi(argv[i]));
}
exit();
}

218
lapic.c Normal file
View File

@ -0,0 +1,218 @@
// The local APIC manages internal (non-I/O) interrupts.
// See Chapter 8 & Appendix C of Intel processor manual volume 3.
#include "param.h"
#include "types.h"
#include "defs.h"
#include "date.h"
#include "memlayout.h"
#include "traps.h"
#include "mmu.h"
#include "x86.h"
// Local APIC registers, divided by 4 for use as uint[] indices.
#define ID (0x0020 / 4) // ID
#define VER (0x0030 / 4) // Version
#define TPR (0x0080 / 4) // Task Priority
#define EOI (0x00B0 / 4) // EOI
#define SVR (0x00F0 / 4) // Spurious Interrupt Vector
#define ENABLE 0x00000100 // Unit Enable
#define ESR (0x0280 / 4) // Error Status
#define ICRLO (0x0300 / 4) // Interrupt Command
#define INIT 0x00000500 // INIT/RESET
#define STARTUP 0x00000600 // Startup IPI
#define DELIVS 0x00001000 // Delivery status
#define ASSERT 0x00004000 // Assert interrupt (vs deassert)
#define DEASSERT 0x00000000
#define LEVEL 0x00008000 // Level triggered
#define BCAST 0x00080000 // Send to all APICs, including self.
#define BUSY 0x00001000
#define FIXED 0x00000000
#define ICRHI (0x0310 / 4) // Interrupt Command [63:32]
#define TIMER (0x0320 / 4) // Local Vector Table 0 (TIMER)
#define X1 0x0000000B // divide counts by 1
#define PERIODIC 0x00020000 // Periodic
#define PCINT (0x0340 / 4) // Performance Counter LVT
#define LINT0 (0x0350 / 4) // Local Vector Table 1 (LINT0)
#define LINT1 (0x0360 / 4) // Local Vector Table 2 (LINT1)
#define ERROR (0x0370 / 4) // Local Vector Table 3 (ERROR)
#define MASKED 0x00010000 // Interrupt masked
#define TICR (0x0380 / 4) // Timer Initial Count
#define TCCR (0x0390 / 4) // Timer Current Count
#define TDCR (0x03E0 / 4) // Timer Divide Configuration
volatile uint *lapic; // Initialized in mp.c
static void lapicw(int index, int value) {
lapic[index] = value;
lapic[ID]; // wait for write to finish, by reading
}
void lapicinit(void) {
if (!lapic) {
return;
}
// Enable local APIC; set spurious interrupt vector.
lapicw(SVR, ENABLE | (T_IRQ0 + IRQ_SPURIOUS));
// The timer repeatedly counts down at bus frequency
// from lapic[TICR] and then issues an interrupt.
// If xv6 cared more about precise timekeeping,
// TICR would be calibrated using an external time source.
lapicw(TDCR, X1);
lapicw(TIMER, PERIODIC | (T_IRQ0 + IRQ_TIMER));
lapicw(TICR, 10000000);
// Disable logical interrupt lines.
lapicw(LINT0, MASKED);
lapicw(LINT1, MASKED);
// Disable performance counter overflow interrupts
// on machines that provide that interrupt entry.
if (((lapic[VER] >> 16) & 0xFF) >= 4) {
lapicw(PCINT, MASKED);
}
// Map error interrupt to IRQ_ERROR.
lapicw(ERROR, T_IRQ0 + IRQ_ERROR);
// Clear error status register (requires back-to-back writes).
lapicw(ESR, 0);
lapicw(ESR, 0);
// Ack any outstanding interrupts.
lapicw(EOI, 0);
// Send an Init Level De-Assert to synchronise arbitration ID's.
lapicw(ICRHI, 0);
lapicw(ICRLO, BCAST | INIT | LEVEL);
while (lapic[ICRLO] & DELIVS) {
;
}
// Enable interrupts on the APIC (but not on the processor).
lapicw(TPR, 0);
}
int lapicid(void) {
if (!lapic) {
return 0;
}
return lapic[ID] >> 24;
}
// Acknowledge interrupt.
void lapiceoi(void) {
if (lapic) {
lapicw(EOI, 0);
}
}
// Spin for a given number of microseconds.
// On real hardware would want to tune this dynamically.
void microdelay(int us) {
}
#define CMOS_PORT 0x70
#define CMOS_RETURN 0x71
// Start additional processor running entry code at addr.
// See Appendix B of MultiProcessor Specification.
void lapicstartap(uchar apicid, uint addr) {
int i;
ushort *wrv;
// "The BSP must initialize CMOS shutdown code to 0AH
// and the warm reset vector (DWORD based at 40:67) to point at
// the AP startup code prior to the [universal startup algorithm]."
outb(CMOS_PORT, 0xF); // offset 0xF is shutdown code
outb(CMOS_PORT + 1, 0x0A);
wrv = (ushort*)P2V((0x40 << 4 | 0x67)); // Warm reset vector
wrv[0] = 0;
wrv[1] = addr >> 4;
// "Universal startup algorithm."
// Send INIT (level-triggered) interrupt to reset other CPU.
lapicw(ICRHI, apicid << 24);
lapicw(ICRLO, INIT | LEVEL | ASSERT);
microdelay(200);
lapicw(ICRLO, INIT | LEVEL);
microdelay(100); // should be 10ms, but too slow in Bochs!
// Send startup IPI (twice!) to enter code.
// Regular hardware is supposed to only accept a STARTUP
// when it is in the halted state due to an INIT. So the second
// should be ignored, but it is part of the official Intel algorithm.
// Bochs complains about the second one. Too bad for Bochs.
for (i = 0; i < 2; i++) {
lapicw(ICRHI, apicid << 24);
lapicw(ICRLO, STARTUP | (addr >> 12));
microdelay(200);
}
}
#define CMOS_STATA 0x0a
#define CMOS_STATB 0x0b
#define CMOS_UIP (1 << 7) // RTC update in progress
#define SECS 0x00
#define MINS 0x02
#define HOURS 0x04
#define DAY 0x07
#define MONTH 0x08
#define YEAR 0x09
static uint cmos_read(uint reg) {
outb(CMOS_PORT, reg);
microdelay(200);
return inb(CMOS_RETURN);
}
static void fill_rtcdate(struct rtcdate *r) {
r->second = cmos_read(SECS);
r->minute = cmos_read(MINS);
r->hour = cmos_read(HOURS);
r->day = cmos_read(DAY);
r->month = cmos_read(MONTH);
r->year = cmos_read(YEAR);
}
// qemu seems to use 24-hour GWT and the values are BCD encoded
void cmostime(struct rtcdate *r) {
struct rtcdate t1, t2;
int sb, bcd;
sb = cmos_read(CMOS_STATB);
bcd = (sb & (1 << 2)) == 0;
// make sure CMOS doesn't modify time while we read it
for (;;) {
fill_rtcdate(&t1);
if (cmos_read(CMOS_STATA) & CMOS_UIP) {
continue;
}
fill_rtcdate(&t2);
if (memcmp(&t1, &t2, sizeof(t1)) == 0) {
break;
}
}
// convert
if (bcd) {
#define CONV(x) (t1.x = ((t1.x >> 4) * 10) + (t1.x & 0xf))
CONV(second);
CONV(minute);
CONV(hour );
CONV(day );
CONV(month );
CONV(year );
#undef CONV
}
*r = t1;
r->year += 2000;
}

14
ln.c Normal file
View File

@ -0,0 +1,14 @@
#include "types.h"
#include "stat.h"
#include "user.h"
int main(int argc, char *argv[]) {
if (argc != 3) {
printf(2, "Usage: ln old new\n");
exit();
}
if (link(argv[1], argv[2]) < 0) {
printf(2, "link %s %s: failed\n", argv[1], argv[2]);
}
exit();
}

234
log.c Normal file
View File

@ -0,0 +1,234 @@
#include "types.h"
#include "defs.h"
#include "param.h"
#include "spinlock.h"
#include "sleeplock.h"
#include "fs.h"
#include "buf.h"
// Simple logging that allows concurrent FS system calls.
//
// A log transaction contains the updates of multiple FS system
// calls. The logging system only commits when there are
// no FS system calls active. Thus there is never
// any reasoning required about whether a commit might
// write an uncommitted system call's updates to disk.
//
// A system call should call begin_op()/end_op() to mark
// its start and end. Usually begin_op() just increments
// the count of in-progress FS system calls and returns.
// But if it thinks the log is close to running out, it
// sleeps until the last outstanding end_op() commits.
//
// The log is a physical re-do log containing disk blocks.
// The on-disk log format:
// header block, containing block #s for block A, B, C, ...
// block A
// block B
// block C
// ...
// Log appends are synchronous.
// 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 block[LOGSIZE];
};
struct log {
struct spinlock lock;
int start;
int size;
int outstanding; // how many FS sys calls are executing.
int committing; // in commit(), please wait.
int dev;
struct logheader lh;
};
struct log log;
static void recover_from_log(void);
static void commit();
void
initlog(int dev)
{
if (sizeof(struct logheader) >= BSIZE)
panic("initlog: too big logheader");
struct superblock sb;
initlock(&log.lock, "log");
readsb(dev, &sb);
log.start = sb.logstart;
log.size = sb.nlog;
log.dev = dev;
recover_from_log();
}
// Copy committed blocks from log to their home location
static void
install_trans(void)
{
int tail;
for (tail = 0; tail < log.lh.n; tail++) {
struct buf *lbuf = bread(log.dev, log.start+tail+1); // read log block
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(dbuf);
}
}
// Read the log header from disk into the in-memory log header
static void
read_head(void)
{
struct buf *buf = bread(log.dev, log.start);
struct logheader *lh = (struct logheader *) (buf->data);
int i;
log.lh.n = lh->n;
for (i = 0; i < log.lh.n; i++) {
log.lh.block[i] = lh->block[i];
}
brelse(buf);
}
// Write in-memory log header to disk.
// This is the true point at which the
// current transaction commits.
static void
write_head(void)
{
struct buf *buf = bread(log.dev, log.start);
struct logheader *hb = (struct logheader *) (buf->data);
int i;
hb->n = log.lh.n;
for (i = 0; i < log.lh.n; i++) {
hb->block[i] = log.lh.block[i];
}
bwrite(buf);
brelse(buf);
}
static void
recover_from_log(void)
{
read_head();
install_trans(); // if committed, copy from log to disk
log.lh.n = 0;
write_head(); // clear the log
}
// called at the start of each FS system call.
void
begin_op(void)
{
acquire(&log.lock);
while(1){
if(log.committing){
sleep(&log, &log.lock);
} else if(log.lh.n + (log.outstanding+1)*MAXOPBLOCKS > LOGSIZE){
// this op might exhaust log space; wait for commit.
sleep(&log, &log.lock);
} else {
log.outstanding += 1;
release(&log.lock);
break;
}
}
}
// called at the end of each FS system call.
// commits if this was the last outstanding operation.
void
end_op(void)
{
int do_commit = 0;
acquire(&log.lock);
log.outstanding -= 1;
if(log.committing)
panic("log.committing");
if(log.outstanding == 0){
do_commit = 1;
log.committing = 1;
} else {
// begin_op() may be waiting for log space,
// and decrementing log.outstanding has decreased
// the amount of reserved space.
wakeup(&log);
}
release(&log.lock);
if(do_commit){
// call commit w/o holding locks, since not allowed
// to sleep with locks.
commit();
acquire(&log.lock);
log.committing = 0;
wakeup(&log);
release(&log.lock);
}
}
// Copy modified blocks from cache to log.
static void
write_log(void)
{
int tail;
for (tail = 0; tail < log.lh.n; tail++) {
struct buf *to = bread(log.dev, log.start+tail+1); // log block
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(to);
}
}
static void
commit()
{
if (log.lh.n > 0) {
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;
write_head(); // Erase the transaction from the log
}
}
// Caller has modified b->data and is done with the buffer.
// Record the block number and pin in the cache with B_DIRTY.
// commit()/write_log() will do the disk write.
//
// log_write() replaces bwrite(); a typical use is:
// bp = bread(...)
// modify bp->data[]
// log_write(bp)
// brelse(bp)
void
log_write(struct buf *b)
{
int i;
if (log.lh.n >= LOGSIZE || log.lh.n >= log.size - 1)
panic("too big a transaction");
if (log.outstanding < 1)
panic("log_write outside of trans");
acquire(&log.lock);
for (i = 0; i < log.lh.n; i++) {
if (log.lh.block[i] == b->blockno) // log absorbtion
break;
}
log.lh.block[i] = b->blockno;
if (i == log.lh.n)
log.lh.n++;
b->flags |= B_DIRTY; // prevent eviction
release(&log.lock);
}

85
ls.c Normal file
View File

@ -0,0 +1,85 @@
#include "types.h"
#include "stat.h"
#include "user.h"
#include "fs.h"
char*
fmtname(char *path)
{
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;
memmove(buf, p, strlen(p));
memset(buf+strlen(p), ' ', DIRSIZ-strlen(p));
return buf;
}
void
ls(char *path)
{
char buf[512], *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");
break;
}
strcpy(buf, path);
p = buf+strlen(buf);
*p++ = '/';
while(read(fd, &de, sizeof(de)) == sizeof(de)){
if(de.inum == 0)
continue;
memmove(p, de.name, DIRSIZ);
p[DIRSIZ] = 0;
if(stat(buf, &st) < 0){
printf(1, "ls: cannot stat %s\n", buf);
continue;
}
printf(1, "%s %d %d %d\n", fmtname(buf), st.type, st.ino, st.size);
}
break;
}
close(fd);
}
int
main(int argc, char *argv[])
{
int i;
if(argc < 2){
ls(".");
exit();
}
for(i=1; i<argc; i++)
ls(argv[i]);
exit();
}

111
main.c Normal file
View File

@ -0,0 +1,111 @@
#include "types.h"
#include "defs.h"
#include "param.h"
#include "memlayout.h"
#include "mmu.h"
#include "proc.h"
#include "x86.h"
static void startothers(void);
static void mpmain(void) __attribute__((noreturn));
extern pde_t *kpgdir;
extern char end[]; // first address after kernel loaded from ELF file
// Bootstrap processor starts running C code here.
// Allocate a real stack and switch to it, first
// doing some setup required for memory allocator to work.
int main(void) {
kinit1(end, P2V(4 * 1024 * 1024)); // phys page allocator
kvmalloc(); // kernel page table
mpinit(); // detect other processors
lapicinit(); // interrupt controller
seginit(); // segment descriptors
picinit(); // disable pic
ioapicinit(); // another interrupt controller
consoleinit(); // console hardware
uartinit(); // serial port
pinit(); // process table
tvinit(); // trap vectors
binit(); // buffer cache
fileinit(); // file table
ideinit(); // disk
startothers(); // start other processors
kinit2(P2V(4 * 1024 * 1024), P2V(PHYSTOP)); // must come after startothers()
userinit(); // first user process
mpmain(); // finish this processor's setup
}
// Other CPUs jump here from entryother.S.
static void mpenter(void) {
switchkvm();
seginit();
lapicinit();
mpmain();
}
// Common CPU setup code.
static void mpmain(void) {
cprintf("cpu%d: starting %d\n", cpuid(), cpuid());
idtinit(); // load idt register
xchg(&(mycpu()->started), 1); // tell startothers() we're up
scheduler(); // start running processes
}
pde_t entrypgdir[]; // For entry.S
// Start the non-boot (AP) processors.
static void startothers(void) {
extern uchar _binary_entryother_start[], _binary_entryother_size[];
uchar *code;
struct cpu *c;
char *stack;
// Write entry code to unused memory at 0x7000.
// The linker has placed the image of entryother.S in
// _binary_entryother_start.
code = P2V(0x7000);
memmove(code, _binary_entryother_start, (uint)_binary_entryother_size);
for (c = cpus; c < cpus + ncpu; c++) {
if (c == mycpu()) { // We've started already.
continue;
}
// 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();
*(void**)(code - 4) = stack + KSTACKSIZE;
*(void(**)(void))(code - 8) = mpenter;
*(int**)(code - 12) = (void *) V2P(entrypgdir);
lapicstartap(c->apicid, V2P(code));
// wait for cpu to finish mpmain()
while (c->started == 0) {
;
}
}
}
// 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.
// PTE_PS in a page directory entry enables 4Mbyte pages.
__attribute__((__aligned__(PGSIZE)))
pde_t entrypgdir[NPDENTRIES] = {
// Map VA's [0, 4MB) to PA's [0, 4MB)
[0] = (0) | PTE_P | PTE_W | PTE_PS,
// Map VA's [KERNBASE, KERNBASE+4MB) to PA's [0, 4MB)
[KERNBASE >> PDXSHIFT] = (0) | PTE_P | PTE_W | PTE_PS,
};

60
memide.c Normal file
View File

@ -0,0 +1,60 @@
// Fake IDE disk; stores blocks in memory.
// Useful for running kernel without scratch disk.
#include "types.h"
#include "defs.h"
#include "param.h"
#include "mmu.h"
#include "proc.h"
#include "x86.h"
#include "traps.h"
#include "spinlock.h"
#include "sleeplock.h"
#include "fs.h"
#include "buf.h"
extern uchar _binary_fs_img_start[], _binary_fs_img_size[];
static int disksize;
static uchar *memdisk;
void ideinit(void) {
memdisk = _binary_fs_img_start;
disksize = (uint)_binary_fs_img_size / BSIZE;
}
// Interrupt handler.
void ideintr(void) {
// no-op
}
// 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 iderw(struct buf *b) {
uchar *p;
if (!holdingsleep(&b->lock)) {
panic("iderw: buf not locked");
}
if ((b->flags & (B_VALID | B_DIRTY)) == B_VALID) {
panic("iderw: nothing to do");
}
if (b->dev != 1) {
panic("iderw: request not for disk 1");
}
if (b->blockno >= disksize) {
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);
}
else {
memmove(b->data, p, BSIZE);
}
b->flags |= B_VALID;
}

15
memlayout.h Normal file
View File

@ -0,0 +1,15 @@
// 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
#define V2P(a) (((uint) (a)) - KERNBASE)
#define P2V(a) ((void *)(((char *) (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

21
mkdir.c Normal file
View File

@ -0,0 +1,21 @@
#include "types.h"
#include "stat.h"
#include "user.h"
int main(int argc, char *argv[]) {
int i;
if (argc < 2) {
printf(2, "Usage: mkdir files...\n");
exit();
}
for (i = 1; i < argc; i++) {
if (mkdir(argv[i]) < 0) {
printf(2, "mkdir: %s failed to create\n", argv[i]);
break;
}
}
exit();
}

283
mkfs.c Normal file
View File

@ -0,0 +1,283 @@
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <assert.h>
#define stat xv6_stat // avoid clash with host struct stat
#include "types.h"
#include "fs.h"
#include "stat.h"
#include "param.h"
#ifndef static_assert
#define static_assert(a, b) do { switch (0) case 0: \
case (a): \
; } while (0)
#endif
#define NINODES 200
// Disk layout:
// [ boot block | sb block | log | inode blocks | free bit map | data blocks ]
int nbitmap = FSSIZE / (BSIZE * 8) + 1;
int ninodeblocks = NINODES / IPB + 1;
int nlog = LOGSIZE;
int nmeta; // Number of meta blocks (boot, sb, nlog, inode, bitmap)
int nblocks; // Number of data blocks
int fsfd;
struct superblock sb;
char zeroes[BSIZE];
uint freeinode = 1;
uint freeblock;
void balloc(int);
void wsect(uint, void*);
void winode(uint, struct dinode*);
void rinode(uint inum, struct dinode *ip);
void rsect(uint sec, void *buf);
uint ialloc(ushort type);
void iappend(uint inum, void *p, int n);
// convert to intel byte order
ushort xshort(ushort x) {
ushort y;
uchar *a = (uchar*)&y;
a[0] = x;
a[1] = x >> 8;
return y;
}
uint xint(uint x) {
uint y;
uchar *a = (uchar*)&y;
a[0] = x;
a[1] = x >> 8;
a[2] = x >> 16;
a[3] = x >> 24;
return y;
}
int main(int argc, char *argv[]) {
int i, cc, fd;
uint rootino, inum, off;
struct dirent de;
char buf[BSIZE];
struct dinode din;
static_assert(sizeof(int) == 4, "Integers must be 4 bytes!");
if (argc < 2) {
fprintf(stderr, "Usage: mkfs fs.img files...\n");
exit(1);
}
assert((BSIZE % sizeof(struct dinode)) == 0);
assert((BSIZE % sizeof(struct dirent)) == 0);
fsfd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC, 0666);
if (fsfd < 0) {
perror(argv[1]);
exit(1);
}
// 1 fs block = 1 disk sector
nmeta = 2 + nlog + ninodeblocks + nbitmap;
nblocks = FSSIZE - nmeta;
sb.size = xint(FSSIZE);
sb.nblocks = xint(nblocks);
sb.ninodes = xint(NINODES);
sb.nlog = xint(nlog);
sb.logstart = xint(2);
sb.inodestart = xint(2 + nlog);
sb.bmapstart = xint(2 + nlog + ninodeblocks);
printf("nmeta %d (boot, super, log blocks %u inode blocks %u, bitmap blocks %u) blocks %d total %d\n",
nmeta, nlog, ninodeblocks, nbitmap, nblocks, FSSIZE);
freeblock = nmeta; // the first free block that we can allocate
for (i = 0; i < FSSIZE; i++) {
wsect(i, zeroes);
}
memset(buf, 0, sizeof(buf));
memmove(buf, &sb, sizeof(sb));
wsect(1, buf);
rootino = ialloc(T_DIR);
assert(rootino == ROOTINO);
bzero(&de, sizeof(de));
de.inum = xshort(rootino);
strcpy(de.name, ".");
iappend(rootino, &de, sizeof(de));
bzero(&de, sizeof(de));
de.inum = xshort(rootino);
strcpy(de.name, "..");
iappend(rootino, &de, sizeof(de));
for (i = 2; i < argc; i++) {
assert(index(argv[i], '/') == 0);
if ((fd = open(argv[i], 0)) < 0) {
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
// in place of system binaries like rm and cat.
if (argv[i][0] == '_') {
++argv[i];
}
inum = ialloc(T_FILE);
bzero(&de, sizeof(de));
de.inum = xshort(inum);
strncpy(de.name, argv[i], DIRSIZ);
iappend(rootino, &de, sizeof(de));
while ((cc = read(fd, buf, sizeof(buf))) > 0) {
iappend(inum, buf, cc);
}
close(fd);
}
// fix size of root inode dir
rinode(rootino, &din);
off = xint(din.size);
off = ((off / BSIZE) + 1) * BSIZE;
din.size = xint(off);
winode(rootino, &din);
balloc(freeblock);
exit(0);
}
void wsect(uint sec, void *buf) {
if (lseek(fsfd, sec * BSIZE, 0) != sec * BSIZE) {
perror("lseek");
exit(1);
}
if (write(fsfd, buf, BSIZE) != BSIZE) {
perror("write");
exit(1);
}
}
void winode(uint inum, struct dinode *ip) {
char buf[BSIZE];
uint bn;
struct dinode *dip;
bn = IBLOCK(inum, sb);
rsect(bn, buf);
dip = ((struct dinode*)buf) + (inum % IPB);
*dip = *ip;
wsect(bn, buf);
}
void rinode(uint inum, struct dinode *ip) {
char buf[BSIZE];
uint bn;
struct dinode *dip;
bn = IBLOCK(inum, sb);
rsect(bn, buf);
dip = ((struct dinode*)buf) + (inum % IPB);
*ip = *dip;
}
void rsect(uint sec, void *buf) {
if (lseek(fsfd, sec * BSIZE, 0) != sec * BSIZE) {
perror("lseek");
exit(1);
}
if (read(fsfd, buf, BSIZE) != BSIZE) {
perror("read");
exit(1);
}
}
uint ialloc(ushort type) {
uint inum = freeinode++;
struct dinode din;
bzero(&din, sizeof(din));
din.type = xshort(type);
din.nlink = xshort(1);
din.size = xint(0);
winode(inum, &din);
return inum;
}
void balloc(int used) {
uchar buf[BSIZE];
int i;
printf("balloc: first %d blocks have been allocated\n", used);
assert(used < BSIZE * 8);
bzero(buf, BSIZE);
for (i = 0; i < used; i++) {
buf[i / 8] = buf[i / 8] | (0x1 << (i % 8));
}
printf("balloc: write bitmap block at sector %d\n", sb.bmapstart);
wsect(sb.bmapstart, buf);
}
#define min(a, b) ((a) < (b) ? (a) : (b))
void iappend(uint inum, void *xp, int n) {
char *p = (char*)xp;
uint fbn, off, n1;
struct dinode din;
char buf[BSIZE];
uint indirect[NINDIRECT];
uint x;
rinode(inum, &din);
off = xint(din.size);
// printf("append inum %d at off %d sz %d\n", inum, off, n);
while (n > 0) {
fbn = off / BSIZE;
assert(fbn < MAXFILE);
if (fbn < NDIRECT) {
if (xint(din.addrs[fbn]) == 0) {
din.addrs[fbn] = xint(freeblock++);
}
x = xint(din.addrs[fbn]);
}
else {
if (xint(din.addrs[NDIRECT]) == 0) {
din.addrs[NDIRECT] = xint(freeblock++);
}
rsect(xint(din.addrs[NDIRECT]), (char*)indirect);
if (indirect[fbn - NDIRECT] == 0) {
indirect[fbn - NDIRECT] = xint(freeblock++);
wsect(xint(din.addrs[NDIRECT]), (char*)indirect);
}
x = xint(indirect[fbn - NDIRECT]);
}
n1 = min(n, (fbn + 1) * BSIZE - off);
rsect(x, buf);
bcopy(p, buf + off - (fbn * BSIZE), n1);
wsect(x, buf);
n -= n1;
off += n1;
p += n1;
}
din.size = xint(off);
winode(inum, &din);
}

181
mmu.h Normal file
View File

@ -0,0 +1,181 @@
// This file contains definitions for the
// x86 memory management unit (MMU).
// Eflags register
#define FL_IF 0x00000200 // Interrupt Enable
// Control Register flags
#define CR0_PE 0x00000001 // Protection Enable
#define CR0_WP 0x00010000 // Write Protect
#define CR0_PG 0x80000000 // Paging
#define CR4_PSE 0x00000010 // Page size extension
// various segment selectors.
#define SEG_KCODE 1 // kernel code
#define SEG_KDATA 2 // kernel data+stack
#define SEG_UCODE 3 // user code
#define SEG_UDATA 4 // user data+stack
#define SEG_TSS 5 // this process's task state
// cpu->gdt[NSEGS] holds the above segments.
#define NSEGS 6
#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_W 0x2 // Writeable (non-executable segments)
#define STA_R 0x2 // Readable (executable segments)
// System segment type bits
#define STS_T32A 0x9 // Available 32-bit TSS
#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 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_PS 0x080 // Page Size
// 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
};
// 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_{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

141
mp.c Normal file
View File

@ -0,0 +1,141 @@
// Multiprocessor support
// Search memory for MP description structures.
// http://developer.intel.com/design/pentium/datashts/24201606.pdf
#include "types.h"
#include "defs.h"
#include "param.h"
#include "memlayout.h"
#include "mp.h"
#include "x86.h"
#include "mmu.h"
#include "proc.h"
struct cpu cpus[NCPU];
int ncpu;
uchar ioapicid;
static uchar sum(uchar *addr, int len) {
int i, sum;
sum = 0;
for (i = 0; i < len; i++) {
sum += addr[i];
}
return sum;
}
// Look for an MP structure in the len bytes at addr.
static struct mp*mpsearch1(uint a, int len) {
uchar *e, *p, *addr;
addr = P2V(a);
e = addr + len;
for (p = addr; p < e; p += sizeof(struct mp)) {
if (memcmp(p, "_MP_", 4) == 0 && sum(p, sizeof(struct mp)) == 0) {
return (struct mp*)p;
}
}
return 0;
}
// Search for the MP Floating Pointer Structure, which according to the
// spec is in one of the following three locations:
// 1) in the first KB of the EBDA;
// 2) in the last KB of system base memory;
// 3) in the BIOS ROM between 0xE0000 and 0xFFFFF.
static struct mp*mpsearch(void) {
uchar *bda;
uint p;
struct mp *mp;
bda = (uchar *) P2V(0x400);
if ((p = ((bda[0x0F] << 8) | bda[0x0E]) << 4)) {
if ((mp = mpsearch1(p, 1024))) {
return mp;
}
}
else {
p = ((bda[0x14] << 8) | bda[0x13]) * 1024;
if ((mp = mpsearch1(p - 1024, 1024))) {
return mp;
}
}
return mpsearch1(0xF0000, 0x10000);
}
// Search for an MP configuration table. For now,
// don't accept the default configurations (physaddr == 0).
// Check for correct signature, calculate the checksum and,
// if correct, check the version.
// To do: check extended table checksum.
static struct mpconf*mpconfig(struct mp **pmp) {
struct mpconf *conf;
struct mp *mp;
if ((mp = mpsearch()) == 0 || mp->physaddr == 0) {
return 0;
}
conf = (struct mpconf*) P2V((uint) mp->physaddr);
if (memcmp(conf, "PCMP", 4) != 0) {
return 0;
}
if (conf->version != 1 && conf->version != 4) {
return 0;
}
if (sum((uchar*)conf, conf->length) != 0) {
return 0;
}
*pmp = mp;
return conf;
}
void mpinit(void) {
uchar *p, *e;
int ismp;
struct mp *mp;
struct mpconf *conf;
struct mpproc *proc;
struct mpioapic *ioapic;
if ((conf = mpconfig(&mp)) == 0) {
panic("Expect to run on an SMP");
}
ismp = 1;
lapic = (uint*)conf->lapicaddr;
for (p = (uchar*)(conf + 1), e = (uchar*)conf + conf->length; p < e;) {
switch (*p) {
case MPPROC:
proc = (struct mpproc*)p;
if (ncpu < NCPU) {
cpus[ncpu].apicid = proc->apicid; // apicid may differ from ncpu
ncpu++;
}
p += sizeof(struct mpproc);
continue;
case MPIOAPIC:
ioapic = (struct mpioapic*)p;
ioapicid = ioapic->apicno;
p += sizeof(struct mpioapic);
continue;
case MPBUS:
case MPIOINTR:
case MPLINTR:
p += 8;
continue;
default:
ismp = 0;
break;
}
}
if (!ismp) {
panic("Didn't find a suitable machine");
}
if (mp->imcrp) {
// Bochs doesn't support IMCR, so this doesn't run on Bochs.
// But it would on real hardware.
outb(0x22, 0x70); // Select IMCR
outb(0x23, inb(0x23) | 1); // Mask external interrupts.
}
}

56
mp.h Normal file
View File

@ -0,0 +1,56 @@
// See MultiProcessor Specification Version 1.[14]
struct mp { // floating pointer
uchar signature[4]; // "_MP_"
void *physaddr; // phys addr of MP config table
uchar length; // 1
uchar specrev; // [14]
uchar checksum; // all bytes must add up to 0
uchar type; // MP system config type
uchar imcrp;
uchar reserved[3];
};
struct mpconf { // configuration table header
uchar signature[4]; // "PCMP"
ushort length; // total table length
uchar version; // [14]
uchar checksum; // all bytes must add up to 0
uchar product[20]; // product id
uint *oemtable; // OEM table pointer
ushort oemlength; // OEM table length
ushort entry; // entry count
uint *lapicaddr; // address of local APIC
ushort xlength; // extended table length
uchar xchecksum; // extended table checksum
uchar reserved;
};
struct mpproc { // processor table entry
uchar type; // entry type (0)
uchar apicid; // local APIC id
uchar version; // local APIC verison
uchar flags; // CPU flags
#define MPBOOT 0x02 // This proc is the bootstrap processor.
uchar signature[4]; // CPU signature
uint feature; // feature flags from CPUID instruction
uchar reserved[8];
};
struct mpioapic { // I/O APIC table entry
uchar type; // entry type (2)
uchar apicno; // I/O APIC id
uchar version; // I/O APIC version
uchar flags; // I/O APIC flags
uint *addr; // I/O APIC address
};
// Table entry types
#define MPPROC 0x00 // One per processor
#define MPBUS 0x01 // One per bus
#define MPIOAPIC 0x02 // One per I/O APIC
#define MPIOINTR 0x03 // One per bus interrupt source
#define MPLINTR 0x04 // One per system interrupt source

14
param.h Normal file
View File

@ -0,0 +1,14 @@
#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

17
picirq.c Normal file
View File

@ -0,0 +1,17 @@
#include "types.h"
#include "x86.h"
#include "traps.h"
// I/O Addresses of the two programmable interrupt controllers
#define IO_PIC1 0x20 // Master (IRQs 0-7)
#define IO_PIC2 0xA0 // Slave (IRQs 8-15)
// Don't use the 8259A interrupt controllers. Xv6 assumes SMP hardware.
void picinit(void) {
// mask all interrupts
outb(IO_PIC1 + 1, 0xFF);
outb(IO_PIC2 + 1, 0xFF);
}

122
pipe.c Normal file
View File

@ -0,0 +1,122 @@
#include "types.h"
#include "defs.h"
#include "param.h"
#include "mmu.h"
#include "proc.h"
#include "fs.h"
#include "spinlock.h"
#include "sleeplock.h"
#include "file.h"
#define PIPESIZE 512
struct pipe {
struct spinlock lock;
char data[PIPESIZE];
uint nread; // number of bytes read
uint nwrite; // number of bytes written
int readopen; // read fd is still open
int writeopen; // write fd is still open
};
void cleanuppipealloc(struct pipe *p, struct file **f0, struct file **f1) {
if (p) {
kfree((char*)p);
}
if (*f0) {
fileclose(*f0);
}
if (*f1) {
fileclose(*f1);
}
}
int pipealloc(struct file **f0, struct file **f1) {
struct pipe *p;
p = 0;
*f0 = *f1 = 0;
if ((*f0 = filealloc()) == 0 || (*f1 = filealloc()) == 0) {
cleanuppipealloc(p, f0, f1);
return -1;
}
if ((p = (struct pipe*)kalloc()) == 0) {
cleanuppipealloc(p, f0, f1);
return -1;
}
p->readopen = 1;
p->writeopen = 1;
p->nwrite = 0;
p->nread = 0;
initlock(&p->lock, "pipe");
(*f0)->type = FD_PIPE;
(*f0)->readable = 1;
(*f0)->writable = 0;
(*f0)->pipe = p;
(*f1)->type = FD_PIPE;
(*f1)->readable = 0;
(*f1)->writable = 1;
(*f1)->pipe = p;
return 0;
}
void pipeclose(struct pipe *p, int writable) {
acquire(&p->lock);
if (writable) {
p->writeopen = 0;
wakeup(&p->nread);
}
else {
p->readopen = 0;
wakeup(&p->nwrite);
}
if (p->readopen == 0 && p->writeopen == 0) {
release(&p->lock);
kfree((char*)p);
}
else {
release(&p->lock);
}
}
int pipewrite(struct pipe *p, char *addr, int n) {
int i;
acquire(&p->lock);
for (i = 0; i < n; i++) {
while (p->nwrite == p->nread + PIPESIZE) { //DOC: pipewrite-full
if (p->readopen == 0 || myproc()->killed) {
release(&p->lock);
return -1;
}
wakeup(&p->nread);
sleep(&p->nwrite, &p->lock); //DOC: pipewrite-sleep
}
p->data[p->nwrite++ % PIPESIZE] = addr[i];
}
wakeup(&p->nread); //DOC: pipewrite-wakeup1
release(&p->lock);
return n;
}
int piperead(struct pipe *p, char *addr, int n) {
int i;
acquire(&p->lock);
while (p->nread == p->nwrite && p->writeopen) { //DOC: pipe-empty
if (myproc()->killed) {
release(&p->lock);
return -1;
}
sleep(&p->nread, &p->lock); //DOC: piperead-sleep
}
for (i = 0; i < n; i++) { //DOC: piperead-copy
if (p->nread == p->nwrite) {
break;
}
addr[i] = p->data[p->nread++ % PIPESIZE];
}
wakeup(&p->nwrite); //DOC: piperead-wakeup
release(&p->lock);
return i;
}

91
printf.c Normal file
View File

@ -0,0 +1,91 @@
#include "types.h"
#include "stat.h"
#include "user.h"
static void putc(int fd, char c) {
write(fd, &c, 1);
}
static void printint(int fd, int xx, int base, int sgn) {
static char digits[] = "0123456789ABCDEF";
char buf[16];
int i, neg;
uint x;
neg = 0;
if (sgn && xx < 0) {
neg = 1;
x = -xx;
}
else {
x = xx;
}
i = 0;
do {
buf[i++] = digits[x % base];
}
while ((x /= base) != 0);
if (neg) {
buf[i++] = '-';
}
while (--i >= 0) {
putc(fd, buf[i]);
}
}
// Print to the given fd. Only understands %d, %x, %p, %s.
void printf(int fd, const char *fmt, ...) {
char *s;
int c, i, state;
uint *ap;
state = 0;
ap = (uint*)(void*)&fmt + 1;
for (i = 0; fmt[i]; i++) {
c = fmt[i] & 0xff;
if (state == 0) {
if (c == '%') {
state = '%';
}
else {
putc(fd, c);
}
}
else if (state == '%') {
if (c == 'd') {
printint(fd, *ap, 10, 1);
ap++;
}
else if (c == 'x' || c == 'p') {
printint(fd, *ap, 16, 0);
ap++;
}
else if (c == 's') {
s = (char*)*ap;
ap++;
if (s == 0) {
s = "(null)";
}
while (*s != 0) {
putc(fd, *s);
s++;
}
}
else if (c == 'c') {
putc(fd, *ap);
ap++;
}
else if (c == '%') {
putc(fd, c);
}
else {
// Unknown % sequence. Print it to draw attention.
putc(fd, '%');
putc(fd, c);
}
state = 0;
}
}
}

527
proc.c Normal file
View File

@ -0,0 +1,527 @@
#include "types.h"
#include "defs.h"
#include "param.h"
#include "memlayout.h"
#include "mmu.h"
#include "x86.h"
#include "proc.h"
#include "spinlock.h"
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");
}
// Must be called with interrupts disabled
int cpuid() {
return mycpu() - cpus;
}
// Must be called with interrupts disabled to avoid the caller being
// rescheduled between reading lapicid and running through the loop.
struct cpu*mycpu(void) {
int apicid, i;
if (readeflags() & FL_IF) {
panic("mycpu called with interrupts enabled\n");
}
apicid = lapicid();
// APIC IDs are not guaranteed to be contiguous. Maybe we should have
// a reverse map, or reserve a register to store &cpus[i].
for (i = 0; i < ncpu; ++i) {
if (cpus[i].apicid == apicid) {
return &cpus[i];
}
}
panic("unknown apicid\n");
}
// Disable interrupts so that we are not rescheduled
// while reading proc from the cpu structure
struct proc*myproc(void) {
struct cpu *c;
struct proc *p;
pushcli();
c = mycpu();
p = c->proc;
popcli();
return p;
}
// 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;
int found = 0;
acquire(&ptable.lock);
p = ptable.proc;
while (p < &ptable.proc[NPROC] && !found) {
if (p->state == UNUSED) {
found = 1;
}
else {
p++;
}
}
if (!found) {
release(&ptable.lock);
return 0;
}
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;
}
// 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("/");
// this assignment to p->state lets other cores
// run this process. the acquire forces the above
// writes to be visible, and the lock is also needed
// because the assignment might not be atomic.
acquire(&ptable.lock);
p->state = RUNNABLE;
release(&ptable.lock);
}
// Grow current process's memory by n bytes.
// Return 0 on success, -1 on failure.
int growproc(int n) {
uint sz;
struct proc *curproc = myproc();
sz = curproc->sz;
if (n > 0) {
if ((sz = allocuvm(curproc->pgdir, sz, sz + n)) == 0) {
return -1;
}
}
else if (n < 0) {
if ((sz = deallocuvm(curproc->pgdir, sz, sz + n)) == 0) {
return -1;
}
}
curproc->sz = sz;
switchuvm(curproc);
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;
struct proc *curproc = myproc();
// Allocate process.
if ((np = allocproc()) == 0) {
return -1;
}
// Copy process state from proc.
if ((np->pgdir = copyuvm(curproc->pgdir, curproc->sz)) == 0) {
kfree(np->kstack);
np->kstack = 0;
np->state = UNUSED;
return -1;
}
np->sz = curproc->sz;
np->parent = curproc;
*np->tf = *curproc->tf;
// Clear %eax so that fork returns 0 in the child.
np->tf->eax = 0;
for (i = 0; i < NOFILE; i++) {
if (curproc->ofile[i]) {
np->ofile[i] = filedup(curproc->ofile[i]);
}
}
np->cwd = idup(curproc->cwd);
safestrcpy(np->name, curproc->name, sizeof(curproc->name));
pid = np->pid;
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 *curproc = myproc();
struct proc *p;
int fd;
if (curproc == initproc) {
panic("init exiting");
}
// Close all open files.
for (fd = 0; fd < NOFILE; fd++) {
if (curproc->ofile[fd]) {
fileclose(curproc->ofile[fd]);
curproc->ofile[fd] = 0;
}
}
begin_op();
iput(curproc->cwd);
end_op();
curproc->cwd = 0;
acquire(&ptable.lock);
// Parent might be sleeping in wait().
wakeup1(curproc->parent);
// Pass abandoned children to init.
for (p = ptable.proc; p < &ptable.proc[NPROC]; p++) {
if (p->parent == curproc) {
p->parent = initproc;
if (p->state == ZOMBIE) {
wakeup1(initproc);
}
}
}
// Jump into the scheduler, never to return.
curproc->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;
struct proc *curproc = myproc();
acquire(&ptable.lock);
for (;;) {
// Scan through table looking for exited children.
havekids = 0;
for (p = ptable.proc; p < &ptable.proc[NPROC]; p++) {
if (p->parent != curproc) {
continue;
}
havekids = 1;
if (p->state == ZOMBIE) {
// Found one.
pid = p->pid;
kfree(p->kstack);
p->kstack = 0;
freevm(p->pgdir);
p->pid = 0;
p->parent = 0;
p->name[0] = 0;
p->killed = 0;
p->state = UNUSED;
release(&ptable.lock);
return pid;
}
}
// No point waiting if we don't have any children.
if (!havekids || curproc->killed) {
release(&ptable.lock);
return -1;
}
// Wait for children to exit. (See wakeup1 call in proc_exit.)
sleep(curproc, &ptable.lock); //DOC: wait-sleep
}
}
// 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;
struct cpu *c = mycpu();
c->proc = 0;
for (;;) {
// Enable interrupts on this processor.
sti();
// Loop over process table looking for process to run.
acquire(&ptable.lock);
for (p = ptable.proc; p < &ptable.proc[NPROC]; p++) {
if (p->state != RUNNABLE) {
continue;
}
// Switch to chosen process. It is the process's job
// to release ptable.lock and then reacquire it
// before jumping back to us.
c->proc = p;
switchuvm(p);
p->state = RUNNING;
swtch(&(c->scheduler), p->context);
switchkvm();
// Process is done running for now.
// It should have changed its p->state before coming back.
c->proc = 0;
}
release(&ptable.lock);
}
}
// Enter scheduler. Must hold only ptable.lock
// and have changed proc->state. Saves and restores
// intena because intena is a property of this
// kernel thread, not this CPU. It should
// be proc->intena and proc->ncli, but that would
// break in the few places where a lock is held but
// there's no process.
void sched(void) {
int intena;
struct proc *p = myproc();
if (!holding(&ptable.lock)) {
panic("sched ptable.lock");
}
if (mycpu()->ncli != 1) {
panic("sched locks");
}
if (p->state == RUNNING) {
panic("sched running");
}
if (readeflags() & FL_IF) {
panic("sched interruptible");
}
intena = mycpu()->intena;
swtch(&p->context, mycpu()->scheduler);
mycpu()->intena = intena;
}
// Give up the CPU for one scheduling round.
void yield(void) {
acquire(&ptable.lock); //DOC: yieldlock
myproc()->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) {
struct proc *p = myproc();
if (p == 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.
p->chan = chan;
p->state = SLEEPING;
sched();
// Tidy up.
p->chan = 0;
// Reacquire original lock.
if (lk != &ptable.lock) { //DOC: sleeplock2
release(&ptable.lock);
acquire(lk);
}
}
// 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;
}
// 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("\n");
}
}

57
proc.h Normal file
View File

@ -0,0 +1,57 @@
// Per-CPU state
struct cpu {
uchar apicid; // Local APIC ID
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?
struct proc *proc; // The process running on this cpu or null
};
extern struct cpu cpus[NCPU];
extern int ncpu;
// 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;
};
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)
};
// Process memory is laid out contiguously, low addresses first:
// text
// original data and bss
// fixed-size stack
// expandable heap

21
rm.c Normal file
View File

@ -0,0 +1,21 @@
#include "types.h"
#include "stat.h"
#include "user.h"
int main(int argc, char *argv[]) {
int i;
if (argc < 2) {
printf(2, "Usage: rm files...\n");
exit();
}
for (i = 1; i < argc; i++) {
if (unlink(argv[i]) < 0) {
printf(2, "rm: %s failed to delete\n", argv[i]);
break;
}
}
exit();
}

482
sh.c Normal file
View File

@ -0,0 +1,482 @@
// Shell.
#include "types.h"
#include "user.h"
#include "fcntl.h"
// Parsed command representation
#define EXEC 1
#define REDIR 2
#define PIPE 3
#define LIST 4
#define BACK 5
#define MAXARGS 10
struct cmd {
int type;
};
struct execcmd {
int type;
char *argv[MAXARGS];
char *eargv[MAXARGS];
};
struct redircmd {
int type;
struct cmd *cmd;
char *file;
char *efile;
int mode;
int fd;
};
struct pipecmd {
int type;
struct cmd *left;
struct cmd *right;
};
struct listcmd {
int type;
struct cmd *left;
struct cmd *right;
};
struct backcmd {
int type;
struct cmd *cmd;
};
int fork1(void); // Fork but panics on failure.
void panic(char*);
struct cmd *parsecmd(char*);
// Execute cmd. Never returns.
void runcmd(struct cmd *cmd) {
int p[2];
struct backcmd *bcmd;
struct execcmd *ecmd;
struct listcmd *lcmd;
struct pipecmd *pcmd;
struct redircmd *rcmd;
if (cmd == 0) {
exit();
}
switch (cmd->type) {
default:
panic("runcmd");
case EXEC:
ecmd = (struct execcmd*)cmd;
if (ecmd->argv[0] == 0) {
exit();
}
exec(ecmd->argv[0], ecmd->argv);
printf(2, "exec %s failed\n", ecmd->argv[0]);
break;
case REDIR:
rcmd = (struct redircmd*)cmd;
close(rcmd->fd);
if (open(rcmd->file, rcmd->mode) < 0) {
printf(2, "open %s failed\n", rcmd->file);
exit();
}
runcmd(rcmd->cmd);
break;
case LIST:
lcmd = (struct listcmd*)cmd;
if (fork1() == 0) {
runcmd(lcmd->left);
}
wait();
runcmd(lcmd->right);
break;
case PIPE:
pcmd = (struct pipecmd*)cmd;
if (pipe(p) < 0) {
panic("pipe");
}
if (fork1() == 0) {
close(1);
dup(p[1]);
close(p[0]);
close(p[1]);
runcmd(pcmd->left);
}
if (fork1() == 0) {
close(0);
dup(p[0]);
close(p[0]);
close(p[1]);
runcmd(pcmd->right);
}
close(p[0]);
close(p[1]);
wait();
wait();
break;
case BACK:
bcmd = (struct backcmd*)cmd;
if (fork1() == 0) {
runcmd(bcmd->cmd);
}
break;
}
exit();
}
int getcmd(char *buf, int nbuf) {
printf(2, "$ ");
memset(buf, 0, nbuf);
gets(buf, nbuf);
if (buf[0] == 0) { // EOF
return -1;
}
return 0;
}
int main(int argc, char* argv[]) {
static char buf[100];
int fd;
// Ensure that three file descriptors are open.
while ((fd = open("console", O_RDWR)) >= 0) {
if (fd >= 3) {
close(fd);
break;
}
}
// Read and run input commands.
while (getcmd(buf, sizeof(buf)) >= 0) {
if (buf[0] == 'e' && buf[1] == 'x' && buf[2] == 'i' && buf[3] == 't') {
exit();
}
if (buf[0] == 'c' && buf[1] == 'd' && buf[2] == ' ') {
// Chdir must be called by the parent, not the child.
buf[strlen(buf) - 1] = 0; // chop \n
if (chdir(buf + 3) < 0) {
printf(2, "cannot cd %s\n", buf + 3);
}
continue;
}
if (fork1() == 0) {
runcmd(parsecmd(buf));
}
wait();
}
exit();
}
void panic(char *s) {
printf(2, "%s\n", s);
exit();
}
int fork1(void) {
int pid;
pid = fork();
if (pid == -1) {
panic("fork");
}
return pid;
}
// Constructors
struct cmd* execcmd(void) {
struct execcmd *cmd;
cmd = malloc(sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd));
cmd->type = EXEC;
return (struct cmd*)cmd;
}
struct cmd* redircmd(struct cmd *subcmd, char *file, char *efile, int mode, int fd) {
struct redircmd *cmd;
cmd = malloc(sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd));
cmd->type = REDIR;
cmd->cmd = subcmd;
cmd->file = file;
cmd->efile = efile;
cmd->mode = mode;
cmd->fd = fd;
return (struct cmd*)cmd;
}
struct cmd* pipecmd(struct cmd *left, struct cmd *right) {
struct pipecmd *cmd;
cmd = malloc(sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd));
cmd->type = PIPE;
cmd->left = left;
cmd->right = right;
return (struct cmd*)cmd;
}
struct cmd* listcmd(struct cmd *left, struct cmd *right) {
struct listcmd *cmd;
cmd = malloc(sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd));
cmd->type = LIST;
cmd->left = left;
cmd->right = right;
return (struct cmd*)cmd;
}
struct cmd* backcmd(struct cmd *subcmd) {
struct backcmd *cmd;
cmd = malloc(sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd));
cmd->type = BACK;
cmd->cmd = subcmd;
return (struct cmd*)cmd;
}
// Parsing
char whitespace[] = " \t\r\n\v";
char symbols[] = "<|>&;()";
int gettoken(char **ps, char *es, char **q, char **eq) {
char *s;
int ret;
s = *ps;
while (s < es && strchr(whitespace, *s)) {
s++;
}
if (q) {
*q = s;
}
ret = *s;
switch (*s) {
case 0:
break;
case '|':
case '(':
case ')':
case ';':
case '&':
case '<':
s++;
break;
case '>':
s++;
if (*s == '>') {
ret = '+';
s++;
}
break;
default:
ret = 'a';
while (s < es && !strchr(whitespace, *s) && !strchr(symbols, *s)) {
s++;
}
break;
}
if (eq) {
*eq = s;
}
while (s < es && strchr(whitespace, *s)) {
s++;
}
*ps = s;
return ret;
}
int peek(char **ps, char *es, char *toks) {
char *s;
s = *ps;
while (s < es && strchr(whitespace, *s)) {
s++;
}
*ps = s;
return *s && strchr(toks, *s);
}
struct cmd *parseline(char**, char*);
struct cmd *parsepipe(char**, char*);
struct cmd *parseexec(char**, char*);
struct cmd *nulterminate(struct cmd*);
struct cmd* parsecmd(char *s) {
char *es;
struct cmd *cmd;
es = s + strlen(s);
cmd = parseline(&s, es);
peek(&s, es, "");
if (s != es) {
printf(2, "leftovers: %s\n", s);
panic("syntax");
}
nulterminate(cmd);
return cmd;
}
struct cmd* parseline(char **ps, char *es) {
struct cmd *cmd;
cmd = parsepipe(ps, es);
while (peek(ps, es, "&")) {
gettoken(ps, es, 0, 0);
cmd = backcmd(cmd);
}
if (peek(ps, es, ";")) {
gettoken(ps, es, 0, 0);
cmd = listcmd(cmd, parseline(ps, es));
}
return cmd;
}
struct cmd* parsepipe(char **ps, char *es) {
struct cmd *cmd;
cmd = parseexec(ps, es);
if (peek(ps, es, "|")) {
gettoken(ps, es, 0, 0);
cmd = pipecmd(cmd, parsepipe(ps, es));
}
return cmd;
}
struct cmd* parseredirs(struct cmd *cmd, char **ps, char *es) {
int tok;
char *q, *eq;
while (peek(ps, es, "<>")) {
tok = gettoken(ps, es, 0, 0);
if (gettoken(ps, es, &q, &eq) != 'a') {
panic("missing file for redirection");
}
switch (tok) {
case '<':
cmd = redircmd(cmd, q, eq, O_RDONLY, 0);
break;
case '>':
cmd = redircmd(cmd, q, eq, O_WRONLY | O_CREATE, 1);
break;
case '+': // >>
cmd = redircmd(cmd, q, eq, O_WRONLY | O_CREATE, 1);
break;
}
}
return cmd;
}
struct cmd* parseblock(char **ps, char *es) {
struct cmd *cmd;
if (!peek(ps, es, "(")) {
panic("parseblock");
}
gettoken(ps, es, 0, 0);
cmd = parseline(ps, es);
if (!peek(ps, es, ")")) {
panic("syntax - missing )");
}
gettoken(ps, es, 0, 0);
cmd = parseredirs(cmd, ps, es);
return cmd;
}
struct cmd* parseexec(char **ps, char *es) {
char *q, *eq;
int tok, argc;
struct execcmd *cmd;
struct cmd *ret;
if (peek(ps, es, "(")) {
return parseblock(ps, es);
}
ret = execcmd();
cmd = (struct execcmd*)ret;
argc = 0;
ret = parseredirs(ret, ps, es);
while (!peek(ps, es, "|)&;")) {
if ((tok = gettoken(ps, es, &q, &eq)) == 0) {
break;
}
if (tok != 'a') {
panic("syntax");
}
cmd->argv[argc] = q;
cmd->eargv[argc] = eq;
argc++;
if (argc >= MAXARGS) {
panic("too many args");
}
ret = parseredirs(ret, ps, es);
}
cmd->argv[argc] = 0;
cmd->eargv[argc] = 0;
return ret;
}
// NUL-terminate all the counted strings.
struct cmd* nulterminate(struct cmd *cmd) {
int i;
struct backcmd *bcmd;
struct execcmd *ecmd;
struct listcmd *lcmd;
struct pipecmd *pcmd;
struct redircmd *rcmd;
if (cmd == 0) {
return 0;
}
switch (cmd->type) {
case EXEC:
ecmd = (struct execcmd*)cmd;
for (i = 0; ecmd->argv[i]; i++) {
*ecmd->eargv[i] = 0;
}
break;
case REDIR:
rcmd = (struct redircmd*)cmd;
nulterminate(rcmd->cmd);
*rcmd->efile = 0;
break;
case PIPE:
pcmd = (struct pipecmd*)cmd;
nulterminate(pcmd->left);
nulterminate(pcmd->right);
break;
case LIST:
lcmd = (struct listcmd*)cmd;
nulterminate(lcmd->left);
nulterminate(lcmd->right);
break;
case BACK:
bcmd = (struct backcmd*)cmd;
nulterminate(bcmd->cmd);
break;
}
return cmd;
}

19
sign.pl Executable file
View File

@ -0,0 +1,19 @@
#!/usr/bin/perl
open(SIG, $ARGV[0]) || die "open $ARGV[0]: $!";
$n = sysread(SIG, $buf, 1000);
if($n > 510){
print STDERR "boot block too large: $n bytes (max 510)\n";
exit 1;
}
print STDERR "boot block is $n bytes (max 510)\n";
$buf .= "\0" x (510-$n);
$buf .= "\x55\xAA";
open(SIG, ">$ARGV[0]") || die "open >$ARGV[0]: $!";
print SIG $buf;
close SIG;

48
sleeplock.c Normal file
View File

@ -0,0 +1,48 @@
// Sleeping 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"
#include "sleeplock.h"
void initsleeplock(struct sleeplock *lk, char *name) {
initlock(&lk->lk, "sleep lock");
lk->name = name;
lk->locked = 0;
lk->pid = 0;
}
void acquiresleep(struct sleeplock *lk) {
acquire(&lk->lk);
while (lk->locked) {
sleep(lk, &lk->lk);
}
lk->locked = 1;
lk->pid = myproc()->pid;
release(&lk->lk);
}
void releasesleep(struct sleeplock *lk) {
acquire(&lk->lk);
lk->locked = 0;
lk->pid = 0;
wakeup(lk);
release(&lk->lk);
}
int holdingsleep(struct sleeplock *lk) {
int r;
acquire(&lk->lk);
r = lk->locked && (lk->pid == myproc()->pid);
release(&lk->lk);
return r;
}

10
sleeplock.h Normal file
View File

@ -0,0 +1,10 @@
// Long-term locks for processes
struct sleeplock {
uint locked; // Is the lock held?
struct spinlock lk; // spinlock protecting this sleep lock
// For debugging:
char *name; // Name of lock.
int pid; // Process holding lock
};

121
spinlock.c Normal file
View File

@ -0,0 +1,121 @@
// 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.
while (xchg(&lk->locked, 1) != 0) {
;
}
// Tell the C compiler and the processor to not move loads or stores
// past this point, to ensure that the critical section's memory
// references happen after the lock is acquired.
__sync_synchronize();
// Record info about lock acquisition for debugging.
lk->cpu = mycpu();
getcallerpcs(&lk, lk->pcs);
}
// Release the lock.
void release(struct spinlock *lk) {
if (!holding(lk)) {
panic("release");
}
lk->pcs[0] = 0;
lk->cpu = 0;
// Tell the C compiler and the processor to not move loads or stores
// past this point, to ensure that all the stores in the critical
// section are visible to other cores before the lock is released.
// Both the C compiler and the hardware may re-order loads and
// stores; __sync_synchronize() tells them both not to.
__sync_synchronize();
// Release the lock, equivalent to lk->locked = 0.
// This code can't use a C assignment, since it might
// not be atomic. A real OS would use C atomics here.
asm volatile ("movl $0, %0" : "+m" (lk->locked) :);
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) {
int r;
pushcli();
r = lock->locked && lock->cpu == mycpu();
popcli();
return r;
}
// 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 (mycpu()->ncli == 0) {
mycpu()->intena = eflags & FL_IF;
}
mycpu()->ncli += 1;
}
void popcli(void) {
if (readeflags() & FL_IF) {
panic("popcli - interruptible");
}
if (--mycpu()->ncli < 0) {
panic("popcli");
}
if (mycpu()->ncli == 0 && mycpu()->intena) {
sti();
}
}

11
spinlock.h Normal file
View File

@ -0,0 +1,11 @@
// 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.
uint pcs[10]; // The call stack (an array of program counters)
// that locked the lock.
};

11
stat.h Normal file
View File

@ -0,0 +1,11 @@
#define T_DIR 1 // Directory
#define T_FILE 2 // File
#define T_DEV 3 // Device
struct stat {
short type; // Type of file
int dev; // File system's disk device
uint ino; // Inode number
short nlink; // Number of links to file
uint size; // Size of file in bytes
};

51
stressfs.c Normal file
View File

@ -0,0 +1,51 @@
// Demonstrate that moving the "acquire" in iderw after the loop that
// appends to the idequeue results in a race.
// For this to work, you should also add a spin within iderw's
// idequeue traversal loop. Adding the following demonstrated a panic
// after about 5 runs of stressfs in QEMU on a 2.1GHz CPU:
// for (i = 0; i < 40000; i++)
// asm volatile("");
#include "types.h"
#include "stat.h"
#include "user.h"
#include "fs.h"
#include "fcntl.h"
int main(int argc, char *argv[]) {
int fd, i;
char path[] = "stressfs0";
char data[512];
printf(1, "stressfs starting\n");
memset(data, 'a', sizeof(data));
for (i = 0; i < 4; i++) {
if (fork() > 0) {
break;
}
}
printf(1, "write %d\n", i);
path[8] += i;
fd = open(path, O_CREATE | O_RDWR);
for (i = 0; i < 20; i++) {
// printf(fd, "%d\n", i);
write(fd, data, sizeof(data));
}
close(fd);
printf(1, "read\n");
fd = open(path, O_RDONLY);
for (i = 0; i < 20; i++) {
read(fd, data, sizeof(data));
}
close(fd);
wait();
exit();
}

103
string.c Normal file
View File

@ -0,0 +1,103 @@
#include "types.h"
#include "x86.h"
void* memset(void *dst, int c, uint n) {
if ((int)dst % 4 == 0 && n % 4 == 0) {
c &= 0xFF;
stosl(dst, (c << 24) | (c << 16) | (c << 8) | c, n / 4);
}
else {
stosb(dst, c, n);
}
return dst;
}
int memcmp(const void *v1, const void *v2, uint n) {
const uchar *s1, *s2;
s1 = v1;
s2 = v2;
while (n-- > 0) {
if (*s1 != *s2) {
return *s1 - *s2;
}
s1++, s2++;
}
return 0;
}
void* memmove(void *dst, const void *src, uint n) {
const char *s;
char *d;
s = src;
d = dst;
if (s < d && s + n > d) {
s += n;
d += n;
while (n-- > 0) {
*--d = *--s;
}
}
else {
while (n-- > 0) {
*d++ = *s++;
}
}
return dst;
}
// memcpy exists to placate GCC. Use memmove.
void* memcpy(void *dst, const void *src, uint n) {
return memmove(dst, src, n);
}
int strncmp(const char *p, const char *q, uint n) {
while (n > 0 && *p && *p == *q) {
n--, p++, q++;
}
if (n == 0) {
return 0;
}
return (uchar) * p - (uchar) * q;
}
char* strncpy(char *s, const char *t, int n) {
char *os;
os = s;
while (n-- > 0 && (*s++ = *t++) != 0) {
;
}
while (n-- > 0) {
*s++ = 0;
}
return os;
}
// Like strncpy but guaranteed to NUL-terminate.
char* safestrcpy(char *s, const char *t, int n) {
char *os;
os = s;
if (n <= 0) {
return os;
}
while (--n > 0 && (*s++ = *t++) != 0) {
;
}
*s = 0;
return os;
}
int strlen(const char *s) {
int n;
for (n = 0; s[n]; n++) {
;
}
return n;
}

29
swtch.S Normal file
View File

@ -0,0 +1,29 @@
# Context switch
#
# void swtch(struct context **old, struct context *new);
#
# Save the current registers on the stack, creating
# a struct context, and save its address in *old.
# Switch stacks to new and pop previously-saved registers.
.globl swtch
swtch:
movl 4(%esp), %eax
movl 8(%esp), %edx
# Save old callee-saved registers
pushl %ebp
pushl %ebx
pushl %esi
pushl %edi
# Switch stacks
movl %esp, (%eax)
movl %edx, %esp
# Load new callee-saved registers
popl %edi
popl %esi
popl %ebx
popl %ebp
ret

95
syscall.c Normal file
View File

@ -0,0 +1,95 @@
#include "types.h"
#include "defs.h"
#include "param.h"
#include "memlayout.h"
#include "mmu.h"
#include "proc.h"
#include "x86.h"
#include "syscall.h"
#include "syscalltable.h"
// User code makes a system call with INT T_SYSCALL.
// System call number in %eax.
// Arguments on the stack, from the user call to the C
// library system call function. The saved user %esp points
// to a saved program counter, and then the first argument.
// Fetch the int at addr from the current process.
int fetchint(uint addr, int *ip) {
struct proc *curproc = myproc();
if (addr >= curproc->sz || addr + 4 > curproc->sz) {
return -1;
}
*ip = *(int*)(addr);
return 0;
}
// Fetch the nul-terminated string at addr from the current process.
// Doesn't actually copy the string - just sets *pp to point at it.
// Returns length of string, not including nul.
int fetchstr(uint addr, char **pp) {
char *s, *ep;
struct proc *curproc = myproc();
if (addr >= curproc->sz) {
return -1;
}
*pp = (char*)addr;
ep = (char*)curproc->sz;
for (s = *pp; s < ep; s++) {
if (*s == 0) {
return s - *pp;
}
}
return -1;
}
// Fetch the nth 32-bit system call argument.
int argint(int n, int *ip) {
return fetchint((myproc()->tf->esp) + 4 + 4 * n, ip);
}
// Fetch the nth word-sized system call argument as a pointer
// to a block of memory of size bytes. Check that the pointer
// lies within the process address space.
int argptr(int n, char **pp, int size) {
int i;
struct proc *curproc = myproc();
if (argint(n, &i) < 0) {
return -1;
}
if (size < 0 || (uint)i >= curproc->sz || (uint)i + size > curproc->sz) {
return -1;
}
*pp = (char*)i;
return 0;
}
// Fetch the nth word-sized system call argument as a string pointer.
// Check that the pointer is valid and the string is nul-terminated.
// (There is no shared writable memory, so the string can't change
// between this check and being used by the kernel.)
int argstr(int n, char **pp) {
int addr;
if (argint(n, &addr) < 0) {
return -1;
}
return fetchstr(addr, pp);
}
void syscall(void) {
int num;
struct proc *curproc = myproc();
num = curproc->tf->eax;
if (num > 0 && num < NELEM(syscalls) && syscalls[num]) {
curproc->tf->eax = syscalls[num]();
}
else {
cprintf("%d %s: unknown sys call %d\n",
curproc->pid, curproc->name, num);
curproc->tf->eax = -1;
}
}

450
sysfile.c Normal file
View File

@ -0,0 +1,450 @@
//
// File-system system calls.
// Mostly argument checking, since we don't trust
// user code, and calls into file.c and fs.c.
//
#include "types.h"
#include "defs.h"
#include "param.h"
#include "stat.h"
#include "mmu.h"
#include "proc.h"
#include "fs.h"
#include "spinlock.h"
#include "sleeplock.h"
#include "file.h"
#include "fcntl.h"
// Fetch the nth word-sized system call argument as a file descriptor
// and return both the descriptor and the corresponding struct file.
static int argfd(int n, int *pfd, struct file **pf) {
int fd;
struct file *f;
if (argint(n, &fd) < 0) {
return -1;
}
if (fd < 0 || fd >= NOFILE || (f = myproc()->ofile[fd]) == 0) {
return -1;
}
if (pfd) {
*pfd = fd;
}
if (pf) {
*pf = f;
}
return 0;
}
// Allocate a file descriptor for the given file.
// Takes over file reference from caller on success.
static int fdalloc(struct file *f) {
int fd;
struct proc *curproc = myproc();
for (fd = 0; fd < NOFILE; fd++) {
if (curproc->ofile[fd] == 0) {
curproc->ofile[fd] = f;
return fd;
}
}
return -1;
}
int sys_dup(void) {
struct file *f;
int fd;
if (argfd(0, 0, &f) < 0) {
return -1;
}
if ((fd = fdalloc(f)) < 0) {
return -1;
}
filedup(f);
return fd;
}
int sys_read(void) {
struct file *f;
int n;
char *p;
if (argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argptr(1, &p, n) < 0) {
return -1;
}
return fileread(f, p, n);
}
int sys_write(void) {
struct file *f;
int n;
char *p;
if (argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argptr(1, &p, n) < 0) {
return -1;
}
return filewrite(f, p, n);
}
int sys_close(void) {
int fd;
struct file *f;
if (argfd(0, &fd, &f) < 0) {
return -1;
}
myproc()->ofile[fd] = 0;
fileclose(f);
return 0;
}
int sys_fstat(void) {
struct file *f;
struct stat *st;
if (argfd(0, 0, &f) < 0 || argptr(1, (void*)&st, sizeof(*st)) < 0) {
return -1;
}
return filestat(f, st);
}
void cleanupsyslink(struct inode * ip) {
ilock(ip);
ip->nlink--;
iupdate(ip);
iunlockput(ip);
end_op();
}
// Create the path new as a link to the same inode as old.
int sys_link(void) {
char name[DIRSIZ], *new, *old;
struct inode *dp, *ip;
if (argstr(0, &old) < 0 || argstr(1, &new) < 0) {
return -1;
}
begin_op();
if ((ip = namei(old)) == 0) {
end_op();
return -1;
}
ilock(ip);
if (ip->type == T_DIR) {
iunlockput(ip);
end_op();
return -1;
}
ip->nlink++;
iupdate(ip);
iunlock(ip);
if ((dp = nameiparent(new, name)) == 0) {
cleanupsyslink(ip);
return -1;
}
ilock(dp);
if (dp->dev != ip->dev || dirlink(dp, name, ip->inum) < 0) {
iunlockput(dp);
cleanupsyslink(ip);
return -1;
}
iunlockput(dp);
iput(ip);
end_op();
return 0;
}
// Is the directory dp empty except for "." and ".." ?
static int isdirempty(struct inode *dp) {
int off;
struct dirent de;
for (off = 2 * sizeof(de); off < dp->size; off += sizeof(de)) {
if (readi(dp, (char*)&de, off, sizeof(de)) != sizeof(de)) {
panic("isdirempty: readi");
}
if (de.inum != 0) {
return 0;
}
}
return 1;
}
int sys_unlink(void) {
struct inode *ip, *dp;
struct dirent de;
char name[DIRSIZ], *path;
uint off;
if (argstr(0, &path) < 0) {
return -1;
}
begin_op();
if ((dp = nameiparent(path, name)) == 0) {
end_op();
return -1;
}
ilock(dp);
// Cannot unlink "." or "..".
if (namecmp(name, ".") == 0 || namecmp(name, "..") == 0) {
iunlockput(dp);
end_op();
return -1;
}
if ((ip = dirlookup(dp, name, &off)) == 0) {
iunlockput(dp);
end_op();
return -1;
}
ilock(ip);
if (ip->nlink < 1) {
panic("unlink: nlink < 1");
}
if (ip->type == T_DIR && !isdirempty(ip)) {
iunlockput(ip);
iunlockput(dp);
end_op();
return -1;
}
memset(&de, 0, sizeof(de));
if (writei(dp, (char*)&de, off, sizeof(de)) != sizeof(de)) {
panic("unlink: writei");
}
if (ip->type == T_DIR) {
dp->nlink--;
iupdate(dp);
}
iunlockput(dp);
ip->nlink--;
iupdate(ip);
iunlockput(ip);
end_op();
return 0;
}
static struct inode* create(char *path, short type, short major, short minor) {
struct inode *ip, *dp;
char name[DIRSIZ];
if ((dp = nameiparent(path, name)) == 0) {
return 0;
}
ilock(dp);
if ((ip = dirlookup(dp, name, 0)) != 0) {
iunlockput(dp);
ilock(ip);
if (type == T_FILE && ip->type == T_FILE) {
return ip;
}
iunlockput(ip);
return 0;
}
if ((ip = ialloc(dp->dev, type)) == 0) {
panic("create: ialloc");
}
ilock(ip);
ip->major = major;
ip->minor = minor;
ip->nlink = 1;
iupdate(ip);
if (type == T_DIR) { // Create . and .. entries.
dp->nlink++; // for ".."
iupdate(dp);
// No ip->nlink++ for ".": avoid cyclic ref count.
if (dirlink(ip, ".", ip->inum) < 0 || dirlink(ip, "..", dp->inum) < 0) {
panic("create dots");
}
}
if (dirlink(dp, name, ip->inum) < 0) {
panic("create: dirlink");
}
iunlockput(dp);
return ip;
}
int sys_open(void) {
char *path;
int fd, omode;
struct file *f;
struct inode *ip;
if (argstr(0, &path) < 0 || argint(1, &omode) < 0) {
return -1;
}
begin_op();
if (omode & O_CREATE) {
ip = create(path, T_FILE, 0, 0);
if (ip == 0) {
end_op();
return -1;
}
}
else {
if ((ip = namei(path)) == 0) {
end_op();
return -1;
}
ilock(ip);
if (ip->type == T_DIR && omode != O_RDONLY) {
iunlockput(ip);
end_op();
return -1;
}
}
if ((f = filealloc()) == 0 || (fd = fdalloc(f)) < 0) {
if (f) {
fileclose(f);
}
iunlockput(ip);
end_op();
return -1;
}
iunlock(ip);
end_op();
f->type = FD_INODE;
f->ip = ip;
f->off = 0;
f->readable = !(omode & O_WRONLY);
f->writable = (omode & O_WRONLY) || (omode & O_RDWR);
return fd;
}
int sys_mkdir(void) {
char *path;
struct inode *ip;
begin_op();
if (argstr(0, &path) < 0 || (ip = create(path, T_DIR, 0, 0)) == 0) {
end_op();
return -1;
}
iunlockput(ip);
end_op();
return 0;
}
int sys_mknod(void) {
struct inode *ip;
char *path;
int major, minor;
begin_op();
if ((argstr(0, &path)) < 0 ||
argint(1, &major) < 0 ||
argint(2, &minor) < 0 ||
(ip = create(path, T_DEV, major, minor)) == 0) {
end_op();
return -1;
}
iunlockput(ip);
end_op();
return 0;
}
int sys_chdir(void) {
char *path;
struct inode *ip;
struct proc *curproc = myproc();
begin_op();
if (argstr(0, &path) < 0 || (ip = namei(path)) == 0) {
end_op();
return -1;
}
ilock(ip);
if (ip->type != T_DIR) {
iunlockput(ip);
end_op();
return -1;
}
iunlock(ip);
iput(curproc->cwd);
end_op();
curproc->cwd = ip;
return 0;
}
int sys_exec(void) {
char *path, *argv[MAXARG];
int i;
uint uargv, uarg;
if (argstr(0, &path) < 0 || argint(1, (int*)&uargv) < 0) {
return -1;
}
memset(argv, 0, sizeof(argv));
for (i = 0;; i++) {
if (i >= NELEM(argv)) {
return -1;
}
if (fetchint(uargv + 4 * i, (int*)&uarg) < 0) {
return -1;
}
if (uarg == 0) {
argv[i] = 0;
break;
}
if (fetchstr(uarg, &argv[i]) < 0) {
return -1;
}
}
return exec(path, argv);
}
int sys_pipe(void) {
int *fd;
struct file *rf, *wf;
int fd0, fd1;
if (argptr(0, (void*)&fd, 2 * sizeof(fd[0])) < 0) {
return -1;
}
if (pipealloc(&rf, &wf) < 0) {
return -1;
}
fd0 = -1;
if ((fd0 = fdalloc(rf)) < 0 || (fd1 = fdalloc(wf)) < 0) {
if (fd0 >= 0) {
myproc()->ofile[fd0] = 0;
}
fileclose(rf);
fileclose(wf);
return -1;
}
fd[0] = fd0;
fd[1] = fd1;
return 0;
}
int sys_getch(void) {
return consoleget();
}

79
sysproc.c Normal file
View File

@ -0,0 +1,79 @@
#include "types.h"
#include "x86.h"
#include "defs.h"
#include "date.h"
#include "param.h"
#include "memlayout.h"
#include "mmu.h"
#include "proc.h"
int sys_fork(void) {
return fork();
}
int sys_exit(void) {
exit();
return 0; // not reached
}
int sys_wait(void) {
return wait();
}
int sys_kill(void) {
int pid;
if (argint(0, &pid) < 0) {
return -1;
}
return kill(pid);
}
int sys_getpid(void) {
return myproc()->pid;
}
int sys_sbrk(void) {
int addr;
int n;
if (argint(0, &n) < 0) {
return -1;
}
addr = myproc()->sz;
if (growproc(n) < 0) {
return -1;
}
return addr;
}
int sys_sleep(void) {
int n;
uint ticks0;
if (argint(0, &n) < 0) {
return -1;
}
acquire(&tickslock);
ticks0 = ticks;
while (ticks - ticks0 < n) {
if (myproc()->killed) {
release(&tickslock);
return -1;
}
sleep(&ticks, &tickslock);
}
release(&tickslock);
return 0;
}
// return how many clock tick interrupts have occurred
// since start.
int sys_uptime(void) {
uint xticks;
acquire(&tickslock);
xticks = ticks;
release(&tickslock);
return xticks;
}

111
trap.c Normal file
View File

@ -0,0 +1,111 @@
#include "types.h"
#include "defs.h"
#include "param.h"
#include "memlayout.h"
#include "mmu.h"
#include "proc.h"
#include "x86.h"
#include "traps.h"
#include "spinlock.h"
// Interrupt descriptor table (shared by all CPUs).
struct gatedesc idt[256];
extern uint vectors[]; // in vectors.S: array of 256 entry pointers
struct spinlock tickslock;
uint ticks;
void tvinit(void) {
int i;
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");
}
void idtinit(void) {
lidt(idt, sizeof(idt));
}
void trap(struct trapframe *tf) {
if (tf->trapno == T_SYSCALL) {
if (myproc()->killed) {
exit();
}
myproc()->tf = tf;
syscall();
if (myproc()->killed) {
exit();
}
return;
}
switch (tf->trapno) {
case T_IRQ0 + IRQ_TIMER:
if (cpuid() == 0) {
acquire(&tickslock);
ticks++;
wakeup(&ticks);
release(&tickslock);
}
lapiceoi();
break;
case T_IRQ0 + IRQ_IDE:
ideintr();
lapiceoi();
break;
case T_IRQ0 + IRQ_IDE + 1:
// Bochs generates spurious IDE1 interrupts.
break;
case T_IRQ0 + IRQ_KBD:
kbdintr();
lapiceoi();
break;
case T_IRQ0 + IRQ_COM1:
uartintr();
lapiceoi();
break;
case T_IRQ0 + 7:
case T_IRQ0 + IRQ_SPURIOUS:
cprintf("cpu%d: spurious interrupt at %x:%x\n",
cpuid(), tf->cs, tf->eip);
lapiceoi();
break;
default:
if (myproc() == 0 || (tf->cs & 3) == 0) {
// In kernel, it must be our mistake.
cprintf("unexpected trap %d from cpu %d eip %x (cr2=0x%x)\n",
tf->trapno, cpuid(), tf->eip, rcr2());
panic("trap");
}
// 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",
myproc()->pid, myproc()->name, tf->trapno,
tf->err, cpuid(), tf->eip, rcr2());
myproc()->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
// until it gets to the regular system call return.)
if (myproc() && myproc()->killed && (tf->cs & 3) == DPL_USER) {
exit();
}
// Force process to give up CPU on clock tick.
// If interrupts were on while locks held, would need to check nlock.
if (myproc() && myproc()->state == RUNNING &&
tf->trapno == T_IRQ0 + IRQ_TIMER) {
yield();
}
// Check if the process has been killed since we yielded
if (myproc() && myproc()->killed && (tf->cs & 3) == DPL_USER) {
exit();
}
}

32
trapasm.S Normal file
View File

@ -0,0 +1,32 @@
#include "mmu.h"
# vectors.S sends all traps here.
.globl alltraps
alltraps:
# Build trap frame.
pushl %ds
pushl %es
pushl %fs
pushl %gs
pushal
# Set up data segments.
movw $(SEG_KDATA<<3), %ax
movw %ax, %ds
movw %ax, %es
# Call trap(tf), where tf=%esp
pushl %esp
call trap
addl $4, %esp
# Return falls through to trapret...
.globl trapret
trapret:
popal
popl %gs
popl %fs
popl %es
popl %ds
addl $0x8, %esp # trapno and errcode
iret

38
traps.h Normal file
View File

@ -0,0 +1,38 @@
// 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
#define T_BRKPT 3 // breakpoint
#define T_OFLOW 4 // overflow
#define T_BOUND 5 // bounds check
#define T_ILLOP 6 // illegal opcode
#define T_DEVICE 7 // device not available
#define T_DBLFLT 8 // double fault
// #define T_COPROC 9 // reserved (not used since 486)
#define T_TSS 10 // invalid task switch segment
#define T_SEGNP 11 // segment not present
#define T_STACK 12 // stack exception
#define T_GPFLT 13 // general protection fault
#define T_PGFLT 14 // page fault
// #define T_RES 15 // reserved
#define T_FPERR 16 // floating point error
#define T_ALIGN 17 // aligment check
#define T_MCHK 18 // machine check
#define T_SIMDERR 19 // SIMD floating point error
// These are arbitrarily chosen, but with care not to overlap
// processor defined exceptions or interrupt vectors.
#define T_SYSCALL 64 // system call
#define T_DEFAULT 500 // catchall
#define T_IRQ0 32 // IRQ 0 corresponds to int T_IRQ
#define IRQ_TIMER 0
#define IRQ_KBD 1
#define IRQ_COM1 4
#define IRQ_IDE 14
#define IRQ_ERROR 19
#define IRQ_SPURIOUS 31

4
types.h Normal file
View File

@ -0,0 +1,4 @@
typedef unsigned int uint;
typedef unsigned short ushort;
typedef unsigned char uchar;
typedef uint pde_t;

75
uart.c Normal file
View File

@ -0,0 +1,75 @@
// Intel 8250 serial port (UART).
#include "types.h"
#include "defs.h"
#include "param.h"
#include "traps.h"
#include "spinlock.h"
#include "sleeplock.h"
#include "fs.h"
#include "file.h"
#include "mmu.h"
#include "proc.h"
#include "x86.h"
#define COM1 0x3f8
static int uart; // is there a uart?
void uartinit(void) {
char *p;
// 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);
outb(COM1 + 1, 0);
outb(COM1 + 3, 0x03); // Lock divisor, 8 data bits.
outb(COM1 + 4, 0);
outb(COM1 + 1, 0x01); // Enable receive interrupts.
// If status is 0xFF, no serial port.
if (inb(COM1 + 5) == 0xFF) {
return;
}
uart = 1;
// Acknowledge pre-existing interrupt conditions;
// enable interrupts.
inb(COM1 + 2);
inb(COM1 + 0);
ioapicenable(IRQ_COM1, 0);
// Announce that we're here.
for (p = "xv6...\n"; *p; p++) {
uartputc(*p);
}
}
void uartputc(int c) {
int i;
if (!uart) {
return;
}
for (i = 0; i < 128 && !(inb(COM1 + 5) & 0x20); i++) {
microdelay(10);
}
outb(COM1 + 0, c);
}
static int uartgetc(void) {
if (!uart) {
return -1;
}
if (!(inb(COM1 + 5) & 0x01)) {
return -1;
}
return inb(COM1 + 0);
}
void uartintr(void) {
consoleintr(uartgetc);
}

106
ulib.c Normal file
View File

@ -0,0 +1,106 @@
#include "types.h"
#include "stat.h"
#include "fcntl.h"
#include "user.h"
#include "x86.h"
char*strcpy(char *s, const char *t) {
char *os;
os = s;
while ((*s++ = *t++) != 0) {
;
}
return os;
}
int strcmp(const char *p, const char *q) {
while (*p && *p == *q) {
p++, q++;
}
return (uchar) * p - (uchar) * q;
}
uint strlen(const char *s) {
int n;
for (n = 0; s[n]; n++) {
;
}
return n;
}
void* memset(void *dst, int c, uint n) {
stosb(dst, c, n);
return dst;
}
char* strchr(const char *s, char c) {
for (; *s; s++) {
if (*s == c) {
return (char*)s;
}
}
return 0;
}
char* gets(char *buf, int max) {
int i, cc;
char c;
for (i = 0; i + 1 < max;) {
cc = read(0, &c, 1);
if (cc < 1) {
break;
}
buf[i++] = c;
if (c == '\n' || c == '\r') {
break;
}
}
buf[i] = '\0';
return buf;
}
int stat(const char *n, struct stat *st) {
int fd;
int r;
fd = open(n, O_RDONLY);
if (fd < 0) {
return -1;
}
r = fstat(fd, st);
close(fd);
return r;
}
int atoi(const char *s) {
int n;
n = 0;
while ('0' <= *s && *s <= '9') {
n = n * 10 + *s++ - '0';
}
return n;
}
void* memmove(void *vdst, const void *vsrc, int n) {
char *dst;
const char *src;
dst = vdst;
src = vsrc;
while (n-- > 0) {
*dst++ = *src++;
}
return vdst;
}
__attribute__((noreturn))
int _init(int argc, char* argv[]) {
main(argc, argv);
exit();
}

95
umalloc.c Normal file
View File

@ -0,0 +1,95 @@
#include "types.h"
#include "stat.h"
#include "user.h"
#include "param.h"
// Memory allocator by Kernighan and Ritchie,
// The C programming Language, 2nd ed. Section 8.7.
typedef long Align;
union header {
struct {
union header *ptr;
uint size;
} s;
Align x;
};
typedef union header Header;
static Header base;
static Header *freep;
void free(void *ap) {
Header *bp, *p;
bp = (Header*)ap - 1;
for (p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr) {
if (p >= p->s.ptr && (bp > p || bp < p->s.ptr)) {
break;
}
}
if (bp + bp->s.size == p->s.ptr) {
bp->s.size += p->s.ptr->s.size;
bp->s.ptr = p->s.ptr->s.ptr;
}
else {
bp->s.ptr = p->s.ptr;
}
if (p + p->s.size == bp) {
p->s.size += bp->s.size;
p->s.ptr = bp->s.ptr;
}
else {
p->s.ptr = bp;
}
freep = p;
}
static Header* morecore(uint nu) {
char *p;
Header *hp;
if (nu < 4096) {
nu = 4096;
}
p = sbrk(nu * sizeof(Header));
if (p == (char*)-1) {
return 0;
}
hp = (Header*)p;
hp->s.size = nu;
free((void*)(hp + 1));
return freep;
}
void* malloc(uint nbytes) {
Header *p, *prevp;
uint nunits;
nunits = (nbytes + sizeof(Header) - 1) / sizeof(Header) + 1;
if ((prevp = freep) == 0) {
base.s.ptr = freep = prevp = &base;
base.s.size = 0;
}
for (p = prevp->s.ptr;; prevp = p, p = p->s.ptr) {
if (p->s.size >= nunits) {
if (p->s.size == nunits) {
prevp->s.ptr = p->s.ptr;
}
else {
p->s.size -= nunits;
p += p->s.size;
p->s.size = nunits;
}
freep = prevp;
return (void*)(p + 1);
}
if (p == freep) {
if ((p = morecore(nunits)) == 0) {
return 0;
}
}
}
}

43
user.h Normal file
View File

@ -0,0 +1,43 @@
struct stat;
struct rtcdate;
// system calls
int fork(void);
int exit(void) __attribute__((noreturn));
int wait(void);
int pipe(int*);
int write(int, const void*, int);
int read(int, void*, int);
int close(int);
int kill(int);
int exec(char*, char**);
int open(const char*, int);
int mknod(const char*, short, short);
int unlink(const char*);
int fstat(int fd, struct stat*);
int link(const char*, const char*);
int mkdir(const char*);
int chdir(const char*);
int dup(int);
int getpid(void);
char* sbrk(int);
int sleep(int);
int uptime(void);
int getch(void);
// ulib.c
int stat(const char*, struct stat*);
char* strcpy(char*, const char*);
void *memmove(void*, const void*, int);
char* strchr(const char*, char c);
int strcmp(const char*, const char*);
void printf(int, const char*, ...);
char* gets(char*, int max);
uint strlen(const char*);
void* memset(void*, int, uint);
void* malloc(uint);
void free(void*);
int atoi(const char*);
// entry-point
extern int main(int, char*[]);

1779
usertests.c Normal file

File diff suppressed because it is too large Load Diff

47
vectors.pl Executable file
View File

@ -0,0 +1,47 @@
#!/usr/bin/perl -w
# Generate vectors.S, the trap/interrupt entry points.
# There has to be one entry point per interrupt number
# since otherwise there's no way for trap() to discover
# the interrupt number.
print "# generated by vectors.pl - do not edit\n";
print "# handlers\n";
print ".globl alltraps\n";
for(my $i = 0; $i < 256; $i++){
print ".globl vector$i\n";
print "vector$i:\n";
if(!($i == 8 || ($i >= 10 && $i <= 14) || $i == 17)){
print " pushl \$0\n";
}
print " pushl \$$i\n";
print " jmp alltraps\n";
}
print "\n# vector table\n";
print ".data\n";
print ".globl vectors\n";
print "vectors:\n";
for(my $i = 0; $i < 256; $i++){
print " .long vector$i\n";
}
# sample output:
# # handlers
# .globl alltraps
# .globl vector0
# vector0:
# pushl $0
# pushl $0
# jmp alltraps
# ...
#
# # vector table
# .data
# .globl vectors
# vectors:
# .long vector0
# .long vector1
# .long vector2
# ...

392
vm.c Normal file
View File

@ -0,0 +1,392 @@
#include "param.h"
#include "types.h"
#include "defs.h"
#include "x86.h"
#include "memlayout.h"
#include "mmu.h"
#include "proc.h"
#include "elf.h"
extern char data[]; // defined by kernel.ld
pde_t *kpgdir; // for use in scheduler()
// Set up CPU's kernel segment descriptors.
// Run once on entry on each CPU.
void seginit(void) {
struct cpu *c;
// Map "logical" addresses to virtual addresses using identity map.
// Cannot share a CODE descriptor for both kernel and user
// because it would have to have DPL_USR, but the CPU forbids
// an interrupt from CPL=0 to DPL=3.
c = &cpus[cpuid()];
c->gdt[SEG_KCODE] = SEG(STA_X | STA_R, 0, 0xffffffff, 0);
c->gdt[SEG_KDATA] = SEG(STA_W, 0, 0xffffffff, 0);
c->gdt[SEG_UCODE] = SEG(STA_X | STA_R, 0, 0xffffffff, DPL_USER);
c->gdt[SEG_UDATA] = SEG(STA_W, 0, 0xffffffff, DPL_USER);
lgdt(c->gdt, sizeof(c->gdt));
}
// Return the address of the PTE in page table pgdir
// that corresponds to virtual address va. If alloc!=0,
// create any required page table pages.
static pte_t * walkpgdir(pde_t *pgdir, const void *va, int alloc) {
pde_t *pde;
pte_t *pgtab;
pde = &pgdir[PDX(va)];
if (*pde & PTE_P) {
pgtab = (pte_t*)P2V(PTE_ADDR(*pde));
}
else {
if (!alloc || (pgtab = (pte_t*)kalloc()) == 0) {
return 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
// entries, if necessary.
*pde = V2P(pgtab) | PTE_P | PTE_W | PTE_U;
}
return &pgtab[PTX(va)];
}
// Create PTEs for virtual addresses starting at va that refer to
// physical addresses starting at pa. va and size might not
// be page-aligned.
static int mappages(pde_t *pgdir, void *va, uint size, uint pa, int perm) {
char *a, *last;
pte_t *pte;
a = (char*)PGROUNDDOWN((uint)va);
last = (char*)PGROUNDDOWN(((uint)va) + size - 1);
for (;;) {
if ((pte = walkpgdir(pgdir, a, 1)) == 0) {
return -1;
}
if (*pte & PTE_P) {
panic("remap");
}
*pte = pa | perm | PTE_P;
if (a == last) {
break;
}
a += PGSIZE;
pa += PGSIZE;
}
return 0;
}
// There is one page table per process, plus one that's used when
// a CPU is not running any process (kpgdir). The kernel uses the
// 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
// phys memory allocated by the kernel
// 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,
// rw data + free physical memory
// 0xfe000000..0: mapped direct (devices such as ioapic)
//
// The kernel allocates physical memory for its heap and for user memory
// between V2P(end) and the end of physical memory (PHYSTOP)
// (directly addressable from end..P2V(PHYSTOP)).
// This table defines the kernel's mappings, which are present in
// every process's page table.
static struct kmap {
void *virt;
uint phys_start;
uint phys_end;
int perm;
} kmap[] = {
{ (void*)KERNBASE, 0, EXTMEM, PTE_W}, // I/O space
{ (void*)KERNLINK, V2P(KERNLINK), V2P(data), 0}, // kern text+rodata
{ (void*)data, V2P(data), PHYSTOP, PTE_W}, // kern data+memory
{ (void*)DEVSPACE, DEVSPACE, 0, PTE_W}, // more devices
};
// Set up kernel part of a page table.
pde_t*setupkvm(void) {
pde_t *pgdir;
struct kmap *k;
if ((pgdir = (pde_t*)kalloc()) == 0) {
return 0;
}
memset(pgdir, 0, PGSIZE);
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,
(uint)k->phys_start, k->perm) < 0) {
freevm(pgdir);
return 0;
}
}
return pgdir;
}
// Allocate one page table for the machine for the kernel address
// space for scheduler processes.
void kvmalloc(void) {
kpgdir = setupkvm();
switchkvm();
}
// Switch h/w page table register to the kernel-only page table,
// for when no process is running.
void switchkvm(void) {
lcr3(V2P(kpgdir)); // switch to the kernel page table
}
// Switch TSS and h/w page table to correspond to process p.
void switchuvm(struct proc *p) {
if (p == 0) {
panic("switchuvm: no process");
}
if (p->kstack == 0) {
panic("switchuvm: no kstack");
}
if (p->pgdir == 0) {
panic("switchuvm: no pgdir");
}
pushcli();
mycpu()->gdt[SEG_TSS] = SEG16(STS_T32A, &mycpu()->ts,
sizeof(mycpu()->ts) - 1, 0);
mycpu()->gdt[SEG_TSS].s = 0;
mycpu()->ts.ss0 = SEG_KDATA << 3;
mycpu()->ts.esp0 = (uint)p->kstack + KSTACKSIZE;
// setting IOPL=0 in eflags *and* iomb beyond the tss segment limit
// forbids I/O instructions (e.g., inb and outb) from user space
mycpu()->ts.iomb = (ushort) 0xFFFF;
ltr(SEG_TSS << 3);
lcr3(V2P(p->pgdir)); // switch to process's address space
popcli();
}
// Load the initcode into address 0 of pgdir.
// sz must be less than a page.
void inituvm(pde_t *pgdir, char *init, uint sz) {
char *mem;
if (sz >= PGSIZE) {
panic("inituvm: more than a page");
}
mem = kalloc();
memset(mem, 0, PGSIZE);
mappages(pgdir, 0, PGSIZE, V2P(mem), PTE_W | PTE_U);
memmove(mem, init, sz);
}
// Load a program segment into pgdir. addr must be page-aligned
// and the pages from addr to addr+sz must already be mapped.
int loaduvm(pde_t *pgdir, char *addr, struct inode *ip, uint offset, uint sz) {
uint i, pa, n;
pte_t *pte;
if ((uint) addr % PGSIZE != 0) {
panic("loaduvm: addr must be page aligned");
}
for (i = 0; i < sz; i += PGSIZE) {
if ((pte = walkpgdir(pgdir, addr + i, 0)) == 0) {
panic("loaduvm: address should exist");
}
pa = PTE_ADDR(*pte);
if (sz - i < PGSIZE) {
n = sz - i;
}
else {
n = PGSIZE;
}
if (readi(ip, P2V(pa), offset + i, n) != n) {
return -1;
}
}
return 0;
}
// Allocate page tables and physical memory to grow process from oldsz to
// newsz, which need not be page aligned. Returns new size or 0 on error.
int allocuvm(pde_t *pgdir, uint oldsz, uint newsz) {
char *mem;
uint a;
if (newsz >= KERNBASE) {
return 0;
}
if (newsz < oldsz) {
return oldsz;
}
a = PGROUNDUP(oldsz);
for (; a < newsz; a += PGSIZE) {
mem = kalloc();
if (mem == 0) {
cprintf("allocuvm out of memory\n");
deallocuvm(pgdir, newsz, oldsz);
return 0;
}
memset(mem, 0, PGSIZE);
if (mappages(pgdir, (char*)a, PGSIZE, V2P(mem), PTE_W | PTE_U) < 0) {
cprintf("allocuvm out of memory (2)\n");
deallocuvm(pgdir, newsz, oldsz);
kfree(mem);
return 0;
}
}
return newsz;
}
// Deallocate user pages to bring the process size from oldsz to
// newsz. oldsz and newsz need not be page-aligned, nor does newsz
// need to be less than oldsz. oldsz can be larger than the actual
// process size. Returns the new process size.
int deallocuvm(pde_t *pgdir, uint oldsz, uint newsz) {
pte_t *pte;
uint a, pa;
if (newsz >= oldsz) {
return oldsz;
}
a = PGROUNDUP(newsz);
for (; a < oldsz; a += PGSIZE) {
pte = walkpgdir(pgdir, (char*)a, 0);
if (!pte) {
a = PGADDR(PDX(a) + 1, 0, 0) - PGSIZE;
}
else if ((*pte & PTE_P) != 0) {
pa = PTE_ADDR(*pte);
if (pa == 0) {
panic("kfree");
}
char *v = P2V(pa);
kfree(v);
*pte = 0;
}
}
return newsz;
}
// Free a page table and all the physical memory pages
// in the user part.
void freevm(pde_t *pgdir) {
uint i;
if (pgdir == 0) {
panic("freevm: no pgdir");
}
deallocuvm(pgdir, KERNBASE, 0);
for (i = 0; i < NPDENTRIES; i++) {
if (pgdir[i] & PTE_P) {
char * v = P2V(PTE_ADDR(pgdir[i]));
kfree(v);
}
}
kfree((char*)pgdir);
}
// Clear PTE_U on a page. Used to create an inaccessible
// page beneath the user stack.
void clearpteu(pde_t *pgdir, char *uva) {
pte_t *pte;
pte = walkpgdir(pgdir, uva, 0);
if (pte == 0) {
panic("clearpteu");
}
*pte &= ~PTE_U;
}
// Given a parent process's page table, create a copy
// of it for a child.
pde_t* copyuvm(pde_t *pgdir, uint sz) {
pde_t *d;
pte_t *pte;
uint pa, i, flags;
char *mem;
if ((d = setupkvm()) == 0) {
return 0;
}
for (i = 0; i < sz; i += PGSIZE) {
if ((pte = walkpgdir(pgdir, (void *) i, 0)) == 0) {
panic("copyuvm: pte should exist");
}
if (!(*pte & PTE_P)) {
panic("copyuvm: page not present");
}
pa = PTE_ADDR(*pte);
flags = PTE_FLAGS(*pte);
if ((mem = kalloc()) == 0) {
freevm(d);
return 0;
}
memmove(mem, (char*)P2V(pa), PGSIZE);
if (mappages(d, (void*)i, PGSIZE, V2P(mem), flags) < 0) {
kfree(mem);
freevm(d);
return 0;
}
}
return d;
}
// Map user virtual address to kernel address.
char*uva2ka(pde_t *pgdir, char *uva) {
pte_t *pte;
pte = walkpgdir(pgdir, uva, 0);
if ((*pte & PTE_P) == 0) {
return 0;
}
if ((*pte & PTE_U) == 0) {
return 0;
}
return (char*)P2V(PTE_ADDR(*pte));
}
// Copy len bytes from p to user address va in page table pgdir.
// Most useful when pgdir is not the current page table.
// uva2ka ensures this only works for PTE_U pages.
int copyout(pde_t *pgdir, uint va, void *p, uint len) {
char *buf, *pa0;
uint n, va0;
buf = (char*)p;
while (len > 0) {
va0 = (uint)PGROUNDDOWN(va);
pa0 = uva2ka(pgdir, (char*)va0);
if (pa0 == 0) {
return -1;
}
n = PGSIZE - (va - va0);
if (n > len) {
n = len;
}
memmove(pa0 + (va - va0), buf, n);
len -= n;
buf += n;
va = va0 + PGSIZE;
}
return 0;
}

52
wc.c Normal file
View File

@ -0,0 +1,52 @@
#include "types.h"
#include "stat.h"
#include "user.h"
char buf[512];
void wc(int fd, char *name) {
int i, n;
int l, w, c, inword;
l = w = c = 0;
inword = 0;
while ((n = read(fd, buf, sizeof(buf))) > 0) {
for (i = 0; i < n; i++) {
c++;
if (buf[i] == '\n') {
l++;
}
if (strchr(" \r\t\n\v", buf[i])) {
inword = 0;
}
else if (!inword) {
w++;
inword = 1;
}
}
}
if (n < 0) {
printf(1, "wc: read error\n");
exit();
}
printf(1, "%d %d %d %s\n", l, w, c, name);
}
int main(int argc, char *argv[]) {
int fd, i;
if (argc <= 1) {
wc(0, "");
exit();
}
for (i = 1; i < argc; i++) {
if ((fd = open(argv[i], 0)) < 0) {
printf(1, "wc: cannot open %s\n", argv[i]);
exit();
}
wc(fd, argv[i]);
close(fd);
}
exit();
}

148
x86.h Normal file
View File

@ -0,0 +1,148 @@
// 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));
}
// 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;
};

13
zombie.c Normal file
View File

@ -0,0 +1,13 @@
// Create a zombie process that
// must be reparented at exit.
#include "types.h"
#include "stat.h"
#include "user.h"
int main(int argc, char* argv[]) {
if (fork() > 0) {
sleep(5); // Let child exit before parent.
}
exit();
}