├── .dir-locals.el ├── .gitignore ├── BUGS ├── LICENSE ├── Makefile ├── Notes ├── README ├── README.64BIT ├── README.CLS ├── README.orig ├── README.syslinux ├── TRICKS ├── include ├── acpi.h ├── asm.h ├── buf.h ├── defs.h ├── elf.h ├── fcntl.h ├── file.h ├── fs.h ├── kbd.h ├── memlayout.h ├── mmu.h ├── mp.h ├── param.h ├── proc.h ├── spinlock.h ├── stat.h ├── symlink.patch ├── syscall.h ├── traps.h ├── types.h ├── user.h └── x86.h ├── kernel ├── acpi.c ├── bio.c ├── bootasm.S ├── bootmain.c ├── console.c ├── entry.S ├── entry64.S ├── entryother.S ├── exec.c ├── file.c ├── fs.c ├── ide.c ├── initcode.S ├── initcode64.S ├── ioapic.c ├── kalloc.c ├── kbd.c ├── kernel.ld ├── kernel64.ld ├── lapic.c ├── log.c ├── main.c ├── memide.c ├── mp.c ├── picirq.c ├── pipe.c ├── proc.c ├── spinlock.c ├── string.c ├── swtch.S ├── swtch64.S ├── syscall.c ├── sysfile.c ├── sysproc.c ├── timer.c ├── trap.c ├── trapasm.S ├── trapasm64.S ├── uart.c ├── vm.c └── vm64.c ├── tools ├── dot-bochsrc ├── gdbinit.tmpl ├── gdbutil ├── mkfs.c ├── printpcs ├── sign.pl ├── sleep1.p ├── spinp ├── vectors.pl └── vectors64.pl ├── ulib ├── printf.c ├── ulib.c ├── umalloc.c └── usys.S └── user ├── cat.c ├── echo.c ├── forktest.c ├── grep.c ├── init.c ├── kill.c ├── ln.c ├── ls.c ├── mkdir.c ├── rm.c ├── sh.c ├── stressfs.c ├── usertests.c ├── wc.c └── zombie.c /.dir-locals.el: -------------------------------------------------------------------------------- 1 | ((c-mode 2 | (indent-tabs-mode . nil) 3 | (c-file-style . "bsd") 4 | (c-basic-offset . 2))) 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | _* 3 | .*.swp 4 | *.o 5 | *.d 6 | *.asm 7 | *.sym 8 | *.img 9 | vectors.S 10 | mkfs 11 | .gdbinit 12 | out 13 | fs 14 | -------------------------------------------------------------------------------- /BUGS: -------------------------------------------------------------------------------- 1 | formatting: 2 | need to fix PAGEBREAK mechanism 3 | 4 | sh: 5 | can't always runcmd in child -- breaks cd. 6 | maybe should hard-code PATH=/ ? 7 | 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The xv6 software is: 2 | 3 | Copyright (c) 2006-2009 Frans Kaashoek, Robert Morris, Russ Cox, 4 | Massachusetts Institute of Technology 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | -include local.mk 2 | 3 | X64 ?= yes 4 | 5 | ifeq ("$(X64)","yes") 6 | BITS = 64 7 | XOBJS = kobj/vm64.o 8 | XFLAGS = -m64 -DX64 -mcmodel=kernel -mtls-direct-seg-refs -mno-red-zone 9 | LDFLAGS = -m elf_x86_64 -nodefaultlibs 10 | QEMU ?= qemu-system-x86_64 11 | else 12 | XFLAGS = -m32 13 | LDFLAGS = -m elf_i386 -nodefaultlibs 14 | QEMU ?= qemu-system-i386 15 | endif 16 | 17 | OPT ?= -O0 18 | 19 | OBJS := \ 20 | kobj/bio.o\ 21 | kobj/console.o\ 22 | kobj/exec.o\ 23 | kobj/file.o\ 24 | kobj/fs.o\ 25 | kobj/ide.o\ 26 | kobj/ioapic.o\ 27 | kobj/kalloc.o\ 28 | kobj/kbd.o\ 29 | kobj/lapic.o\ 30 | kobj/log.o\ 31 | kobj/main.o\ 32 | kobj/mp.o\ 33 | kobj/acpi.o\ 34 | kobj/picirq.o\ 35 | kobj/pipe.o\ 36 | kobj/proc.o\ 37 | kobj/spinlock.o\ 38 | kobj/string.o\ 39 | kobj/swtch$(BITS).o\ 40 | kobj/syscall.o\ 41 | kobj/sysfile.o\ 42 | kobj/sysproc.o\ 43 | kobj/timer.o\ 44 | kobj/trapasm$(BITS).o\ 45 | kobj/trap.o\ 46 | kobj/uart.o\ 47 | kobj/vectors.o\ 48 | kobj/vm.o\ 49 | $(XOBJS) 50 | 51 | ifneq ("$(MEMFS)","") 52 | # build filesystem image in to kernel and use memory-ide-device 53 | # instead of mounting the filesystem on ide1 54 | OBJS := $(filter-out kobj/ide.o,$(OBJS)) kobj/memide.o 55 | FSIMAGE := fs.img 56 | endif 57 | 58 | # Cross-compiling (e.g., on Mac OS X) 59 | #TOOLPREFIX = i386-jos-elf- 60 | 61 | # Using native tools (e.g., on X86 Linux) 62 | #TOOLPREFIX = 63 | 64 | CC = $(TOOLPREFIX)gcc 65 | AS = $(TOOLPREFIX)gas 66 | LD = $(TOOLPREFIX)ld 67 | OBJCOPY = $(TOOLPREFIX)objcopy 68 | OBJDUMP = $(TOOLPREFIX)objdump 69 | CFLAGS = -fno-pic -static -fno-builtin -fno-strict-aliasing -Wall -MD -ggdb -fno-omit-frame-pointer 70 | CFLAGS += -ffreestanding -fno-common -nostdlib -Iinclude -gdwarf-2 $(XFLAGS) $(OPT) 71 | CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector) 72 | ASFLAGS = -fno-pic -gdwarf-2 -Wa,-divide -Iinclude $(XFLAGS) 73 | 74 | xv6.img: out/bootblock out/kernel.elf fs.img 75 | dd if=/dev/zero of=xv6.img count=10000 76 | dd if=out/bootblock of=xv6.img conv=notrunc 77 | dd if=out/kernel.elf of=xv6.img seek=1 conv=notrunc 78 | 79 | xv6memfs.img: out/bootblock out/kernelmemfs.elf 80 | dd if=/dev/zero of=xv6memfs.img count=10000 81 | dd if=out/bootblock of=xv6memfs.img conv=notrunc 82 | dd if=out/kernelmemfs.elf of=xv6memfs.img seek=1 conv=notrunc 83 | 84 | # kernel object files 85 | kobj/%.o: kernel/%.c 86 | @mkdir -p kobj 87 | $(CC) $(CFLAGS) -c -o $@ $< 88 | 89 | kobj/%.o: kernel/%.S 90 | @mkdir -p kobj 91 | $(CC) $(ASFLAGS) -c -o $@ $< 92 | 93 | # userspace object files 94 | uobj/%.o: user/%.c 95 | @mkdir -p uobj 96 | $(CC) $(CFLAGS) -c -o $@ $< 97 | 98 | uobj/%.o: ulib/%.c 99 | @mkdir -p uobj 100 | $(CC) $(CFLAGS) -c -o $@ $< 101 | 102 | uobj/%.o: ulib/%.S 103 | @mkdir -p uobj 104 | $(CC) $(ASFLAGS) -c -o $@ $< 105 | 106 | out/bootblock: kernel/bootasm.S kernel/bootmain.c 107 | @mkdir -p out 108 | $(CC) -fno-builtin -fno-pic -m32 -nostdinc -Iinclude -O -o out/bootmain.o -c kernel/bootmain.c 109 | $(CC) -fno-builtin -fno-pic -m32 -nostdinc -Iinclude -o out/bootasm.o -c kernel/bootasm.S 110 | $(LD) -m elf_i386 -nodefaultlibs -N -e start -Ttext 0x7C00 -o out/bootblock.o out/bootasm.o out/bootmain.o 111 | $(OBJDUMP) -S out/bootblock.o > out/bootblock.asm 112 | $(OBJCOPY) -S -O binary -j .text out/bootblock.o out/bootblock 113 | tools/sign.pl out/bootblock 114 | 115 | out/entryother: kernel/entryother.S 116 | @mkdir -p out 117 | $(CC) $(CFLAGS) -fno-pic -nostdinc -I. -o out/entryother.o -c kernel/entryother.S 118 | $(LD) $(LDFLAGS) -N -e start -Ttext 0x7000 -o out/bootblockother.o out/entryother.o 119 | $(OBJCOPY) -S -O binary -j .text out/bootblockother.o out/entryother 120 | $(OBJDUMP) -S out/bootblockother.o > out/entryother.asm 121 | 122 | INITCODESRC = kernel/initcode$(BITS).S 123 | out/initcode: $(INITCODESRC) 124 | @mkdir -p out 125 | $(CC) $(CFLAGS) -nostdinc -I. -o out/initcode.o -c $(INITCODESRC) 126 | $(LD) $(LDFLAGS) -N -e start -Ttext 0 -o out/initcode.out out/initcode.o 127 | $(OBJCOPY) -S -O binary out/initcode.out out/initcode 128 | $(OBJDUMP) -S out/initcode.o > out/initcode.asm 129 | 130 | ENTRYCODE = kobj/entry$(BITS).o 131 | LINKSCRIPT = kernel/kernel$(BITS).ld 132 | out/kernel.elf: $(OBJS) $(ENTRYCODE) out/entryother out/initcode $(LINKSCRIPT) $(FSIMAGE) 133 | $(LD) $(LDFLAGS) -T $(LINKSCRIPT) -o out/kernel.elf $(ENTRYCODE) $(OBJS) -b binary out/initcode out/entryother $(FSIMAGE) 134 | $(OBJDUMP) -S out/kernel.elf > out/kernel.asm 135 | $(OBJDUMP) -t out/kernel.elf | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > out/kernel.sym 136 | 137 | MKVECTORS = tools/vectors$(BITS).pl 138 | kernel/vectors.S: $(MKVECTORS) 139 | perl $(MKVECTORS) > kernel/vectors.S 140 | 141 | ULIB = uobj/ulib.o uobj/usys.o uobj/printf.o uobj/umalloc.o 142 | 143 | fs/%: uobj/%.o $(ULIB) 144 | @mkdir -p fs out 145 | $(LD) $(LDFLAGS) -N -e main -Ttext 0 -o $@ $^ 146 | $(OBJDUMP) -S $@ > out/$*.asm 147 | $(OBJDUMP) -t $@ | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > out/$*.sym 148 | 149 | fs/forktest: uobj/forktest.o $(ULIB) 150 | @mkdir -p fs 151 | # forktest has less library code linked in - needs to be small 152 | # in order to be able to max out the proc table. 153 | $(LD) $(LDFLAGS) -N -e main -Ttext 0 -o fs/forktest uobj/forktest.o uobj/ulib.o uobj/usys.o 154 | $(OBJDUMP) -S fs/forktest > out/forktest.asm 155 | 156 | out/mkfs: tools/mkfs.c include/fs.h 157 | gcc -Werror -Wall -o out/mkfs tools/mkfs.c 158 | 159 | # Prevent deletion of intermediate files, e.g. cat.o, after first build, so 160 | # that disk image changes after first build are persistent until clean. More 161 | # details: 162 | # http://www.gnu.org/software/make/manual/html_node/Chained-Rules.html 163 | .PRECIOUS: uobj/%.o 164 | 165 | UPROGS=\ 166 | fs/cat\ 167 | fs/echo\ 168 | fs/forktest\ 169 | fs/grep\ 170 | fs/init\ 171 | fs/kill\ 172 | fs/ln\ 173 | fs/ls\ 174 | fs/mkdir\ 175 | fs/rm\ 176 | fs/sh\ 177 | fs/stressfs\ 178 | fs/usertests\ 179 | fs/wc\ 180 | fs/zombie\ 181 | 182 | fs/README: README 183 | @mkdir -p fs 184 | cp README fs/README 185 | 186 | fs.img: out/mkfs README $(UPROGS) 187 | out/mkfs fs.img README $(UPROGS) 188 | 189 | -include */*.d 190 | 191 | clean: 192 | rm -rf out fs uobj kobj 193 | rm -f kernel/vectors.S xv6.img xv6memfs.img fs.img .gdbinit 194 | 195 | # run in emulators 196 | 197 | bochs : fs.img xv6.img 198 | if [ ! -e .bochsrc ]; then ln -s tools/dot-bochsrc .bochsrc; fi 199 | bochs -q 200 | 201 | # try to generate a unique GDB port 202 | GDBPORT = $(shell expr `id -u` % 5000 + 25000) 203 | # QEMU's gdb stub command line changed in 0.11 204 | QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \ 205 | then echo "-gdb tcp::$(GDBPORT)"; \ 206 | else echo "-s -p $(GDBPORT)"; fi) 207 | ifndef CPUS 208 | CPUS := 2 209 | endif 210 | QEMUOPTS = -net none -hda xv6.img -hdb fs.img -smp $(CPUS) -m 512 $(QEMUEXTRA) 211 | 212 | qemu: fs.img xv6.img 213 | $(QEMU) -serial mon:stdio $(QEMUOPTS) 214 | 215 | qemu-memfs: xv6memfs.img 216 | $(QEMU) xv6memfs.img -smp $(CPUS) 217 | 218 | qemu-nox: fs.img xv6.img 219 | $(QEMU) -nographic $(QEMUOPTS) 220 | 221 | .gdbinit: tools/gdbinit.tmpl 222 | sed "s/localhost:1234/localhost:$(GDBPORT)/" < $^ > $@ 223 | 224 | qemu-gdb: fs.img xv6.img .gdbinit 225 | @echo "*** Now run 'gdb'." 1>&2 226 | $(QEMU) -serial mon:stdio $(QEMUOPTS) -S $(QEMUGDB) 227 | 228 | qemu-nox-gdb: fs.img xv6.img .gdbinit 229 | @echo "*** Now run 'gdb'." 1>&2 230 | $(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB) 231 | 232 | -------------------------------------------------------------------------------- /Notes: -------------------------------------------------------------------------------- 1 | bochs 2.2.6: 2 | ./configure --enable-smp --enable-disasm --enable-debugger --enable-all-optimizations --enable-4meg-pages --enable-global-pages --enable-pae --disable-reset-on-triple-fault 3 | bochs CVS after 2.2.6: 4 | ./configure --enable-smp --enable-disasm --enable-debugger --enable-all-optimizations --enable-4meg-pages --enable-global-pages --enable-pae 5 | 6 | bootmain.c doesn't work right if the ELF sections aren't 7 | sector-aligned. so you can't use ld -N. and the sections may also need 8 | to be non-zero length, only really matters for tiny "kernels". 9 | 10 | kernel loaded at 1 megabyte. stack same place that bootasm.S left it. 11 | 12 | kinit() should find real mem size 13 | and rescue useable memory below 1 meg 14 | 15 | no paging, no use of page table hardware, just segments 16 | 17 | no user area: no magic kernel stack mapping 18 | so no copying of kernel stack during fork 19 | though there is a kernel stack page for each process 20 | 21 | no kernel malloc(), just kalloc() for user core 22 | 23 | user pointers aren't valid in the kernel 24 | 25 | are interrupts turned on in the kernel? yes. 26 | 27 | pass curproc explicitly, or implicit from cpu #? 28 | e.g. argument to newproc()? 29 | hmm, you need a global curproc[cpu] for trap() &c 30 | 31 | no stack expansion 32 | 33 | test running out of memory, process slots 34 | 35 | we can't really use a separate stack segment, since stack addresses 36 | need to work correctly as ordinary pointers. the same may be true of 37 | data vs text. how can we have a gap between data and stack, so that 38 | both can grow, without committing 4GB of physical memory? does this 39 | mean we need paging? 40 | 41 | perhaps have fixed-size stack, put it in the data segment? 42 | 43 | oops, if kernel stack is in contiguous user phys mem, then moving 44 | users' memory (e.g. to expand it) will wreck any pointers into the 45 | kernel stack. 46 | 47 | do we need to set fs and gs? so user processes can't abuse them? 48 | 49 | setupsegs() may modify current segment table, is that legal? 50 | 51 | trap() ought to lgdt on return, since currently only done in swtch() 52 | 53 | protect hardware interrupt vectors from user INT instructions? 54 | 55 | test out-of-fd cases for creating pipe. 56 | test pipe reader closes then write 57 | test two readers, two writers. 58 | test children being inherited by grandparent &c 59 | 60 | some sleep()s should be interruptible by kill() 61 | 62 | locks 63 | init_lock 64 | sequences CPU startup 65 | proc_table_lock 66 | also protects next_pid 67 | per-fd lock *just* protects count read-modify-write 68 | also maybe freeness? 69 | memory allocator 70 | printf 71 | 72 | in general, the table locks protect both free-ness and 73 | public variables of table elements 74 | in many cases you can use table elements w/o a lock 75 | e.g. if you are the process, or you are using an fd 76 | 77 | lock order 78 | per-pipe lock 79 | proc_table_lock fd_table_lock kalloc_lock 80 | console_lock 81 | 82 | do you have to be holding the mutex in order to call wakeup()? yes 83 | 84 | device interrupts don't clear FL_IF 85 | so a recursive timer interrupt is possible 86 | 87 | what does inode->busy mean? 88 | might be held across disk reads 89 | no-one is allowed to do anything to the inode 90 | protected by inode_table_lock 91 | inode->count counts in-memory pointers to the struct 92 | prevents inode[] element from being re-used 93 | protected by inode_table_lock 94 | 95 | blocks and inodes have ad-hoc sleep-locks 96 | provide a single mechanism? 97 | 98 | kalloc() can return 0; do callers handle this right? 99 | 100 | test: one process unlinks a file while another links to it 101 | test: one process opens a file while another deletes it 102 | test: deadlock d/.. vs ../d, two processes. 103 | test: dup() shared fd->off 104 | test: does echo foo > x truncate x? 105 | 106 | sh: ioredirection incorrect now we have pipes 107 | sh: chain of pipes won't work, also ugly that parent closes fdarray entries too 108 | sh: dynamic memory allocation? 109 | sh: should sh support ; () & 110 | sh: stop stdin on ctrl-d (for cat > y) 111 | 112 | really should have bdwrite() for file content 113 | and make some inode updates async 114 | so soft updates make sense 115 | 116 | disk scheduling 117 | echo foo > bar should truncate bar 118 | so O_CREATE should not truncate 119 | but O_TRUNC should 120 | 121 | make it work on a real machine 122 | release before acquire at end of sleep? 123 | check 2nd disk (i.e. if not in .bochsrc) 124 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | 2 | This is a port of xv6 to the x86-64 platform (from i386), attempting to 3 | be as minimally intrusive as possible (beyond some initial reorganization 4 | of the source code layout and build system). 5 | 6 | "xv6 is a re-implementation of Dennis Ritchie's and Ken Thompson's Unix 7 | Version 6 (v6). xv6 loosely follows the structure and style of v6, 8 | but is implemented for a modern x86-based multiprocessor using ANSI C." 9 | 10 | The original upstream branch (mirror) was pulled from the git repository 11 | https://github.com/mit-pdos/xv6-public 12 | (previously git://pdos.csail.mit.edu/xv6/xv6.git) 13 | 14 | The original project homepage is here: 15 | https://pdos.csail.mit.edu/6.828/2014/xv6.html 16 | (previously http://pdos.csail.mit.edu/6.828/2012/xv6.html) 17 | 18 | The PDF books about the rev7 version of xv6 are checked into the books branch 19 | of this repository for easy access. They come from the xv6 project's home site. 20 | 21 | The original README remains as README.orig -- renamed to avoid misrepresenting 22 | this working branch and any local changes as the original project and to 23 | hopefully avoid people pestering the original authors about bugs I may introduce 24 | while poking at their code. 25 | 26 | . . . 27 | 28 | The code has been organized into kernel/, include/, user/, ulib/, and tools/ 29 | subdirectories, and arranged so that the build puts intermediate object files 30 | into kobj/ and uobj/ directories, other intermediates into out/, and the 31 | filesystem contents into fs/. 32 | 33 | The Makefile defaults to building the 32bit version of xv6. Define the X64 34 | environment variable to build the 64bit version. 35 | 36 | README.64BIT contains notes about the 64bit port. 37 | -------------------------------------------------------------------------------- /README.64BIT: -------------------------------------------------------------------------------- 1 | 2 | While porting Xv6 to 64bit Intel/AMD platforms, the goal is to keep changes 3 | to the codebase as non-intrusive as possible, leaving it as short, clean, 4 | and readable as it was in the original 32bit version. When possible, 5 | #ifdefs will be avoided, and the existing style will be maintained. Also, 6 | it should continue to be possible to build, boot, and run Xv6 on 32bit 7 | platforms from the same codebase. 8 | 9 | 10 | DONE 11 | * Quick hack-and-slash modifications to get a buildable 64bit kernel.elf 12 | * stubbed out or quickly patched up assembly glue where it didn't build 13 | * temporarily turned off int-to-pointer and pointer-to-int warnings 14 | to make finding build breakages faster 15 | * chase down link errors due to too-large relocations 16 | * switch to cross-x86-64 compiler to avoid weirdness from host compiler 17 | * https://github.com/travisg/toolchains 18 | * make printf() and cprintf() use stdarg.h instead of rolling their own 19 | * x86-64's calling conventions for var-args are more complex 20 | * notably the first six arguments may be passed in registers 21 | * implement 32bit-to-64bit kernel entry glue (entry64.S) 22 | * qemu cannot load 64bit ELF kernels 23 | * sounds like most linux bootloaders don't support that yet either 24 | * implement a 32bit multiboot header and shim 25 | * setup an initial identity-mapped and kernel-address-mapped 26 | pagetable since you cannot enter 64bit mode without paging on 27 | * adjust syscall assembly for 64bit 28 | * implement 64bit task switch glue 29 | * introduce new type (currently 'uintp' for unsigned integer the size 30 | of a pointer) to use in all the places where Xv6 assumes pointers and 31 | integers are interchangeable. 32 | * almost all warnings squashed 33 | * consider a better name. addr_t? 34 | * implement 64bit GDT and TSS setup 35 | * implement 64bit interrupt support and IDT setup 36 | * adjust process entry code for 64bit 37 | * 64bit pointers and ABI (pass args in regs) 38 | * implement 64bit mmu support (vm64.c) 39 | * verify usermode works 40 | * turn back on pre-emption 41 | * verify usertests work 42 | * update boot sector to support multiboot extended header 43 | * the ELF header no longer handles the entrypoint 44 | * get 32bit build working again 45 | * tidy up exec() for 64bit (argument passing, stack alignment) 46 | * test on real hardware 47 | * enable SMP and verify it 48 | * tidy up vm64.c 49 | * fix procdump for 64bit mode 50 | * figure out how to enumerate CPUs without BIOS MP table 51 | * had to implement minimal ACPI table parsing 52 | 53 | IN PROGRESS 54 | 55 | TODO 56 | * detect actual memory size, make PHYSTOP dynamic 57 | * handle >2GB ram 58 | * will have to move the linear map region below the kernel 59 | since we can only map 2GB from the kernel base to end of vram 60 | * fix validateint() of usertests for 64bit 61 | 62 | THINGS TO FIX LATER / NICE TO HAVE 63 | * I wish qemu had a "halt-on-exception" mode. Would help early bringup 64 | * gdb pukes when qemu switches from 32bit to 64bit mode 65 | * this made debugging the mode change entertaining 66 | * for now attach gdb after the switch 67 | * move userspace entry code to ulib 68 | 69 | LESSONS LEARNED / UNRESOLVED 70 | * using 1GB pages in the initial page table did not work 71 | * In qemu you can do cheesy debugging by writing bytes to port 0x3f8 72 | (the first UART) without any normal UART setup. 73 | * Xv6:32 creates kernel mappings (separate copies) in every process's 74 | page table, roughly 70k pages worth, consuming about 280MB of ram 75 | for page tables. 76 | * Xv6:32's trick for cpu-local storage does not work on gcc x86-64 77 | - see README.CLS for an exploration of alternate options 78 | * Not sure why you're double/triple-faulting? 79 | drop some instrumentation code in qemu/target-i386/seg_helper.c's 80 | do_interrupt64() -- this is where all the validation on irq/trap 81 | entry happens 82 | 83 | IMPLEMENTATION NOTES 84 | * use "kernel" addressing mode for kernel compilation 85 | * kernel virtual address space 0xFFFFFFFF80000000:0xFFFFFFFFFF000000 86 | * linear-map first 1GB of RAM to kernel virtual address space 87 | * linear-map first 1GB of RAM 1:1 during boot 88 | * x64 has 4 levels of page tables instead of just 2 89 | * continue to return a 2nd-level page table from setupkvm() 90 | but actually create 4th, 3rd, and 2nd level tables 91 | * use the top two entries of the 2nd level table for backpointers 92 | to the 4th and 3rd level tables so that switchkvm(), switchuvm(), 93 | and freevm() can get at these when needed 94 | * use the top entry of the 4th level table to point to a shared 95 | (among all processes) 3rd level table which handles the kernel 96 | memory mapping (which is the same everywhere) 97 | * userland will have 4GB - 8KB address space 98 | 99 | -------------------------------------------------------------------------------- /README.CLS: -------------------------------------------------------------------------------- 1 | 2 | CPU LOCAL STORAGE 3 | 4 | Basically a similar concept to thread local storage, but in Xv6's case 5 | these are per-cpu, not per-thread. 6 | 7 | GLOBAL REGISTER VARIABLES 8 | ------------------------- 9 | 10 | Xv6:32 uses this gcc trick to generate GS: relative access to a few 11 | globals for cpu-local-storage: 12 | 13 | extern struct cpu *cpu asm("%gs:0); 14 | 15 | Sadly this does not work on x86-64, instead generating a pc-relative 16 | load and various unhappiness results. In this case and the other 17 | options I explored, I took a look at a chunk of code generated by 18 | a common expression using a structure from cpu local storage: 19 | 20 | if (proc->killed) ... 21 | 22 | with asm("%gs:4") on i386 23 | 24 | : 65 a1 04 00 00 00 mov %gs:0x4,%eax 25 | : 8b 40 24 mov 0x24(%eax),%eax 26 | : 85 c0 test %eax,%eax 27 | 28 | with asm("%gs:8") on x86-64 29 | 30 | : 65 48 8b 05 04 00 00 mov %gs:0x8(%rip),%rax 31 | : 00 32 | : 8b 40 50 mov 0x50(%rax),%eax 33 | : 85 c0 test %eax,%eax 34 | 35 | This results in rax = [ gs + rip + 8 ] which is never what we want... 36 | 37 | With -O1, in both cases the mov and test are combined into something like 38 | 39 | : 65 a1 04 00 00 00 mov %gs:0x4,%eax 40 | : 83 78 24 00 cmpl $0x0,0x24(%eax) 41 | 42 | 43 | __THREAD MODIFIER 44 | ----------------- 45 | 46 | gcc supports a construct for thread-local variables: 47 | 48 | extern __thread struct cpu *cpu; 49 | 50 | with __thread and -mtls-direct-seg-refs on i386 51 | 52 | : 48 c7 c0 f8 ff ff ff mov $0xfffffffffffffff8,%rax 53 | : 64 48 8b 00 mov %fs:(%rax),%rax 54 | : 8b 40 50 mov 0x50(%rax),%eax 55 | : 85 c0 test %eax,%eax 56 | 57 | with __thread and -mtls-direct-seg-refs on x86-64 58 | 59 | : b8 fc ff ff ff mov $0xfffffffc,%eax 60 | : 65 8b 00 mov %gs:(%eax),%eax 61 | : 8b 40 24 mov 0x24(%eax),%eax 62 | : 85 c0 test %eax,%eax 63 | 64 | The choice of segment (fs or gs) seems to be baked into gcc and 65 | is chosen based on 32bit or 64bit compilation mode. 66 | 67 | The linker generates an TLS section: 68 | 69 | Type Offset VirtAddr PhysAddr 70 | FileSiz MemSiz Flags Align 71 | LOAD 0x0000000000001000 0xffffffff80100000 0x0000000000100000 72 | 0x000000000000da00 0x0000000000016778 RWE 1000 73 | TLS 0x000000000000ea00 0xffffffff8010da00 0x000000000010da00 74 | 0x0000000000000000 0x0000000000000010 R 8 75 | and TLS symbols: 76 | 77 | 168: 0000000000000008 8 TLS GLOBAL DEFAULT 5 proc 78 | 233: 0000000000000000 8 TLS GLOBAL DEFAULT 5 cpu 79 | 80 | In this model I just point fs (or gs) at the top of a page of local 81 | storage space I allocate (since I only have a handful of local items 82 | to track). 83 | 84 | These are a bit less convenient because of the negative indexing and 85 | the fact that you're at the compiler and linker's whim for where things 86 | end up. Also they require longer (and probably slower) instruction 87 | sequences to allow the local index to be patched up by the linker. 88 | 89 | Lack of control over which segment register is used is a further 90 | downside. 91 | 92 | 93 | MACROS AND INLINE ASSEMBLY 94 | -------------------------- 95 | 96 | #define __local_get(n) ({ \ 97 | uint64 res; \ 98 | asm ("mov %%gs:" #n ",%0" : "=r" (res)); \ 99 | res; \ 100 | }) 101 | 102 | #define __local_put(n, v) ({ \ 103 | uint64 val = v; \ 104 | asm ("mov %0, %%gs:" #n : : "r" (val)); \ 105 | }) 106 | 107 | #define __proc() ((struct proc*) __local_get(4)) 108 | 109 | if (__proc()->killed) ... 110 | 111 | x86-64 without optimization: 112 | 113 | : 65 48 8b 04 25 08 00 mov %gs:0x4,%rax 114 | : 00 00 115 | : 48 89 45 d0 mov %rax,-0x30(%rbp) 116 | : 48 8b 45 d0 mov -0x30(%rbp),%rax 117 | : 8b 40 50 mov 0x50(%rax),%eax 118 | : 85 c0 test %eax,%eax 119 | 120 | x86-64 with -O1: 121 | 122 | : 65 48 8b 04 25 08 00 mov %gs:0x4,%rax 123 | : 00 00 124 | : 83 78 50 00 cmpl $0x0,0x50(%rax) 125 | 126 | i386 without optimization: 127 | 128 | : 65 8b 1d 04 00 00 00 mov %gs:0x4,%ebx 129 | : 89 5d f4 mov %ebx,-0xc(%ebp) 130 | : 8b 45 f4 mov -0xc(%ebp),%eax 131 | : 8b 40 24 mov 0x24(%eax),%eax 132 | : 85 c0 test %eax,%eax 133 | 134 | i386 with -O1: 135 | 136 | : 65 a1 04 00 00 00 mov %gs:0x4,%eax 137 | : 83 78 24 00 cmpl $0x0,0x24(%eax) 138 | 139 | These are less efficient than the others when compiling unoptimized 140 | (though that's an unusual state), but they cost no more than the 141 | global register variable trick originally used and have the benefit 142 | of generating correct code for both 32 and 64 bit modes. 143 | 144 | They do have the downside that you can't use one construct 145 | for both setting and getting the contents of a local storage 146 | variable. 147 | -------------------------------------------------------------------------------- /README.orig: -------------------------------------------------------------------------------- 1 | xv6 is a re-implementation of Dennis Ritchie's and Ken Thompson's Unix 2 | Version 6 (v6). xv6 loosely follows the structure and style of v6, 3 | but is implemented for a modern x86-based multiprocessor using ANSI C. 4 | 5 | ACKNOWLEDGMENTS 6 | 7 | xv6 is inspired by John Lions's Commentary on UNIX 6th Edition (Peer 8 | to Peer Communications; ISBN: 1-57398-013-7; 1st edition (June 14, 9 | 2000)). See also http://pdos.csail.mit.edu/6.828/2012/v6.html, which 10 | provides pointers to on-line resources for v6. 11 | 12 | xv6 borrows code from the following sources: 13 | JOS (asm.h, elf.h, mmu.h, bootasm.S, ide.c, console.c, and others) 14 | Plan 9 (entryother.S, mp.h, mp.c, lapic.c) 15 | FreeBSD (ioapic.c) 16 | NetBSD (console.c) 17 | 18 | The following people have made contributions: 19 | Russ Cox (context switching, locking) 20 | Cliff Frey (MP) 21 | Xiao Yu (MP) 22 | Nickolai Zeldovich 23 | Austin Clements 24 | 25 | In addition, we are grateful for the patches contributed by Greg 26 | Price, Yandong Mao, and Hitoshi Mitake. 27 | 28 | The code in the files that constitute xv6 is 29 | Copyright 2006-2012 Frans Kaashoek, Robert Morris, and Russ Cox. 30 | 31 | ERROR REPORTS 32 | 33 | If you spot errors or have suggestions for improvement, please send 34 | email to Frans Kaashoek and Robert Morris (kaashoek,rtm@csail.mit.edu). 35 | 36 | BUILDING AND RUNNING XV6 37 | 38 | To build xv6 on an x86 ELF machine (like Linux or FreeBSD), run "make". 39 | On non-x86 or non-ELF machines (like OS X, even on x86), you will 40 | need to install a cross-compiler gcc suite capable of producing x86 ELF 41 | binaries. See http://pdos.csail.mit.edu/6.828/2012/tools.html. 42 | Then run "make TOOLPREFIX=i386-jos-elf-". 43 | 44 | To run xv6, you can use the Bochs or QEMU PC simulators. Bochs makes 45 | debugging easier, but QEMU is much faster. To run in Bochs, run "make 46 | bochs" and then type "c" at the bochs prompt. To run in QEMU, run 47 | "make qemu". 48 | 49 | To create a typeset version of the code, run "make xv6.pdf". This 50 | requires the "mpage" utility. See http://www.mesa.nl/pub/mpage/. 51 | -------------------------------------------------------------------------------- /README.syslinux: -------------------------------------------------------------------------------- 1 | 2 | # create a 513MB disk with 512MB partition 1 3 | % truncate -s 513M hdd 4 | % fdisk hdd 5 | n / p / 1 / / 6 | a / 1 7 | w 8 | 9 | # configure mtools to see the first partition on that image 10 | % cat < mtools.rc 11 | drive s: 12 | file="hdd" 13 | offset=1048576 14 | cylinders=512 heads=32 sectors=32 15 | mformat_only 16 | EOF 17 | % export MTOOLSRC=mtools.rc 18 | 19 | % mformat s: 20 | 21 | # install and configure syslinux 22 | % dd if=/usr/lib/syslinux/mbr.bin of=hdd bs=440 count=1 conv=notrunc 23 | % syslinux -t 1048576 -i hdd 24 | % mmd s:syslinux 25 | % mcopy -o /usr/lib/syslinux/mboot.c32 s:syslinux 26 | % echo 'DEFAULT mboot.c32 -aout /kernel.elf' > syslinux.cfg 27 | % mcopy -o syslinux.cfg s:syslinux/syslinux.cfg 28 | 29 | # install xv6 30 | % mcopy -o out/kernel.elf s:kernel.elf 31 | 32 | # boot 33 | % qemu-system-i386 hdd -net none 34 | 35 | 36 | -------------------------------------------------------------------------------- /TRICKS: -------------------------------------------------------------------------------- 1 | This file lists subtle things that might not be commented 2 | as well as they should be in the source code and that 3 | might be worth pointing out in a longer explanation or in class. 4 | 5 | --- 6 | 7 | [2009/07/12: No longer relevant; forkret1 changed 8 | and this is now cleaner.] 9 | 10 | forkret1 in trapasm.S is called with a tf argument. 11 | In order to use it, forkret1 copies the tf pointer into 12 | %esp and then jumps to trapret, which pops the 13 | register state out of the trap frame. If an interrupt 14 | came in between the mov tf, %esp and the iret that 15 | goes back out to user space, the interrupt stack frame 16 | would end up scribbling over the tf and whatever memory 17 | lay under it. 18 | 19 | Why is this safe? Because forkret1 is only called 20 | the first time a process returns to user space, and 21 | at that point, cp->tf is set to point to a trap frame 22 | constructed at the top of cp's kernel stack. So tf 23 | *is* a valid %esp that can hold interrupt state. 24 | 25 | If other tf's were used in forkret1, we could add 26 | a cli before the mov tf, %esp. 27 | 28 | --- 29 | 30 | In pushcli, must cli() no matter what. It is not safe to do 31 | 32 | if(cpus[cpu()].ncli == 0) 33 | cli(); 34 | cpus[cpu()].ncli++; 35 | 36 | because if interrupts are off then we might call cpu(), get 37 | rescheduled to a different cpu, look at cpus[oldcpu].ncli, 38 | and wrongly decide not to disable interrupts on the new cpu. 39 | 40 | Instead do 41 | 42 | cli(); 43 | cpus[cpu()].ncli++; 44 | 45 | always. 46 | 47 | --- 48 | 49 | There is a (harmless) race in pushcli, which does 50 | 51 | eflags = readeflags(); 52 | cli(); 53 | if(c->ncli++ == 0) 54 | c->intena = eflags & FL_IF; 55 | 56 | Consider a bottom-level pushcli. 57 | If interrupts are disabled already, then the right thing 58 | happens: read_eflags finds that FL_IF is not set, 59 | and intena = 0. If interrupts are enabled, then 60 | it is less clear that the right thing happens: 61 | the readeflags can execute, then the process 62 | can get preempted and rescheduled on another cpu, 63 | and then once it starts running, perhaps with 64 | interrupts disabled (can happen since the scheduler 65 | only enables interrupts once per scheduling loop, 66 | not every time it schedules a process), it will 67 | incorrectly record that interrupts *were* enabled. 68 | This doesn't matter, because if it was safe to be 69 | running with interrupts enabled before the context 70 | switch, it is still safe (and arguably more correct) 71 | to run with them enabled after the context switch too. 72 | 73 | In fact it would be safe if scheduler always set 74 | c->intena = 1; 75 | before calling swtch, and perhaps it should. 76 | 77 | --- 78 | 79 | The x86's processor-ordering memory model 80 | matches spin locks well, so no explicit memory 81 | synchronization instructions are required in 82 | acquire and release. 83 | 84 | Consider two sequences of code on different CPUs: 85 | 86 | CPU0 87 | A; 88 | release(lk); 89 | 90 | and 91 | 92 | CPU1 93 | acquire(lk); 94 | B; 95 | 96 | We want to make sure that: 97 | - all reads in B see the effects of writes in A. 98 | - all reads in A do *not* see the effects of writes in B. 99 | 100 | The x86 guarantees that writes in A will go out 101 | to memory before the write of lk->locked = 0 in 102 | release(lk). It further guarantees that CPU1 103 | will observe CPU0's write of lk->locked = 0 only 104 | after observing the earlier writes by CPU0. 105 | So any reads in B are guaranteed to observe the 106 | effects of writes in A. 107 | 108 | According to the Intel manual behavior spec, the 109 | second condition requires a serialization instruction 110 | in release, to avoid reads in A happening after giving 111 | up lk. No Intel SMP processor in existence actually 112 | moves reads down after writes, but the language in 113 | the spec allows it. There is no telling whether future 114 | processors will need it. 115 | 116 | --- 117 | 118 | The code in fork needs to read np->pid before 119 | setting np->state to RUNNABLE. 120 | 121 | int 122 | fork(void) 123 | { 124 | ... 125 | pid = np->pid; 126 | np->state = RUNNABLE; 127 | return pid; 128 | } 129 | 130 | After setting np->state to RUNNABLE, some other CPU 131 | might run the process, it might exit, and then it might 132 | get reused for a different process (with a new pid), all 133 | before the return statement. So it's not safe to just do 134 | "return np->pid;". 135 | 136 | This works because proc.h marks the pid as volatile. 137 | -------------------------------------------------------------------------------- /include/acpi.h: -------------------------------------------------------------------------------- 1 | // References: ACPI 5.0 Errata A 2 | // http://acpi.info/spec.htm 3 | 4 | // 5.2.5.3 5 | #define SIG_RDSP "RSD PTR " 6 | struct acpi_rdsp { 7 | uchar signature[8]; 8 | uchar checksum; 9 | uchar oem_id[6]; 10 | uchar revision; 11 | uint32 rsdt_addr_phys; 12 | uint32 length; 13 | uint64 xsdt_addr_phys; 14 | uchar xchecksum; 15 | uchar reserved[3]; 16 | } __attribute__((__packed__)); 17 | 18 | // 5.2.6 19 | struct acpi_desc_header { 20 | uchar signature[4]; 21 | uint32 length; 22 | uchar revision; 23 | uchar checksum; 24 | uchar oem_id[6]; 25 | uchar oem_tableid[8]; 26 | uint32 oem_revision; 27 | uchar creator_id[4]; 28 | uint32 creator_revision; 29 | } __attribute__((__packed__)); 30 | 31 | // 5.2.7 32 | struct acpi_rsdt { 33 | struct acpi_desc_header header; 34 | uint32 entry[0]; 35 | } __attribute__((__packed__)); 36 | 37 | #define TYPE_LAPIC 0 38 | #define TYPE_IOAPIC 1 39 | #define TYPE_INT_SRC_OVERRIDE 2 40 | #define TYPE_NMI_INT_SRC 3 41 | #define TYPE_LAPIC_NMI 4 42 | 43 | // 5.2.12 Multiple APIC Description Table 44 | #define SIG_MADT "APIC" 45 | struct acpi_madt { 46 | struct acpi_desc_header header; 47 | uint32 lapic_addr_phys; 48 | uint32 flags; 49 | uchar table[0]; 50 | } __attribute__((__packed__)); 51 | 52 | // 5.2.12.2 53 | #define APIC_LAPIC_ENABLED 1 54 | struct madt_lapic { 55 | uchar type; 56 | uchar length; 57 | uchar acpi_id; 58 | uchar apic_id; 59 | uint32 flags; 60 | } __attribute__((__packed__)); 61 | 62 | // 5.2.12.3 63 | struct madt_ioapic { 64 | uchar type; 65 | uchar length; 66 | uchar id; 67 | uchar reserved; 68 | uint32 addr; 69 | uint32 interrupt_base; 70 | } __attribute__((__packed__)); 71 | 72 | 73 | -------------------------------------------------------------------------------- /include/asm.h: -------------------------------------------------------------------------------- 1 | // 2 | // assembler macros to create x86 segments 3 | // 4 | 5 | #define SEG_NULLASM \ 6 | .word 0, 0; \ 7 | .byte 0, 0, 0, 0 8 | 9 | // The 0xC0 means the limit is in 4096-byte units 10 | // and (for executable segments) 32-bit mode. 11 | #define SEG_ASM(type,base,lim) \ 12 | .word (((lim) >> 12) & 0xffff), ((base) & 0xffff); \ 13 | .byte (((base) >> 16) & 0xff), (0x90 | (type)), \ 14 | (0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff) 15 | 16 | #define STA_X 0x8 // Executable segment 17 | #define STA_E 0x4 // Expand down (non-executable segments) 18 | #define STA_C 0x4 // Conforming code segment (executable only) 19 | #define STA_W 0x2 // Writeable (non-executable segments) 20 | #define STA_R 0x2 // Readable (executable segments) 21 | #define STA_A 0x1 // Accessed 22 | -------------------------------------------------------------------------------- /include/buf.h: -------------------------------------------------------------------------------- 1 | struct buf { 2 | int flags; 3 | uint dev; 4 | uint sector; 5 | struct buf *prev; // LRU cache list 6 | struct buf *next; 7 | struct buf *qnext; // disk queue 8 | uchar data[512]; 9 | }; 10 | #define B_BUSY 0x1 // buffer is locked by some process 11 | #define B_VALID 0x2 // buffer has been read from disk 12 | #define B_DIRTY 0x4 // buffer needs to be written to disk 13 | 14 | -------------------------------------------------------------------------------- /include/defs.h: -------------------------------------------------------------------------------- 1 | struct buf; 2 | struct context; 3 | struct file; 4 | struct inode; 5 | struct pipe; 6 | struct proc; 7 | struct spinlock; 8 | struct stat; 9 | struct superblock; 10 | 11 | // bio.c 12 | void binit(void); 13 | struct buf* bread(uint, uint); 14 | void brelse(struct buf*); 15 | void bwrite(struct buf*); 16 | 17 | // console.c 18 | void consoleinit(void); 19 | void cprintf(char*, ...); 20 | void consoleintr(int(*)(void)); 21 | void panic(char*) __attribute__((noreturn)); 22 | 23 | // exec.c 24 | int exec(char*, char**); 25 | 26 | // file.c 27 | struct file* filealloc(void); 28 | void fileclose(struct file*); 29 | struct file* filedup(struct file*); 30 | void fileinit(void); 31 | int fileread(struct file*, char*, int n); 32 | int filestat(struct file*, struct stat*); 33 | int filewrite(struct file*, char*, int n); 34 | 35 | // fs.c 36 | void readsb(int dev, struct superblock *sb); 37 | int dirlink(struct inode*, char*, uint); 38 | struct inode* dirlookup(struct inode*, char*, uint*); 39 | struct inode* ialloc(uint, short); 40 | struct inode* idup(struct inode*); 41 | void iinit(void); 42 | void ilock(struct inode*); 43 | void iput(struct inode*); 44 | void iunlock(struct inode*); 45 | void iunlockput(struct inode*); 46 | void iupdate(struct inode*); 47 | int namecmp(const char*, const char*); 48 | struct inode* namei(char*); 49 | struct inode* nameiparent(char*, char*); 50 | int readi(struct inode*, char*, uint, uint); 51 | void stati(struct inode*, struct stat*); 52 | int writei(struct inode*, char*, uint, uint); 53 | 54 | // ide.c 55 | void ideinit(void); 56 | void ideintr(void); 57 | void iderw(struct buf*); 58 | 59 | // ioapic.c 60 | void ioapicenable(int irq, int cpu); 61 | extern uchar ioapicid; 62 | void ioapicinit(void); 63 | 64 | // kalloc.c 65 | char* kalloc(void); 66 | void kfree(char*); 67 | void kinit1(void*, void*); 68 | void kinit2(void*, void*); 69 | 70 | // kbd.c 71 | void kbdintr(void); 72 | 73 | // lapic.c 74 | int cpunum(void); 75 | extern volatile uint* lapic; 76 | void lapiceoi(void); 77 | void lapicinit(void); 78 | void lapicstartap(uchar, uint); 79 | void microdelay(int); 80 | 81 | // log.c 82 | void initlog(void); 83 | void log_write(struct buf*); 84 | void begin_trans(); 85 | void commit_trans(); 86 | 87 | // mp.c 88 | extern int ismp; 89 | int mpbcpu(void); 90 | void mpinit(void); 91 | void mpstartthem(void); 92 | 93 | // apic.c 94 | int acpiinit(void); 95 | 96 | // picirq.c 97 | void picenable(int); 98 | void picinit(void); 99 | 100 | // pipe.c 101 | int pipealloc(struct file**, struct file**); 102 | void pipeclose(struct pipe*, int); 103 | int piperead(struct pipe*, char*, int); 104 | int pipewrite(struct pipe*, char*, int); 105 | 106 | //PAGEBREAK: 16 107 | // proc.c 108 | struct proc* copyproc(struct proc*); 109 | void exit(void); 110 | int fork(void); 111 | int growproc(int); 112 | int kill(int); 113 | void pinit(void); 114 | void procdump(void); 115 | void scheduler(void) __attribute__((noreturn)); 116 | void sched(void); 117 | void sleep(void*, struct spinlock*); 118 | void userinit(void); 119 | int wait(void); 120 | void wakeup(void*); 121 | void yield(void); 122 | 123 | // swtch.S 124 | void swtch(struct context**, struct context*); 125 | 126 | // spinlock.c 127 | void acquire(struct spinlock*); 128 | void getcallerpcs(void*, uintp*); 129 | void getstackpcs(uintp*, uintp*); 130 | int holding(struct spinlock*); 131 | void initlock(struct spinlock*, char*); 132 | void release(struct spinlock*); 133 | void pushcli(void); 134 | void popcli(void); 135 | 136 | // string.c 137 | int memcmp(const void*, const void*, uint); 138 | void* memmove(void*, const void*, uint); 139 | void* memset(void*, int, uint); 140 | char* safestrcpy(char*, const char*, int); 141 | int strlen(const char*); 142 | int strncmp(const char*, const char*, uint); 143 | char* strncpy(char*, const char*, int); 144 | 145 | // syscall.c 146 | int argint(int, int*); 147 | int argptr(int, char**, int); 148 | int argstr(int, char**); 149 | int arguintp(int, uintp*); 150 | int fetchuintp(uintp, uintp*); 151 | int fetchstr(uintp, char**); 152 | void syscall(void); 153 | 154 | // timer.c 155 | void timerinit(void); 156 | 157 | // trap.c 158 | void idtinit(void); 159 | extern uint ticks; 160 | void tvinit(void); 161 | extern struct spinlock tickslock; 162 | 163 | // uart.c 164 | void uartearlyinit(void); 165 | void uartinit(void); 166 | void uartintr(void); 167 | void uartputc(int); 168 | 169 | // vm.c 170 | void seginit(void); 171 | void kvmalloc(void); 172 | void vmenable(void); 173 | pde_t* setupkvm(void); 174 | char* uva2ka(pde_t*, char*); 175 | int allocuvm(pde_t*, uint, uint); 176 | int deallocuvm(pde_t*, uintp, uintp); 177 | void freevm(pde_t*); 178 | void inituvm(pde_t*, char*, uint); 179 | int loaduvm(pde_t*, char*, struct inode*, uint, uint); 180 | pde_t* copyuvm(pde_t*, uint); 181 | void switchuvm(struct proc*); 182 | void switchkvm(void); 183 | int copyout(pde_t*, uint, void*, uint); 184 | void clearpteu(pde_t *pgdir, char *uva); 185 | 186 | // number of elements in fixed-size array 187 | #define NELEM(x) (sizeof(x)/sizeof((x)[0])) 188 | -------------------------------------------------------------------------------- /include/elf.h: -------------------------------------------------------------------------------- 1 | // Format of an ELF executable file 2 | 3 | #define ELF_MAGIC 0x464C457FU // "\x7FELF" in little endian 4 | 5 | // File header 6 | struct elfhdr { 7 | uint magic; // must equal ELF_MAGIC 8 | uchar elf[12]; 9 | ushort type; 10 | ushort machine; 11 | uint version; 12 | uintp entry; 13 | uintp phoff; 14 | uintp shoff; 15 | uint flags; 16 | ushort ehsize; 17 | ushort phentsize; 18 | ushort phnum; 19 | ushort shentsize; 20 | ushort shnum; 21 | ushort shstrndx; 22 | }; 23 | 24 | // Program section header 25 | #if X64 26 | struct proghdr { 27 | uint32 type; 28 | uint32 flags; 29 | uint64 off; 30 | uint64 vaddr; 31 | uint64 paddr; 32 | uint64 filesz; 33 | uint64 memsz; 34 | uint64 align; 35 | }; 36 | #else 37 | struct proghdr { 38 | uint type; 39 | uint off; 40 | uint vaddr; 41 | uint paddr; 42 | uint filesz; 43 | uint memsz; 44 | uint flags; 45 | uint align; 46 | }; 47 | #endif 48 | 49 | // Values for Proghdr type 50 | #define ELF_PROG_LOAD 1 51 | 52 | // Flag bits for Proghdr flags 53 | #define ELF_PROG_FLAG_EXEC 1 54 | #define ELF_PROG_FLAG_WRITE 2 55 | #define ELF_PROG_FLAG_READ 4 56 | -------------------------------------------------------------------------------- /include/fcntl.h: -------------------------------------------------------------------------------- 1 | #define O_RDONLY 0x000 2 | #define O_WRONLY 0x001 3 | #define O_RDWR 0x002 4 | #define O_CREATE 0x200 5 | -------------------------------------------------------------------------------- /include/file.h: -------------------------------------------------------------------------------- 1 | struct file { 2 | enum { FD_NONE, FD_PIPE, FD_INODE } type; 3 | int ref; // reference count 4 | char readable; 5 | char writable; 6 | struct pipe *pipe; 7 | struct inode *ip; 8 | uint off; 9 | }; 10 | 11 | 12 | // in-memory copy of an inode 13 | struct inode { 14 | uint dev; // Device number 15 | uint inum; // Inode number 16 | int ref; // Reference count 17 | int flags; // I_BUSY, I_VALID 18 | 19 | short type; // copy of disk inode 20 | short major; 21 | short minor; 22 | short nlink; 23 | uint size; 24 | uint addrs[NDIRECT+1]; 25 | }; 26 | #define I_BUSY 0x1 27 | #define I_VALID 0x2 28 | 29 | // table mapping major device number to 30 | // device functions 31 | struct devsw { 32 | int (*read)(struct inode*, char*, int); 33 | int (*write)(struct inode*, char*, int); 34 | }; 35 | 36 | extern struct devsw devsw[]; 37 | 38 | #define CONSOLE 1 39 | -------------------------------------------------------------------------------- /include/fs.h: -------------------------------------------------------------------------------- 1 | // On-disk file system format. 2 | // Both the kernel and user programs use this header file. 3 | 4 | // Block 0 is unused. 5 | // Block 1 is super block. 6 | // Blocks 2 through sb.ninodes/IPB hold inodes. 7 | // Then free bitmap blocks holding sb.size bits. 8 | // Then sb.nblocks data blocks. 9 | // Then sb.nlog log blocks. 10 | 11 | #define ROOTINO 1 // root i-number 12 | #define BSIZE 512 // block size 13 | 14 | // File system super block 15 | struct superblock { 16 | uint size; // Size of file system image (blocks) 17 | uint nblocks; // Number of data blocks 18 | uint ninodes; // Number of inodes. 19 | uint nlog; // Number of log blocks 20 | }; 21 | 22 | #define NDIRECT 12 23 | #define NINDIRECT (BSIZE / sizeof(uint)) 24 | #define MAXFILE (NDIRECT + NINDIRECT) 25 | 26 | // On-disk inode structure 27 | struct dinode { 28 | short type; // File type 29 | short major; // Major device number (T_DEV only) 30 | short minor; // Minor device number (T_DEV only) 31 | short nlink; // Number of links to inode in file system 32 | uint size; // Size of file (bytes) 33 | uint addrs[NDIRECT+1]; // Data block addresses 34 | }; 35 | 36 | // Inodes per block. 37 | #define IPB (BSIZE / sizeof(struct dinode)) 38 | 39 | // Block containing inode i 40 | #define IBLOCK(i) ((i) / IPB + 2) 41 | 42 | // Bitmap bits per block 43 | #define BPB (BSIZE*8) 44 | 45 | // Block containing bit for block b 46 | #define BBLOCK(b, ninodes) (b/BPB + (ninodes)/IPB + 3) 47 | 48 | // Directory is a file containing a sequence of dirent structures. 49 | #define DIRSIZ 14 50 | 51 | struct dirent { 52 | ushort inum; 53 | char name[DIRSIZ]; 54 | }; 55 | 56 | -------------------------------------------------------------------------------- /include/kbd.h: -------------------------------------------------------------------------------- 1 | // PC keyboard interface constants 2 | 3 | #define KBSTATP 0x64 // kbd controller status port(I) 4 | #define KBS_DIB 0x01 // kbd data in buffer 5 | #define KBDATAP 0x60 // kbd data port(I) 6 | 7 | #define NO 0 8 | 9 | #define SHIFT (1<<0) 10 | #define CTL (1<<1) 11 | #define ALT (1<<2) 12 | 13 | #define CAPSLOCK (1<<3) 14 | #define NUMLOCK (1<<4) 15 | #define SCROLLLOCK (1<<5) 16 | 17 | #define E0ESC (1<<6) 18 | 19 | // Special keycodes 20 | #define KEY_HOME 0xE0 21 | #define KEY_END 0xE1 22 | #define KEY_UP 0xE2 23 | #define KEY_DN 0xE3 24 | #define KEY_LF 0xE4 25 | #define KEY_RT 0xE5 26 | #define KEY_PGUP 0xE6 27 | #define KEY_PGDN 0xE7 28 | #define KEY_INS 0xE8 29 | #define KEY_DEL 0xE9 30 | 31 | // C('A') == Control-A 32 | #define C(x) (x - '@') 33 | 34 | static uchar shiftcode[256] = 35 | { 36 | [0x1D] CTL, 37 | [0x2A] SHIFT, 38 | [0x36] SHIFT, 39 | [0x38] ALT, 40 | [0x9D] CTL, 41 | [0xB8] ALT 42 | }; 43 | 44 | static uchar togglecode[256] = 45 | { 46 | [0x3A] CAPSLOCK, 47 | [0x45] NUMLOCK, 48 | [0x46] SCROLLLOCK 49 | }; 50 | 51 | static uchar normalmap[256] = 52 | { 53 | NO, 0x1B, '1', '2', '3', '4', '5', '6', // 0x00 54 | '7', '8', '9', '0', '-', '=', '\b', '\t', 55 | 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', // 0x10 56 | 'o', 'p', '[', ']', '\n', NO, 'a', 's', 57 | 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', // 0x20 58 | '\'', '`', NO, '\\', 'z', 'x', 'c', 'v', 59 | 'b', 'n', 'm', ',', '.', '/', NO, '*', // 0x30 60 | NO, ' ', NO, NO, NO, NO, NO, NO, 61 | NO, NO, NO, NO, NO, NO, NO, '7', // 0x40 62 | '8', '9', '-', '4', '5', '6', '+', '1', 63 | '2', '3', '0', '.', NO, NO, NO, NO, // 0x50 64 | [0x9C] '\n', // KP_Enter 65 | [0xB5] '/', // KP_Div 66 | [0xC8] KEY_UP, [0xD0] KEY_DN, 67 | [0xC9] KEY_PGUP, [0xD1] KEY_PGDN, 68 | [0xCB] KEY_LF, [0xCD] KEY_RT, 69 | [0x97] KEY_HOME, [0xCF] KEY_END, 70 | [0xD2] KEY_INS, [0xD3] KEY_DEL 71 | }; 72 | 73 | static uchar shiftmap[256] = 74 | { 75 | NO, 033, '!', '@', '#', '$', '%', '^', // 0x00 76 | '&', '*', '(', ')', '_', '+', '\b', '\t', 77 | 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', // 0x10 78 | 'O', 'P', '{', '}', '\n', NO, 'A', 'S', 79 | 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', // 0x20 80 | '"', '~', NO, '|', 'Z', 'X', 'C', 'V', 81 | 'B', 'N', 'M', '<', '>', '?', NO, '*', // 0x30 82 | NO, ' ', NO, NO, NO, NO, NO, NO, 83 | NO, NO, NO, NO, NO, NO, NO, '7', // 0x40 84 | '8', '9', '-', '4', '5', '6', '+', '1', 85 | '2', '3', '0', '.', NO, NO, NO, NO, // 0x50 86 | [0x9C] '\n', // KP_Enter 87 | [0xB5] '/', // KP_Div 88 | [0xC8] KEY_UP, [0xD0] KEY_DN, 89 | [0xC9] KEY_PGUP, [0xD1] KEY_PGDN, 90 | [0xCB] KEY_LF, [0xCD] KEY_RT, 91 | [0x97] KEY_HOME, [0xCF] KEY_END, 92 | [0xD2] KEY_INS, [0xD3] KEY_DEL 93 | }; 94 | 95 | static uchar ctlmap[256] = 96 | { 97 | NO, NO, NO, NO, NO, NO, NO, NO, 98 | NO, NO, NO, NO, NO, NO, NO, NO, 99 | C('Q'), C('W'), C('E'), C('R'), C('T'), C('Y'), C('U'), C('I'), 100 | C('O'), C('P'), NO, NO, '\r', NO, C('A'), C('S'), 101 | C('D'), C('F'), C('G'), C('H'), C('J'), C('K'), C('L'), NO, 102 | NO, NO, NO, C('\\'), C('Z'), C('X'), C('C'), C('V'), 103 | C('B'), C('N'), C('M'), NO, NO, C('/'), NO, NO, 104 | [0x9C] '\r', // KP_Enter 105 | [0xB5] C('/'), // KP_Div 106 | [0xC8] KEY_UP, [0xD0] KEY_DN, 107 | [0xC9] KEY_PGUP, [0xD1] KEY_PGDN, 108 | [0xCB] KEY_LF, [0xCD] KEY_RT, 109 | [0x97] KEY_HOME, [0xCF] KEY_END, 110 | [0xD2] KEY_INS, [0xD3] KEY_DEL 111 | }; 112 | 113 | -------------------------------------------------------------------------------- /include/memlayout.h: -------------------------------------------------------------------------------- 1 | // Memory layout 2 | 3 | #define EXTMEM 0x100000 // Start of extended memory 4 | #define PHYSTOP 0xE000000 // Top physical memory 5 | #define DEVSPACE 0xFE000000 // Other devices are at high addresses 6 | 7 | // Key addresses for address space layout (see kmap in vm.c for layout) 8 | #if X64 9 | #define KERNBASE 0xFFFFFFFF80000000 // First kernel virtual address 10 | #define DEVBASE 0xFFFFFFFF40000000 // First device virtual address 11 | #else 12 | #define KERNBASE 0x80000000 // First kernel virtual address 13 | #define DEVBASE 0xFE000000 // First device virtual address 14 | #endif 15 | #define KERNLINK (KERNBASE+EXTMEM) // Address where kernel is linked 16 | 17 | #ifndef __ASSEMBLER__ 18 | 19 | static inline uintp v2p(void *a) { return ((uintp) (a)) - ((uintp)KERNBASE); } 20 | static inline void *p2v(uintp a) { return (void *) ((a) + ((uintp)KERNBASE)); } 21 | 22 | #endif 23 | 24 | #define V2P(a) (((uintp) (a)) - KERNBASE) 25 | #define P2V(a) (((void *) (a)) + KERNBASE) 26 | #define IO2V(a) (((void *) (a)) + DEVBASE - DEVSPACE) 27 | 28 | #define V2P_WO(x) ((x) - KERNBASE) // same as V2P, but without casts 29 | #define P2V_WO(x) ((x) + KERNBASE) // same as V2P, but without casts 30 | -------------------------------------------------------------------------------- /include/mp.h: -------------------------------------------------------------------------------- 1 | // See MultiProcessor Specification Version 1.[14] 2 | 3 | struct mp { // floating pointer 4 | uchar signature[4]; // "_MP_" 5 | uint physaddr; // phys addr of MP config table 6 | uchar length; // 1 7 | uchar specrev; // [14] 8 | uchar checksum; // all bytes must add up to 0 9 | uchar type; // MP system config type 10 | uchar imcrp; 11 | uchar reserved[3]; 12 | }; 13 | 14 | struct mpconf { // configuration table header 15 | uchar signature[4]; // "PCMP" 16 | ushort length; // total table length 17 | uchar version; // [14] 18 | uchar checksum; // all bytes must add up to 0 19 | uchar product[20]; // product id 20 | uint oemtable; // OEM table pointer 21 | ushort oemlength; // OEM table length 22 | ushort entry; // entry count 23 | uint lapicaddr; // address of local APIC 24 | ushort xlength; // extended table length 25 | uchar xchecksum; // extended table checksum 26 | uchar reserved; 27 | }; 28 | 29 | struct mpproc { // processor table entry 30 | uchar type; // entry type (0) 31 | uchar apicid; // local APIC id 32 | uchar version; // local APIC verison 33 | uchar flags; // CPU flags 34 | #define MPBOOT 0x02 // This proc is the bootstrap processor. 35 | uchar signature[4]; // CPU signature 36 | uint feature; // feature flags from CPUID instruction 37 | uchar reserved[8]; 38 | }; 39 | 40 | struct mpioapic { // I/O APIC table entry 41 | uchar type; // entry type (2) 42 | uchar apicno; // I/O APIC id 43 | uchar version; // I/O APIC version 44 | uchar flags; // I/O APIC flags 45 | uint addr; // I/O APIC address 46 | }; 47 | 48 | // Table entry types 49 | #define MPPROC 0x00 // One per processor 50 | #define MPBUS 0x01 // One per bus 51 | #define MPIOAPIC 0x02 // One per I/O APIC 52 | #define MPIOINTR 0x03 // One per bus interrupt source 53 | #define MPLINTR 0x04 // One per system interrupt source 54 | 55 | -------------------------------------------------------------------------------- /include/param.h: -------------------------------------------------------------------------------- 1 | #define NPROC 64 // maximum number of processes 2 | #define KSTACKSIZE 4096 // size of per-process kernel stack 3 | #define NCPU 8 // maximum number of CPUs 4 | #define NOFILE 16 // open files per process 5 | #define NFILE 100 // open files per system 6 | #define NBUF 10 // size of disk block cache 7 | #define NINODE 50 // maximum number of active i-nodes 8 | #define NDEV 10 // maximum major device number 9 | #define ROOTDEV 1 // device number of file system root disk 10 | #define MAXARG 32 // max exec arguments 11 | #define LOGSIZE 10 // max data sectors in on-disk log 12 | 13 | -------------------------------------------------------------------------------- /include/proc.h: -------------------------------------------------------------------------------- 1 | // Segments in proc->gdt. 2 | #define NSEGS 7 3 | 4 | // Per-CPU state 5 | struct cpu { 6 | uchar id; // index into cpus[] below 7 | uchar apicid; // Local APIC ID 8 | struct context *scheduler; // swtch() here to enter scheduler 9 | struct taskstate ts; // Used by x86 to find stack for interrupt 10 | struct segdesc gdt[NSEGS]; // x86 global descriptor table 11 | volatile uint started; // Has the CPU started? 12 | int ncli; // Depth of pushcli nesting. 13 | int intena; // Were interrupts enabled before pushcli? 14 | 15 | // Cpu-local storage variables; see below 16 | #if X64 17 | void *local; 18 | #else 19 | struct cpu *cpu; 20 | struct proc *proc; // The currently-running process. 21 | #endif 22 | }; 23 | 24 | extern struct cpu cpus[NCPU]; 25 | extern int ncpu; 26 | 27 | // Per-CPU variables, holding pointers to the 28 | // current cpu and to the current process. 29 | // The asm suffix tells gcc to use "%gs:0" to refer to cpu 30 | // and "%gs:4" to refer to proc. seginit sets up the 31 | // %gs segment register so that %gs refers to the memory 32 | // holding those two variables in the local cpu's struct cpu. 33 | // This is similar to how thread-local variables are implemented 34 | // in thread libraries such as Linux pthreads. 35 | #if X64 36 | extern __thread struct cpu *cpu; 37 | extern __thread struct proc *proc; 38 | #else 39 | extern struct cpu *cpu asm("%gs:0"); // &cpus[cpunum()] 40 | extern struct proc *proc asm("%gs:4"); // cpus[cpunum()].proc 41 | #endif 42 | 43 | //PAGEBREAK: 17 44 | // Saved registers for kernel context switches. 45 | // Don't need to save all the segment registers (%cs, etc), 46 | // because they are constant across kernel contexts. 47 | // Don't need to save %eax, %ecx, %edx, because the 48 | // x86 convention is that the caller has saved them. 49 | // Contexts are stored at the bottom of the stack they 50 | // describe; the stack pointer is the address of the context. 51 | // The layout of the context matches the layout of the stack in swtch.S 52 | // at the "Switch stacks" comment. Switch doesn't save eip explicitly, 53 | // but it is on the stack and allocproc() manipulates it. 54 | #if X64 55 | struct context { 56 | uintp r15; 57 | uintp r14; 58 | uintp r13; 59 | uintp r12; 60 | uintp r11; 61 | uintp rbx; 62 | uintp ebp; //rbp 63 | uintp eip; //rip; 64 | }; 65 | #else 66 | struct context { 67 | uintp edi; 68 | uintp esi; 69 | uintp ebx; 70 | uintp ebp; 71 | uintp eip; 72 | }; 73 | #endif 74 | 75 | enum procstate { UNUSED, EMBRYO, SLEEPING, RUNNABLE, RUNNING, ZOMBIE }; 76 | 77 | // Per-process state 78 | struct proc { 79 | uintp sz; // Size of process memory (bytes) 80 | pde_t* pgdir; // Page table 81 | char *kstack; // Bottom of kernel stack for this process 82 | enum procstate state; // Process state 83 | volatile int pid; // Process ID 84 | struct proc *parent; // Parent process 85 | struct trapframe *tf; // Trap frame for current syscall 86 | struct context *context; // swtch() here to run process 87 | void *chan; // If non-zero, sleeping on chan 88 | int killed; // If non-zero, have been killed 89 | struct file *ofile[NOFILE]; // Open files 90 | struct inode *cwd; // Current directory 91 | char name[16]; // Process name (debugging) 92 | }; 93 | 94 | // Process memory is laid out contiguously, low addresses first: 95 | // text 96 | // original data and bss 97 | // fixed-size stack 98 | // expandable heap 99 | -------------------------------------------------------------------------------- /include/spinlock.h: -------------------------------------------------------------------------------- 1 | // Mutual exclusion lock. 2 | struct spinlock { 3 | uint locked; // Is the lock held? 4 | 5 | // For debugging: 6 | char *name; // Name of lock. 7 | struct cpu *cpu; // The cpu holding the lock. 8 | uintp pcs[10]; // The call stack (an array of program counters) 9 | // that locked the lock. 10 | }; 11 | 12 | -------------------------------------------------------------------------------- /include/stat.h: -------------------------------------------------------------------------------- 1 | #define T_DIR 1 // Directory 2 | #define T_FILE 2 // File 3 | #define T_DEV 3 // Device 4 | 5 | struct stat { 6 | short type; // Type of file 7 | int dev; // File system's disk device 8 | uint ino; // Inode number 9 | short nlink; // Number of links to file 10 | uint size; // Size of file in bytes 11 | }; 12 | -------------------------------------------------------------------------------- /include/symlink.patch: -------------------------------------------------------------------------------- 1 | diff -r f8a4e40ab1d6 fs.c 2 | --- a/fs.c Thu Aug 30 14:32:06 2007 -0400 3 | +++ b/fs.c Thu Aug 30 14:29:02 2007 -0400 4 | @@ -577,12 +577,18 @@ skipelem(char *path, char *name) 5 | // If parent != 0, return the inode for the parent and copy the final 6 | // path element into name, which must have room for DIRSIZ bytes. 7 | static struct inode* 8 | -_namei(char *path, int parent, char *name) 9 | +_namei(struct inode *root, char *path, int parent, char *name, int depth) 10 | { 11 | struct inode *ip, *next; 12 | + char buf[100], tname[DIRSIZ]; 13 | + 14 | + if(depth > 5) 15 | + return 0; 16 | 17 | if(*path == '/') 18 | ip = iget(ROOTDEV, 1); 19 | + else if(root) 20 | + ip = idup(root); 21 | else 22 | ip = idup(cp->cwd); 23 | 24 | @@ -598,10 +604,24 @@ _namei(char *path, int parent, char *nam 25 | return ip; 26 | } 27 | if((next = dirlookup(ip, name, 0)) == 0){ 28 | + cprintf("did not find %s\n", name); 29 | iunlockput(ip); 30 | return 0; 31 | } 32 | - iunlockput(ip); 33 | + iunlock(ip); 34 | + ilock(next); 35 | + if(next->type == T_SYMLINK){ 36 | + if(next->size >= sizeof(buf) || readi(next, buf, 0, next->size) != next->size){ 37 | + iunlockput(next); 38 | + iput(ip); 39 | + return 0; 40 | + } 41 | + buf[next->size] = 0; 42 | + iunlockput(next); 43 | + next = _namei(ip, buf, 0, tname, depth+1); 44 | + }else 45 | + iunlock(next); 46 | + iput(ip); 47 | ip = next; 48 | } 49 | if(parent){ 50 | @@ -615,11 +635,11 @@ namei(char *path) 51 | namei(char *path) 52 | { 53 | char name[DIRSIZ]; 54 | - return _namei(path, 0, name); 55 | + return _namei(0, path, 0, name, 0); 56 | } 57 | 58 | struct inode* 59 | nameiparent(char *path, char *name) 60 | { 61 | - return _namei(path, 1, name); 62 | -} 63 | + return _namei(0, path, 1, name, 0); 64 | +} 65 | diff -r f8a4e40ab1d6 fs.h 66 | --- a/fs.h Thu Aug 30 14:32:06 2007 -0400 67 | +++ b/fs.h Thu Aug 30 13:05:43 2007 -0400 68 | @@ -33,6 +33,7 @@ struct dinode { 69 | #define T_DIR 1 // Directory 70 | #define T_FILE 2 // File 71 | #define T_DEV 3 // Special device 72 | +#define T_SYMLINK 4 // Symlink 73 | 74 | // Inodes per block. 75 | #define IPB (BSIZE / sizeof(struct dinode)) 76 | diff -r f8a4e40ab1d6 syscall.c 77 | --- a/syscall.c Thu Aug 30 14:32:06 2007 -0400 78 | +++ b/syscall.c Thu Aug 30 13:05:29 2007 -0400 79 | @@ -96,6 +96,7 @@ extern int sys_unlink(void); 80 | extern int sys_unlink(void); 81 | extern int sys_wait(void); 82 | extern int sys_write(void); 83 | +extern int sys_symlink(void); 84 | 85 | static int (*syscalls[])(void) = { 86 | [SYS_chdir] sys_chdir, 87 | @@ -118,6 +119,7 @@ static int (*syscalls[])(void) = { 88 | [SYS_unlink] sys_unlink, 89 | [SYS_wait] sys_wait, 90 | [SYS_write] sys_write, 91 | +[SYS_symlink] sys_symlink, 92 | }; 93 | 94 | void 95 | diff -r f8a4e40ab1d6 syscall.h 96 | --- a/syscall.h Thu Aug 30 14:32:06 2007 -0400 97 | +++ b/syscall.h Thu Aug 30 13:02:48 2007 -0400 98 | @@ -19,3 +19,4 @@ 99 | #define SYS_getpid 18 100 | #define SYS_sbrk 19 101 | #define SYS_sleep 20 102 | +#define SYS_symlink 21 103 | diff -r f8a4e40ab1d6 sysfile.c 104 | --- a/sysfile.c Thu Aug 30 14:32:06 2007 -0400 105 | +++ b/sysfile.c Thu Aug 30 13:10:31 2007 -0400 106 | @@ -257,6 +257,21 @@ create(char *path, int canexist, short t 107 | } 108 | 109 | int 110 | +sys_symlink(void) 111 | +{ 112 | + char *old, *new; 113 | + struct inode *ip; 114 | + 115 | + if(argstr(0, &old) < 0 || argstr(1, &new) < 0) 116 | + return -1; 117 | + if((ip = create(new, 0, T_SYMLINK, 0, 0)) == 0) 118 | + return -1; 119 | + writei(ip, old, 0, strlen(old)); 120 | + iunlockput(ip); 121 | + return 0; 122 | +} 123 | + 124 | +int 125 | sys_open(void) 126 | { 127 | char *path; 128 | @@ -393,3 +408,4 @@ sys_pipe(void) 129 | fd[1] = fd1; 130 | return 0; 131 | } 132 | + 133 | diff -r f8a4e40ab1d6 user.h 134 | --- a/user.h Thu Aug 30 14:32:06 2007 -0400 135 | +++ b/user.h Thu Aug 30 13:02:34 2007 -0400 136 | @@ -21,6 +21,7 @@ int getpid(); 137 | int getpid(); 138 | char* sbrk(int); 139 | int sleep(int); 140 | +int symlink(int); 141 | 142 | // ulib.c 143 | int stat(char*, struct stat*); 144 | diff -r f8a4e40ab1d6 usys.S 145 | --- a/usys.S Thu Aug 30 14:32:06 2007 -0400 146 | +++ b/usys.S Thu Aug 30 13:05:54 2007 -0400 147 | @@ -28,3 +28,4 @@ STUB(getpid) 148 | STUB(getpid) 149 | STUB(sbrk) 150 | STUB(sleep) 151 | +STUB(symlink) 152 | -------------------------------------------------------------------------------- /include/syscall.h: -------------------------------------------------------------------------------- 1 | // System call numbers 2 | #define SYS_fork 1 3 | #define SYS_exit 2 4 | #define SYS_wait 3 5 | #define SYS_pipe 4 6 | #define SYS_read 5 7 | #define SYS_kill 6 8 | #define SYS_exec 7 9 | #define SYS_fstat 8 10 | #define SYS_chdir 9 11 | #define SYS_dup 10 12 | #define SYS_getpid 11 13 | #define SYS_sbrk 12 14 | #define SYS_sleep 13 15 | #define SYS_uptime 14 16 | #define SYS_open 15 17 | #define SYS_write 16 18 | #define SYS_mknod 17 19 | #define SYS_unlink 18 20 | #define SYS_link 19 21 | #define SYS_mkdir 20 22 | #define SYS_close 21 23 | -------------------------------------------------------------------------------- /include/traps.h: -------------------------------------------------------------------------------- 1 | // x86 trap and interrupt constants. 2 | 3 | // Processor-defined: 4 | #define T_DIVIDE 0 // divide error 5 | #define T_DEBUG 1 // debug exception 6 | #define T_NMI 2 // non-maskable interrupt 7 | #define T_BRKPT 3 // breakpoint 8 | #define T_OFLOW 4 // overflow 9 | #define T_BOUND 5 // bounds check 10 | #define T_ILLOP 6 // illegal opcode 11 | #define T_DEVICE 7 // device not available 12 | #define T_DBLFLT 8 // double fault 13 | // #define T_COPROC 9 // reserved (not used since 486) 14 | #define T_TSS 10 // invalid task switch segment 15 | #define T_SEGNP 11 // segment not present 16 | #define T_STACK 12 // stack exception 17 | #define T_GPFLT 13 // general protection fault 18 | #define T_PGFLT 14 // page fault 19 | // #define T_RES 15 // reserved 20 | #define T_FPERR 16 // floating point error 21 | #define T_ALIGN 17 // aligment check 22 | #define T_MCHK 18 // machine check 23 | #define T_SIMDERR 19 // SIMD floating point error 24 | 25 | // These are arbitrarily chosen, but with care not to overlap 26 | // processor defined exceptions or interrupt vectors. 27 | #define T_SYSCALL 64 // system call 28 | #define T_DEFAULT 500 // catchall 29 | 30 | #define T_IRQ0 32 // IRQ 0 corresponds to int T_IRQ 31 | 32 | #define IRQ_TIMER 0 33 | #define IRQ_KBD 1 34 | #define IRQ_COM1 4 35 | #define IRQ_IDE 14 36 | #define IRQ_ERROR 19 37 | #define IRQ_SPURIOUS 31 38 | 39 | -------------------------------------------------------------------------------- /include/types.h: -------------------------------------------------------------------------------- 1 | typedef unsigned int uint; 2 | typedef unsigned short ushort; 3 | typedef unsigned char uchar; 4 | 5 | typedef unsigned int uint32; 6 | typedef unsigned long uint64; 7 | 8 | #if X64 9 | typedef unsigned long uintp; 10 | #else 11 | typedef unsigned int uintp; 12 | #endif 13 | 14 | typedef uintp pde_t; 15 | -------------------------------------------------------------------------------- /include/user.h: -------------------------------------------------------------------------------- 1 | struct stat; 2 | 3 | // system calls 4 | int fork(void); 5 | int exit(void) __attribute__((noreturn)); 6 | int wait(void); 7 | int pipe(int*); 8 | int write(int, void*, int); 9 | int read(int, void*, int); 10 | int close(int); 11 | int kill(int); 12 | int exec(char*, char**); 13 | int open(char*, int); 14 | int mknod(char*, short, short); 15 | int unlink(char*); 16 | int fstat(int fd, struct stat*); 17 | int link(char*, char*); 18 | int mkdir(char*); 19 | int chdir(char*); 20 | int dup(int); 21 | int getpid(void); 22 | char* sbrk(int); 23 | int sleep(int); 24 | int uptime(void); 25 | 26 | // ulib.c 27 | int stat(char*, struct stat*); 28 | char* strcpy(char*, char*); 29 | void *memmove(void*, void*, int); 30 | char* strchr(const char*, char c); 31 | int strcmp(const char*, const char*); 32 | void printf(int, char*, ...); 33 | char* gets(char*, int max); 34 | uint strlen(char*); 35 | void* memset(void*, int, uint); 36 | void* malloc(uint); 37 | void free(void*); 38 | int atoi(const char*); 39 | -------------------------------------------------------------------------------- /include/x86.h: -------------------------------------------------------------------------------- 1 | // Routines to let C code use special x86 instructions. 2 | 3 | static inline uchar 4 | inb(ushort port) 5 | { 6 | uchar data; 7 | 8 | asm volatile("in %1,%0" : "=a" (data) : "d" (port)); 9 | return data; 10 | } 11 | 12 | static inline void 13 | insl(int port, void *addr, int cnt) 14 | { 15 | asm volatile("cld; rep insl" : 16 | "=D" (addr), "=c" (cnt) : 17 | "d" (port), "0" (addr), "1" (cnt) : 18 | "memory", "cc"); 19 | } 20 | 21 | static inline void 22 | outb(ushort port, uchar data) 23 | { 24 | asm volatile("out %0,%1" : : "a" (data), "d" (port)); 25 | } 26 | 27 | static inline void 28 | outw(ushort port, ushort data) 29 | { 30 | asm volatile("out %0,%1" : : "a" (data), "d" (port)); 31 | } 32 | 33 | static inline void 34 | outsl(int port, const void *addr, int cnt) 35 | { 36 | asm volatile("cld; rep outsl" : 37 | "=S" (addr), "=c" (cnt) : 38 | "d" (port), "0" (addr), "1" (cnt) : 39 | "cc"); 40 | } 41 | 42 | static inline void 43 | stosb(void *addr, int data, int cnt) 44 | { 45 | asm volatile("cld; rep stosb" : 46 | "=D" (addr), "=c" (cnt) : 47 | "0" (addr), "1" (cnt), "a" (data) : 48 | "memory", "cc"); 49 | } 50 | 51 | static inline void 52 | stosl(void *addr, int data, int cnt) 53 | { 54 | asm volatile("cld; rep stosl" : 55 | "=D" (addr), "=c" (cnt) : 56 | "0" (addr), "1" (cnt), "a" (data) : 57 | "memory", "cc"); 58 | } 59 | 60 | struct segdesc; 61 | 62 | static inline void 63 | lgdt(struct segdesc *p, int size) 64 | { 65 | volatile ushort pd[5]; 66 | 67 | pd[0] = size-1; 68 | pd[1] = (uintp)p; 69 | pd[2] = (uintp)p >> 16; 70 | #if X64 71 | pd[3] = (uintp)p >> 32; 72 | pd[4] = (uintp)p >> 48; 73 | #endif 74 | asm volatile("lgdt (%0)" : : "r" (pd)); 75 | } 76 | 77 | struct gatedesc; 78 | 79 | static inline void 80 | lidt(struct gatedesc *p, int size) 81 | { 82 | volatile ushort pd[5]; 83 | 84 | pd[0] = size-1; 85 | pd[1] = (uintp)p; 86 | pd[2] = (uintp)p >> 16; 87 | #if X64 88 | pd[3] = (uintp)p >> 32; 89 | pd[4] = (uintp)p >> 48; 90 | #endif 91 | asm volatile("lidt (%0)" : : "r" (pd)); 92 | } 93 | 94 | static inline void 95 | ltr(ushort sel) 96 | { 97 | asm volatile("ltr %0" : : "r" (sel)); 98 | } 99 | 100 | static inline uintp 101 | readeflags(void) 102 | { 103 | uintp eflags; 104 | asm volatile("pushf; pop %0" : "=r" (eflags)); 105 | return eflags; 106 | } 107 | 108 | static inline void 109 | loadgs(ushort v) 110 | { 111 | asm volatile("movw %0, %%gs" : : "r" (v)); 112 | } 113 | 114 | static inline void 115 | cli(void) 116 | { 117 | asm volatile("cli"); 118 | } 119 | 120 | static inline void 121 | sti(void) 122 | { 123 | asm volatile("sti"); 124 | } 125 | 126 | static inline void 127 | hlt(void) 128 | { 129 | asm volatile("hlt"); 130 | } 131 | 132 | static inline uint 133 | xchg(volatile uint *addr, uintp newval) 134 | { 135 | uint result; 136 | 137 | // The + in "+m" denotes a read-modify-write operand. 138 | asm volatile("lock; xchgl %0, %1" : 139 | "+m" (*addr), "=a" (result) : 140 | "1" (newval) : 141 | "cc"); 142 | return result; 143 | } 144 | 145 | static inline uintp 146 | rcr2(void) 147 | { 148 | uintp val; 149 | asm volatile("mov %%cr2,%0" : "=r" (val)); 150 | return val; 151 | } 152 | 153 | static inline void 154 | lcr3(uintp val) 155 | { 156 | asm volatile("mov %0,%%cr3" : : "r" (val)); 157 | } 158 | 159 | //PAGEBREAK: 36 160 | // Layout of the trap frame built on the stack by the 161 | // hardware and by trapasm.S, and passed to trap(). 162 | #if X64 163 | // lie about some register names in 64bit mode to avoid 164 | // clunky ifdefs in proc.c and trap.c. 165 | struct trapframe { 166 | uint64 eax; // rax 167 | uint64 rbx; 168 | uint64 rcx; 169 | uint64 rdx; 170 | uint64 rbp; 171 | uint64 rsi; 172 | uint64 rdi; 173 | uint64 r8; 174 | uint64 r9; 175 | uint64 r10; 176 | uint64 r11; 177 | uint64 r12; 178 | uint64 r13; 179 | uint64 r14; 180 | uint64 r15; 181 | 182 | uint64 trapno; 183 | uint64 err; 184 | 185 | uint64 eip; // rip 186 | uint64 cs; 187 | uint64 eflags; // rflags 188 | uint64 esp; // rsp 189 | uint64 ds; // ss 190 | }; 191 | #else 192 | struct trapframe { 193 | // registers as pushed by pusha 194 | uint edi; 195 | uint esi; 196 | uint ebp; 197 | uint oesp; // useless & ignored 198 | uint ebx; 199 | uint edx; 200 | uint ecx; 201 | uint eax; 202 | 203 | // rest of trap frame 204 | ushort gs; 205 | ushort padding1; 206 | ushort fs; 207 | ushort padding2; 208 | ushort es; 209 | ushort padding3; 210 | ushort ds; 211 | ushort padding4; 212 | uint trapno; 213 | 214 | // below here defined by x86 hardware 215 | uint err; 216 | uint eip; 217 | ushort cs; 218 | ushort padding5; 219 | uint eflags; 220 | 221 | // below here only when crossing rings, such as from user to kernel 222 | uint esp; 223 | ushort ss; 224 | ushort padding6; 225 | }; 226 | #endif 227 | -------------------------------------------------------------------------------- /kernel/acpi.c: -------------------------------------------------------------------------------- 1 | /* vm64.c 2 | * 3 | * Copyright (c) 2013 Brian Swetland 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining 6 | * a copy of this software and associated documentation files (the 7 | * "Software"), to deal in the Software without restriction, including 8 | * without limitation the rights to use, copy, modify, merge, publish, 9 | * distribute, sublicense, and/or sell copies of the Software, and to 10 | * permit persons to whom the Software is furnished to do so, subject to 11 | * the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be 14 | * included in all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #include "types.h" 27 | #include "defs.h" 28 | #include "param.h" 29 | #include "memlayout.h" 30 | #include "mp.h" 31 | #include "x86.h" 32 | #include "mmu.h" 33 | #include "proc.h" 34 | #include "acpi.h" 35 | 36 | extern struct cpu cpus[NCPU]; 37 | extern int ismp; 38 | extern int ncpu; 39 | extern uchar ioapicid; 40 | 41 | static struct acpi_rdsp *scan_rdsp(uint base, uint len) { 42 | uchar *p; 43 | for (p = p2v(base); len >= sizeof(struct acpi_rdsp); len -= 4, p += 4) { 44 | if (memcmp(p, SIG_RDSP, 8) == 0) { 45 | uint sum, n; 46 | for (sum = 0, n = 0; n < 20; n++) 47 | sum += p[n]; 48 | if ((sum & 0xff) == 0) 49 | return (struct acpi_rdsp *) p; 50 | } 51 | } 52 | return (struct acpi_rdsp *) 0; 53 | } 54 | 55 | static struct acpi_rdsp *find_rdsp(void) { 56 | struct acpi_rdsp *rdsp; 57 | uintp pa; 58 | pa = *((ushort*) P2V(0x40E)) << 4; // EBDA 59 | if (pa && (rdsp = scan_rdsp(pa, 1024))) 60 | return rdsp; 61 | return scan_rdsp(0xE0000, 0x20000); 62 | } 63 | 64 | static int acpi_config_smp(struct acpi_madt *madt) { 65 | uint32 lapic_addr; 66 | uint nioapic = 0; 67 | uchar *p, *e; 68 | 69 | if (!madt) 70 | return -1; 71 | if (madt->header.length < sizeof(struct acpi_madt)) 72 | return -1; 73 | 74 | lapic_addr = madt->lapic_addr_phys; 75 | 76 | p = madt->table; 77 | e = p + madt->header.length - sizeof(struct acpi_madt); 78 | 79 | while (p < e) { 80 | uint len; 81 | if ((e - p) < 2) 82 | break; 83 | len = p[1]; 84 | if ((e - p) < len) 85 | break; 86 | switch (p[0]) { 87 | case TYPE_LAPIC: { 88 | struct madt_lapic *lapic = (void*) p; 89 | if (len < sizeof(*lapic)) 90 | break; 91 | if (!(lapic->flags & APIC_LAPIC_ENABLED)) 92 | break; 93 | cprintf("acpi: cpu#%d apicid %d\n", ncpu, lapic->apic_id); 94 | cpus[ncpu].id = ncpu; 95 | cpus[ncpu].apicid = lapic->apic_id; 96 | ncpu++; 97 | break; 98 | } 99 | case TYPE_IOAPIC: { 100 | struct madt_ioapic *ioapic = (void*) p; 101 | if (len < sizeof(*ioapic)) 102 | break; 103 | cprintf("acpi: ioapic#%d @%x id=%d base=%d\n", 104 | nioapic, ioapic->addr, ioapic->id, ioapic->interrupt_base); 105 | if (nioapic) { 106 | cprintf("warning: multiple ioapics are not supported"); 107 | } else { 108 | ioapicid = ioapic->id; 109 | } 110 | nioapic++; 111 | break; 112 | } 113 | } 114 | p += len; 115 | } 116 | 117 | if (ncpu) { 118 | ismp = 1; 119 | lapic = IO2V(((uintp)lapic_addr)); 120 | return 0; 121 | } 122 | 123 | return -1; 124 | } 125 | 126 | #if X64 127 | #define PHYSLIMIT 0x80000000 128 | #else 129 | #define PHYSLIMIT 0x0E000000 130 | #endif 131 | 132 | int acpiinit(void) { 133 | unsigned n, count; 134 | struct acpi_rdsp *rdsp; 135 | struct acpi_rsdt *rsdt; 136 | struct acpi_madt *madt = 0; 137 | 138 | rdsp = find_rdsp(); 139 | if (rdsp->rsdt_addr_phys > PHYSLIMIT) 140 | goto notmapped; 141 | rsdt = p2v(rdsp->rsdt_addr_phys); 142 | count = (rsdt->header.length - sizeof(*rsdt)) / 4; 143 | for (n = 0; n < count; n++) { 144 | struct acpi_desc_header *hdr = p2v(rsdt->entry[n]); 145 | if (rsdt->entry[n] > PHYSLIMIT) 146 | goto notmapped; 147 | #if DEBUG 148 | uchar sig[5], id[7], tableid[9], creator[5]; 149 | memmove(sig, hdr->signature, 4); sig[4] = 0; 150 | memmove(id, hdr->oem_id, 6); id[6] = 0; 151 | memmove(tableid, hdr->oem_tableid, 8); tableid[8] = 0; 152 | memmove(creator, hdr->creator_id, 4); creator[4] = 0; 153 | cprintf("acpi: %s %s %s %x %s %x\n", 154 | sig, id, tableid, hdr->oem_revision, 155 | creator, hdr->creator_revision); 156 | #endif 157 | if (!memcmp(hdr->signature, SIG_MADT, 4)) 158 | madt = (void*) hdr; 159 | } 160 | 161 | return acpi_config_smp(madt); 162 | 163 | notmapped: 164 | cprintf("acpi: tables above 0x%x not mapped.\n", PHYSLIMIT); 165 | return -1; 166 | } 167 | -------------------------------------------------------------------------------- /kernel/bio.c: -------------------------------------------------------------------------------- 1 | // Buffer cache. 2 | // 3 | // The buffer cache is a linked list of buf structures holding 4 | // cached copies of disk block contents. Caching disk blocks 5 | // in memory reduces the number of disk reads and also provides 6 | // a synchronization point for disk blocks used by multiple processes. 7 | // 8 | // Interface: 9 | // * To get a buffer for a particular disk block, call bread. 10 | // * After changing buffer data, call bwrite to write it to disk. 11 | // * When done with the buffer, call brelse. 12 | // * Do not use the buffer after calling brelse. 13 | // * Only one process at a time can use a buffer, 14 | // so do not keep them longer than necessary. 15 | // 16 | // The implementation uses three state flags internally: 17 | // * B_BUSY: the block has been returned from bread 18 | // and has not been passed back to brelse. 19 | // * B_VALID: the buffer data has been read from the disk. 20 | // * B_DIRTY: the buffer data has been modified 21 | // and needs to be written to disk. 22 | 23 | #include "types.h" 24 | #include "defs.h" 25 | #include "param.h" 26 | #include "spinlock.h" 27 | #include "buf.h" 28 | 29 | struct { 30 | struct spinlock lock; 31 | struct buf buf[NBUF]; 32 | 33 | // Linked list of all buffers, through prev/next. 34 | // head.next is most recently used. 35 | struct buf head; 36 | } bcache; 37 | 38 | void 39 | binit(void) 40 | { 41 | struct buf *b; 42 | 43 | initlock(&bcache.lock, "bcache"); 44 | 45 | //PAGEBREAK! 46 | // Create linked list of buffers 47 | bcache.head.prev = &bcache.head; 48 | bcache.head.next = &bcache.head; 49 | for(b = bcache.buf; b < bcache.buf+NBUF; b++){ 50 | b->next = bcache.head.next; 51 | b->prev = &bcache.head; 52 | b->dev = -1; 53 | bcache.head.next->prev = b; 54 | bcache.head.next = b; 55 | } 56 | } 57 | 58 | // Look through buffer cache for sector on device dev. 59 | // If not found, allocate fresh block. 60 | // In either case, return B_BUSY buffer. 61 | static struct buf* 62 | bget(uint dev, uint sector) 63 | { 64 | struct buf *b; 65 | 66 | acquire(&bcache.lock); 67 | 68 | loop: 69 | // Is the sector already cached? 70 | for(b = bcache.head.next; b != &bcache.head; b = b->next){ 71 | if(b->dev == dev && b->sector == sector){ 72 | if(!(b->flags & B_BUSY)){ 73 | b->flags |= B_BUSY; 74 | release(&bcache.lock); 75 | return b; 76 | } 77 | sleep(b, &bcache.lock); 78 | goto loop; 79 | } 80 | } 81 | 82 | // Not cached; recycle some non-busy and clean buffer. 83 | for(b = bcache.head.prev; b != &bcache.head; b = b->prev){ 84 | if((b->flags & B_BUSY) == 0 && (b->flags & B_DIRTY) == 0){ 85 | b->dev = dev; 86 | b->sector = sector; 87 | b->flags = B_BUSY; 88 | release(&bcache.lock); 89 | return b; 90 | } 91 | } 92 | panic("bget: no buffers"); 93 | } 94 | 95 | // Return a B_BUSY buf with the contents of the indicated disk sector. 96 | struct buf* 97 | bread(uint dev, uint sector) 98 | { 99 | struct buf *b; 100 | 101 | b = bget(dev, sector); 102 | if(!(b->flags & B_VALID)) 103 | iderw(b); 104 | return b; 105 | } 106 | 107 | // Write b's contents to disk. Must be B_BUSY. 108 | void 109 | bwrite(struct buf *b) 110 | { 111 | if((b->flags & B_BUSY) == 0) 112 | panic("bwrite"); 113 | b->flags |= B_DIRTY; 114 | iderw(b); 115 | } 116 | 117 | // Release a B_BUSY buffer. 118 | // Move to the head of the MRU list. 119 | void 120 | brelse(struct buf *b) 121 | { 122 | if((b->flags & B_BUSY) == 0) 123 | panic("brelse"); 124 | 125 | acquire(&bcache.lock); 126 | 127 | b->next->prev = b->prev; 128 | b->prev->next = b->next; 129 | b->next = bcache.head.next; 130 | b->prev = &bcache.head; 131 | bcache.head.next->prev = b; 132 | bcache.head.next = b; 133 | 134 | b->flags &= ~B_BUSY; 135 | wakeup(b); 136 | 137 | release(&bcache.lock); 138 | } 139 | 140 | -------------------------------------------------------------------------------- /kernel/bootasm.S: -------------------------------------------------------------------------------- 1 | #include "asm.h" 2 | #include "memlayout.h" 3 | #include "mmu.h" 4 | 5 | # Start the first CPU: switch to 32-bit protected mode, jump into C. 6 | # The BIOS loads this code from the first sector of the hard disk into 7 | # memory at physical address 0x7c00 and starts executing in real mode 8 | # with %cs=0 %ip=7c00. 9 | 10 | .code16 # Assemble for 16-bit mode 11 | .globl start 12 | start: 13 | cli # BIOS enabled interrupts; disable 14 | 15 | # Zero data segment registers DS, ES, and SS. 16 | xorw %ax,%ax # Set %ax to zero 17 | movw %ax,%ds # -> Data Segment 18 | movw %ax,%es # -> Extra Segment 19 | movw %ax,%ss # -> Stack Segment 20 | 21 | # Physical address line A20 is tied to zero so that the first PCs 22 | # with 2 MB would run software that assumed 1 MB. Undo that. 23 | seta20.1: 24 | inb $0x64,%al # Wait for not busy 25 | testb $0x2,%al 26 | jnz seta20.1 27 | 28 | movb $0xd1,%al # 0xd1 -> port 0x64 29 | outb %al,$0x64 30 | 31 | seta20.2: 32 | inb $0x64,%al # Wait for not busy 33 | testb $0x2,%al 34 | jnz seta20.2 35 | 36 | movb $0xdf,%al # 0xdf -> port 0x60 37 | outb %al,$0x60 38 | 39 | # Switch from real to protected mode. Use a bootstrap GDT that makes 40 | # virtual addresses map directly to physical addresses so that the 41 | # effective memory map doesn't change during the transition. 42 | lgdt gdtdesc 43 | movl %cr0, %eax 44 | orl $CR0_PE, %eax 45 | movl %eax, %cr0 46 | 47 | //PAGEBREAK! 48 | # Complete transition to 32-bit protected mode by using long jmp 49 | # to reload %cs and %eip. The segment descriptors are set up with no 50 | # translation, so that the mapping is still the identity mapping. 51 | ljmp $(SEG_KCODE<<3), $start32 52 | 53 | .code32 # Tell assembler to generate 32-bit code now. 54 | start32: 55 | # Set up the protected-mode data segment registers 56 | movw $(SEG_KDATA<<3), %ax # Our data segment selector 57 | movw %ax, %ds # -> DS: Data Segment 58 | movw %ax, %es # -> ES: Extra Segment 59 | movw %ax, %ss # -> SS: Stack Segment 60 | movw $0, %ax # Zero segments not ready for use 61 | movw %ax, %fs # -> FS 62 | movw %ax, %gs # -> GS 63 | 64 | # Set up the stack pointer and call into C. 65 | movl $start, %esp 66 | call bootmain 67 | 68 | # If bootmain returns (it shouldn't), trigger a Bochs 69 | # breakpoint if running under Bochs, then loop. 70 | movw $0x8a00, %ax # 0x8a00 -> port 0x8a00 71 | movw %ax, %dx 72 | outw %ax, %dx 73 | movw $0x8ae0, %ax # 0x8ae0 -> port 0x8a00 74 | outw %ax, %dx 75 | spin: 76 | jmp spin 77 | 78 | # Bootstrap GDT 79 | .p2align 2 # force 4 byte alignment 80 | gdt: 81 | SEG_NULLASM # null seg 82 | SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg 83 | SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg 84 | 85 | gdtdesc: 86 | .word (gdtdesc - gdt - 1) # sizeof(gdt) - 1 87 | .long gdt # address gdt 88 | 89 | -------------------------------------------------------------------------------- /kernel/bootmain.c: -------------------------------------------------------------------------------- 1 | // Boot loader. 2 | // 3 | // Part of the boot sector, along with bootasm.S, which calls bootmain(). 4 | // bootasm.S has put the processor into protected 32-bit mode. 5 | // bootmain() loads a multiboot kernel image from the disk starting at 6 | // sector 1 and then jumps to the kernel entry routine. 7 | 8 | #include "types.h" 9 | #include "x86.h" 10 | #include "memlayout.h" 11 | 12 | #define SECTSIZE 512 13 | 14 | struct mbheader { 15 | uint32 magic; 16 | uint32 flags; 17 | uint32 checksum; 18 | uint32 header_addr; 19 | uint32 load_addr; 20 | uint32 load_end_addr; 21 | uint32 bss_end_addr; 22 | uint32 entry_addr; 23 | }; 24 | 25 | void readseg(uchar*, uint, uint); 26 | 27 | void 28 | bootmain(void) 29 | { 30 | struct mbheader *hdr; 31 | void (*entry)(void); 32 | uint32 *x; 33 | uint n; 34 | 35 | x = (uint32*) 0x10000; // scratch space 36 | 37 | // multiboot header must be in the first 8192 bytes 38 | readseg((uchar*)x, 8192, 0); 39 | 40 | for (n = 0; n < 8192/4; n++) 41 | if (x[n] == 0x1BADB002) 42 | if ((x[n] + x[n+1] + x[n+2]) == 0) 43 | goto found_it; 44 | 45 | // failure 46 | return; 47 | 48 | found_it: 49 | hdr = (struct mbheader *) (x + n); 50 | 51 | if (!(hdr->flags & 0x10000)) 52 | return; // does not have load_* fields, cannot proceed 53 | if (hdr->load_addr > hdr->header_addr) 54 | return; // invalid; 55 | if (hdr->load_end_addr < hdr->load_addr) 56 | return; // no idea how much to load 57 | 58 | readseg((uchar*) hdr->load_addr, 59 | (hdr->load_end_addr - hdr->load_addr), 60 | (n * 4) - (hdr->header_addr - hdr->load_addr)); 61 | 62 | if (hdr->bss_end_addr > hdr->load_end_addr) 63 | stosb((void*) hdr->load_end_addr, 0, 64 | hdr->bss_end_addr - hdr->load_end_addr); 65 | 66 | // Call the entry point from the multiboot header. 67 | // Does not return! 68 | entry = (void(*)(void))(hdr->entry_addr); 69 | entry(); 70 | } 71 | 72 | void 73 | waitdisk(void) 74 | { 75 | // Wait for disk ready. 76 | while((inb(0x1F7) & 0xC0) != 0x40) 77 | ; 78 | } 79 | 80 | // Read a single sector at offset into dst. 81 | void 82 | readsect(void *dst, uint offset) 83 | { 84 | // Issue command. 85 | waitdisk(); 86 | outb(0x1F2, 1); // count = 1 87 | outb(0x1F3, offset); 88 | outb(0x1F4, offset >> 8); 89 | outb(0x1F5, offset >> 16); 90 | outb(0x1F6, (offset >> 24) | 0xE0); 91 | outb(0x1F7, 0x20); // cmd 0x20 - read sectors 92 | 93 | // Read data. 94 | waitdisk(); 95 | insl(0x1F0, dst, SECTSIZE/4); 96 | } 97 | 98 | // Read 'count' bytes at 'offset' from kernel into physical address 'pa'. 99 | // Might copy more than asked. 100 | void 101 | readseg(uchar* pa, uint count, uint offset) 102 | { 103 | uchar* epa; 104 | 105 | epa = pa + count; 106 | 107 | // Round down to sector boundary. 108 | pa -= offset % SECTSIZE; 109 | 110 | // Translate from bytes to sectors; kernel starts at sector 1. 111 | offset = (offset / SECTSIZE) + 1; 112 | 113 | // If this is too slow, we could read lots of sectors at a time. 114 | // We'd write more to memory than asked, but it doesn't matter -- 115 | // we load in increasing order. 116 | for(; pa < epa; pa += SECTSIZE, offset++) 117 | readsect(pa, offset); 118 | } 119 | -------------------------------------------------------------------------------- /kernel/console.c: -------------------------------------------------------------------------------- 1 | // Console input and output. 2 | // Input is from the keyboard or serial port. 3 | // Output is written to the screen and serial port. 4 | 5 | #include 6 | 7 | #include "types.h" 8 | #include "defs.h" 9 | #include "param.h" 10 | #include "traps.h" 11 | #include "spinlock.h" 12 | #include "fs.h" 13 | #include "file.h" 14 | #include "memlayout.h" 15 | #include "mmu.h" 16 | #include "proc.h" 17 | #include "x86.h" 18 | 19 | static void consputc(int); 20 | 21 | static int panicked = 0; 22 | 23 | static struct { 24 | struct spinlock lock; 25 | int locking; 26 | } cons; 27 | 28 | static char digits[] = "0123456789abcdef"; 29 | 30 | static void 31 | printptr(uintp x) { 32 | int i; 33 | for (i = 0; i < (sizeof(uintp) * 2); i++, x <<= 4) 34 | consputc(digits[x >> (sizeof(uintp) * 8 - 4)]); 35 | } 36 | 37 | static void 38 | printint(int xx, int base, int sign) 39 | { 40 | char buf[16]; 41 | int i; 42 | uint x; 43 | 44 | if(sign && (sign = xx < 0)) 45 | x = -xx; 46 | else 47 | x = xx; 48 | 49 | i = 0; 50 | do{ 51 | buf[i++] = digits[x % base]; 52 | }while((x /= base) != 0); 53 | 54 | if(sign) 55 | buf[i++] = '-'; 56 | 57 | while(--i >= 0) 58 | consputc(buf[i]); 59 | } 60 | //PAGEBREAK: 50 61 | 62 | // Print to the console. only understands %d, %x, %p, %s. 63 | void 64 | cprintf(char *fmt, ...) 65 | { 66 | va_list ap; 67 | int i, c, locking; 68 | char *s; 69 | 70 | va_start(ap, fmt); 71 | 72 | locking = cons.locking; 73 | if(locking) 74 | acquire(&cons.lock); 75 | 76 | if (fmt == 0) 77 | panic("null fmt"); 78 | 79 | for(i = 0; (c = fmt[i] & 0xff) != 0; i++){ 80 | if(c != '%'){ 81 | consputc(c); 82 | continue; 83 | } 84 | c = fmt[++i] & 0xff; 85 | if(c == 0) 86 | break; 87 | switch(c){ 88 | case 'd': 89 | printint(va_arg(ap, int), 10, 1); 90 | break; 91 | case 'x': 92 | printint(va_arg(ap, int), 16, 0); 93 | break; 94 | case 'p': 95 | printptr(va_arg(ap, uintp)); 96 | break; 97 | case 's': 98 | if((s = va_arg(ap, char*)) == 0) 99 | s = "(null)"; 100 | for(; *s; s++) 101 | consputc(*s); 102 | break; 103 | case '%': 104 | consputc('%'); 105 | break; 106 | default: 107 | // Print unknown % sequence to draw attention. 108 | consputc('%'); 109 | consputc(c); 110 | break; 111 | } 112 | } 113 | 114 | if(locking) 115 | release(&cons.lock); 116 | } 117 | 118 | void 119 | panic(char *s) 120 | { 121 | int i; 122 | uintp pcs[10]; 123 | 124 | cli(); 125 | cons.locking = 0; 126 | cprintf("cpu%d: panic: ", cpu->id); 127 | cprintf(s); 128 | cprintf("\n"); 129 | getcallerpcs(&s, pcs); 130 | for(i=0; i<10; i++) 131 | cprintf(" %p", pcs[i]); 132 | panicked = 1; // freeze other CPU 133 | for(;;) 134 | ; 135 | } 136 | 137 | //PAGEBREAK: 50 138 | #define BACKSPACE 0x100 139 | #define CRTPORT 0x3d4 140 | static ushort *crt = (ushort*)P2V(0xb8000); // CGA memory 141 | 142 | static void 143 | cgaputc(int c) 144 | { 145 | int pos; 146 | 147 | // Cursor position: col + 80*row. 148 | outb(CRTPORT, 14); 149 | pos = inb(CRTPORT+1) << 8; 150 | outb(CRTPORT, 15); 151 | pos |= inb(CRTPORT+1); 152 | 153 | if(c == '\n') 154 | pos += 80 - pos%80; 155 | else if(c == BACKSPACE){ 156 | if(pos > 0) --pos; 157 | } else 158 | crt[pos++] = (c&0xff) | 0x0700; // black on white 159 | 160 | if((pos/80) >= 24){ // Scroll up. 161 | memmove(crt, crt+80, sizeof(crt[0])*23*80); 162 | pos -= 80; 163 | memset(crt+pos, 0, sizeof(crt[0])*(24*80 - pos)); 164 | } 165 | 166 | outb(CRTPORT, 14); 167 | outb(CRTPORT+1, pos>>8); 168 | outb(CRTPORT, 15); 169 | outb(CRTPORT+1, pos); 170 | crt[pos] = ' ' | 0x0700; 171 | } 172 | 173 | void 174 | consputc(int c) 175 | { 176 | if(panicked){ 177 | cli(); 178 | for(;;) 179 | ; 180 | } 181 | 182 | if(c == BACKSPACE){ 183 | uartputc('\b'); uartputc(' '); uartputc('\b'); 184 | } else 185 | uartputc(c); 186 | cgaputc(c); 187 | } 188 | 189 | #define INPUT_BUF 128 190 | struct { 191 | struct spinlock lock; 192 | char buf[INPUT_BUF]; 193 | uint r; // Read index 194 | uint w; // Write index 195 | uint e; // Edit index 196 | } input; 197 | 198 | #define C(x) ((x)-'@') // Control-x 199 | 200 | void 201 | consoleintr(int (*getc)(void)) 202 | { 203 | int c; 204 | 205 | acquire(&input.lock); 206 | while((c = getc()) >= 0){ 207 | switch(c){ 208 | case C('Z'): // reboot 209 | lidt(0,0); 210 | break; 211 | case C('P'): // Process listing. 212 | procdump(); 213 | break; 214 | case C('U'): // Kill line. 215 | while(input.e != input.w && 216 | input.buf[(input.e-1) % INPUT_BUF] != '\n'){ 217 | input.e--; 218 | consputc(BACKSPACE); 219 | } 220 | break; 221 | case C('H'): case '\x7f': // Backspace 222 | if(input.e != input.w){ 223 | input.e--; 224 | consputc(BACKSPACE); 225 | } 226 | break; 227 | default: 228 | if(c != 0 && input.e-input.r < INPUT_BUF){ 229 | c = (c == '\r') ? '\n' : c; 230 | input.buf[input.e++ % INPUT_BUF] = c; 231 | consputc(c); 232 | if(c == '\n' || c == C('D') || input.e == input.r+INPUT_BUF){ 233 | input.w = input.e; 234 | wakeup(&input.r); 235 | } 236 | } 237 | break; 238 | } 239 | } 240 | release(&input.lock); 241 | } 242 | 243 | int 244 | consoleread(struct inode *ip, char *dst, int n) 245 | { 246 | uint target; 247 | int c; 248 | 249 | iunlock(ip); 250 | target = n; 251 | acquire(&input.lock); 252 | while(n > 0){ 253 | while(input.r == input.w){ 254 | if(proc->killed){ 255 | release(&input.lock); 256 | ilock(ip); 257 | return -1; 258 | } 259 | sleep(&input.r, &input.lock); 260 | } 261 | c = input.buf[input.r++ % INPUT_BUF]; 262 | if(c == C('D')){ // EOF 263 | if(n < target){ 264 | // Save ^D for next time, to make sure 265 | // caller gets a 0-byte result. 266 | input.r--; 267 | } 268 | break; 269 | } 270 | *dst++ = c; 271 | --n; 272 | if(c == '\n') 273 | break; 274 | } 275 | release(&input.lock); 276 | ilock(ip); 277 | 278 | return target - n; 279 | } 280 | 281 | int 282 | consolewrite(struct inode *ip, char *buf, int n) 283 | { 284 | int i; 285 | 286 | iunlock(ip); 287 | acquire(&cons.lock); 288 | for(i = 0; i < n; i++) 289 | consputc(buf[i] & 0xff); 290 | release(&cons.lock); 291 | ilock(ip); 292 | 293 | return n; 294 | } 295 | 296 | void 297 | consoleinit(void) 298 | { 299 | initlock(&cons.lock, "console"); 300 | initlock(&input.lock, "input"); 301 | 302 | devsw[CONSOLE].write = consolewrite; 303 | devsw[CONSOLE].read = consoleread; 304 | cons.locking = 1; 305 | 306 | picenable(IRQ_KBD); 307 | ioapicenable(IRQ_KBD, 0); 308 | } 309 | 310 | -------------------------------------------------------------------------------- /kernel/entry.S: -------------------------------------------------------------------------------- 1 | # Multiboot header, for multiboot boot loaders like GNU Grub. 2 | # http://www.gnu.org/software/grub/manual/multiboot/multiboot.html 3 | # 4 | # Using GRUB 2, you can boot xv6 from a file stored in a 5 | # Linux file system by copying kernel or kernelmemfs to /boot 6 | # and then adding this menu entry: 7 | # 8 | # menuentry "xv6" { 9 | # insmod ext2 10 | # set root='(hd0,msdos1)' 11 | # set kernel='/boot/kernel' 12 | # echo "Loading ${kernel}..." 13 | # multiboot ${kernel} ${kernel} 14 | # boot 15 | # } 16 | 17 | #include "asm.h" 18 | #include "memlayout.h" 19 | #include "mmu.h" 20 | #include "param.h" 21 | 22 | # Multiboot header. Data to direct multiboot loader. 23 | .p2align 2 24 | .text 25 | .globl multiboot_header 26 | multiboot_header: 27 | #define magic 0x1badb002 28 | #define flags 0x10000 29 | .long magic 30 | .long flags 31 | .long (-magic-flags) 32 | .long V2P_WO(multiboot_header) 33 | .long V2P_WO(multiboot_header) 34 | .long V2P_WO(edata) 35 | .long V2P_WO(end) 36 | .long _start 37 | 38 | # By convention, the _start symbol specifies the ELF entry point. 39 | # Since we haven't set up virtual memory yet, our entry point is 40 | # the physical address of 'entry'. 41 | .globl _start 42 | _start = V2P_WO(entry) 43 | 44 | # Entering xv6 on boot processor, with paging off. 45 | .globl entry 46 | entry: 47 | # Turn on page size extension for 4Mbyte pages 48 | movl %cr4, %eax 49 | orl $(CR4_PSE), %eax 50 | movl %eax, %cr4 51 | # Set page directory 52 | movl $(V2P_WO(entrypgdir)), %eax 53 | movl %eax, %cr3 54 | # Turn on paging. 55 | movl %cr0, %eax 56 | orl $(CR0_PG|CR0_WP), %eax 57 | movl %eax, %cr0 58 | 59 | # Set up the stack pointer. 60 | movl $(stack + KSTACKSIZE), %esp 61 | 62 | # Jump to main(), and switch to executing at 63 | # high addresses. The indirect call is needed because 64 | # the assembler produces a PC-relative instruction 65 | # for a direct jump. 66 | mov $main, %eax 67 | jmp *%eax 68 | 69 | .comm stack, KSTACKSIZE 70 | -------------------------------------------------------------------------------- /kernel/entry64.S: -------------------------------------------------------------------------------- 1 | /* entry64.S 2 | * 3 | * Copyright (c) 2013 Brian Swetland 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining 6 | * a copy of this software and associated documentation files (the 7 | * "Software"), to deal in the Software without restriction, including 8 | * without limitation the rights to use, copy, modify, merge, publish, 9 | * distribute, sublicense, and/or sell copies of the Software, and to 10 | * permit persons to whom the Software is furnished to do so, subject to 11 | * the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be 14 | * included in all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #define mboot_magic 0x1badb002 27 | #define mboot_flags 0x00010000 28 | 29 | .code32 30 | .global mboot_header 31 | .global mboot_entry 32 | 33 | mboot_header: 34 | .long mboot_magic 35 | .long mboot_flags 36 | .long (-mboot_magic -mboot_flags) # checksum 37 | .long mboot_load_addr # header_addr 38 | .long mboot_load_addr 39 | .long mboot_load_end 40 | .long mboot_bss_end 41 | .long mboot_entry_addr 42 | 43 | mboot_entry: 44 | 45 | # zero 4 pages for our bootstrap page tables 46 | xor %eax, %eax 47 | mov $0x1000, %edi 48 | mov $0x5000, %ecx 49 | rep stosb 50 | 51 | # P4ML[0] -> 0x2000 (PDPT-A) 52 | mov $(0x2000 | 3), %eax 53 | mov %eax, 0x1000 54 | 55 | # P4ML[511] -> 0x3000 (PDPT-B) 56 | mov $(0x3000 | 3), %eax 57 | mov %eax, 0x1FF8 58 | 59 | # PDPT-A[0] -> 0x4000 (PD) 60 | mov $(0x4000 | 3), %eax 61 | mov %eax, 0x2000 62 | 63 | # PDPT-B[510] -> 0x4000 (PD) 64 | mov $(0x4000 | 3), %eax 65 | mov %eax, 0x3FF0 66 | 67 | # PD[0..511] -> 0..1022MB 68 | mov $0x83, %eax 69 | mov $0x4000, %ebx 70 | mov $512, %ecx 71 | ptbl_loop: 72 | mov %eax, (%ebx) 73 | add $0x200000, %eax 74 | add $0x8, %ebx 75 | dec %ecx 76 | jnz ptbl_loop 77 | 78 | # Clear ebx for initial processor boot. 79 | # When secondary processors boot, they'll call through 80 | # entry32mp (from entryother), but with a nonzero ebx. 81 | # We'll reuse these bootstrap pagetables and GDT. 82 | xor %ebx, %ebx 83 | 84 | .global entry32mp 85 | entry32mp: 86 | # CR3 -> 0x1000 (P4ML) 87 | mov $0x1000, %eax 88 | mov %eax, %cr3 89 | 90 | lgdt (gdtr64 - mboot_header + mboot_load_addr) 91 | 92 | # Enable PAE - CR4.PAE=1 93 | mov %cr4, %eax 94 | bts $5, %eax 95 | mov %eax, %cr4 96 | 97 | # enable long mode - EFER.LME=1 98 | mov $0xc0000080, %ecx 99 | rdmsr 100 | bts $8, %eax 101 | wrmsr 102 | 103 | # enable paging 104 | mov %cr0, %eax 105 | bts $31, %eax 106 | mov %eax, %cr0 107 | 108 | # shift to 64bit segment 109 | ljmp $8,$(entry64low - mboot_header + mboot_load_addr) 110 | 111 | .align 16 112 | gdtr64: 113 | .word gdt64_end - gdt64_begin - 1; 114 | .quad gdt64_begin - mboot_header + mboot_load_addr 115 | 116 | .align 16 117 | gdt64_begin: 118 | .long 0x00000000 # 0: null desc 119 | .long 0x00000000 120 | .long 0x00000000 # 1: Code, R/X, Nonconforming 121 | .long 0x00209800 122 | .long 0x00000000 # 2: Data, R/W, Expand Down 123 | .long 0x00009000 124 | gdt64_end: 125 | 126 | .align 16 127 | .code64 128 | entry64low: 129 | movq $entry64high, %rax 130 | jmp *%rax 131 | 132 | .global _start 133 | _start: 134 | entry64high: 135 | 136 | # ensure data segment registers are sane 137 | xor %rax, %rax 138 | mov %ax, %ss 139 | mov %ax, %ds 140 | mov %ax, %es 141 | mov %ax, %fs 142 | mov %ax, %gs 143 | 144 | # check to see if we're booting a secondary core 145 | test %ebx, %ebx 146 | jnz entry64mp 147 | 148 | # setup initial stack 149 | mov $0xFFFFFFFF80010000, %rax 150 | mov %rax, %rsp 151 | 152 | # enter main() 153 | jmp main 154 | 155 | .global __deadloop 156 | __deadloop: 157 | # we should never return here... 158 | jmp . 159 | 160 | entry64mp: 161 | # obtain kstack from data block before entryother 162 | mov $0x7000, %rax 163 | mov -16(%rax), %rsp 164 | jmp mpenter 165 | 166 | .global wrmsr 167 | wrmsr: 168 | mov %rdi, %rcx # arg0 -> msrnum 169 | mov %rsi, %rax # val.low -> eax 170 | shr $32, %rsi 171 | mov %rsi, %rdx # val.high -> edx 172 | wrmsr 173 | retq 174 | 175 | -------------------------------------------------------------------------------- /kernel/entryother.S: -------------------------------------------------------------------------------- 1 | #include "asm.h" 2 | #include "memlayout.h" 3 | #include "mmu.h" 4 | 5 | # Each non-boot CPU ("AP") is started up in response to a STARTUP 6 | # IPI from the boot CPU. Section B.4.2 of the Multi-Processor 7 | # Specification says that the AP will start in real mode with CS:IP 8 | # set to XY00:0000, where XY is an 8-bit value sent with the 9 | # STARTUP. Thus this code must start at a 4096-byte boundary. 10 | # 11 | # Because this code sets DS to zero, it must sit 12 | # at an address in the low 2^16 bytes. 13 | # 14 | # Startothers (in main.c) sends the STARTUPs one at a time. 15 | # It copies this code (start) at 0x7000. It puts the address of 16 | # a newly allocated per-core stack in start-4,the address of the 17 | # place to jump to (mpenter) in start-8, and the physical address 18 | # of entrypgdir in start-12. 19 | # 20 | # This code is identical to bootasm.S except: 21 | # - it does not need to enable A20 22 | # - it uses the address at start-4, start-8, and start-12 23 | 24 | .code16 25 | .globl start 26 | start: 27 | cli 28 | 29 | xorw %ax,%ax 30 | movw %ax,%ds 31 | movw %ax,%es 32 | movw %ax,%ss 33 | 34 | lgdt gdtdesc 35 | movl %cr0, %eax 36 | orl $CR0_PE, %eax 37 | movl %eax, %cr0 38 | 39 | //PAGEBREAK! 40 | ljmpl $(SEG_KCODE<<3), $(start32) 41 | 42 | .code32 43 | start32: 44 | movw $(SEG_KDATA<<3), %ax 45 | movw %ax, %ds 46 | movw %ax, %es 47 | movw %ax, %ss 48 | movw $0, %ax 49 | movw %ax, %fs 50 | movw %ax, %gs 51 | 52 | #if X64 53 | # defer paging until we switch to 64bit mode 54 | # set ebx=1 so shared boot code knows we're booting a secondary core 55 | mov $1, %ebx 56 | #else 57 | # Turn on page size extension for 4Mbyte pages 58 | movl %cr4, %eax 59 | orl $(CR4_PSE), %eax 60 | movl %eax, %cr4 61 | # Use enterpgdir as our initial page table 62 | movl (start-12), %eax 63 | movl %eax, %cr3 64 | # Turn on paging. 65 | movl %cr0, %eax 66 | orl $(CR0_PE|CR0_PG|CR0_WP), %eax 67 | movl %eax, %cr0 68 | #endif 69 | 70 | # Switch to the stack allocated by startothers() 71 | movl (start-4), %esp 72 | # Call mpenter() 73 | call *(start-8) 74 | 75 | movw $0x8a00, %ax 76 | movw %ax, %dx 77 | outw %ax, %dx 78 | movw $0x8ae0, %ax 79 | outw %ax, %dx 80 | spin: 81 | jmp spin 82 | 83 | .p2align 2 84 | gdt: 85 | SEG_NULLASM 86 | SEG_ASM(STA_X|STA_R, 0, 0xffffffff) 87 | SEG_ASM(STA_W, 0, 0xffffffff) 88 | 89 | 90 | gdtdesc: 91 | .word (gdtdesc - gdt - 1) 92 | .long gdt 93 | 94 | -------------------------------------------------------------------------------- /kernel/exec.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "param.h" 3 | #include "memlayout.h" 4 | #include "mmu.h" 5 | #include "proc.h" 6 | #include "defs.h" 7 | #include "x86.h" 8 | #include "elf.h" 9 | 10 | int 11 | exec(char *path, char **argv) 12 | { 13 | char *s, *last; 14 | int i, off; 15 | uintp argc, sz, sp, ustack[3+MAXARG+1]; 16 | struct elfhdr elf; 17 | struct inode *ip; 18 | struct proghdr ph; 19 | pde_t *pgdir, *oldpgdir; 20 | 21 | if((ip = namei(path)) == 0) 22 | return -1; 23 | ilock(ip); 24 | pgdir = 0; 25 | 26 | // Check ELF header 27 | if(readi(ip, (char*)&elf, 0, sizeof(elf)) < sizeof(elf)) 28 | goto bad; 29 | if(elf.magic != ELF_MAGIC) 30 | goto bad; 31 | 32 | if((pgdir = setupkvm()) == 0) 33 | goto bad; 34 | 35 | // Load program into memory. 36 | sz = 0; 37 | for(i=0, off=elf.phoff; i= MAXARG) 63 | goto bad; 64 | sp = (sp - (strlen(argv[argc]) + 1)) & ~(sizeof(uintp)-1); 65 | if(copyout(pgdir, sp, argv[argc], strlen(argv[argc]) + 1) < 0) 66 | goto bad; 67 | ustack[3+argc] = sp; 68 | } 69 | ustack[3+argc] = 0; 70 | 71 | ustack[0] = 0xffffffff; // fake return PC 72 | ustack[1] = argc; 73 | ustack[2] = sp - (argc+1)*sizeof(uintp); // argv pointer 74 | 75 | #if X64 76 | proc->tf->rdi = argc; 77 | proc->tf->rsi = sp - (argc+1)*sizeof(uintp); 78 | #endif 79 | 80 | sp -= (3+argc+1) * sizeof(uintp); 81 | if(copyout(pgdir, sp, ustack, (3+argc+1)*sizeof(uintp)) < 0) 82 | goto bad; 83 | 84 | // Save program name for debugging. 85 | for(last=s=path; *s; s++) 86 | if(*s == '/') 87 | last = s+1; 88 | safestrcpy(proc->name, last, sizeof(proc->name)); 89 | 90 | // Commit to the user image. 91 | oldpgdir = proc->pgdir; 92 | proc->pgdir = pgdir; 93 | proc->sz = sz; 94 | proc->tf->eip = elf.entry; // main 95 | proc->tf->esp = sp; 96 | switchuvm(proc); 97 | freevm(oldpgdir); 98 | return 0; 99 | 100 | bad: 101 | if(pgdir) 102 | freevm(pgdir); 103 | if(ip) 104 | iunlockput(ip); 105 | return -1; 106 | } 107 | -------------------------------------------------------------------------------- /kernel/file.c: -------------------------------------------------------------------------------- 1 | // 2 | // File descriptors 3 | // 4 | 5 | #include "types.h" 6 | #include "defs.h" 7 | #include "param.h" 8 | #include "fs.h" 9 | #include "file.h" 10 | #include "spinlock.h" 11 | 12 | struct devsw devsw[NDEV]; 13 | struct { 14 | struct spinlock lock; 15 | struct file file[NFILE]; 16 | } ftable; 17 | 18 | void 19 | fileinit(void) 20 | { 21 | initlock(&ftable.lock, "ftable"); 22 | } 23 | 24 | // Allocate a file structure. 25 | struct file* 26 | filealloc(void) 27 | { 28 | struct file *f; 29 | 30 | acquire(&ftable.lock); 31 | for(f = ftable.file; f < ftable.file + NFILE; f++){ 32 | if(f->ref == 0){ 33 | f->ref = 1; 34 | release(&ftable.lock); 35 | return f; 36 | } 37 | } 38 | release(&ftable.lock); 39 | return 0; 40 | } 41 | 42 | // Increment ref count for file f. 43 | struct file* 44 | filedup(struct file *f) 45 | { 46 | acquire(&ftable.lock); 47 | if(f->ref < 1) 48 | panic("filedup"); 49 | f->ref++; 50 | release(&ftable.lock); 51 | return f; 52 | } 53 | 54 | // Close file f. (Decrement ref count, close when reaches 0.) 55 | void 56 | fileclose(struct file *f) 57 | { 58 | struct file ff; 59 | 60 | acquire(&ftable.lock); 61 | if(f->ref < 1) 62 | panic("fileclose"); 63 | if(--f->ref > 0){ 64 | release(&ftable.lock); 65 | return; 66 | } 67 | ff = *f; 68 | f->ref = 0; 69 | f->type = FD_NONE; 70 | release(&ftable.lock); 71 | 72 | if(ff.type == FD_PIPE) 73 | pipeclose(ff.pipe, ff.writable); 74 | else if(ff.type == FD_INODE){ 75 | begin_trans(); 76 | iput(ff.ip); 77 | commit_trans(); 78 | } 79 | } 80 | 81 | // Get metadata about file f. 82 | int 83 | filestat(struct file *f, struct stat *st) 84 | { 85 | if(f->type == FD_INODE){ 86 | ilock(f->ip); 87 | stati(f->ip, st); 88 | iunlock(f->ip); 89 | return 0; 90 | } 91 | return -1; 92 | } 93 | 94 | // Read from file f. 95 | int 96 | fileread(struct file *f, char *addr, int n) 97 | { 98 | int r; 99 | 100 | if(f->readable == 0) 101 | return -1; 102 | if(f->type == FD_PIPE) 103 | return piperead(f->pipe, addr, n); 104 | if(f->type == FD_INODE){ 105 | ilock(f->ip); 106 | if((r = readi(f->ip, addr, f->off, n)) > 0) 107 | f->off += r; 108 | iunlock(f->ip); 109 | return r; 110 | } 111 | panic("fileread"); 112 | } 113 | 114 | //PAGEBREAK! 115 | // Write to file f. 116 | int 117 | filewrite(struct file *f, char *addr, int n) 118 | { 119 | int r; 120 | 121 | if(f->writable == 0) 122 | return -1; 123 | if(f->type == FD_PIPE) 124 | return pipewrite(f->pipe, addr, n); 125 | if(f->type == FD_INODE){ 126 | // write a few blocks at a time to avoid exceeding 127 | // the maximum log transaction size, including 128 | // i-node, indirect block, allocation blocks, 129 | // and 2 blocks of slop for non-aligned writes. 130 | // this really belongs lower down, since writei() 131 | // might be writing a device like the console. 132 | int max = ((LOGSIZE-1-1-2) / 2) * 512; 133 | int i = 0; 134 | while(i < n){ 135 | int n1 = n - i; 136 | if(n1 > max) 137 | n1 = max; 138 | 139 | begin_trans(); 140 | ilock(f->ip); 141 | if ((r = writei(f->ip, addr + i, f->off, n1)) > 0) 142 | f->off += r; 143 | iunlock(f->ip); 144 | commit_trans(); 145 | 146 | if(r < 0) 147 | break; 148 | if(r != n1) 149 | panic("short filewrite"); 150 | i += r; 151 | } 152 | return i == n ? n : -1; 153 | } 154 | panic("filewrite"); 155 | } 156 | 157 | -------------------------------------------------------------------------------- /kernel/ide.c: -------------------------------------------------------------------------------- 1 | // Simple PIO-based (non-DMA) IDE driver code. 2 | 3 | #include "types.h" 4 | #include "defs.h" 5 | #include "param.h" 6 | #include "memlayout.h" 7 | #include "mmu.h" 8 | #include "proc.h" 9 | #include "x86.h" 10 | #include "traps.h" 11 | #include "spinlock.h" 12 | #include "buf.h" 13 | 14 | #define IDE_BSY 0x80 15 | #define IDE_DRDY 0x40 16 | #define IDE_DF 0x20 17 | #define IDE_ERR 0x01 18 | 19 | #define IDE_CMD_READ 0x20 20 | #define IDE_CMD_WRITE 0x30 21 | 22 | // idequeue points to the buf now being read/written to the disk. 23 | // idequeue->qnext points to the next buf to be processed. 24 | // You must hold idelock while manipulating queue. 25 | 26 | static struct spinlock idelock; 27 | static struct buf *idequeue; 28 | 29 | static int havedisk1; 30 | static void idestart(struct buf*); 31 | 32 | // Wait for IDE disk to become ready. 33 | static int 34 | idewait(int checkerr) 35 | { 36 | int r; 37 | 38 | while(((r = inb(0x1f7)) & (IDE_BSY|IDE_DRDY)) != IDE_DRDY) 39 | ; 40 | if(checkerr && (r & (IDE_DF|IDE_ERR)) != 0) 41 | return -1; 42 | return 0; 43 | } 44 | 45 | void 46 | ideinit(void) 47 | { 48 | int i; 49 | 50 | initlock(&idelock, "ide"); 51 | picenable(IRQ_IDE); 52 | ioapicenable(IRQ_IDE, ncpu - 1); 53 | idewait(0); 54 | 55 | // Check if disk 1 is present 56 | outb(0x1f6, 0xe0 | (1<<4)); 57 | for(i=0; i<1000; i++){ 58 | if(inb(0x1f7) != 0){ 59 | havedisk1 = 1; 60 | break; 61 | } 62 | } 63 | 64 | // Switch back to disk 0. 65 | outb(0x1f6, 0xe0 | (0<<4)); 66 | } 67 | 68 | // Start the request for b. Caller must hold idelock. 69 | static void 70 | idestart(struct buf *b) 71 | { 72 | if(b == 0) 73 | panic("idestart"); 74 | 75 | idewait(0); 76 | outb(0x3f6, 0); // generate interrupt 77 | outb(0x1f2, 1); // number of sectors 78 | outb(0x1f3, b->sector & 0xff); 79 | outb(0x1f4, (b->sector >> 8) & 0xff); 80 | outb(0x1f5, (b->sector >> 16) & 0xff); 81 | outb(0x1f6, 0xe0 | ((b->dev&1)<<4) | ((b->sector>>24)&0x0f)); 82 | if(b->flags & B_DIRTY){ 83 | outb(0x1f7, IDE_CMD_WRITE); 84 | outsl(0x1f0, b->data, 512/4); 85 | } else { 86 | outb(0x1f7, IDE_CMD_READ); 87 | } 88 | } 89 | 90 | // Interrupt handler. 91 | void 92 | ideintr(void) 93 | { 94 | struct buf *b; 95 | 96 | // First queued buffer is the active request. 97 | acquire(&idelock); 98 | if((b = idequeue) == 0){ 99 | release(&idelock); 100 | // cprintf("spurious IDE interrupt\n"); 101 | return; 102 | } 103 | idequeue = b->qnext; 104 | 105 | // Read data if needed. 106 | if(!(b->flags & B_DIRTY) && idewait(1) >= 0) 107 | insl(0x1f0, b->data, 512/4); 108 | 109 | // Wake process waiting for this buf. 110 | b->flags |= B_VALID; 111 | b->flags &= ~B_DIRTY; 112 | wakeup(b); 113 | 114 | // Start disk on next buf in queue. 115 | if(idequeue != 0) 116 | idestart(idequeue); 117 | 118 | release(&idelock); 119 | } 120 | 121 | //PAGEBREAK! 122 | // Sync buf with disk. 123 | // If B_DIRTY is set, write buf to disk, clear B_DIRTY, set B_VALID. 124 | // Else if B_VALID is not set, read buf from disk, set B_VALID. 125 | void 126 | iderw(struct buf *b) 127 | { 128 | struct buf **pp; 129 | 130 | if(!(b->flags & B_BUSY)) 131 | panic("iderw: buf not busy"); 132 | if((b->flags & (B_VALID|B_DIRTY)) == B_VALID) 133 | panic("iderw: nothing to do"); 134 | if(b->dev != 0 && !havedisk1) 135 | panic("iderw: ide disk 1 not present"); 136 | 137 | acquire(&idelock); //DOC:acquire-lock 138 | 139 | // Append b to idequeue. 140 | b->qnext = 0; 141 | for(pp=&idequeue; *pp; pp=&(*pp)->qnext) //DOC:insert-queue 142 | ; 143 | *pp = b; 144 | 145 | // Start disk if necessary. 146 | if(idequeue == b) 147 | idestart(b); 148 | 149 | // Wait for request to finish. 150 | while((b->flags & (B_VALID|B_DIRTY)) != B_VALID){ 151 | sleep(b, &idelock); 152 | } 153 | 154 | release(&idelock); 155 | } 156 | -------------------------------------------------------------------------------- /kernel/initcode.S: -------------------------------------------------------------------------------- 1 | # Initial process execs /init. 2 | 3 | #include "syscall.h" 4 | #include "traps.h" 5 | 6 | 7 | # exec(init, argv) 8 | .globl start 9 | start: 10 | pushl $argv 11 | pushl $init 12 | pushl $0 // where caller pc would be 13 | movl $SYS_exec, %eax 14 | int $T_SYSCALL 15 | 16 | # for(;;) exit(); 17 | exit: 18 | movl $SYS_exit, %eax 19 | int $T_SYSCALL 20 | jmp exit 21 | 22 | # char init[] = "/init\0"; 23 | init: 24 | .string "/init\0" 25 | 26 | # char *argv[] = { init, 0 }; 27 | .p2align 2 28 | argv: 29 | .long init 30 | .long 0 31 | 32 | -------------------------------------------------------------------------------- /kernel/initcode64.S: -------------------------------------------------------------------------------- 1 | # Initial process execs /init. 2 | 3 | #include "syscall.h" 4 | #include "traps.h" 5 | 6 | 7 | # exec(init, argv) 8 | .globl start 9 | start: 10 | mov $init, %rdi 11 | mov $argv, %rsi 12 | mov $SYS_exec, %rax 13 | int $T_SYSCALL 14 | 15 | # for(;;) exit(); 16 | exit: 17 | mov $SYS_exit, %rax 18 | int $T_SYSCALL 19 | jmp exit 20 | 21 | # char init[] = "/init\0"; 22 | init: 23 | .string "/init\0" 24 | 25 | # char *argv[] = { init, 0 }; 26 | .p2align 2 27 | argv: 28 | .quad init 29 | .quad 0 30 | 31 | -------------------------------------------------------------------------------- /kernel/ioapic.c: -------------------------------------------------------------------------------- 1 | // The I/O APIC manages hardware interrupts for an SMP system. 2 | // http://www.intel.com/design/chipsets/datashts/29056601.pdf 3 | // See also picirq.c. 4 | 5 | #include "types.h" 6 | #include "defs.h" 7 | #include "traps.h" 8 | #include "memlayout.h" 9 | 10 | #define IOAPIC 0xFEC00000 // Default physical address of IO APIC 11 | 12 | #define REG_ID 0x00 // Register index: ID 13 | #define REG_VER 0x01 // Register index: version 14 | #define REG_TABLE 0x10 // Redirection table base 15 | 16 | // The redirection table starts at REG_TABLE and uses 17 | // two registers to configure each interrupt. 18 | // The first (low) register in a pair contains configuration bits. 19 | // The second (high) register contains a bitmask telling which 20 | // CPUs can serve that interrupt. 21 | #define INT_DISABLED 0x00010000 // Interrupt disabled 22 | #define INT_LEVEL 0x00008000 // Level-triggered (vs edge-) 23 | #define INT_ACTIVELOW 0x00002000 // Active low (vs high) 24 | #define INT_LOGICAL 0x00000800 // Destination is CPU id (vs APIC ID) 25 | 26 | volatile struct ioapic *ioapic; 27 | 28 | // IO APIC MMIO structure: write reg, then read or write data. 29 | struct ioapic { 30 | uint reg; 31 | uint pad[3]; 32 | uint data; 33 | }; 34 | 35 | static uint 36 | ioapicread(int reg) 37 | { 38 | ioapic->reg = reg; 39 | return ioapic->data; 40 | } 41 | 42 | static void 43 | ioapicwrite(int reg, uint data) 44 | { 45 | ioapic->reg = reg; 46 | ioapic->data = data; 47 | } 48 | 49 | void 50 | ioapicinit(void) 51 | { 52 | int i, id, maxintr; 53 | 54 | if(!ismp) 55 | return; 56 | 57 | ioapic = (volatile struct ioapic*) IO2V(IOAPIC); 58 | maxintr = (ioapicread(REG_VER) >> 16) & 0xFF; 59 | id = ioapicread(REG_ID) >> 24; 60 | if(id != ioapicid) 61 | cprintf("ioapicinit: id isn't equal to ioapicid; not a MP\n"); 62 | 63 | // Mark all interrupts edge-triggered, active high, disabled, 64 | // and not routed to any CPUs. 65 | for(i = 0; i <= maxintr; i++){ 66 | ioapicwrite(REG_TABLE+2*i, INT_DISABLED | (T_IRQ0 + i)); 67 | ioapicwrite(REG_TABLE+2*i+1, 0); 68 | } 69 | } 70 | 71 | void 72 | ioapicenable(int irq, int cpunum) 73 | { 74 | if(!ismp) 75 | return; 76 | 77 | // Mark interrupt edge-triggered, active high, 78 | // enabled, and routed to the given cpunum, 79 | // which happens to be that cpu's APIC ID. 80 | ioapicwrite(REG_TABLE+2*irq, T_IRQ0 + irq); 81 | ioapicwrite(REG_TABLE+2*irq+1, cpunum << 24); 82 | } 83 | -------------------------------------------------------------------------------- /kernel/kalloc.c: -------------------------------------------------------------------------------- 1 | // Physical memory allocator, intended to allocate 2 | // memory for user processes, kernel stacks, page table pages, 3 | // and pipe buffers. Allocates 4096-byte pages. 4 | 5 | #include "types.h" 6 | #include "defs.h" 7 | #include "param.h" 8 | #include "memlayout.h" 9 | #include "mmu.h" 10 | #include "spinlock.h" 11 | 12 | void freerange(void *vstart, void *vend); 13 | extern char end[]; // first address after kernel loaded from ELF file 14 | 15 | struct run { 16 | struct run *next; 17 | }; 18 | 19 | struct { 20 | struct spinlock lock; 21 | int use_lock; 22 | struct run *freelist; 23 | } kmem; 24 | 25 | // Initialization happens in two phases. 26 | // 1. main() calls kinit1() while still using entrypgdir to place just 27 | // the pages mapped by entrypgdir on free list. 28 | // 2. main() calls kinit2() with the rest of the physical pages 29 | // after installing a full page table that maps them on all cores. 30 | void 31 | kinit1(void *vstart, void *vend) 32 | { 33 | initlock(&kmem.lock, "kmem"); 34 | kmem.use_lock = 0; 35 | freerange(vstart, vend); 36 | } 37 | 38 | void 39 | kinit2(void *vstart, void *vend) 40 | { 41 | freerange(vstart, vend); 42 | kmem.use_lock = 1; 43 | } 44 | 45 | void 46 | freerange(void *vstart, void *vend) 47 | { 48 | char *p; 49 | p = (char*)PGROUNDUP((uintp)vstart); 50 | for(; p + PGSIZE <= (char*)vend; p += PGSIZE) 51 | kfree(p); 52 | } 53 | 54 | //PAGEBREAK: 21 55 | // Free the page of physical memory pointed at by v, 56 | // which normally should have been returned by a 57 | // call to kalloc(). (The exception is when 58 | // initializing the allocator; see kinit above.) 59 | void 60 | kfree(char *v) 61 | { 62 | struct run *r; 63 | 64 | if((uintp)v % PGSIZE || v < end || v2p(v) >= PHYSTOP) 65 | panic("kfree"); 66 | 67 | // Fill with junk to catch dangling refs. 68 | memset(v, 1, PGSIZE); 69 | 70 | if(kmem.use_lock) 71 | acquire(&kmem.lock); 72 | r = (struct run*)v; 73 | r->next = kmem.freelist; 74 | kmem.freelist = r; 75 | if(kmem.use_lock) 76 | release(&kmem.lock); 77 | } 78 | 79 | // Allocate one 4096-byte page of physical memory. 80 | // Returns a pointer that the kernel can use. 81 | // Returns 0 if the memory cannot be allocated. 82 | char* 83 | kalloc(void) 84 | { 85 | struct run *r; 86 | 87 | if(kmem.use_lock) 88 | acquire(&kmem.lock); 89 | r = kmem.freelist; 90 | if(r) 91 | kmem.freelist = r->next; 92 | if(kmem.use_lock) 93 | release(&kmem.lock); 94 | return (char*)r; 95 | } 96 | 97 | -------------------------------------------------------------------------------- /kernel/kbd.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "x86.h" 3 | #include "defs.h" 4 | #include "kbd.h" 5 | 6 | int 7 | kbdgetc(void) 8 | { 9 | static uint shift; 10 | static uchar *charcode[4] = { 11 | normalmap, shiftmap, ctlmap, ctlmap 12 | }; 13 | uint st, data, c; 14 | 15 | st = inb(KBSTATP); 16 | if((st & KBS_DIB) == 0) 17 | return -1; 18 | data = inb(KBDATAP); 19 | 20 | if(data == 0xE0){ 21 | shift |= E0ESC; 22 | return 0; 23 | } else if(data & 0x80){ 24 | // Key released 25 | data = (shift & E0ESC ? data : data & 0x7F); 26 | shift &= ~(shiftcode[data] | E0ESC); 27 | return 0; 28 | } else if(shift & E0ESC){ 29 | // Last character was an E0 escape; or with 0x80 30 | data |= 0x80; 31 | shift &= ~E0ESC; 32 | } 33 | 34 | shift |= shiftcode[data]; 35 | shift ^= togglecode[data]; 36 | c = charcode[shift & (CTL | SHIFT)][data]; 37 | if(shift & CAPSLOCK){ 38 | if('a' <= c && c <= 'z') 39 | c += 'A' - 'a'; 40 | else if('A' <= c && c <= 'Z') 41 | c += 'a' - 'A'; 42 | } 43 | return c; 44 | } 45 | 46 | void 47 | kbdintr(void) 48 | { 49 | consoleintr(kbdgetc); 50 | } 51 | -------------------------------------------------------------------------------- /kernel/kernel.ld: -------------------------------------------------------------------------------- 1 | /* Simple linker script for the JOS kernel. 2 | See the GNU ld 'info' manual ("info ld") to learn the syntax. */ 3 | 4 | OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") 5 | OUTPUT_ARCH(i386) 6 | ENTRY(_start) 7 | 8 | SECTIONS 9 | { 10 | /* Link the kernel at this address: "." means the current address */ 11 | /* Must be equal to KERNLINK */ 12 | . = 0x80100000; 13 | 14 | .text : AT(0x100000) { 15 | *(.text .stub .text.* .gnu.linkonce.t.*) 16 | } 17 | 18 | PROVIDE(etext = .); /* Define the 'etext' symbol to this value */ 19 | 20 | .rodata : { 21 | *(.rodata .rodata.* .gnu.linkonce.r.*) 22 | } 23 | 24 | /* Include debugging information in kernel memory */ 25 | .stab : { 26 | PROVIDE(__STAB_BEGIN__ = .); 27 | *(.stab); 28 | PROVIDE(__STAB_END__ = .); 29 | BYTE(0) /* Force the linker to allocate space 30 | for this section */ 31 | } 32 | 33 | .stabstr : { 34 | PROVIDE(__STABSTR_BEGIN__ = .); 35 | *(.stabstr); 36 | PROVIDE(__STABSTR_END__ = .); 37 | BYTE(0) /* Force the linker to allocate space 38 | for this section */ 39 | } 40 | 41 | /* Adjust the address for the data segment to the next page */ 42 | . = ALIGN(0x1000); 43 | 44 | /* Conventionally, Unix linkers provide pseudo-symbols 45 | * etext, edata, and end, at the end of the text, data, and bss. 46 | * For the kernel mapping, we need the address at the beginning 47 | * of the data section, but that's not one of the conventional 48 | * symbols, because the convention started before there was a 49 | * read-only rodata section between text and data. */ 50 | PROVIDE(data = .); 51 | 52 | /* The data segment */ 53 | .data : { 54 | *(.data) 55 | } 56 | 57 | PROVIDE(edata = .); 58 | 59 | .bss : { 60 | *(.bss) 61 | } 62 | 63 | PROVIDE(end = .); 64 | 65 | /DISCARD/ : { 66 | *(.eh_frame .note.GNU-stack) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /kernel/kernel64.ld: -------------------------------------------------------------------------------- 1 | /* Simple linker script for the JOS kernel. 2 | See the GNU ld 'info' manual ("info ld") to learn the syntax. */ 3 | 4 | /* OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") */ 5 | OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64") 6 | OUTPUT_ARCH(i386:x86-64) 7 | ENTRY(_start) 8 | 9 | mboot_load_addr = 0x00100000; 10 | 11 | SECTIONS 12 | { 13 | /* Link the kernel at this address: "." means the current address */ 14 | /* Must be equal to KERNLINK */ 15 | . = 0xFFFFFFFF80100000; 16 | 17 | PROVIDE(begin = .); 18 | 19 | .text : AT(mboot_load_addr) { 20 | *(.text .rela.text .stub .text.* .gnu.linkonce.t.*) 21 | } 22 | 23 | PROVIDE(etext = .); /* Define the 'etext' symbol to this value */ 24 | 25 | .rodata : { 26 | *(.rodata .rodata.* .gnu.linkonce.r.*) 27 | } 28 | 29 | /* Adjust the address for the data segment to the next page */ 30 | . = ALIGN(0x1000); 31 | 32 | /* Conventionally, Unix linkers provide pseudo-symbols 33 | * etext, edata, and end, at the end of the text, data, and bss. 34 | * For the kernel mapping, we need the address at the beginning 35 | * of the data section, but that's not one of the conventional 36 | * symbols, because the convention started before there was a 37 | * read-only rodata section between text and data. */ 38 | PROVIDE(data = .); 39 | 40 | /* The data segment */ 41 | .data : { 42 | *(.data) 43 | } 44 | 45 | . = ALIGN(0x1000); 46 | 47 | PROVIDE(edata = .); 48 | 49 | .bss : { 50 | *(.bss) 51 | *(COMMON) 52 | } 53 | 54 | . = ALIGN(0x1000); 55 | 56 | PROVIDE(end = .); 57 | 58 | /DISCARD/ : { 59 | *(.eh_frame .rela.eh_frame .note.GNU-stack) 60 | } 61 | } 62 | 63 | mboot_load_end = mboot_load_addr + (edata - begin); 64 | mboot_bss_end = mboot_load_addr + (end - begin); 65 | mboot_entry_addr = mboot_load_addr + (mboot_entry - begin); 66 | -------------------------------------------------------------------------------- /kernel/lapic.c: -------------------------------------------------------------------------------- 1 | // The local APIC manages internal (non-I/O) interrupts. 2 | // See Chapter 8 & Appendix C of Intel processor manual volume 3. 3 | 4 | #include "types.h" 5 | #include "defs.h" 6 | #include "memlayout.h" 7 | #include "traps.h" 8 | #include "mmu.h" 9 | #include "x86.h" 10 | #include "param.h" 11 | #include "proc.h" 12 | 13 | // Local APIC registers, divided by 4 for use as uint[] indices. 14 | #define ID (0x0020/4) // ID 15 | #define VER (0x0030/4) // Version 16 | #define TPR (0x0080/4) // Task Priority 17 | #define EOI (0x00B0/4) // EOI 18 | #define SVR (0x00F0/4) // Spurious Interrupt Vector 19 | #define ENABLE 0x00000100 // Unit Enable 20 | #define ESR (0x0280/4) // Error Status 21 | #define ICRLO (0x0300/4) // Interrupt Command 22 | #define INIT 0x00000500 // INIT/RESET 23 | #define STARTUP 0x00000600 // Startup IPI 24 | #define DELIVS 0x00001000 // Delivery status 25 | #define ASSERT 0x00004000 // Assert interrupt (vs deassert) 26 | #define DEASSERT 0x00000000 27 | #define LEVEL 0x00008000 // Level triggered 28 | #define BCAST 0x00080000 // Send to all APICs, including self. 29 | #define BUSY 0x00001000 30 | #define FIXED 0x00000000 31 | #define ICRHI (0x0310/4) // Interrupt Command [63:32] 32 | #define TIMER (0x0320/4) // Local Vector Table 0 (TIMER) 33 | #define X1 0x0000000B // divide counts by 1 34 | #define PERIODIC 0x00020000 // Periodic 35 | #define PCINT (0x0340/4) // Performance Counter LVT 36 | #define LINT0 (0x0350/4) // Local Vector Table 1 (LINT0) 37 | #define LINT1 (0x0360/4) // Local Vector Table 2 (LINT1) 38 | #define ERROR (0x0370/4) // Local Vector Table 3 (ERROR) 39 | #define MASKED 0x00010000 // Interrupt masked 40 | #define TICR (0x0380/4) // Timer Initial Count 41 | #define TCCR (0x0390/4) // Timer Current Count 42 | #define TDCR (0x03E0/4) // Timer Divide Configuration 43 | 44 | volatile uint *lapic; // Initialized in mp.c 45 | 46 | static void 47 | lapicw(int index, int value) 48 | { 49 | lapic[index] = value; 50 | lapic[ID]; // wait for write to finish, by reading 51 | } 52 | //PAGEBREAK! 53 | 54 | void 55 | lapicinit(void) 56 | { 57 | if(!lapic) 58 | return; 59 | 60 | // Enable local APIC; set spurious interrupt vector. 61 | lapicw(SVR, ENABLE | (T_IRQ0 + IRQ_SPURIOUS)); 62 | 63 | // The timer repeatedly counts down at bus frequency 64 | // from lapic[TICR] and then issues an interrupt. 65 | // If xv6 cared more about precise timekeeping, 66 | // TICR would be calibrated using an external time source. 67 | lapicw(TDCR, X1); 68 | lapicw(TIMER, PERIODIC | (T_IRQ0 + IRQ_TIMER)); 69 | lapicw(TICR, 10000000); 70 | 71 | // Disable logical interrupt lines. 72 | lapicw(LINT0, MASKED); 73 | lapicw(LINT1, MASKED); 74 | 75 | // Disable performance counter overflow interrupts 76 | // on machines that provide that interrupt entry. 77 | if(((lapic[VER]>>16) & 0xFF) >= 4) 78 | lapicw(PCINT, MASKED); 79 | 80 | // Map error interrupt to IRQ_ERROR. 81 | lapicw(ERROR, T_IRQ0 + IRQ_ERROR); 82 | 83 | // Clear error status register (requires back-to-back writes). 84 | lapicw(ESR, 0); 85 | lapicw(ESR, 0); 86 | 87 | // Ack any outstanding interrupts. 88 | lapicw(EOI, 0); 89 | 90 | // Send an Init Level De-Assert to synchronise arbitration ID's. 91 | lapicw(ICRHI, 0); 92 | lapicw(ICRLO, BCAST | INIT | LEVEL); 93 | while(lapic[ICRLO] & DELIVS) 94 | ; 95 | 96 | // Enable interrupts on the APIC (but not on the processor). 97 | lapicw(TPR, 0); 98 | } 99 | 100 | // This is only used during secondary processor startup. 101 | // cpu->id is the fast way to get the cpu number, once the 102 | // processor is fully started. 103 | int 104 | cpunum(void) 105 | { 106 | int n, id; 107 | // Cannot call cpu when interrupts are enabled: 108 | // result not guaranteed to last long enough to be used! 109 | // Would prefer to panic but even printing is chancy here: 110 | // almost everything, including cprintf and panic, calls cpu, 111 | // often indirectly through acquire and release. 112 | if(readeflags()&FL_IF){ 113 | static int n; 114 | if(n++ == 0) 115 | cprintf("cpu called from %x with interrupts enabled\n", 116 | __builtin_return_address(0)); 117 | } 118 | 119 | if(!lapic) 120 | return 0; 121 | 122 | id = lapic[ID]>>24; 123 | for (n = 0; n < ncpu; n++) 124 | if (id == cpus[n].apicid) 125 | return n; 126 | 127 | return 0; 128 | } 129 | 130 | // Acknowledge interrupt. 131 | void 132 | lapiceoi(void) 133 | { 134 | if(lapic) 135 | lapicw(EOI, 0); 136 | } 137 | 138 | // Spin for a given number of microseconds. 139 | // On real hardware would want to tune this dynamically. 140 | void 141 | microdelay(int us) 142 | { 143 | } 144 | 145 | #define IO_RTC 0x70 146 | 147 | // Start additional processor running entry code at addr. 148 | // See Appendix B of MultiProcessor Specification. 149 | void 150 | lapicstartap(uchar apicid, uint addr) 151 | { 152 | int i; 153 | ushort *wrv; 154 | 155 | // "The BSP must initialize CMOS shutdown code to 0AH 156 | // and the warm reset vector (DWORD based at 40:67) to point at 157 | // the AP startup code prior to the [universal startup algorithm]." 158 | outb(IO_RTC, 0xF); // offset 0xF is shutdown code 159 | outb(IO_RTC+1, 0x0A); 160 | wrv = (ushort*)P2V((0x40<<4 | 0x67)); // Warm reset vector 161 | wrv[0] = 0; 162 | wrv[1] = addr >> 4; 163 | 164 | // "Universal startup algorithm." 165 | // Send INIT (level-triggered) interrupt to reset other CPU. 166 | lapicw(ICRHI, apicid<<24); 167 | lapicw(ICRLO, INIT | LEVEL | ASSERT); 168 | microdelay(200); 169 | lapicw(ICRLO, INIT | LEVEL); 170 | microdelay(100); // should be 10ms, but too slow in Bochs! 171 | 172 | // Send startup IPI (twice!) to enter code. 173 | // Regular hardware is supposed to only accept a STARTUP 174 | // when it is in the halted state due to an INIT. So the second 175 | // should be ignored, but it is part of the official Intel algorithm. 176 | // Bochs complains about the second one. Too bad for Bochs. 177 | for(i = 0; i < 2; i++){ 178 | lapicw(ICRHI, apicid<<24); 179 | lapicw(ICRLO, STARTUP | (addr>>12)); 180 | microdelay(200); 181 | } 182 | } 183 | 184 | 185 | -------------------------------------------------------------------------------- /kernel/log.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "defs.h" 3 | #include "param.h" 4 | #include "spinlock.h" 5 | #include "fs.h" 6 | #include "buf.h" 7 | 8 | // Simple logging. Each system call that might write the file system 9 | // should be surrounded with begin_trans() and commit_trans() calls. 10 | // 11 | // The log holds at most one transaction at a time. Commit forces 12 | // the log (with commit record) to disk, then installs the affected 13 | // blocks to disk, then erases the log. begin_trans() ensures that 14 | // only one system call can be in a transaction; others must wait. 15 | // 16 | // Allowing only one transaction at a time means that the file 17 | // system code doesn't have to worry about the possibility of 18 | // one transaction reading a block that another one has modified, 19 | // for example an i-node block. 20 | // 21 | // Read-only system calls don't need to use transactions, though 22 | // this means that they may observe uncommitted data. I-node and 23 | // buffer locks prevent read-only calls from seeing inconsistent data. 24 | // 25 | // The log is a physical re-do log containing disk blocks. 26 | // The on-disk log format: 27 | // header block, containing sector #s for block A, B, C, ... 28 | // block A 29 | // block B 30 | // block C 31 | // ... 32 | // Log appends are synchronous. 33 | 34 | // Contents of the header block, used for both the on-disk header block 35 | // and to keep track in memory of logged sector #s before commit. 36 | struct logheader { 37 | int n; 38 | int sector[LOGSIZE]; 39 | }; 40 | 41 | struct log { 42 | struct spinlock lock; 43 | int start; 44 | int size; 45 | int busy; // a transaction is active 46 | int dev; 47 | struct logheader lh; 48 | }; 49 | struct log log; 50 | 51 | static void recover_from_log(void); 52 | 53 | void 54 | initlog(void) 55 | { 56 | if (sizeof(struct logheader) >= BSIZE) 57 | panic("initlog: too big logheader"); 58 | 59 | struct superblock sb; 60 | initlock(&log.lock, "log"); 61 | readsb(ROOTDEV, &sb); 62 | log.start = sb.size - sb.nlog; 63 | log.size = sb.nlog; 64 | log.dev = ROOTDEV; 65 | recover_from_log(); 66 | } 67 | 68 | // Copy committed blocks from log to their home location 69 | static void 70 | install_trans(void) 71 | { 72 | int tail; 73 | 74 | for (tail = 0; tail < log.lh.n; tail++) { 75 | struct buf *lbuf = bread(log.dev, log.start+tail+1); // read log block 76 | struct buf *dbuf = bread(log.dev, log.lh.sector[tail]); // read dst 77 | memmove(dbuf->data, lbuf->data, BSIZE); // copy block to dst 78 | bwrite(dbuf); // write dst to disk 79 | brelse(lbuf); 80 | brelse(dbuf); 81 | } 82 | } 83 | 84 | // Read the log header from disk into the in-memory log header 85 | static void 86 | read_head(void) 87 | { 88 | struct buf *buf = bread(log.dev, log.start); 89 | struct logheader *lh = (struct logheader *) (buf->data); 90 | int i; 91 | log.lh.n = lh->n; 92 | for (i = 0; i < log.lh.n; i++) { 93 | log.lh.sector[i] = lh->sector[i]; 94 | } 95 | brelse(buf); 96 | } 97 | 98 | // Write in-memory log header to disk. 99 | // This is the true point at which the 100 | // current transaction commits. 101 | static void 102 | write_head(void) 103 | { 104 | struct buf *buf = bread(log.dev, log.start); 105 | struct logheader *hb = (struct logheader *) (buf->data); 106 | int i; 107 | hb->n = log.lh.n; 108 | for (i = 0; i < log.lh.n; i++) { 109 | hb->sector[i] = log.lh.sector[i]; 110 | } 111 | bwrite(buf); 112 | brelse(buf); 113 | } 114 | 115 | static void 116 | recover_from_log(void) 117 | { 118 | read_head(); 119 | install_trans(); // if committed, copy from log to disk 120 | log.lh.n = 0; 121 | write_head(); // clear the log 122 | } 123 | 124 | void 125 | begin_trans(void) 126 | { 127 | acquire(&log.lock); 128 | while (log.busy) { 129 | sleep(&log, &log.lock); 130 | } 131 | log.busy = 1; 132 | release(&log.lock); 133 | } 134 | 135 | void 136 | commit_trans(void) 137 | { 138 | if (log.lh.n > 0) { 139 | write_head(); // Write header to disk -- the real commit 140 | install_trans(); // Now install writes to home locations 141 | log.lh.n = 0; 142 | write_head(); // Erase the transaction from the log 143 | } 144 | 145 | acquire(&log.lock); 146 | log.busy = 0; 147 | wakeup(&log); 148 | release(&log.lock); 149 | } 150 | 151 | // Caller has modified b->data and is done with the buffer. 152 | // Append the block to the log and record the block number, 153 | // but don't write the log header (which would commit the write). 154 | // log_write() replaces bwrite(); a typical use is: 155 | // bp = bread(...) 156 | // modify bp->data[] 157 | // log_write(bp) 158 | // brelse(bp) 159 | void 160 | log_write(struct buf *b) 161 | { 162 | int i; 163 | 164 | if (log.lh.n >= LOGSIZE || log.lh.n >= log.size - 1) 165 | panic("too big a transaction"); 166 | if (!log.busy) 167 | panic("write outside of trans"); 168 | 169 | for (i = 0; i < log.lh.n; i++) { 170 | if (log.lh.sector[i] == b->sector) // log absorbtion? 171 | break; 172 | } 173 | log.lh.sector[i] = b->sector; 174 | struct buf *lbuf = bread(b->dev, log.start+i+1); 175 | memmove(lbuf->data, b->data, BSIZE); 176 | bwrite(lbuf); 177 | brelse(lbuf); 178 | if (i == log.lh.n) 179 | log.lh.n++; 180 | b->flags |= B_DIRTY; // XXX prevent eviction 181 | } 182 | 183 | //PAGEBREAK! 184 | // Blank page. 185 | 186 | -------------------------------------------------------------------------------- /kernel/main.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "defs.h" 3 | #include "param.h" 4 | #include "memlayout.h" 5 | #include "mmu.h" 6 | #include "proc.h" 7 | #include "x86.h" 8 | 9 | static void startothers(void); 10 | static void mpmain(void) __attribute__((noreturn)); 11 | extern pde_t *kpgdir; 12 | extern char end[]; // first address after kernel loaded from ELF file 13 | 14 | // Bootstrap processor starts running C code here. 15 | // Allocate a real stack and switch to it, first 16 | // doing some setup required for memory allocator to work. 17 | int 18 | main(void) 19 | { 20 | uartearlyinit(); 21 | kinit1(end, P2V(4*1024*1024)); // phys page allocator 22 | kvmalloc(); // kernel page table 23 | if (acpiinit()) // try to use acpi for machine info 24 | mpinit(); // otherwise use bios MP tables 25 | lapicinit(); 26 | seginit(); // set up segments 27 | cprintf("\ncpu%d: starting xv6\n\n", cpu->id); 28 | picinit(); // interrupt controller 29 | ioapicinit(); // another interrupt controller 30 | consoleinit(); // I/O devices & their interrupts 31 | uartinit(); // serial port 32 | pinit(); // process table 33 | tvinit(); // trap vectors 34 | binit(); // buffer cache 35 | fileinit(); // file table 36 | iinit(); // inode cache 37 | ideinit(); // disk 38 | if(!ismp) 39 | timerinit(); // uniprocessor timer 40 | startothers(); // start other processors 41 | kinit2(P2V(4*1024*1024), P2V(PHYSTOP)); // must come after startothers() 42 | userinit(); // first user process 43 | // Finish setting up this processor in mpmain. 44 | mpmain(); 45 | } 46 | 47 | // Other CPUs jump here from entryother.S. 48 | void 49 | mpenter(void) 50 | { 51 | switchkvm(); 52 | seginit(); 53 | lapicinit(); 54 | mpmain(); 55 | } 56 | 57 | // Common CPU setup code. 58 | static void 59 | mpmain(void) 60 | { 61 | cprintf("cpu%d: starting\n", cpu->id); 62 | idtinit(); // load idt register 63 | xchg(&cpu->started, 1); // tell startothers() we're up 64 | scheduler(); // start running processes 65 | } 66 | 67 | pde_t entrypgdir[]; // For entry.S 68 | void entry32mp(void); 69 | 70 | // Start the non-boot (AP) processors. 71 | static void 72 | startothers(void) 73 | { 74 | extern uchar _binary_out_entryother_start[], _binary_out_entryother_size[]; 75 | uchar *code; 76 | struct cpu *c; 77 | char *stack; 78 | 79 | // Write entry code to unused memory at 0x7000. 80 | // The linker has placed the image of entryother.S in 81 | // _binary_entryother_start. 82 | code = p2v(0x7000); 83 | memmove(code, _binary_out_entryother_start, (uintp)_binary_out_entryother_size); 84 | 85 | for(c = cpus; c < cpus+ncpu; c++){ 86 | if(c == cpus+cpunum()) // We've started already. 87 | continue; 88 | 89 | // Tell entryother.S what stack to use, where to enter, and what 90 | // pgdir to use. We cannot use kpgdir yet, because the AP processor 91 | // is running in low memory, so we use entrypgdir for the APs too. 92 | stack = kalloc(); 93 | #if X64 94 | *(uint32*)(code-4) = 0x8000; // just enough stack to get us to entry64mp 95 | *(uint32*)(code-8) = v2p(entry32mp); 96 | *(uint64*)(code-16) = (uint64) (stack + KSTACKSIZE); 97 | #else 98 | *(void**)(code-4) = stack + KSTACKSIZE; 99 | *(void**)(code-8) = mpenter; 100 | *(int**)(code-12) = (void *) v2p(entrypgdir); 101 | #endif 102 | 103 | lapicstartap(c->apicid, v2p(code)); 104 | 105 | // wait for cpu to finish mpmain() 106 | while(c->started == 0) 107 | ; 108 | } 109 | } 110 | 111 | #ifndef X64 112 | // Boot page table used in entry.S and entryother.S. 113 | // Page directories (and page tables), must start on a page boundary, 114 | // hence the "__aligned__" attribute. 115 | // Use PTE_PS in page directory entry to enable 4Mbyte pages. 116 | __attribute__((__aligned__(PGSIZE))) 117 | pde_t entrypgdir[NPDENTRIES] = { 118 | // Map VA's [0, 4MB) to PA's [0, 4MB) 119 | [0] = (0) | PTE_P | PTE_W | PTE_PS, 120 | // Map VA's [KERNBASE, KERNBASE+4MB) to PA's [0, 4MB) 121 | [KERNBASE>>PDXSHIFT] = (0) | PTE_P | PTE_W | PTE_PS, 122 | }; 123 | #endif 124 | 125 | //PAGEBREAK! 126 | // Blank page. 127 | 128 | -------------------------------------------------------------------------------- /kernel/memide.c: -------------------------------------------------------------------------------- 1 | // Fake IDE disk; stores blocks in memory. 2 | // Useful for running kernel without scratch disk. 3 | 4 | #include "types.h" 5 | #include "defs.h" 6 | #include "param.h" 7 | #include "mmu.h" 8 | #include "proc.h" 9 | #include "x86.h" 10 | #include "traps.h" 11 | #include "spinlock.h" 12 | #include "buf.h" 13 | 14 | extern uchar _binary_fs_img_start[], _binary_fs_img_size[]; 15 | 16 | static int disksize; 17 | static uchar *memdisk; 18 | 19 | void 20 | ideinit(void) 21 | { 22 | memdisk = _binary_fs_img_start; 23 | disksize = (uint)_binary_fs_img_size/512; 24 | } 25 | 26 | // Interrupt handler. 27 | void 28 | ideintr(void) 29 | { 30 | // no-op 31 | } 32 | 33 | // Sync buf with disk. 34 | // If B_DIRTY is set, write buf to disk, clear B_DIRTY, set B_VALID. 35 | // Else if B_VALID is not set, read buf from disk, set B_VALID. 36 | void 37 | iderw(struct buf *b) 38 | { 39 | uchar *p; 40 | 41 | if(!(b->flags & B_BUSY)) 42 | panic("iderw: buf not busy"); 43 | if((b->flags & (B_VALID|B_DIRTY)) == B_VALID) 44 | panic("iderw: nothing to do"); 45 | if(b->dev != 1) 46 | panic("iderw: request not for disk 1"); 47 | if(b->sector >= disksize) 48 | panic("iderw: sector out of range"); 49 | 50 | p = memdisk + b->sector*512; 51 | 52 | if(b->flags & B_DIRTY){ 53 | b->flags &= ~B_DIRTY; 54 | memmove(p, b->data, 512); 55 | } else 56 | memmove(b->data, p, 512); 57 | b->flags |= B_VALID; 58 | } 59 | -------------------------------------------------------------------------------- /kernel/mp.c: -------------------------------------------------------------------------------- 1 | // Multiprocessor support 2 | // Search memory for MP description structures. 3 | // http://developer.intel.com/design/pentium/datashts/24201606.pdf 4 | 5 | #include "types.h" 6 | #include "defs.h" 7 | #include "param.h" 8 | #include "memlayout.h" 9 | #include "mp.h" 10 | #include "x86.h" 11 | #include "mmu.h" 12 | #include "proc.h" 13 | 14 | struct cpu cpus[NCPU]; 15 | static struct cpu *bcpu; 16 | int ismp; 17 | int ncpu; 18 | uchar ioapicid; 19 | 20 | int 21 | mpbcpu(void) 22 | { 23 | return bcpu-cpus; 24 | } 25 | 26 | static uchar 27 | sum(uchar *addr, int len) 28 | { 29 | int i, sum; 30 | 31 | sum = 0; 32 | for(i=0; iphysaddr == 0) 87 | return 0; 88 | conf = (struct mpconf*) p2v((uintp) mp->physaddr); 89 | if(memcmp(conf, "PCMP", 4) != 0) 90 | return 0; 91 | if(conf->version != 1 && conf->version != 4) 92 | return 0; 93 | if(sum((uchar*)conf, conf->length) != 0) 94 | return 0; 95 | *pmp = mp; 96 | return conf; 97 | } 98 | 99 | void 100 | mpinit(void) 101 | { 102 | uchar *p, *e; 103 | struct mp *mp; 104 | struct mpconf *conf; 105 | struct mpproc *proc; 106 | struct mpioapic *ioapic; 107 | 108 | bcpu = &cpus[0]; 109 | if((conf = mpconfig(&mp)) == 0) 110 | return; 111 | ismp = 1; 112 | lapic = IO2V((uintp)conf->lapicaddr); 113 | for(p=(uchar*)(conf+1), e=(uchar*)conf+conf->length; papicid); 118 | if(proc->flags & MPBOOT) 119 | bcpu = &cpus[ncpu]; 120 | cpus[ncpu].id = ncpu; 121 | cpus[ncpu].apicid = proc->apicid; 122 | ncpu++; 123 | p += sizeof(struct mpproc); 124 | continue; 125 | case MPIOAPIC: 126 | ioapic = (struct mpioapic*)p; 127 | ioapicid = ioapic->apicno; 128 | p += sizeof(struct mpioapic); 129 | continue; 130 | case MPBUS: 131 | case MPIOINTR: 132 | case MPLINTR: 133 | p += 8; 134 | continue; 135 | default: 136 | cprintf("mpinit: unknown config type %x\n", *p); 137 | ismp = 0; 138 | } 139 | } 140 | if(!ismp){ 141 | // Didn't like what we found; fall back to no MP. 142 | ncpu = 1; 143 | lapic = 0; 144 | ioapicid = 0; 145 | return; 146 | } 147 | 148 | if(mp->imcrp){ 149 | // Bochs doesn't support IMCR, so this doesn't run on Bochs. 150 | // But it would on real hardware. 151 | outb(0x22, 0x70); // Select IMCR 152 | outb(0x23, inb(0x23) | 1); // Mask external interrupts. 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /kernel/picirq.c: -------------------------------------------------------------------------------- 1 | // Intel 8259A programmable interrupt controllers. 2 | 3 | #include "types.h" 4 | #include "x86.h" 5 | #include "traps.h" 6 | 7 | // I/O Addresses of the two programmable interrupt controllers 8 | #define IO_PIC1 0x20 // Master (IRQs 0-7) 9 | #define IO_PIC2 0xA0 // Slave (IRQs 8-15) 10 | 11 | #define IRQ_SLAVE 2 // IRQ at which slave connects to master 12 | 13 | // Current IRQ mask. 14 | // Initial IRQ mask has interrupt 2 enabled (for slave 8259A). 15 | static ushort irqmask = 0xFFFF & ~(1<> 8); 23 | } 24 | 25 | void 26 | picenable(int irq) 27 | { 28 | picsetmask(irqmask & ~(1<readopen = 1; 33 | p->writeopen = 1; 34 | p->nwrite = 0; 35 | p->nread = 0; 36 | initlock(&p->lock, "pipe"); 37 | (*f0)->type = FD_PIPE; 38 | (*f0)->readable = 1; 39 | (*f0)->writable = 0; 40 | (*f0)->pipe = p; 41 | (*f1)->type = FD_PIPE; 42 | (*f1)->readable = 0; 43 | (*f1)->writable = 1; 44 | (*f1)->pipe = p; 45 | return 0; 46 | 47 | //PAGEBREAK: 20 48 | bad: 49 | if(p) 50 | kfree((char*)p); 51 | if(*f0) 52 | fileclose(*f0); 53 | if(*f1) 54 | fileclose(*f1); 55 | return -1; 56 | } 57 | 58 | void 59 | pipeclose(struct pipe *p, int writable) 60 | { 61 | acquire(&p->lock); 62 | if(writable){ 63 | p->writeopen = 0; 64 | wakeup(&p->nread); 65 | } else { 66 | p->readopen = 0; 67 | wakeup(&p->nwrite); 68 | } 69 | if(p->readopen == 0 && p->writeopen == 0){ 70 | release(&p->lock); 71 | kfree((char*)p); 72 | } else 73 | release(&p->lock); 74 | } 75 | 76 | //PAGEBREAK: 40 77 | int 78 | pipewrite(struct pipe *p, char *addr, int n) 79 | { 80 | int i; 81 | 82 | acquire(&p->lock); 83 | for(i = 0; i < n; i++){ 84 | while(p->nwrite == p->nread + PIPESIZE){ //DOC: pipewrite-full 85 | if(p->readopen == 0 || proc->killed){ 86 | release(&p->lock); 87 | return -1; 88 | } 89 | wakeup(&p->nread); 90 | sleep(&p->nwrite, &p->lock); //DOC: pipewrite-sleep 91 | } 92 | p->data[p->nwrite++ % PIPESIZE] = addr[i]; 93 | } 94 | wakeup(&p->nread); //DOC: pipewrite-wakeup1 95 | release(&p->lock); 96 | return n; 97 | } 98 | 99 | int 100 | piperead(struct pipe *p, char *addr, int n) 101 | { 102 | int i; 103 | 104 | acquire(&p->lock); 105 | while(p->nread == p->nwrite && p->writeopen){ //DOC: pipe-empty 106 | if(proc->killed){ 107 | release(&p->lock); 108 | return -1; 109 | } 110 | sleep(&p->nread, &p->lock); //DOC: piperead-sleep 111 | } 112 | for(i = 0; i < n; i++){ //DOC: piperead-copy 113 | if(p->nread == p->nwrite) 114 | break; 115 | addr[i] = p->data[p->nread++ % PIPESIZE]; 116 | } 117 | wakeup(&p->nwrite); //DOC: piperead-wakeup 118 | release(&p->lock); 119 | return i; 120 | } 121 | -------------------------------------------------------------------------------- /kernel/spinlock.c: -------------------------------------------------------------------------------- 1 | // Mutual exclusion spin locks. 2 | 3 | #include "types.h" 4 | #include "defs.h" 5 | #include "param.h" 6 | #include "x86.h" 7 | #include "memlayout.h" 8 | #include "mmu.h" 9 | #include "proc.h" 10 | #include "spinlock.h" 11 | 12 | void 13 | initlock(struct spinlock *lk, char *name) 14 | { 15 | lk->name = name; 16 | lk->locked = 0; 17 | lk->cpu = 0; 18 | } 19 | 20 | // Acquire the lock. 21 | // Loops (spins) until the lock is acquired. 22 | // Holding a lock for a long time may cause 23 | // other CPUs to waste time spinning to acquire it. 24 | void 25 | acquire(struct spinlock *lk) 26 | { 27 | pushcli(); // disable interrupts to avoid deadlock. 28 | if(holding(lk)) { 29 | int i; 30 | cprintf("lock '%s':\n", lk->name); 31 | for (i = 0; i < 10; i++) 32 | cprintf(" %p", lk->pcs[i]); 33 | cprintf("\n"); 34 | panic("acquire"); 35 | } 36 | 37 | // The xchg is atomic. 38 | // It also serializes, so that reads after acquire are not 39 | // reordered before it. 40 | while(xchg(&lk->locked, 1) != 0) 41 | ; 42 | 43 | // Record info about lock acquisition for debugging. 44 | lk->cpu = cpu; 45 | getcallerpcs(&lk, lk->pcs); 46 | } 47 | 48 | // Release the lock. 49 | void 50 | release(struct spinlock *lk) 51 | { 52 | if(!holding(lk)) 53 | panic("release"); 54 | 55 | lk->pcs[0] = 0; 56 | lk->cpu = 0; 57 | 58 | // The xchg serializes, so that reads before release are 59 | // not reordered after it. The 1996 PentiumPro manual (Volume 3, 60 | // 7.2) says reads can be carried out speculatively and in 61 | // any order, which implies we need to serialize here. 62 | // But the 2007 Intel 64 Architecture Memory Ordering White 63 | // Paper says that Intel 64 and IA-32 will not move a load 64 | // after a store. So lock->locked = 0 would work here. 65 | // The xchg being asm volatile ensures gcc emits it after 66 | // the above assignments (and after the critical section). 67 | xchg(&lk->locked, 0); 68 | 69 | popcli(); 70 | } 71 | 72 | // Record the current call stack in pcs[] by following the %ebp chain. 73 | void 74 | getcallerpcs(void *v, uintp pcs[]) 75 | { 76 | uintp *ebp; 77 | #if X64 78 | asm volatile("mov %%rbp, %0" : "=r" (ebp)); 79 | #else 80 | ebp = (uintp*)v - 2; 81 | #endif 82 | getstackpcs(ebp, pcs); 83 | } 84 | 85 | void 86 | getstackpcs(uintp *ebp, uintp pcs[]) 87 | { 88 | int i; 89 | 90 | for(i = 0; i < 10; i++){ 91 | if(ebp == 0 || ebp < (uintp*)KERNBASE || ebp == (uintp*)0xffffffff) 92 | break; 93 | pcs[i] = ebp[1]; // saved %eip 94 | ebp = (uintp*)ebp[0]; // saved %ebp 95 | } 96 | for(; i < 10; i++) 97 | pcs[i] = 0; 98 | } 99 | 100 | // Check whether this cpu is holding the lock. 101 | int 102 | holding(struct spinlock *lock) 103 | { 104 | return lock->locked && lock->cpu == cpu; 105 | } 106 | 107 | 108 | // Pushcli/popcli are like cli/sti except that they are matched: 109 | // it takes two popcli to undo two pushcli. Also, if interrupts 110 | // are off, then pushcli, popcli leaves them off. 111 | 112 | void 113 | pushcli(void) 114 | { 115 | int eflags; 116 | 117 | eflags = readeflags(); 118 | cli(); 119 | if(cpu->ncli++ == 0) 120 | cpu->intena = eflags & FL_IF; 121 | } 122 | 123 | void 124 | popcli(void) 125 | { 126 | if(readeflags()&FL_IF) 127 | panic("popcli - interruptible"); 128 | if(--cpu->ncli < 0) 129 | panic("popcli"); 130 | if(cpu->ncli == 0 && cpu->intena) 131 | sti(); 132 | } 133 | 134 | -------------------------------------------------------------------------------- /kernel/string.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "x86.h" 3 | 4 | void* 5 | memset(void *dst, int c, uint n) 6 | { 7 | if ((uintp)dst%4 == 0 && n%4 == 0){ 8 | c &= 0xFF; 9 | stosl(dst, (c<<24)|(c<<16)|(c<<8)|c, n/4); 10 | } else 11 | stosb(dst, c, n); 12 | return dst; 13 | } 14 | 15 | int 16 | memcmp(const void *v1, const void *v2, uint n) 17 | { 18 | const uchar *s1, *s2; 19 | 20 | s1 = v1; 21 | s2 = v2; 22 | while(n-- > 0){ 23 | if(*s1 != *s2) 24 | return *s1 - *s2; 25 | s1++, s2++; 26 | } 27 | 28 | return 0; 29 | } 30 | 31 | void* 32 | memmove(void *dst, const void *src, uint n) 33 | { 34 | const char *s; 35 | char *d; 36 | 37 | s = src; 38 | d = dst; 39 | if(s < d && s + n > d){ 40 | s += n; 41 | d += n; 42 | while(n-- > 0) 43 | *--d = *--s; 44 | } else 45 | while(n-- > 0) 46 | *d++ = *s++; 47 | 48 | return dst; 49 | } 50 | 51 | // memcpy exists to placate GCC. Use memmove. 52 | void* 53 | memcpy(void *dst, const void *src, uint n) 54 | { 55 | return memmove(dst, src, n); 56 | } 57 | 58 | int 59 | strncmp(const char *p, const char *q, uint n) 60 | { 61 | while(n > 0 && *p && *p == *q) 62 | n--, p++, q++; 63 | if(n == 0) 64 | return 0; 65 | return (uchar)*p - (uchar)*q; 66 | } 67 | 68 | char* 69 | strncpy(char *s, const char *t, int n) 70 | { 71 | char *os; 72 | 73 | os = s; 74 | while(n-- > 0 && (*s++ = *t++) != 0) 75 | ; 76 | while(n-- > 0) 77 | *s++ = 0; 78 | return os; 79 | } 80 | 81 | // Like strncpy but guaranteed to NUL-terminate. 82 | char* 83 | safestrcpy(char *s, const char *t, int n) 84 | { 85 | char *os; 86 | 87 | os = s; 88 | if(n <= 0) 89 | return os; 90 | while(--n > 0 && (*s++ = *t++) != 0) 91 | ; 92 | *s = 0; 93 | return os; 94 | } 95 | 96 | int 97 | strlen(const char *s) 98 | { 99 | int n; 100 | 101 | for(n = 0; s[n]; n++) 102 | ; 103 | return n; 104 | } 105 | 106 | -------------------------------------------------------------------------------- /kernel/swtch.S: -------------------------------------------------------------------------------- 1 | # Context switch 2 | # 3 | # void swtch(struct context **old, struct context *new); 4 | # 5 | # Save current register context in old 6 | # and then load register context from new. 7 | 8 | .globl swtch 9 | swtch: 10 | movl 4(%esp), %eax 11 | movl 8(%esp), %edx 12 | 13 | # Save old callee-save registers 14 | pushl %ebp 15 | pushl %ebx 16 | pushl %esi 17 | pushl %edi 18 | 19 | # Switch stacks 20 | movl %esp, (%eax) 21 | movl %edx, %esp 22 | 23 | # Load new callee-save registers 24 | popl %edi 25 | popl %esi 26 | popl %ebx 27 | popl %ebp 28 | ret 29 | -------------------------------------------------------------------------------- /kernel/swtch64.S: -------------------------------------------------------------------------------- 1 | # Context switch 2 | # 3 | # void swtch(struct context **old, struct context *new); 4 | # 5 | # Save current register context in old 6 | # and then load register context from new. 7 | 8 | .globl swtch 9 | swtch: 10 | # Save old callee-save registers 11 | push %rbp 12 | push %rbx 13 | push %r11 14 | push %r12 15 | push %r13 16 | push %r14 17 | push %r15 18 | 19 | # Switch stacks 20 | mov %rsp, (%rdi) 21 | mov %rsi, %rsp 22 | 23 | # Load new callee-save registers 24 | pop %r15 25 | pop %r14 26 | pop %r13 27 | pop %r12 28 | pop %r11 29 | pop %rbx 30 | pop %rbp 31 | 32 | ret #?? 33 | -------------------------------------------------------------------------------- /kernel/syscall.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "defs.h" 3 | #include "param.h" 4 | #include "memlayout.h" 5 | #include "mmu.h" 6 | #include "proc.h" 7 | #include "x86.h" 8 | #include "syscall.h" 9 | 10 | // User code makes a system call with INT T_SYSCALL. 11 | // System call number in %eax. 12 | // Arguments on the stack, from the user call to the C 13 | // library system call function. The saved user %esp points 14 | // to a saved program counter, and then the first argument. 15 | 16 | // Fetch the int at addr from the current process. 17 | int 18 | fetchint(uintp addr, int *ip) 19 | { 20 | if(addr >= proc->sz || addr+sizeof(int) > proc->sz) 21 | return -1; 22 | *ip = *(int*)(addr); 23 | return 0; 24 | } 25 | 26 | int 27 | fetchuintp(uintp addr, uintp *ip) 28 | { 29 | if(addr >= proc->sz || addr+sizeof(uintp) > proc->sz) 30 | return -1; 31 | *ip = *(uintp*)(addr); 32 | return 0; 33 | } 34 | 35 | // Fetch the nul-terminated string at addr from the current process. 36 | // Doesn't actually copy the string - just sets *pp to point at it. 37 | // Returns length of string, not including nul. 38 | int 39 | fetchstr(uintp addr, char **pp) 40 | { 41 | char *s, *ep; 42 | 43 | if(addr >= proc->sz) 44 | return -1; 45 | *pp = (char*)addr; 46 | ep = (char*)proc->sz; 47 | for(s = *pp; s < ep; s++) 48 | if(*s == 0) 49 | return s - *pp; 50 | return -1; 51 | } 52 | 53 | #if X64 54 | // arguments passed in registers on x64 55 | static uintp 56 | fetcharg(int n) 57 | { 58 | switch (n) { 59 | case 0: return proc->tf->rdi; 60 | case 1: return proc->tf->rsi; 61 | case 2: return proc->tf->rdx; 62 | case 3: return proc->tf->rcx; 63 | case 4: return proc->tf->r8; 64 | case 5: return proc->tf->r9; 65 | } 66 | } 67 | 68 | int 69 | argint(int n, int *ip) 70 | { 71 | *ip = fetcharg(n); 72 | return 0; 73 | } 74 | 75 | int 76 | arguintp(int n, uintp *ip) 77 | { 78 | *ip = fetcharg(n); 79 | return 0; 80 | } 81 | #else 82 | // Fetch the nth 32-bit system call argument. 83 | int 84 | argint(int n, int *ip) 85 | { 86 | return fetchint(proc->tf->esp + 4 + 4*n, ip); 87 | } 88 | 89 | int 90 | arguintp(int n, uintp *ip) 91 | { 92 | return fetchuintp(proc->tf->esp + sizeof(uintp) + sizeof(uintp)*n, ip); 93 | } 94 | #endif 95 | 96 | // Fetch the nth word-sized system call argument as a pointer 97 | // to a block of memory of size n bytes. Check that the pointer 98 | // lies within the process address space. 99 | int 100 | argptr(int n, char **pp, int size) 101 | { 102 | uintp i; 103 | 104 | if(arguintp(n, &i) < 0) 105 | return -1; 106 | if(i >= proc->sz || i+size > proc->sz) 107 | return -1; 108 | *pp = (char*)i; 109 | return 0; 110 | } 111 | 112 | // Fetch the nth word-sized system call argument as a string pointer. 113 | // Check that the pointer is valid and the string is nul-terminated. 114 | // (There is no shared writable memory, so the string can't change 115 | // between this check and being used by the kernel.) 116 | int 117 | argstr(int n, char **pp) 118 | { 119 | uintp addr; 120 | if(arguintp(n, &addr) < 0) 121 | return -1; 122 | return fetchstr(addr, pp); 123 | } 124 | 125 | extern int sys_chdir(void); 126 | extern int sys_close(void); 127 | extern int sys_dup(void); 128 | extern int sys_exec(void); 129 | extern int sys_exit(void); 130 | extern int sys_fork(void); 131 | extern int sys_fstat(void); 132 | extern int sys_getpid(void); 133 | extern int sys_kill(void); 134 | extern int sys_link(void); 135 | extern int sys_mkdir(void); 136 | extern int sys_mknod(void); 137 | extern int sys_open(void); 138 | extern int sys_pipe(void); 139 | extern int sys_read(void); 140 | extern int sys_sbrk(void); 141 | extern int sys_sleep(void); 142 | extern int sys_unlink(void); 143 | extern int sys_wait(void); 144 | extern int sys_write(void); 145 | extern int sys_uptime(void); 146 | 147 | static int (*syscalls[])(void) = { 148 | [SYS_fork] sys_fork, 149 | [SYS_exit] sys_exit, 150 | [SYS_wait] sys_wait, 151 | [SYS_pipe] sys_pipe, 152 | [SYS_read] sys_read, 153 | [SYS_kill] sys_kill, 154 | [SYS_exec] sys_exec, 155 | [SYS_fstat] sys_fstat, 156 | [SYS_chdir] sys_chdir, 157 | [SYS_dup] sys_dup, 158 | [SYS_getpid] sys_getpid, 159 | [SYS_sbrk] sys_sbrk, 160 | [SYS_sleep] sys_sleep, 161 | [SYS_uptime] sys_uptime, 162 | [SYS_open] sys_open, 163 | [SYS_write] sys_write, 164 | [SYS_mknod] sys_mknod, 165 | [SYS_unlink] sys_unlink, 166 | [SYS_link] sys_link, 167 | [SYS_mkdir] sys_mkdir, 168 | [SYS_close] sys_close, 169 | }; 170 | 171 | void 172 | syscall(void) 173 | { 174 | int num; 175 | 176 | num = proc->tf->eax; 177 | if(num > 0 && num < NELEM(syscalls) && syscalls[num]) { 178 | proc->tf->eax = syscalls[num](); 179 | } else { 180 | cprintf("%d %s: unknown sys call %d\n", 181 | proc->pid, proc->name, num); 182 | proc->tf->eax = -1; 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /kernel/sysproc.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "x86.h" 3 | #include "defs.h" 4 | #include "param.h" 5 | #include "memlayout.h" 6 | #include "mmu.h" 7 | #include "proc.h" 8 | 9 | int 10 | sys_fork(void) 11 | { 12 | return fork(); 13 | } 14 | 15 | int 16 | sys_exit(void) 17 | { 18 | exit(); 19 | return 0; // not reached 20 | } 21 | 22 | int 23 | sys_wait(void) 24 | { 25 | return wait(); 26 | } 27 | 28 | int 29 | sys_kill(void) 30 | { 31 | int pid; 32 | 33 | if(argint(0, &pid) < 0) 34 | return -1; 35 | return kill(pid); 36 | } 37 | 38 | int 39 | sys_getpid(void) 40 | { 41 | return proc->pid; 42 | } 43 | 44 | uintp 45 | sys_sbrk(void) 46 | { 47 | uintp addr; 48 | uintp n; 49 | 50 | if(arguintp(0, &n) < 0) 51 | return -1; 52 | addr = proc->sz; 53 | if(growproc(n) < 0) 54 | return -1; 55 | return addr; 56 | } 57 | 58 | int 59 | sys_sleep(void) 60 | { 61 | int n; 62 | uint ticks0; 63 | 64 | if(argint(0, &n) < 0) 65 | return -1; 66 | acquire(&tickslock); 67 | ticks0 = ticks; 68 | while(ticks - ticks0 < n){ 69 | if(proc->killed){ 70 | release(&tickslock); 71 | return -1; 72 | } 73 | sleep(&ticks, &tickslock); 74 | } 75 | release(&tickslock); 76 | return 0; 77 | } 78 | 79 | // return how many clock tick interrupts have occurred 80 | // since start. 81 | int 82 | sys_uptime(void) 83 | { 84 | uint xticks; 85 | 86 | acquire(&tickslock); 87 | xticks = ticks; 88 | release(&tickslock); 89 | return xticks; 90 | } 91 | -------------------------------------------------------------------------------- /kernel/timer.c: -------------------------------------------------------------------------------- 1 | // Intel 8253/8254/82C54 Programmable Interval Timer (PIT). 2 | // Only used on uniprocessors; 3 | // SMP machines use the local APIC timer. 4 | 5 | #include "types.h" 6 | #include "defs.h" 7 | #include "traps.h" 8 | #include "x86.h" 9 | 10 | #define IO_TIMER1 0x040 // 8253 Timer #1 11 | 12 | // Frequency of all three count-down timers; 13 | // (TIMER_FREQ/freq) is the appropriate count 14 | // to generate a frequency of freq Hz. 15 | 16 | #define TIMER_FREQ 1193182 17 | #define TIMER_DIV(x) ((TIMER_FREQ+(x)/2)/(x)) 18 | 19 | #define TIMER_MODE (IO_TIMER1 + 3) // timer mode port 20 | #define TIMER_SEL0 0x00 // select counter 0 21 | #define TIMER_RATEGEN 0x04 // mode 2, rate generator 22 | #define TIMER_16BIT 0x30 // r/w counter 16 bits, LSB first 23 | 24 | void 25 | timerinit(void) 26 | { 27 | // Interrupt 100 times/sec. 28 | outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT); 29 | outb(IO_TIMER1, TIMER_DIV(100) % 256); 30 | outb(IO_TIMER1, TIMER_DIV(100) / 256); 31 | picenable(IRQ_TIMER); 32 | } 33 | -------------------------------------------------------------------------------- /kernel/trap.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "defs.h" 3 | #include "param.h" 4 | #include "memlayout.h" 5 | #include "mmu.h" 6 | #include "proc.h" 7 | #include "x86.h" 8 | #include "traps.h" 9 | #include "spinlock.h" 10 | 11 | // Interrupt descriptor table (shared by all CPUs). 12 | struct gatedesc idt[256]; 13 | extern uintp vectors[]; // in vectors.S: array of 256 entry pointers 14 | struct spinlock tickslock; 15 | uint ticks; 16 | 17 | #ifndef X64 18 | void 19 | tvinit(void) 20 | { 21 | int i; 22 | 23 | for(i = 0; i < 256; i++) 24 | SETGATE(idt[i], 0, SEG_KCODE<<3, vectors[i], 0); 25 | SETGATE(idt[T_SYSCALL], 1, SEG_KCODE<<3, vectors[T_SYSCALL], DPL_USER); 26 | 27 | initlock(&tickslock, "time"); 28 | } 29 | 30 | void 31 | idtinit(void) 32 | { 33 | lidt(idt, sizeof(idt)); 34 | } 35 | #endif 36 | 37 | //PAGEBREAK: 41 38 | void 39 | trap(struct trapframe *tf) 40 | { 41 | if(tf->trapno == T_SYSCALL){ 42 | if(proc->killed) 43 | exit(); 44 | proc->tf = tf; 45 | syscall(); 46 | if(proc->killed) 47 | exit(); 48 | return; 49 | } 50 | 51 | switch(tf->trapno){ 52 | case T_IRQ0 + IRQ_TIMER: 53 | if(cpu->id == 0){ 54 | acquire(&tickslock); 55 | ticks++; 56 | wakeup(&ticks); 57 | release(&tickslock); 58 | } 59 | lapiceoi(); 60 | break; 61 | case T_IRQ0 + IRQ_IDE: 62 | ideintr(); 63 | lapiceoi(); 64 | break; 65 | case T_IRQ0 + IRQ_IDE+1: 66 | // Bochs generates spurious IDE1 interrupts. 67 | break; 68 | case T_IRQ0 + IRQ_KBD: 69 | kbdintr(); 70 | lapiceoi(); 71 | break; 72 | case T_IRQ0 + IRQ_COM1: 73 | uartintr(); 74 | lapiceoi(); 75 | break; 76 | case T_IRQ0 + 7: 77 | case T_IRQ0 + IRQ_SPURIOUS: 78 | cprintf("cpu%d: spurious interrupt at %x:%x\n", 79 | cpu->id, tf->cs, tf->eip); 80 | lapiceoi(); 81 | break; 82 | 83 | //PAGEBREAK: 13 84 | default: 85 | if(proc == 0 || (tf->cs&3) == 0){ 86 | // In kernel, it must be our mistake. 87 | cprintf("unexpected trap %d from cpu %d eip %x (cr2=0x%x)\n", 88 | tf->trapno, cpu->id, tf->eip, rcr2()); 89 | panic("trap"); 90 | } 91 | // In user space, assume process misbehaved. 92 | cprintf("pid %d %s: trap %d err %d on cpu %d " 93 | "eip 0x%x addr 0x%x--kill proc\n", 94 | proc->pid, proc->name, tf->trapno, tf->err, cpu->id, tf->eip, 95 | rcr2()); 96 | proc->killed = 1; 97 | } 98 | 99 | // Force process exit if it has been killed and is in user space. 100 | // (If it is still executing in the kernel, let it keep running 101 | // until it gets to the regular system call return.) 102 | if(proc && proc->killed && (tf->cs&3) == DPL_USER) 103 | exit(); 104 | 105 | // Force process to give up CPU on clock tick. 106 | // If interrupts were on while locks held, would need to check nlock. 107 | if(proc && proc->state == RUNNING && tf->trapno == T_IRQ0+IRQ_TIMER) 108 | yield(); 109 | 110 | // Check if the process has been killed since we yielded 111 | if(proc && proc->killed && (tf->cs&3) == DPL_USER) 112 | exit(); 113 | } 114 | -------------------------------------------------------------------------------- /kernel/trapasm.S: -------------------------------------------------------------------------------- 1 | #include "mmu.h" 2 | 3 | # vectors.S sends all traps here. 4 | .globl alltraps 5 | alltraps: 6 | # Build trap frame. 7 | pushl %ds 8 | pushl %es 9 | pushl %fs 10 | pushl %gs 11 | pushal 12 | 13 | # Set up data and per-cpu segments. 14 | movw $(SEG_KDATA<<3), %ax 15 | movw %ax, %ds 16 | movw %ax, %es 17 | movw $(SEG_KCPU<<3), %ax 18 | movw %ax, %fs 19 | movw %ax, %gs 20 | 21 | # Call trap(tf), where tf=%esp 22 | pushl %esp 23 | call trap 24 | addl $4, %esp 25 | 26 | # Return falls through to trapret... 27 | .globl trapret 28 | trapret: 29 | popal 30 | popl %gs 31 | popl %fs 32 | popl %es 33 | popl %ds 34 | addl $0x8, %esp # trapno and errcode 35 | iret 36 | -------------------------------------------------------------------------------- /kernel/trapasm64.S: -------------------------------------------------------------------------------- 1 | 2 | # vectors.S sends all traps here. 3 | .globl alltraps 4 | alltraps: 5 | # Build trap frame. 6 | push %r15 7 | push %r14 8 | push %r13 9 | push %r12 10 | push %r11 11 | push %r10 12 | push %r9 13 | push %r8 14 | push %rdi 15 | push %rsi 16 | push %rbp 17 | push %rdx 18 | push %rcx 19 | push %rbx 20 | push %rax 21 | 22 | mov %rsp, %rdi # frame in arg1 23 | call trap 24 | 25 | # Return falls through to trapret... 26 | .globl trapret 27 | trapret: 28 | pop %rax 29 | pop %rbx 30 | pop %rcx 31 | pop %rdx 32 | pop %rbp 33 | pop %rsi 34 | pop %rdi 35 | pop %r8 36 | pop %r9 37 | pop %r10 38 | pop %r11 39 | pop %r12 40 | pop %r13 41 | pop %r14 42 | pop %r15 43 | 44 | # discard trapnum and errorcode 45 | add $16, %rsp 46 | iretq 47 | -------------------------------------------------------------------------------- /kernel/uart.c: -------------------------------------------------------------------------------- 1 | // Intel 8250 serial port (UART). 2 | 3 | #include "types.h" 4 | #include "defs.h" 5 | #include "param.h" 6 | #include "traps.h" 7 | #include "spinlock.h" 8 | #include "fs.h" 9 | #include "file.h" 10 | #include "mmu.h" 11 | #include "proc.h" 12 | #include "x86.h" 13 | 14 | #define COM1 0x3f8 15 | 16 | static int uart; // is there a uart? 17 | 18 | void 19 | uartearlyinit(void) 20 | { 21 | char *p; 22 | 23 | // Turn off the FIFO 24 | outb(COM1+2, 0); 25 | 26 | // 9600 baud, 8 data bits, 1 stop bit, parity off. 27 | outb(COM1+3, 0x80); // Unlock divisor 28 | outb(COM1+0, 115200/9600); 29 | outb(COM1+1, 0); 30 | outb(COM1+3, 0x03); // Lock divisor, 8 data bits. 31 | outb(COM1+4, 0); 32 | outb(COM1+1, 0x01); // Enable receive interrupts. 33 | 34 | // If status is 0xFF, no serial port. 35 | if(inb(COM1+5) == 0xFF) 36 | return; 37 | uart = 1; 38 | 39 | // Announce that we're here. 40 | for(p="xv6...\n"; *p; p++) 41 | uartputc(*p); 42 | } 43 | 44 | void 45 | uartinit(void) 46 | { 47 | if (!uart) 48 | return; 49 | 50 | // Acknowledge pre-existing interrupt conditions; 51 | // enable interrupts. 52 | inb(COM1+2); 53 | inb(COM1+0); 54 | picenable(IRQ_COM1); 55 | ioapicenable(IRQ_COM1, 0); 56 | } 57 | 58 | void 59 | uartputc(int c) 60 | { 61 | int i; 62 | 63 | if(!uart) 64 | return; 65 | for(i = 0; i < 128 && !(inb(COM1+5) & 0x20); i++) 66 | microdelay(10); 67 | outb(COM1+0, c); 68 | } 69 | 70 | static int 71 | uartgetc(void) 72 | { 73 | if(!uart) 74 | return -1; 75 | if(!(inb(COM1+5) & 0x01)) 76 | return -1; 77 | return inb(COM1+0); 78 | } 79 | 80 | void 81 | uartintr(void) 82 | { 83 | consoleintr(uartgetc); 84 | } 85 | -------------------------------------------------------------------------------- /kernel/vm64.c: -------------------------------------------------------------------------------- 1 | /* vm64.c 2 | * 3 | * Copyright (c) 2013 Brian Swetland 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining 6 | * a copy of this software and associated documentation files (the 7 | * "Software"), to deal in the Software without restriction, including 8 | * without limitation the rights to use, copy, modify, merge, publish, 9 | * distribute, sublicense, and/or sell copies of the Software, and to 10 | * permit persons to whom the Software is furnished to do so, subject to 11 | * the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be 14 | * included in all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #include "param.h" 27 | #include "types.h" 28 | #include "defs.h" 29 | #include "x86.h" 30 | #include "memlayout.h" 31 | #include "mmu.h" 32 | #include "proc.h" 33 | #include "elf.h" 34 | 35 | __thread struct cpu *cpu; 36 | __thread struct proc *proc; 37 | 38 | static pde_t *kpml4; 39 | static pde_t *kpdpt; 40 | static pde_t *iopgdir; 41 | static pde_t *kpgdir0; 42 | static pde_t *kpgdir1; 43 | 44 | void wrmsr(uint msr, uint64 val); 45 | 46 | void tvinit(void) {} 47 | void idtinit(void) {} 48 | 49 | static void mkgate(uint *idt, uint n, void *kva, uint pl, uint trap) { 50 | uint64 addr = (uint64) kva; 51 | n *= 4; 52 | trap = trap ? 0x8F00 : 0x8E00; // TRAP vs INTERRUPT gate; 53 | idt[n+0] = (addr & 0xFFFF) | ((SEG_KCODE << 3) << 16); 54 | idt[n+1] = (addr & 0xFFFF0000) | trap | ((pl & 3) << 13); // P=1 DPL=pl 55 | idt[n+2] = addr >> 32; 56 | idt[n+3] = 0; 57 | } 58 | 59 | static void tss_set_rsp(uint *tss, uint n, uint64 rsp) { 60 | tss[n*2 + 1] = rsp; 61 | tss[n*2 + 2] = rsp >> 32; 62 | } 63 | 64 | static void tss_set_ist(uint *tss, uint n, uint64 ist) { 65 | tss[n*2 + 7] = ist; 66 | tss[n*2 + 8] = ist >> 32; 67 | } 68 | 69 | extern void* vectors[]; 70 | 71 | // Set up CPU's kernel segment descriptors. 72 | // Run once on entry on each CPU. 73 | void 74 | seginit(void) 75 | { 76 | uint64 *gdt; 77 | uint *tss; 78 | uint64 addr; 79 | void *local; 80 | struct cpu *c; 81 | uint *idt = (uint*) kalloc(); 82 | int n; 83 | memset(idt, 0, PGSIZE); 84 | 85 | for (n = 0; n < 256; n++) 86 | mkgate(idt, n, vectors[n], 0, 0); 87 | mkgate(idt, 64, vectors[64], 3, 1); 88 | 89 | lidt((void*) idt, PGSIZE); 90 | 91 | // create a page for cpu local storage 92 | local = kalloc(); 93 | memset(local, 0, PGSIZE); 94 | 95 | gdt = (uint64*) local; 96 | tss = (uint*) (((char*) local) + 1024); 97 | tss[16] = 0x00680000; // IO Map Base = End of TSS 98 | 99 | // point FS smack in the middle of our local storage page 100 | wrmsr(0xC0000100, ((uint64) local) + (PGSIZE / 2)); 101 | 102 | c = &cpus[cpunum()]; 103 | c->local = local; 104 | 105 | cpu = c; 106 | proc = 0; 107 | 108 | addr = (uint64) tss; 109 | gdt[0] = 0x0000000000000000; 110 | gdt[SEG_KCODE] = 0x0020980000000000; // Code, DPL=0, R/X 111 | gdt[SEG_UCODE] = 0x0020F80000000000; // Code, DPL=3, R/X 112 | gdt[SEG_KDATA] = 0x0000920000000000; // Data, DPL=0, W 113 | gdt[SEG_KCPU] = 0x0000000000000000; // unused 114 | gdt[SEG_UDATA] = 0x0000F20000000000; // Data, DPL=3, W 115 | gdt[SEG_TSS+0] = (0x0067) | ((addr & 0xFFFFFF) << 16) | 116 | (0x00E9LL << 40) | (((addr >> 24) & 0xFF) << 56); 117 | gdt[SEG_TSS+1] = (addr >> 32); 118 | 119 | lgdt((void*) gdt, 8 * sizeof(uint64)); 120 | 121 | ltr(SEG_TSS << 3); 122 | }; 123 | 124 | // The core xv6 code only knows about two levels of page tables, 125 | // so we will create all four, but only return the second level. 126 | // because we need to find the other levels later, we'll stash 127 | // backpointers to them in the top two entries of the level two 128 | // table. 129 | pde_t* 130 | setupkvm(void) 131 | { 132 | pde_t *pml4 = (pde_t*) kalloc(); 133 | pde_t *pdpt = (pde_t*) kalloc(); 134 | pde_t *pgdir = (pde_t*) kalloc(); 135 | 136 | memset(pml4, 0, PGSIZE); 137 | memset(pdpt, 0, PGSIZE); 138 | memset(pgdir, 0, PGSIZE); 139 | pml4[511] = v2p(kpdpt) | PTE_P | PTE_W | PTE_U; 140 | pml4[0] = v2p(pdpt) | PTE_P | PTE_W | PTE_U; 141 | pdpt[0] = v2p(pgdir) | PTE_P | PTE_W | PTE_U; 142 | 143 | // virtual backpointers 144 | pgdir[511] = ((uintp) pml4) | PTE_P; 145 | pgdir[510] = ((uintp) pdpt) | PTE_P; 146 | 147 | return pgdir; 148 | }; 149 | 150 | // Allocate one page table for the machine for the kernel address 151 | // space for scheduler processes. 152 | // 153 | // linear map the first 4GB of physical memory starting at 0xFFFFFFFF80000000 154 | void 155 | kvmalloc(void) 156 | { 157 | int n; 158 | kpml4 = (pde_t*) kalloc(); 159 | kpdpt = (pde_t*) kalloc(); 160 | kpgdir0 = (pde_t*) kalloc(); 161 | kpgdir1 = (pde_t*) kalloc(); 162 | iopgdir = (pde_t*) kalloc(); 163 | memset(kpml4, 0, PGSIZE); 164 | memset(kpdpt, 0, PGSIZE); 165 | memset(iopgdir, 0, PGSIZE); 166 | kpml4[511] = v2p(kpdpt) | PTE_P | PTE_W; 167 | kpdpt[511] = v2p(kpgdir1) | PTE_P | PTE_W; 168 | kpdpt[510] = v2p(kpgdir0) | PTE_P | PTE_W; 169 | kpdpt[509] = v2p(iopgdir) | PTE_P | PTE_W; 170 | for (n = 0; n < NPDENTRIES; n++) { 171 | kpgdir0[n] = (n << PDXSHIFT) | PTE_PS | PTE_P | PTE_W; 172 | kpgdir1[n] = ((n + 512) << PDXSHIFT) | PTE_PS | PTE_P | PTE_W; 173 | } 174 | for (n = 0; n < 16; n++) 175 | iopgdir[n] = (DEVSPACE + (n << PDXSHIFT)) | PTE_PS | PTE_P | PTE_W | PTE_PWT | PTE_PCD; 176 | switchkvm(); 177 | } 178 | 179 | void 180 | switchkvm(void) 181 | { 182 | lcr3(v2p(kpml4)); 183 | } 184 | 185 | void 186 | switchuvm(struct proc *p) 187 | { 188 | void *pml4; 189 | uint *tss; 190 | pushcli(); 191 | if(p->pgdir == 0) 192 | panic("switchuvm: no pgdir"); 193 | tss = (uint*) (((char*) cpu->local) + 1024); 194 | tss_set_rsp(tss, 0, (uintp)proc->kstack + KSTACKSIZE); 195 | pml4 = (void*) PTE_ADDR(p->pgdir[511]); 196 | lcr3(v2p(pml4)); 197 | popcli(); 198 | } 199 | 200 | -------------------------------------------------------------------------------- /tools/gdbinit.tmpl: -------------------------------------------------------------------------------- 1 | set $lastcs = -1 2 | 3 | define hook-stop 4 | # There doesn't seem to be a good way to detect if we're in 16- or 5 | # 32-bit mode, but in 32-bit mode we always run with CS == 8 in the 6 | # kernel and CS == 35 in user space 7 | if $cs == 8 || $cs == 35 8 | if $lastcs != 8 && $lastcs != 35 9 | set architecture i386 10 | end 11 | x/i $pc 12 | else 13 | if $lastcs == -1 || $lastcs == 8 || $lastcs == 35 14 | set architecture i8086 15 | end 16 | # Translate the segment:offset into a physical address 17 | printf "[%4x:%4x] ", $cs, $eip 18 | x/i $cs*16+$eip 19 | end 20 | set $lastcs = $cs 21 | end 22 | 23 | echo + target remote localhost:1234\n 24 | target remote localhost:1234 25 | 26 | echo + symbol-file kernel\n 27 | symbol-file kernel 28 | -------------------------------------------------------------------------------- /tools/gdbutil: -------------------------------------------------------------------------------- 1 | # -*- gdb-script -*- 2 | 3 | # Utility functions to pretty-print x86 segment/interrupt descriptors. 4 | # To load this file, run "source gdbutil" in gdb. 5 | # printdesc and printdescs are the main entry points. 6 | 7 | # IA32 2007, Volume 3A, Table 3-2 8 | set $STS_T16A = 0x1 9 | set $STS_LDT = 0x2 10 | set $STS_T16B = 0x3 11 | set $STS_CG16 = 0x4 12 | set $STS_TG = 0x5 13 | set $STS_IG16 = 0x6 14 | set $STS_TG16 = 0x7 15 | set $STS_T32A = 0x9 16 | set $STS_T32B = 0xB 17 | set $STS_CG32 = 0xC 18 | set $STS_IG32 = 0xE 19 | set $STS_TG32 = 0xF 20 | 21 | define outputsts 22 | while 1 23 | if $arg0 == $STS_T16A 24 | echo STS_T16A 25 | loop_break 26 | end 27 | if $arg0 == $STS_LDT 28 | echo STS_LDT\ 29 | loop_break 30 | end 31 | if $arg0 == $STS_T16B 32 | echo STS_T16B 33 | loop_break 34 | end 35 | if $arg0 == $STS_CG16 36 | echo STS_CG16 37 | loop_break 38 | end 39 | if $arg0 == $STS_TG 40 | echo STS_TG\ \ 41 | loop_break 42 | end 43 | if $arg0 == $STS_IG16 44 | echo STS_IG16 45 | loop_break 46 | end 47 | if $arg0 == $STS_TG16 48 | echo STS_TG16 49 | loop_break 50 | end 51 | if $arg0 == $STS_T32A 52 | echo STS_T32A 53 | loop_break 54 | end 55 | if $arg0 == $STS_T32B 56 | echo STS_T32B 57 | loop_break 58 | end 59 | if $arg0 == $STS_CG32 60 | echo STS_CG32 61 | loop_break 62 | end 63 | if $arg0 == $STS_IG32 64 | echo STS_IG32 65 | loop_break 66 | end 67 | if $arg0 == $STS_TG32 68 | echo STS_TG32 69 | loop_break 70 | end 71 | echo Reserved 72 | loop_break 73 | end 74 | end 75 | 76 | # IA32 2007, Volume 3A, Table 3-1 77 | set $STA_X = 0x8 78 | set $STA_E = 0x4 79 | set $STA_C = 0x4 80 | set $STA_W = 0x2 81 | set $STA_R = 0x2 82 | set $STA_A = 0x1 83 | 84 | define outputsta 85 | if $arg0 & $STA_X 86 | # Code segment 87 | echo code 88 | if $arg0 & $STA_C 89 | echo |STA_C 90 | end 91 | if $arg0 & $STA_R 92 | echo |STA_R 93 | end 94 | else 95 | # Data segment 96 | echo data 97 | if $arg0 & $STA_E 98 | echo |STA_E 99 | end 100 | if $arg0 & $STA_W 101 | echo |STA_W 102 | end 103 | end 104 | if $arg0 & $STA_A 105 | echo |STA_A 106 | else 107 | printf " " 108 | end 109 | end 110 | 111 | # xv6-specific 112 | set $SEG_KCODE = 1 113 | set $SEG_KDATA = 2 114 | set $SEG_KCPU = 3 115 | set $SEG_UCODE = 4 116 | set $SEG_UDATA = 5 117 | set $SEG_TSS = 6 118 | 119 | define outputcs 120 | if ($arg0 & 4) == 0 121 | if $arg0 >> 3 == $SEG_KCODE 122 | printf "SEG_KCODE<<3" 123 | end 124 | if $arg0 >> 3 == $SEG_KDATA 125 | printf "SEG_KDATA<<3" 126 | end 127 | if $arg0 >> 3 == $SEG_KCPU 128 | printf "SEG_KCPU<<3" 129 | end 130 | if $arg0 >> 3 == $SEG_UCODE 131 | printf "SEG_UCODE<<3" 132 | end 133 | if $arg0 >> 3 == $SEG_UDATA 134 | printf "SEG_UDATA<<3" 135 | end 136 | if $arg0 >> 3 == $SEG_TSS 137 | printf "SEG_TSS<<3" 138 | end 139 | if ($arg0 >> 3 < 1) + ($arg0 >> 3 > 6) 140 | printf "GDT[%d]", $arg0 >> 3 141 | end 142 | else 143 | printf "LDT[%d]", $arg0 >> 3 144 | end 145 | if ($arg0 & 3) > 0 146 | printf "|" 147 | outputdpl ($arg0&3) 148 | end 149 | end 150 | 151 | define outputdpl 152 | if $arg0 == 0 153 | printf "DPL_KERN" 154 | else 155 | if $arg0 == 3 156 | printf "DPL_USER" 157 | else 158 | printf "DPL%d", $arg0 159 | end 160 | end 161 | end 162 | 163 | define printdesc 164 | if $argc != 1 165 | echo Usage: printdesc expr 166 | else 167 | _printdesc ((uint*)&($arg0))[0] ((uint*)&($arg0))[1] 168 | printf "\n" 169 | end 170 | end 171 | 172 | document printdesc 173 | Print an x86 segment or gate descriptor. 174 | printdesc EXPR 175 | EXPR must evaluate to a descriptor value. It can be of any C type. 176 | end 177 | 178 | define _printdesc 179 | _printdesc1 $arg0 $arg1 ($arg1>>15&1) ($arg1>>13&3) ($arg1>>12&1) ($arg1>>8&15) 180 | end 181 | 182 | define _printdesc1 183 | # 2:P 3:DPL 4:S 5:Type 184 | if $arg2 == 0 185 | printf "P = 0 (Not present)" 186 | else 187 | printf "type = " 188 | if $arg4 == 0 189 | # System segment 190 | outputsts $arg5 191 | printf " (0x%x) ", $arg5 192 | _printsysdesc $arg0 $arg1 $arg5 193 | else 194 | # Code/data segment 195 | outputsta $arg5 196 | printf " " 197 | _printsegdesc $arg0 $arg1 198 | end 199 | 200 | printf " DPL = " 201 | outputdpl $arg3 202 | printf " (%d)", $arg3 203 | end 204 | end 205 | 206 | define _printsysdesc 207 | # 2:Type 208 | # GDB's || is buggy 209 | if ($arg2 == $STS_TG) + (($arg2&7) == $STS_IG16) + (($arg2&7) == $STS_TG16) 210 | # Gate descriptor 211 | _printgate $arg2 ($arg0>>16) ($arg0&0xFFFF) ($arg1>>16) 212 | else 213 | # System segment descriptor 214 | _printsegdesc $arg0 $arg1 215 | end 216 | end 217 | 218 | define _printgate 219 | # IA32 2007, Voume 3A, Figure 5-2 220 | # 0:Type 1:CS 2:Offset 15..0 3:Offset 31..16 221 | printf "CS = " 222 | outputcs $arg1 223 | printf " (%d)", $arg1 224 | 225 | if (($arg0&7) == $STS_IG16) + (($arg0&7) == $STS_TG16) 226 | printf " Offset = " 227 | output/a $arg3 << 16 | $arg2 228 | end 229 | end 230 | 231 | define _printsegdesc 232 | # IA32 20007, Volume 3A, Figure 3-8 and Figure 4-1 233 | _printsegdesc1 ($arg0>>16) ($arg1&0xFF) ($arg1>>24) ($arg0&0xFFFF) ($arg1>>16&15) ($arg1>>23&1) 234 | if ($arg1>>12&1) == 1 235 | printf " AVL = %d", $arg1>>20&1 236 | if ($arg1>>11&1) == 0 237 | # Data segment 238 | if ($arg1>>22&1) == 0 239 | printf " B = small (0) " 240 | else 241 | printf " B = big (1) " 242 | end 243 | else 244 | # Code segment 245 | printf " D = " 246 | if ($arg1>>22&1) == 0 247 | printf "16-bit (0)" 248 | else 249 | printf "32-bit (1)" 250 | end 251 | end 252 | end 253 | end 254 | 255 | define _printsegdesc1 256 | # 0:Base 0..15 1:Base 16..23 2:Base 24..32 3:Limit 0..15 4:Limit 16..19 5:G 257 | printf "base = 0x%08x", $arg0 | ($arg1<<16) | ($arg2<<24) 258 | printf " limit = 0x" 259 | if $arg5 == 0 260 | printf "%08x", $arg3 | ($arg4<<16) 261 | else 262 | printf "%08x", (($arg3 | ($arg4<<16)) << 12) | 0xFFF 263 | end 264 | end 265 | 266 | define printdescs 267 | if $argc < 1 || $argc > 2 268 | echo Usage: printdescs expr [count] 269 | else 270 | if $argc == 1 271 | _printdescs ($arg0) (sizeof($arg0)/sizeof(($arg0)[0])) 272 | else 273 | _printdescs ($arg0) ($arg1) 274 | end 275 | end 276 | end 277 | 278 | document printdescs 279 | Print an array of x86 segment or gate descriptors. 280 | printdescs EXPR [COUNT] 281 | EXPR must evaluate to an array of descriptors. 282 | end 283 | 284 | define _printdescs 285 | set $i = 0 286 | while $i < $arg1 287 | printf "[%d] ", $i 288 | printdesc $arg0[$i] 289 | set $i = $i + 1 290 | end 291 | end 292 | -------------------------------------------------------------------------------- /tools/mkfs.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define stat xv6_stat // avoid clash with host struct stat 9 | #include "../include/types.h" 10 | #include "../include/fs.h" 11 | #include "../include/stat.h" 12 | #include "../include/param.h" 13 | 14 | #ifndef static_assert 15 | # define static_assert(a, b) do { switch (0) case 0: case (a): ; } while (0) 16 | #endif // static_assert 17 | 18 | int nblocks = 985; 19 | int nlog = LOGSIZE; 20 | int ninodes = 200; 21 | int size = 1024; 22 | 23 | int fsfd; 24 | struct superblock sb; 25 | char zeroes[512]; 26 | uint freeblock; 27 | uint usedblocks; 28 | uint bitblocks; 29 | uint freeinode = 1; 30 | 31 | void balloc(int); 32 | void wsect(uint, void*); 33 | void winode(uint, struct dinode*); 34 | void rinode(uint inum, struct dinode *ip); 35 | void rsect(uint sec, void *buf); 36 | uint ialloc(ushort type); 37 | void iappend(uint inum, void *p, int n); 38 | 39 | // convert to intel byte order 40 | ushort 41 | xshort(ushort x) 42 | { 43 | ushort y; 44 | uchar *a = (uchar*)&y; 45 | a[0] = x; 46 | a[1] = x >> 8; 47 | return y; 48 | } 49 | 50 | uint 51 | xint(uint x) 52 | { 53 | uint y; 54 | uchar *a = (uchar*)&y; 55 | a[0] = x; 56 | a[1] = x >> 8; 57 | a[2] = x >> 16; 58 | a[3] = x >> 24; 59 | return y; 60 | } 61 | 62 | int 63 | main(int argc, char *argv[]) 64 | { 65 | int i, cc, fd; 66 | uint rootino, inum, off; 67 | struct dirent de; 68 | char buf[512]; 69 | struct dinode din; 70 | 71 | 72 | static_assert(sizeof(int) == 4, "Integers must be 4 bytes!"); 73 | 74 | if(argc < 2){ 75 | fprintf(stderr, "Usage: mkfs fs.img files...\n"); 76 | exit(1); 77 | } 78 | 79 | assert((512 % sizeof(struct dinode)) == 0); 80 | assert((512 % sizeof(struct dirent)) == 0); 81 | 82 | fsfd = open(argv[1], O_RDWR|O_CREAT|O_TRUNC, 0666); 83 | if(fsfd < 0){ 84 | perror(argv[1]); 85 | exit(1); 86 | } 87 | 88 | sb.size = xint(size); 89 | sb.nblocks = xint(nblocks); // so whole disk is size sectors 90 | sb.ninodes = xint(ninodes); 91 | sb.nlog = xint(nlog); 92 | 93 | bitblocks = size/(512*8) + 1; 94 | usedblocks = ninodes / IPB + 3 + bitblocks; 95 | freeblock = usedblocks; 96 | 97 | printf("used %d (bit %d ninode %zu) free %u log %u total %d\n", usedblocks, 98 | bitblocks, ninodes/IPB + 1, freeblock, nlog, nblocks+usedblocks+nlog); 99 | 100 | assert(nblocks + usedblocks + nlog == size); 101 | 102 | for(i = 0; i < nblocks + usedblocks + nlog; i++) 103 | wsect(i, zeroes); 104 | 105 | memset(buf, 0, sizeof(buf)); 106 | memmove(buf, &sb, sizeof(sb)); 107 | wsect(1, buf); 108 | 109 | rootino = ialloc(T_DIR); 110 | assert(rootino == ROOTINO); 111 | 112 | bzero(&de, sizeof(de)); 113 | de.inum = xshort(rootino); 114 | strcpy(de.name, "."); 115 | iappend(rootino, &de, sizeof(de)); 116 | 117 | bzero(&de, sizeof(de)); 118 | de.inum = xshort(rootino); 119 | strcpy(de.name, ".."); 120 | iappend(rootino, &de, sizeof(de)); 121 | 122 | for(i = 2; i < argc; i++){ 123 | char *name = argv[i]; 124 | 125 | if (!strncmp(name, "fs/", 3)) 126 | name += 3; 127 | 128 | assert(index(name, '/') == 0); 129 | 130 | if((fd = open(argv[i], 0)) < 0){ 131 | perror(argv[i]); 132 | exit(1); 133 | } 134 | 135 | inum = ialloc(T_FILE); 136 | 137 | bzero(&de, sizeof(de)); 138 | de.inum = xshort(inum); 139 | strncpy(de.name, name, DIRSIZ); 140 | iappend(rootino, &de, sizeof(de)); 141 | 142 | while((cc = read(fd, buf, sizeof(buf))) > 0) 143 | iappend(inum, buf, cc); 144 | 145 | close(fd); 146 | } 147 | 148 | // fix size of root inode dir 149 | rinode(rootino, &din); 150 | off = xint(din.size); 151 | off = ((off/BSIZE) + 1) * BSIZE; 152 | din.size = xint(off); 153 | winode(rootino, &din); 154 | 155 | balloc(usedblocks); 156 | 157 | exit(0); 158 | } 159 | 160 | void 161 | wsect(uint sec, void *buf) 162 | { 163 | if(lseek(fsfd, sec * 512L, 0) != sec * 512L){ 164 | perror("lseek"); 165 | exit(1); 166 | } 167 | if(write(fsfd, buf, 512) != 512){ 168 | perror("write"); 169 | exit(1); 170 | } 171 | } 172 | 173 | uint 174 | i2b(uint inum) 175 | { 176 | return (inum / IPB) + 2; 177 | } 178 | 179 | void 180 | winode(uint inum, struct dinode *ip) 181 | { 182 | char buf[512]; 183 | uint bn; 184 | struct dinode *dip; 185 | 186 | bn = i2b(inum); 187 | rsect(bn, buf); 188 | dip = ((struct dinode*)buf) + (inum % IPB); 189 | *dip = *ip; 190 | wsect(bn, buf); 191 | } 192 | 193 | void 194 | rinode(uint inum, struct dinode *ip) 195 | { 196 | char buf[512]; 197 | uint bn; 198 | struct dinode *dip; 199 | 200 | bn = i2b(inum); 201 | rsect(bn, buf); 202 | dip = ((struct dinode*)buf) + (inum % IPB); 203 | *ip = *dip; 204 | } 205 | 206 | void 207 | rsect(uint sec, void *buf) 208 | { 209 | if(lseek(fsfd, sec * 512L, 0) != sec * 512L){ 210 | perror("lseek"); 211 | exit(1); 212 | } 213 | if(read(fsfd, buf, 512) != 512){ 214 | perror("read"); 215 | exit(1); 216 | } 217 | } 218 | 219 | uint 220 | ialloc(ushort type) 221 | { 222 | uint inum = freeinode++; 223 | struct dinode din; 224 | 225 | bzero(&din, sizeof(din)); 226 | din.type = xshort(type); 227 | din.nlink = xshort(1); 228 | din.size = xint(0); 229 | winode(inum, &din); 230 | return inum; 231 | } 232 | 233 | void 234 | balloc(int used) 235 | { 236 | uchar buf[512]; 237 | int i; 238 | 239 | printf("balloc: first %d blocks have been allocated\n", used); 240 | assert(used < 512*8); 241 | bzero(buf, 512); 242 | for(i = 0; i < used; i++){ 243 | buf[i/8] = buf[i/8] | (0x1 << (i%8)); 244 | } 245 | printf("balloc: write bitmap block at sector %zu\n", ninodes/IPB + 3); 246 | wsect(ninodes / IPB + 3, buf); 247 | } 248 | 249 | #define min(a, b) ((a) < (b) ? (a) : (b)) 250 | 251 | void 252 | iappend(uint inum, void *xp, int n) 253 | { 254 | char *p = (char*)xp; 255 | uint fbn, off, n1; 256 | struct dinode din; 257 | char buf[512]; 258 | uint indirect[NINDIRECT]; 259 | uint x; 260 | 261 | rinode(inum, &din); 262 | 263 | off = xint(din.size); 264 | while(n > 0){ 265 | fbn = off / 512; 266 | assert(fbn < MAXFILE); 267 | if(fbn < NDIRECT){ 268 | if(xint(din.addrs[fbn]) == 0){ 269 | din.addrs[fbn] = xint(freeblock++); 270 | usedblocks++; 271 | } 272 | x = xint(din.addrs[fbn]); 273 | } else { 274 | if(xint(din.addrs[NDIRECT]) == 0){ 275 | // printf("allocate indirect block\n"); 276 | din.addrs[NDIRECT] = xint(freeblock++); 277 | usedblocks++; 278 | } 279 | // printf("read indirect block\n"); 280 | rsect(xint(din.addrs[NDIRECT]), (char*)indirect); 281 | if(indirect[fbn - NDIRECT] == 0){ 282 | indirect[fbn - NDIRECT] = xint(freeblock++); 283 | usedblocks++; 284 | wsect(xint(din.addrs[NDIRECT]), (char*)indirect); 285 | } 286 | x = xint(indirect[fbn-NDIRECT]); 287 | } 288 | n1 = min(n, (fbn + 1) * 512 - off); 289 | rsect(x, buf); 290 | bcopy(p, buf + off - (fbn * 512), n1); 291 | wsect(x, buf); 292 | n -= n1; 293 | off += n1; 294 | p += n1; 295 | } 296 | din.size = xint(off); 297 | winode(inum, &din); 298 | } 299 | -------------------------------------------------------------------------------- /tools/printpcs: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Decode the symbols from a panic EIP list 4 | 5 | # Find a working addr2line 6 | for p in i386-jos-elf-addr2line addr2line; do 7 | if which $p 2>&1 >/dev/null && \ 8 | $p -h 2>&1 | grep -q '\belf32-i386\b'; then 9 | break 10 | fi 11 | done 12 | 13 | # Enable as much pretty-printing as this addr2line can do 14 | $p $($p -h | grep ' -[aipsf] ' | awk '{print $1}') -e kernel "$@" 15 | -------------------------------------------------------------------------------- /tools/sign.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | open(SIG, $ARGV[0]) || die "open $ARGV[0]: $!"; 4 | 5 | $n = sysread(SIG, $buf, 1000); 6 | 7 | if($n > 510){ 8 | print STDERR "boot block too large: $n bytes (max 510)\n"; 9 | exit 1; 10 | } 11 | 12 | print STDERR "boot block is $n bytes (max 510)\n"; 13 | 14 | $buf .= "\0" x (510-$n); 15 | $buf .= "\x55\xAA"; 16 | 17 | open(SIG, ">$ARGV[0]") || die "open >$ARGV[0]: $!"; 18 | print SIG $buf; 19 | close SIG; 20 | -------------------------------------------------------------------------------- /tools/sleep1.p: -------------------------------------------------------------------------------- 1 | /* 2 | This file defines a Promela model for xv6's 3 | acquire, release, sleep, and wakeup, along with 4 | a model of a simple producer/consumer queue. 5 | 6 | To run: 7 | spinp sleep1.p 8 | 9 | (You may need to install Spin, available at http://spinroot.com/.) 10 | 11 | After a successful run spin prints something like: 12 | 13 | unreached in proctype consumer 14 | (0 of 37 states) 15 | unreached in proctype producer 16 | (0 of 23 states) 17 | 18 | After an unsuccessful run, the spinp script prints 19 | an execution trace that causes a deadlock. 20 | 21 | The safe body of producer reads: 22 | 23 | acquire(lk); 24 | x = value; value = x + 1; x = 0; 25 | wakeup(0); 26 | release(lk); 27 | i = i + 1; 28 | 29 | If this is changed to: 30 | 31 | x = value; value = x + 1; x = 0; 32 | acquire(lk); 33 | wakeup(0); 34 | release(lk); 35 | i = i + 1; 36 | 37 | then a deadlock can happen, because the non-atomic 38 | increment of value conflicts with the non-atomic 39 | decrement in consumer, causing value to have a bad value. 40 | Try this. 41 | 42 | If it is changed to: 43 | 44 | acquire(lk); 45 | x = value; value = x + 1; x = 0; 46 | release(lk); 47 | wakeup(0); 48 | i = i + 1; 49 | 50 | then nothing bad happens: it is okay to wakeup after release 51 | instead of before, although it seems morally wrong. 52 | */ 53 | 54 | #define ITER 4 55 | #define N 2 56 | 57 | bit lk; 58 | byte value; 59 | bit sleeping[N]; 60 | 61 | inline acquire(x) 62 | { 63 | atomic { x == 0; x = 1 } 64 | } 65 | 66 | inline release(x) 67 | { 68 | assert x==1; 69 | x = 0 70 | } 71 | 72 | inline sleep(cond, lk) 73 | { 74 | assert !sleeping[_pid]; 75 | if 76 | :: cond -> 77 | skip 78 | :: else -> 79 | atomic { release(lk); sleeping[_pid] = 1 }; 80 | sleeping[_pid] == 0; 81 | acquire(lk) 82 | fi 83 | } 84 | 85 | inline wakeup() 86 | { 87 | w = 0; 88 | do 89 | :: w < N -> 90 | sleeping[w] = 0; 91 | w = w + 1 92 | :: else -> 93 | break 94 | od 95 | } 96 | 97 | active[N] proctype consumer() 98 | { 99 | byte i, x; 100 | 101 | i = 0; 102 | do 103 | :: i < ITER -> 104 | acquire(lk); 105 | sleep(value > 0, lk); 106 | x = value; value = x - 1; x = 0; 107 | release(lk); 108 | i = i + 1; 109 | :: else -> 110 | break 111 | od; 112 | i = 0; 113 | skip 114 | } 115 | 116 | active[N] proctype producer() 117 | { 118 | byte i, x, w; 119 | 120 | i = 0; 121 | do 122 | :: i < ITER -> 123 | acquire(lk); 124 | x = value; value = x + 1; x = 0; 125 | release(lk); 126 | wakeup(); 127 | i = i + 1; 128 | :: else -> 129 | break 130 | od; 131 | i = 0; 132 | skip 133 | } 134 | 135 | -------------------------------------------------------------------------------- /tools/spinp: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ $# != 1 ] || [ ! -f "$1" ]; then 4 | echo 'usage: spinp file.p' 1>&2 5 | exit 1 6 | fi 7 | 8 | rm -f $1.trail 9 | spin -a $1 || exit 1 10 | cc -DSAFETY -DREACH -DMEMLIM=500 -o pan pan.c 11 | pan -i 12 | rm pan.* pan 13 | if [ -f $1.trail ]; then 14 | spin -t -p $1 15 | fi 16 | 17 | -------------------------------------------------------------------------------- /tools/vectors.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | 3 | # Generate vectors.S, the trap/interrupt entry points. 4 | # There has to be one entry point per interrupt number 5 | # since otherwise there's no way for trap() to discover 6 | # the interrupt number. 7 | 8 | print "# generated by vectors.pl - do not edit\n"; 9 | print "# handlers\n"; 10 | print ".globl alltraps\n"; 11 | for(my $i = 0; $i < 256; $i++){ 12 | print ".globl vector$i\n"; 13 | print "vector$i:\n"; 14 | if(!($i == 8 || ($i >= 10 && $i <= 14) || $i == 17)){ 15 | print " pushl \$0\n"; 16 | } 17 | print " pushl \$$i\n"; 18 | print " jmp alltraps\n"; 19 | } 20 | 21 | print "\n# vector table\n"; 22 | print ".data\n"; 23 | print ".globl vectors\n"; 24 | print "vectors:\n"; 25 | for(my $i = 0; $i < 256; $i++){ 26 | print " .long vector$i\n"; 27 | } 28 | 29 | # sample output: 30 | # # handlers 31 | # .globl alltraps 32 | # .globl vector0 33 | # vector0: 34 | # pushl $0 35 | # pushl $0 36 | # jmp alltraps 37 | # ... 38 | # 39 | # # vector table 40 | # .data 41 | # .globl vectors 42 | # vectors: 43 | # .long vector0 44 | # .long vector1 45 | # .long vector2 46 | # ... 47 | 48 | -------------------------------------------------------------------------------- /tools/vectors64.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | 3 | # Generate vectors.S, the trap/interrupt entry points. 4 | # There has to be one entry point per interrupt number 5 | # since otherwise there's no way for trap() to discover 6 | # the interrupt number. 7 | 8 | print "# generated by vectors.pl - do not edit\n"; 9 | print "# handlers\n"; 10 | print ".globl alltraps\n"; 11 | for(my $i = 0; $i < 256; $i++){ 12 | print ".globl vector$i\n"; 13 | print "vector$i:\n"; 14 | if(!($i == 8 || ($i >= 10 && $i <= 14) || $i == 17)){ 15 | print " push \$0\n"; 16 | } 17 | print " push \$$i\n"; 18 | print " jmp alltraps\n"; 19 | } 20 | 21 | print "\n# vector table\n"; 22 | print ".data\n"; 23 | print ".globl vectors\n"; 24 | print "vectors:\n"; 25 | for(my $i = 0; $i < 256; $i++){ 26 | print " .quad vector$i\n"; 27 | } 28 | -------------------------------------------------------------------------------- /ulib/printf.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "types.h" 4 | #include "stat.h" 5 | #include "user.h" 6 | 7 | static void 8 | putc(int fd, char c) 9 | { 10 | write(fd, &c, 1); 11 | } 12 | 13 | static void 14 | printint(int fd, int xx, int base, int sgn) 15 | { 16 | static char digits[] = "0123456789ABCDEF"; 17 | char buf[16]; 18 | int i, neg; 19 | uint x; 20 | 21 | neg = 0; 22 | if(sgn && xx < 0){ 23 | neg = 1; 24 | x = -xx; 25 | } else { 26 | x = xx; 27 | } 28 | 29 | i = 0; 30 | do{ 31 | buf[i++] = digits[x % base]; 32 | }while((x /= base) != 0); 33 | if(neg) 34 | buf[i++] = '-'; 35 | 36 | while(--i >= 0) 37 | putc(fd, buf[i]); 38 | } 39 | 40 | // Print to the given fd. Only understands %d, %x, %p, %s. 41 | void 42 | printf(int fd, char *fmt, ...) 43 | { 44 | va_list ap; 45 | char *s; 46 | int c, i, state; 47 | va_start(ap, fmt); 48 | 49 | state = 0; 50 | for(i = 0; fmt[i]; i++){ 51 | c = fmt[i] & 0xff; 52 | if(state == 0){ 53 | if(c == '%'){ 54 | state = '%'; 55 | } else { 56 | putc(fd, c); 57 | } 58 | } else if(state == '%'){ 59 | if(c == 'd'){ 60 | printint(fd, va_arg(ap, int), 10, 1); 61 | } else if(c == 'x' || c == 'p'){ 62 | printint(fd, va_arg(ap, int), 16, 0); 63 | } else if(c == 's'){ 64 | s = va_arg(ap, char*); 65 | if(s == 0) 66 | s = "(null)"; 67 | while(*s != 0){ 68 | putc(fd, *s); 69 | s++; 70 | } 71 | } else if(c == 'c'){ 72 | putc(fd, va_arg(ap, uint)); 73 | } else if(c == '%'){ 74 | putc(fd, c); 75 | } else { 76 | // Unknown % sequence. Print it to draw attention. 77 | putc(fd, '%'); 78 | putc(fd, c); 79 | } 80 | state = 0; 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /ulib/ulib.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "stat.h" 3 | #include "fcntl.h" 4 | #include "user.h" 5 | #include "x86.h" 6 | 7 | char* 8 | strcpy(char *s, char *t) 9 | { 10 | char *os; 11 | 12 | os = s; 13 | while((*s++ = *t++) != 0) 14 | ; 15 | return os; 16 | } 17 | 18 | int 19 | strcmp(const char *p, const char *q) 20 | { 21 | while(*p && *p == *q) 22 | p++, q++; 23 | return (uchar)*p - (uchar)*q; 24 | } 25 | 26 | uint 27 | strlen(char *s) 28 | { 29 | int n; 30 | 31 | for(n = 0; s[n]; n++) 32 | ; 33 | return n; 34 | } 35 | 36 | void* 37 | memset(void *dst, int c, uint n) 38 | { 39 | stosb(dst, c, n); 40 | return dst; 41 | } 42 | 43 | char* 44 | strchr(const char *s, char c) 45 | { 46 | for(; *s; s++) 47 | if(*s == c) 48 | return (char*)s; 49 | return 0; 50 | } 51 | 52 | char* 53 | gets(char *buf, int max) 54 | { 55 | int i, cc; 56 | char c; 57 | 58 | for(i=0; i+1 < max; ){ 59 | cc = read(0, &c, 1); 60 | if(cc < 1) 61 | break; 62 | buf[i++] = c; 63 | if(c == '\n' || c == '\r') 64 | break; 65 | } 66 | buf[i] = '\0'; 67 | return buf; 68 | } 69 | 70 | int 71 | stat(char *n, struct stat *st) 72 | { 73 | int fd; 74 | int r; 75 | 76 | fd = open(n, O_RDONLY); 77 | if(fd < 0) 78 | return -1; 79 | r = fstat(fd, st); 80 | close(fd); 81 | return r; 82 | } 83 | 84 | int 85 | atoi(const char *s) 86 | { 87 | int n; 88 | 89 | n = 0; 90 | while('0' <= *s && *s <= '9') 91 | n = n*10 + *s++ - '0'; 92 | return n; 93 | } 94 | 95 | void* 96 | memmove(void *vdst, void *vsrc, int n) 97 | { 98 | char *dst, *src; 99 | 100 | dst = vdst; 101 | src = vsrc; 102 | while(n-- > 0) 103 | *dst++ = *src++; 104 | return vdst; 105 | } 106 | -------------------------------------------------------------------------------- /ulib/umalloc.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "stat.h" 3 | #include "user.h" 4 | #include "param.h" 5 | 6 | // Memory allocator by Kernighan and Ritchie, 7 | // The C programming Language, 2nd ed. Section 8.7. 8 | 9 | typedef long Align; 10 | 11 | union header { 12 | struct { 13 | union header *ptr; 14 | uint size; 15 | } s; 16 | Align x; 17 | }; 18 | 19 | typedef union header Header; 20 | 21 | static Header base; 22 | static Header *freep; 23 | 24 | void 25 | free(void *ap) 26 | { 27 | Header *bp, *p; 28 | 29 | bp = (Header*)ap - 1; 30 | for(p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr) 31 | if(p >= p->s.ptr && (bp > p || bp < p->s.ptr)) 32 | break; 33 | if(bp + bp->s.size == p->s.ptr){ 34 | bp->s.size += p->s.ptr->s.size; 35 | bp->s.ptr = p->s.ptr->s.ptr; 36 | } else 37 | bp->s.ptr = p->s.ptr; 38 | if(p + p->s.size == bp){ 39 | p->s.size += bp->s.size; 40 | p->s.ptr = bp->s.ptr; 41 | } else 42 | p->s.ptr = bp; 43 | freep = p; 44 | } 45 | 46 | static Header* 47 | morecore(uint nu) 48 | { 49 | char *p; 50 | Header *hp; 51 | 52 | if(nu < 4096) 53 | nu = 4096; 54 | p = sbrk(nu * sizeof(Header)); 55 | if(p == (char*)-1) 56 | return 0; 57 | hp = (Header*)p; 58 | hp->s.size = nu; 59 | free((void*)(hp + 1)); 60 | return freep; 61 | } 62 | 63 | void* 64 | malloc(uint nbytes) 65 | { 66 | Header *p, *prevp; 67 | uint nunits; 68 | 69 | nunits = (nbytes + sizeof(Header) - 1)/sizeof(Header) + 1; 70 | if((prevp = freep) == 0){ 71 | base.s.ptr = freep = prevp = &base; 72 | base.s.size = 0; 73 | } 74 | for(p = prevp->s.ptr; ; prevp = p, p = p->s.ptr){ 75 | if(p->s.size >= nunits){ 76 | if(p->s.size == nunits) 77 | prevp->s.ptr = p->s.ptr; 78 | else { 79 | p->s.size -= nunits; 80 | p += p->s.size; 81 | p->s.size = nunits; 82 | } 83 | freep = prevp; 84 | return (void*)(p + 1); 85 | } 86 | if(p == freep) 87 | if((p = morecore(nunits)) == 0) 88 | return 0; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /ulib/usys.S: -------------------------------------------------------------------------------- 1 | #include "syscall.h" 2 | #include "traps.h" 3 | 4 | #define SYSCALL(name) \ 5 | .globl name; \ 6 | name: \ 7 | movl $SYS_ ## name, %eax; \ 8 | int $T_SYSCALL; \ 9 | ret 10 | 11 | SYSCALL(fork) 12 | SYSCALL(exit) 13 | SYSCALL(wait) 14 | SYSCALL(pipe) 15 | SYSCALL(read) 16 | SYSCALL(write) 17 | SYSCALL(close) 18 | SYSCALL(kill) 19 | SYSCALL(exec) 20 | SYSCALL(open) 21 | SYSCALL(mknod) 22 | SYSCALL(unlink) 23 | SYSCALL(fstat) 24 | SYSCALL(link) 25 | SYSCALL(mkdir) 26 | SYSCALL(chdir) 27 | SYSCALL(dup) 28 | SYSCALL(getpid) 29 | SYSCALL(sbrk) 30 | SYSCALL(sleep) 31 | SYSCALL(uptime) 32 | -------------------------------------------------------------------------------- /user/cat.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "stat.h" 3 | #include "user.h" 4 | 5 | char buf[512]; 6 | 7 | void 8 | cat(int fd) 9 | { 10 | int n; 11 | 12 | while((n = read(fd, buf, sizeof(buf))) > 0) 13 | write(1, buf, n); 14 | if(n < 0){ 15 | printf(1, "cat: read error\n"); 16 | exit(); 17 | } 18 | } 19 | 20 | int 21 | main(int argc, char *argv[]) 22 | { 23 | int fd, i; 24 | 25 | if(argc <= 1){ 26 | cat(0); 27 | exit(); 28 | } 29 | 30 | for(i = 1; i < argc; i++){ 31 | if((fd = open(argv[i], 0)) < 0){ 32 | printf(1, "cat: cannot open %s\n", argv[i]); 33 | exit(); 34 | } 35 | cat(fd); 36 | close(fd); 37 | } 38 | exit(); 39 | } 40 | -------------------------------------------------------------------------------- /user/echo.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "stat.h" 3 | #include "user.h" 4 | 5 | int 6 | main(int argc, char *argv[]) 7 | { 8 | int i; 9 | 10 | for(i = 1; i < argc; i++) 11 | printf(1, "%s%s", argv[i], i+1 < argc ? " " : "\n"); 12 | exit(); 13 | } 14 | -------------------------------------------------------------------------------- /user/forktest.c: -------------------------------------------------------------------------------- 1 | // Test that fork fails gracefully. 2 | // Tiny executable so that the limit can be filling the proc table. 3 | 4 | #include "types.h" 5 | #include "stat.h" 6 | #include "user.h" 7 | 8 | #define N 1000 9 | 10 | void 11 | printf(int fd, char *s, ...) 12 | { 13 | write(fd, s, strlen(s)); 14 | } 15 | 16 | void 17 | forktest(void) 18 | { 19 | int n, pid; 20 | 21 | printf(1, "fork test\n"); 22 | 23 | for(n=0; n 0; n--){ 37 | if(wait() < 0){ 38 | printf(1, "wait stopped early\n"); 39 | exit(); 40 | } 41 | } 42 | 43 | if(wait() != -1){ 44 | printf(1, "wait got too many\n"); 45 | exit(); 46 | } 47 | 48 | printf(1, "fork test OK\n"); 49 | } 50 | 51 | int 52 | main(void) 53 | { 54 | forktest(); 55 | exit(); 56 | } 57 | -------------------------------------------------------------------------------- /user/grep.c: -------------------------------------------------------------------------------- 1 | // Simple grep. Only supports ^ . * $ operators. 2 | 3 | #include "types.h" 4 | #include "stat.h" 5 | #include "user.h" 6 | 7 | char buf[1024]; 8 | int match(char*, char*); 9 | 10 | void 11 | grep(char *pattern, int fd) 12 | { 13 | int n, m; 14 | char *p, *q; 15 | 16 | m = 0; 17 | while((n = read(fd, buf+m, sizeof(buf)-m)) > 0){ 18 | m += n; 19 | p = buf; 20 | while((q = strchr(p, '\n')) != 0){ 21 | *q = 0; 22 | if(match(pattern, p)){ 23 | *q = '\n'; 24 | write(1, p, q+1 - p); 25 | } 26 | p = q+1; 27 | } 28 | if(p == buf) 29 | m = 0; 30 | if(m > 0){ 31 | m -= p - buf; 32 | memmove(buf, p, m); 33 | } 34 | } 35 | } 36 | 37 | int 38 | main(int argc, char *argv[]) 39 | { 40 | int fd, i; 41 | char *pattern; 42 | 43 | if(argc <= 1){ 44 | printf(2, "usage: grep pattern [file ...]\n"); 45 | exit(); 46 | } 47 | pattern = argv[1]; 48 | 49 | if(argc <= 2){ 50 | grep(pattern, 0); 51 | exit(); 52 | } 53 | 54 | for(i = 2; i < argc; i++){ 55 | if((fd = open(argv[i], 0)) < 0){ 56 | printf(1, "grep: cannot open %s\n", argv[i]); 57 | exit(); 58 | } 59 | grep(pattern, fd); 60 | close(fd); 61 | } 62 | exit(); 63 | } 64 | 65 | // Regexp matcher from Kernighan & Pike, 66 | // The Practice of Programming, Chapter 9. 67 | 68 | int matchhere(char*, char*); 69 | int matchstar(int, char*, char*); 70 | 71 | int 72 | match(char *re, char *text) 73 | { 74 | if(re[0] == '^') 75 | return matchhere(re+1, text); 76 | do{ // must look at empty string 77 | if(matchhere(re, text)) 78 | return 1; 79 | }while(*text++ != '\0'); 80 | return 0; 81 | } 82 | 83 | // matchhere: search for re at beginning of text 84 | int matchhere(char *re, char *text) 85 | { 86 | if(re[0] == '\0') 87 | return 1; 88 | if(re[1] == '*') 89 | return matchstar(re[0], re+2, text); 90 | if(re[0] == '$' && re[1] == '\0') 91 | return *text == '\0'; 92 | if(*text!='\0' && (re[0]=='.' || re[0]==*text)) 93 | return matchhere(re+1, text+1); 94 | return 0; 95 | } 96 | 97 | // matchstar: search for c*re at beginning of text 98 | int matchstar(int c, char *re, char *text) 99 | { 100 | do{ // a * matches zero or more instances 101 | if(matchhere(re, text)) 102 | return 1; 103 | }while(*text!='\0' && (*text++==c || c=='.')); 104 | return 0; 105 | } 106 | 107 | -------------------------------------------------------------------------------- /user/init.c: -------------------------------------------------------------------------------- 1 | // init: The initial user-level program 2 | 3 | #include "types.h" 4 | #include "stat.h" 5 | #include "user.h" 6 | #include "fcntl.h" 7 | 8 | char *argv[] = { "sh", 0 }; 9 | 10 | int 11 | main(void) 12 | { 13 | int pid, wpid; 14 | 15 | if(open("console", O_RDWR) < 0){ 16 | mknod("console", 1, 1); 17 | open("console", O_RDWR); 18 | } 19 | dup(0); // stdout 20 | dup(0); // stderr 21 | 22 | for(;;){ 23 | printf(1, "init: starting sh\n"); 24 | pid = fork(); 25 | if(pid < 0){ 26 | printf(1, "init: fork failed\n"); 27 | exit(); 28 | } 29 | if(pid == 0){ 30 | exec("sh", argv); 31 | printf(1, "init: exec sh failed\n"); 32 | exit(); 33 | } 34 | while((wpid=wait()) >= 0 && wpid != pid) 35 | printf(1, "zombie!\n"); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /user/kill.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "stat.h" 3 | #include "user.h" 4 | 5 | int 6 | main(int argc, char **argv) 7 | { 8 | int i; 9 | 10 | if(argc < 1){ 11 | printf(2, "usage: kill pid...\n"); 12 | exit(); 13 | } 14 | for(i=1; i= path && *p != '/'; p--) 14 | ; 15 | p++; 16 | 17 | // Return blank-padded name. 18 | if(strlen(p) >= DIRSIZ) 19 | return p; 20 | memmove(buf, p, strlen(p)); 21 | memset(buf+strlen(p), ' ', DIRSIZ-strlen(p)); 22 | return buf; 23 | } 24 | 25 | void 26 | ls(char *path) 27 | { 28 | char buf[512], *p; 29 | int fd; 30 | struct dirent de; 31 | struct stat st; 32 | 33 | if((fd = open(path, 0)) < 0){ 34 | printf(2, "ls: cannot open %s\n", path); 35 | return; 36 | } 37 | 38 | if(fstat(fd, &st) < 0){ 39 | printf(2, "ls: cannot stat %s\n", path); 40 | close(fd); 41 | return; 42 | } 43 | 44 | switch(st.type){ 45 | case T_FILE: 46 | printf(1, "%s %d %d %d\n", fmtname(path), st.type, st.ino, st.size); 47 | break; 48 | 49 | case T_DIR: 50 | if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){ 51 | printf(1, "ls: path too long\n"); 52 | break; 53 | } 54 | strcpy(buf, path); 55 | p = buf+strlen(buf); 56 | *p++ = '/'; 57 | while(read(fd, &de, sizeof(de)) == sizeof(de)){ 58 | if(de.inum == 0) 59 | continue; 60 | memmove(p, de.name, DIRSIZ); 61 | p[DIRSIZ] = 0; 62 | if(stat(buf, &st) < 0){ 63 | printf(1, "ls: cannot stat %s\n", buf); 64 | continue; 65 | } 66 | printf(1, "%s %d %d %d\n", fmtname(buf), st.type, st.ino, st.size); 67 | } 68 | break; 69 | } 70 | close(fd); 71 | } 72 | 73 | int 74 | main(int argc, char *argv[]) 75 | { 76 | int i; 77 | 78 | if(argc < 2){ 79 | ls("."); 80 | exit(); 81 | } 82 | for(i=1; i 0) 28 | break; 29 | 30 | printf(1, "write %d\n", i); 31 | 32 | path[8] += i; 33 | fd = open(path, O_CREATE | O_RDWR); 34 | for(i = 0; i < 20; i++) 35 | // printf(fd, "%d\n", i); 36 | write(fd, data, sizeof(data)); 37 | close(fd); 38 | 39 | printf(1, "read\n"); 40 | 41 | fd = open(path, O_RDONLY); 42 | for (i = 0; i < 20; i++) 43 | read(fd, data, sizeof(data)); 44 | close(fd); 45 | 46 | wait(); 47 | 48 | exit(); 49 | } 50 | -------------------------------------------------------------------------------- /user/wc.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "stat.h" 3 | #include "user.h" 4 | 5 | char buf[512]; 6 | 7 | void 8 | wc(int fd, char *name) 9 | { 10 | int i, n; 11 | int l, w, c, inword; 12 | 13 | l = w = c = 0; 14 | inword = 0; 15 | while((n = read(fd, buf, sizeof(buf))) > 0){ 16 | for(i=0; i 0) 12 | sleep(5); // Let child exit before parent. 13 | exit(); 14 | } 15 | --------------------------------------------------------------------------------