├── .gdbinit.tmpl ├── CODING ├── GNUmakefile ├── boot ├── Makefrag ├── boot.S ├── main.c └── sign.pl ├── conf ├── env.mk └── lab.mk ├── dev ├── kbd.c ├── kbd.h ├── nvram.c ├── nvram.h ├── serial.c ├── serial.h ├── video.c └── video.h ├── inc ├── assert.h ├── cdefs.h ├── elf.h ├── mmu.h ├── stdarg.h ├── stdio.h ├── stdlib.h ├── string.h ├── trap.h ├── types.h └── x86.h ├── kern ├── Makefrag ├── cons.c ├── cons.h ├── cpu.c ├── cpu.h ├── debug.c ├── debug.h ├── entry.S ├── init.c ├── init.h ├── mem.c ├── mem.h ├── trap.c ├── trap.h └── trapasm.S ├── lib ├── cprintf.c ├── printfmt.c └── string.c └── misc ├── gccprefix.sh ├── grade-functions.sh ├── grade-lab1.sh ├── mergedep.pl └── which-qemu.sh /.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 we always run with CS == 8 in 32-bit mode. 6 | if $cs == 8 || $cs == 27 7 | if $lastcs != 8 && $lastcs != 27 8 | set architecture i386 9 | end 10 | x/i $pc 11 | else 12 | if $lastcs == -1 || $lastcs == 8 || $lastcs == 27 13 | set architecture i8086 14 | end 15 | # Translate the segment:offset into a physical address 16 | printf "[%4x:%4x] ", $cs, $eip 17 | x/i $cs*16+$eip 18 | end 19 | set $lastcs = $cs 20 | end 21 | 22 | echo + target remote localhost:1234\n 23 | target remote localhost:1234 24 | 25 | # If this fails, it's probably because your GDB doesn't support ELF. 26 | # Look at the tools page at 27 | # http://pdos.csail.mit.edu/6.828/2009/tools.html 28 | # for instructions on building GDB with ELF support. 29 | echo + symbol-file obj/kern/kernel\n 30 | symbol-file obj/kern/kernel 31 | -------------------------------------------------------------------------------- /CODING: -------------------------------------------------------------------------------- 1 | CODING STANDARDS 2 | 3 | It's easier on everyone if all authors working on a shared 4 | code base are consistent in the way they write their programs. 5 | We have the following conventions in our code: 6 | 7 | * No space after the name of a function in a call 8 | For example, printf("hello") not printf ("hello"). 9 | 10 | * One space after keywords "if", "for", "while", "switch". 11 | For example, if (x) not if(x). 12 | 13 | * Space before braces. 14 | For example, if (x) { not if (x){. 15 | 16 | * Function names are all lower-case separated by underscores. 17 | 18 | * Beginning-of-line indentation via tabs, not spaces. 19 | 20 | * Preprocessor macros are always UPPERCASE. 21 | There are a few grandfathered exceptions: assert, panic, 22 | static_assert, offsetof. 23 | 24 | * Pointer types have spaces: (uint16_t *) not (uint16_t*). 25 | 26 | * Multi-word names are lower_case_with_underscores. 27 | 28 | * Comments in imported code are usually C /* ... */ comments. 29 | Comments in new code are C++ style //. 30 | 31 | * In a function definition, the function name starts a new line. 32 | Then you can grep -n '^foo' */*.c to find the definition of foo. 33 | 34 | * Functions that take no arguments are declared f(void) not f(). 35 | 36 | -------------------------------------------------------------------------------- /GNUmakefile: -------------------------------------------------------------------------------- 1 | # 2 | # This makefile system follows the structuring conventions 3 | # recommended by Peter Miller in his excellent paper: 4 | # 5 | # Recursive Make Considered Harmful 6 | # http://aegis.sourceforge.net/auug97.pdf 7 | # 8 | # Copyright (C) 2003 Massachusetts Institute of Technology 9 | # See section "MIT License" in the file LICENSES for licensing terms. 10 | # Primary authors: Bryan Ford, Eddie Kohler, Austin Clemens 11 | # 12 | OBJDIR := obj 13 | 14 | ifdef LAB 15 | SETTINGLAB := true 16 | else 17 | -include conf/lab.mk 18 | endif 19 | 20 | -include conf/env.mk 21 | 22 | ifndef SOL 23 | SOL := 0 24 | endif 25 | ifndef LABADJUST 26 | LABADJUST := 0 27 | endif 28 | 29 | 30 | TOP = . 31 | 32 | # Cross-compiler toolchain 33 | # 34 | # This Makefile will automatically use a cross-compiler toolchain installed 35 | # as 'pios-*' or 'i386-elf-*', if one exists. If the host tools ('gcc', 36 | # 'objdump', and so forth) compile for a 32-bit x86 ELF target, that will 37 | # be detected as well. If you have the right compiler toolchain installed 38 | # using a different name, set GCCPREFIX explicitly in conf/env.mk 39 | 40 | # try to infer the correct GCCPREFIX 41 | ifndef GCCPREFIX 42 | GCCPREFIX := $(shell sh misc/gccprefix.sh) 43 | endif 44 | 45 | # try to infer the correct QEMU 46 | ifndef QEMU 47 | QEMU := $(shell sh misc/which-qemu.sh) 48 | endif 49 | 50 | # try to generate unique GDB and network port numbers 51 | GDBPORT := $(shell expr `id -u` % 5000 + 25000) 52 | NETPORT := $(shell expr `id -u` % 5000 + 30000) 53 | 54 | # Correct option to enable the GDB stub and specify its port number to qemu. 55 | # First is for qemu versions <= 0.10, second is for later qemu versions. 56 | # QEMUPORT := -s -p $(GDBPORT) 57 | QEMUPORT := -gdb tcp::$(GDBPORT) 58 | 59 | CC := $(GCCPREFIX)gcc -pipe 60 | AS := $(GCCPREFIX)as 61 | AR := $(GCCPREFIX)ar 62 | LD := $(GCCPREFIX)ld 63 | OBJCOPY := $(GCCPREFIX)objcopy 64 | OBJDUMP := $(GCCPREFIX)objdump 65 | NM := $(GCCPREFIX)nm 66 | GDB := $(GCCPREFIX)gdb 67 | 68 | # Native commands 69 | NCC := gcc $(CC_VER) -pipe 70 | TAR := gtar 71 | PERL := perl 72 | 73 | # If we're not using the special "PIOS edition" of GCC, 74 | # reconfigure the host OS's compiler for our purposes. 75 | ifneq ($(GCCPREFIX),pios-) 76 | CFLAGS += -nostdinc -m32 77 | LDFLAGS += -nostdlib -m elf_i386 78 | USER_LDFLAGS += -e start -Ttext=0x40000100 79 | endif 80 | 81 | # Where does GCC have its libgcc.a and libgcc's include directory? 82 | GCCDIR := $(dir $(shell $(CC) $(CFLAGS) -print-libgcc-file-name)) 83 | 84 | # x86-64 systems may put libgcc's include directory with the 64-bit compiler 85 | GCCALTDIR := $(dir $(shell $(CC) -print-libgcc-file-name)) 86 | 87 | # Compiler flags 88 | # -fno-builtin is required to avoid refs to undefined functions in the kernel. 89 | # Only optimize to -O1 to discourage inlining, which complicates backtraces. 90 | CFLAGS += $(DEFS) $(LABDEFS) -fno-builtin -I$(TOP) -I$(TOP)/inc \ 91 | -I$(GCCDIR)/include -I$(GCCALTDIR)/include \ 92 | -MD -Wall -Wno-unused -Werror -gstabs 93 | 94 | # Add -fno-stack-protector if the option exists. 95 | CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 \ 96 | && echo -fno-stack-protector) 97 | 98 | LDFLAGS += -L$(OBJDIR)/lib -L$(GCCDIR) 99 | 100 | # Compiler flags that differ for kernel versus user-level code. 101 | KERN_CFLAGS += $(CFLAGS) -DPIOS_KERNEL 102 | KERN_LDFLAGS += $(LDFLAGS) -nostdlib -Ttext=0x00100000 -L$(GCCDIR) 103 | KERN_LDLIBS += $(LDLIBS) -lgcc 104 | 105 | USER_CFLAGS += $(CFLAGS) -DPIOS_USER 106 | USER_LDFLAGS += $(LDFLAGS) 107 | USER_LDINIT += $(OBJDIR)/lib/crt0.o 108 | USER_LDDEPS += $(USER_LDINIT) $(OBJDIR)/lib/libc.a 109 | USER_LDLIBS += $(LDLIBS) -lc -lgcc 110 | 111 | # Lists that the */Makefrag makefile fragments will add to 112 | OBJDIRS := 113 | 114 | # Make sure that 'all' is the first target 115 | all: 116 | 117 | # Eliminate default suffix rules 118 | .SUFFIXES: 119 | 120 | # Delete target files if there is an error (or make is interrupted) 121 | .DELETE_ON_ERROR: 122 | 123 | # make it so that no intermediate .o files are ever deleted 124 | .PRECIOUS: %.o $(OBJDIR)/boot/%.o $(OBJDIR)/kern/%.o \ 125 | $(OBJDIR)/lib/%.o $(OBJDIR)/fs/%.o $(OBJDIR)/net/%.o \ 126 | $(OBJDIR)/user/%.o 127 | 128 | 129 | 130 | 131 | # Include Makefrags for subdirectories 132 | include boot/Makefrag 133 | include kern/Makefrag 134 | 135 | 136 | 137 | NCPUS = 2 138 | IMAGES = $(OBJDIR)/kern/kernel.img 139 | QEMUOPTS = -smp $(NCPUS) -hda $(OBJDIR)/kern/kernel.img -serial mon:stdio \ 140 | -k en-us -m 1100M 141 | #QEMUNET = -net socket,mcast=230.0.0.1:$(NETPORT) -net nic,model=i82559er 142 | QEMUNET1 = -net nic,model=i82559er,macaddr=52:54:00:12:34:01 \ 143 | -net socket,connect=:$(NETPORT) -net dump,file=node1.dump 144 | QEMUNET2 = -net nic,model=i82559er,macaddr=52:54:00:12:34:02 \ 145 | -net socket,listen=:$(NETPORT) -net dump,file=node2.dump 146 | 147 | .gdbinit: .gdbinit.tmpl 148 | sed "s/localhost:1234/localhost:$(GDBPORT)/" < $^ > $@ 149 | 150 | ifneq ($(LAB),5) 151 | # Launch QEMU and run PIOS. Labs 1-4 need only one instance of QEMU. 152 | qemu: $(IMAGES) 153 | $(QEMU) $(QEMUOPTS) 154 | else 155 | # Lab 5 is a distributed system, so we need (at least) two instances. 156 | # Only one instance gets input from the terminal, to avoid confusion. 157 | qemu: $(IMAGES) 158 | @rm -f node?.dump 159 | $(QEMU) $(QEMUOPTS) $(QEMUNET2) &2 173 | $(QEMU) $(QEMUOPTS) -S $(QEMUPORT) 174 | else 175 | # Launch QEMU for debugging the 2-node distributed system in Lab 5. 176 | qemu-gdb: $(IMAGES) .gdbinit 177 | @echo "*** Now run 'gdb'." 1>&2 178 | @rm -f node?.dump 179 | $(QEMU) $(QEMUOPTS) $(QEMUNET2) &2 187 | $(QEMU) -nographic $(QEMUOPTS) -S $(QEMUPORT) 188 | 189 | # For deleting the build 190 | clean: 191 | rm -rf $(OBJDIR) grade-out 192 | 193 | realclean: clean 194 | rm -rf lab$(LAB).tar.gz 195 | 196 | distclean: realclean 197 | rm -rf conf/gcc.mk 198 | 199 | grade: misc/grade-lab$(LAB).sh 200 | $(V)$(MAKE) clean >/dev/null 2>/dev/null 201 | $(MAKE) all 202 | sh misc/grade-lab$(LAB).sh 203 | 204 | tarball: realclean 205 | tar cf - `find . -type f | grep -v '^\.*$$' | grep -v '/CVS/' | grep -v '/\.svn/' | grep -v '/\.git/' | grep -v 'lab[0-9].*\.tar\.gz'` | gzip > lab$(LAB)-handin.tar.gz 206 | 207 | 208 | # This magic automatically generates makefile dependencies 209 | # for header files included from C source files we compile, 210 | # and keeps those dependencies up-to-date every time we recompile. 211 | # See 'misc/mergedep.pl' for more information. 212 | $(OBJDIR)/.deps: $(foreach dir, $(OBJDIRS), $(wildcard $(OBJDIR)/$(dir)/*.d)) 213 | @mkdir -p $(@D) 214 | @$(PERL) misc/mergedep.pl $@ $^ 215 | 216 | -include $(OBJDIR)/.deps 217 | 218 | always: 219 | @: 220 | 221 | .PHONY: all always \ 222 | handin tarball clean realclean clean-labsetup distclean grade labsetup 223 | 224 | -------------------------------------------------------------------------------- /boot/Makefrag: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile fragment for the boot loader. 3 | # This is NOT a complete makefile; 4 | # you must run GNU make in the top-level directory 5 | # where the GNUmakefile is located. 6 | # 7 | # Copyright (C) 2003 Massachusetts Institute of Technology 8 | # See section "MIT License" in the file LICENSES for licensing terms. 9 | # Primary authors: Bryan Ford, Eddie Kohler 10 | # 11 | 12 | OBJDIRS += boot 13 | 14 | BOOT_OBJS := $(OBJDIR)/boot/boot.o $(OBJDIR)/boot/main.o 15 | 16 | $(OBJDIR)/boot/%.o: boot/%.c 17 | @echo + cc -Os $< 18 | @mkdir -p $(@D) 19 | $(V)$(CC) $(KERN_CFLAGS) -Os -c -o $@ $< 20 | 21 | $(OBJDIR)/boot/%.o: boot/%.S 22 | @echo + as $< 23 | @mkdir -p $(@D) 24 | $(V)$(CC) $(KERN_CFLAGS) -c -o $@ $< 25 | 26 | $(OBJDIR)/boot/main.o: boot/main.c 27 | @echo + cc -Os $< 28 | $(V)$(CC) $(KERN_CFLAGS) -Os -c -o $(OBJDIR)/boot/main.o boot/main.c 29 | 30 | $(OBJDIR)/boot/bootblock: $(BOOT_OBJS) 31 | @echo + ld boot/bootblock 32 | $(V)$(LD) $(LDFLAGS) -N -e start -Ttext 0x7C00 -o $@.elf $^ 33 | $(V)$(OBJDUMP) -S $@.elf >$@.asm 34 | $(V)$(OBJCOPY) -S -O binary $@.elf $@ 35 | $(V)perl boot/sign.pl $(OBJDIR)/boot/bootblock 36 | 37 | $(OBJDIR)/boot/bootother: $(OBJDIR)/boot/bootother.o 38 | @echo + ld boot/bootother 39 | $(V)$(LD) $(LDFLAGS) -N -e start -Ttext 0x1000 -o $@.elf $^ 40 | $(V)$(OBJDUMP) -S $@.elf >$@.asm 41 | $(V)$(OBJCOPY) -S -O binary $@.elf $@ 42 | 43 | -------------------------------------------------------------------------------- /boot/boot.S: -------------------------------------------------------------------------------- 1 | /* 2 | * Boot loader entrypoint and protected mode setup. 3 | * 4 | * Copyright (C) 1997 Massachusetts Institute of Technology 5 | * See section "MIT License" in the file LICENSES for licensing terms. 6 | * 7 | * Derived from the MIT Exokernel and JOS. 8 | */ 9 | #include 10 | 11 | # Start the CPU: switch to 32-bit protected mode, jump into C. 12 | # The BIOS loads this code from the first sector of the hard disk into 13 | # memory at physical address 0x7c00 and starts executing in real mode 14 | # with %cs=0 %ip=7c00. 15 | 16 | .set PROT_MODE_CSEG, 0x8 # kernel code segment selector 17 | .set PROT_MODE_DSEG, 0x10 # kernel data segment selector 18 | .set CR0_PE_ON, 0x1 # protected mode enable flag 19 | 20 | .globl start 21 | start: 22 | .code16 # Assemble for 16-bit mode 23 | cli # Disable interrupts 24 | cld # String operations increment 25 | 26 | # Set up the important data segment registers (DS, ES, SS). 27 | xorw %ax,%ax # Segment number zero 28 | movw %ax,%ds # -> Data Segment 29 | movw %ax,%es # -> Extra Segment 30 | movw %ax,%ss # -> Stack Segment 31 | 32 | # Enable A20: 33 | # For backwards compatibility with the earliest PCs, physical 34 | # address line 20 is tied low, so that addresses higher than 35 | # 1MB wrap around to zero by default. This code undoes this. 36 | seta20.1: 37 | inb $0x64,%al # Wait for not busy 38 | testb $0x2,%al 39 | jnz seta20.1 40 | 41 | movb $0xd1,%al # 0xd1 -> port 0x64 42 | outb %al,$0x64 43 | 44 | seta20.2: 45 | inb $0x64,%al # Wait for not busy 46 | testb $0x2,%al 47 | jnz seta20.2 48 | 49 | movb $0xdf,%al # 0xdf -> port 0x60 50 | outb %al,$0x60 51 | 52 | # Switch from real to protected mode, using a bootstrap GDT 53 | # and segment translation that makes virtual addresses 54 | # identical to their physical addresses, so that the 55 | # effective memory map does not change during the switch. 56 | lgdt gdtdesc 57 | movl %cr0, %eax 58 | orl $CR0_PE_ON, %eax 59 | movl %eax, %cr0 60 | 61 | # Jump to next instruction, but in 32-bit code segment. 62 | # Switches processor into 32-bit mode. 63 | ljmp $PROT_MODE_CSEG, $protcseg 64 | 65 | .code32 # Assemble for 32-bit mode 66 | protcseg: 67 | # Set up the protected-mode data segment registers 68 | movw $PROT_MODE_DSEG, %ax # Our data segment selector 69 | movw %ax, %ds # -> DS: Data Segment 70 | movw %ax, %es # -> ES: Extra Segment 71 | movw %ax, %fs # -> FS 72 | movw %ax, %gs # -> GS 73 | movw %ax, %ss # -> SS: Stack Segment 74 | 75 | # Set up the stack pointer and call into C. 76 | movl $start, %esp 77 | call bootmain 78 | 79 | # If bootmain returns (it shouldn't), loop. 80 | spin: 81 | jmp spin 82 | 83 | # Bootstrap GDT 84 | .p2align 2 # force 4 byte alignment 85 | gdt: 86 | SEG_NULL # null seg 87 | SEG(STA_X|STA_R, 0x0, 0xffffffff) # code seg 88 | SEG(STA_W, 0x0, 0xffffffff) # data seg 89 | 90 | gdtdesc: 91 | .word 0x17 # sizeof(gdt) - 1 92 | .long gdt # address gdt 93 | 94 | -------------------------------------------------------------------------------- /boot/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Boot loader code to read the kernel image from disk and start it. 3 | * 4 | * Copyright (C) 1997 Massachusetts Institute of Technology 5 | * See section "MIT License" in the file LICENSES for licensing terms. 6 | * 7 | * Derived from the MIT Exokernel and JOS. 8 | */ 9 | #include 10 | #include 11 | 12 | /********************************************************************** 13 | * This a dirt simple boot loader, whose sole job is to boot 14 | * an ELF kernel image from the first IDE hard disk. 15 | * 16 | * DISK LAYOUT 17 | * * This program(boot.S and main.c) is the bootloader. It should 18 | * be stored in the first sector of the disk. 19 | * 20 | * * The 2nd sector onward holds the kernel image. 21 | * 22 | * * The kernel image must be in ELF format. 23 | * 24 | * BOOT UP STEPS 25 | * * when the CPU boots it loads the BIOS into memory and executes it 26 | * 27 | * * the BIOS intializes devices, sets of the interrupt routines, and 28 | * reads the first sector of the boot device(e.g., hard-drive) 29 | * into memory and jumps to it. 30 | * 31 | * * Assuming this boot loader is stored in the first sector of the 32 | * hard-drive, this code takes over... 33 | * 34 | * * control starts in boot.S -- which sets up protected mode, 35 | * and a stack so C code then run, then calls bootmain() 36 | * 37 | * * bootmain() in this file takes over, reads in the kernel and jumps to it. 38 | **********************************************************************/ 39 | 40 | #define SECTSIZE 512 41 | #define ELFHDR ((elfhdr *) 0x10000) // scratch space 42 | 43 | void readsect(void*, uint32_t); 44 | void readseg(uint32_t, uint32_t, uint32_t); 45 | 46 | void 47 | bootmain(void) 48 | { 49 | proghdr *ph, *eph; 50 | 51 | // read 1st page off disk 52 | readseg((uint32_t) ELFHDR, SECTSIZE*8, 0); 53 | 54 | // is this a valid ELF? 55 | if (ELFHDR->e_magic != ELF_MAGIC) 56 | goto bad; 57 | 58 | // load each program segment (ignores ph flags) 59 | ph = (proghdr *) ((uint8_t *) ELFHDR + ELFHDR->e_phoff); 60 | eph = ph + ELFHDR->e_phnum; 61 | for (; ph < eph; ph++) 62 | readseg(ph->p_va, ph->p_memsz, ph->p_offset); 63 | 64 | // call the entry point from the ELF header 65 | // note: does not return! 66 | ((void (*)(void)) (ELFHDR->e_entry & 0xFFFFFF))(); 67 | 68 | bad: 69 | outw(0x8A00, 0x8A00); 70 | outw(0x8A00, 0x8E00); 71 | while (1) 72 | /* do nothing */; 73 | } 74 | 75 | // Read 'count' bytes at 'offset' from kernel into virtual address 'va'. 76 | // Might copy more than asked 77 | void 78 | readseg(uint32_t va, uint32_t count, uint32_t offset) 79 | { 80 | uint32_t end_va; 81 | 82 | va &= 0xFFFFFF; 83 | end_va = va + count; 84 | 85 | // round down to sector boundary 86 | va &= ~(SECTSIZE - 1); 87 | 88 | // translate from bytes to sectors, and kernel starts at sector 1 89 | offset = (offset / SECTSIZE) + 1; 90 | 91 | // If this is too slow, we could read lots of sectors at a time. 92 | // We'd write more to memory than asked, but it doesn't matter -- 93 | // we load in increasing order. 94 | while (va < end_va) { 95 | readsect((uint8_t*) va, offset); 96 | va += SECTSIZE; 97 | offset++; 98 | } 99 | } 100 | 101 | void 102 | waitdisk(void) 103 | { 104 | // wait for disk reaady 105 | while ((inb(0x1F7) & 0xC0) != 0x40) 106 | /* do nothing */; 107 | } 108 | 109 | void 110 | readsect(void *dst, uint32_t offset) 111 | { 112 | // wait for disk to be ready 113 | waitdisk(); 114 | 115 | outb(0x1F2, 1); // count = 1 116 | outb(0x1F3, offset); 117 | outb(0x1F4, offset >> 8); 118 | outb(0x1F5, offset >> 16); 119 | outb(0x1F6, (offset >> 24) | 0xE0); 120 | outb(0x1F7, 0x20); // cmd 0x20 - read sectors 121 | 122 | // wait for disk to be ready 123 | waitdisk(); 124 | 125 | // read a sector 126 | insl(0x1F0, dst, SECTSIZE/4); 127 | } 128 | 129 | -------------------------------------------------------------------------------- /boot/sign.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # 3 | # This script pads a boot block to the required 512 byte sector size 4 | # and attaches the magic "signature" (0x55aa) at the end. 5 | # 6 | # Copyright (C) 1997 Massachusetts Institute of Technology 7 | # See section "MIT License" in the file LICENSES for licensing terms. 8 | # 9 | # Derived from the MIT Exokernel and JOS. 10 | # 11 | 12 | open(BB, $ARGV[0]) || die "open $ARGV[0]: $!"; 13 | 14 | binmode BB; 15 | my $buf; 16 | read(BB, $buf, 1000); 17 | $n = length($buf); 18 | 19 | if($n > 510){ 20 | print STDERR "boot block too large: $n bytes (max 510)\n"; 21 | exit 1; 22 | } 23 | 24 | print STDERR "boot block is $n bytes (max 510)\n"; 25 | 26 | $buf .= "\0" x (510-$n); 27 | $buf .= "\x55\xAA"; 28 | 29 | open(BB, ">$ARGV[0]") || die "open >$ARGV[0]: $!"; 30 | binmode BB; 31 | print BB $buf; 32 | close BB; 33 | -------------------------------------------------------------------------------- /conf/env.mk: -------------------------------------------------------------------------------- 1 | # env.mk - configuration variables for the PIOS labs 2 | 3 | # '$(V)' controls whether the lab makefiles print verbose commands (the 4 | # actual shell commands run by Make), as well as the "overview" commands 5 | # (such as '+ cc lib/readline.c'). 6 | # 7 | # For overview commands only, the line should read 'V = @'. 8 | # For overview and verbose commands, the line should read 'V ='. 9 | V = @ 10 | 11 | # If your system-standard GNU toolchain is ELF-compatible, then comment 12 | # out the following line to use those tools (as opposed to the i386-elf 13 | # tools that the 6.828 make system looks for by default). 14 | # 15 | # GCCPREFIX='' 16 | 17 | # If the makefile cannot find your QEMU binary, uncomment the 18 | # following line and set it to the full path to QEMU. 19 | # 20 | # QEMU= 21 | -------------------------------------------------------------------------------- /conf/lab.mk: -------------------------------------------------------------------------------- 1 | LAB=1 2 | PACKAGEDATE=Sat Jan 15 18:24:38 EST 2011 3 | -------------------------------------------------------------------------------- /dev/kbd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * PC keyboard input device driver. 3 | * 4 | * Copyright (c) 2010 Yale University. 5 | * Copyright (c) 1993, 1994, 1995 Charles Hannum. 6 | * Copyright (c) 1990 The Regents of the University of California. 7 | * See section "BSD License" in the file LICENSES for licensing terms. 8 | * 9 | * This code is derived from the NetBSD pcons driver, and in turn derived 10 | * from software contributed to Berkeley by William Jolitz and Don Ahn. 11 | * Adapted for PIOS by Bryan Ford at Yale University. 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | 21 | #include 22 | 23 | 24 | #define NO 0 25 | 26 | #define SHIFT (1<<0) 27 | #define CTL (1<<1) 28 | #define ALT (1<<2) 29 | #define CAPSLOCK (1<<3) 30 | #define NUMLOCK (1<<4) 31 | #define SCROLLLOCK (1<<5) 32 | #define E0ESC (1<<6) 33 | 34 | 35 | static uint8_t shiftcode[256] = 36 | { 37 | [0x1D] CTL, 38 | [0x2A] SHIFT, 39 | [0x36] SHIFT, 40 | [0x38] ALT, 41 | [0x9D] CTL, 42 | [0xB8] ALT 43 | }; 44 | 45 | static uint8_t togglecode[256] = 46 | { 47 | [0x3A] CAPSLOCK, 48 | [0x45] NUMLOCK, 49 | [0x46] SCROLLLOCK 50 | }; 51 | 52 | static uint8_t normalmap[256] = 53 | { 54 | NO, 0x1B, '1', '2', '3', '4', '5', '6', // 0x00 55 | '7', '8', '9', '0', '-', '=', '\b', '\t', 56 | 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', // 0x10 57 | 'o', 'p', '[', ']', '\n', NO, 'a', 's', 58 | 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', // 0x20 59 | '\'', '`', NO, '\\', 'z', 'x', 'c', 'v', 60 | 'b', 'n', 'm', ',', '.', '/', NO, '*', // 0x30 61 | NO, ' ', NO, NO, NO, NO, NO, NO, 62 | NO, NO, NO, NO, NO, NO, NO, '7', // 0x40 63 | '8', '9', '-', '4', '5', '6', '+', '1', 64 | '2', '3', '0', '.', NO, NO, NO, NO, // 0x50 65 | [0xC7] KEY_HOME, [0x9C] '\n' /*KP_Enter*/, 66 | [0xB5] '/' /*KP_Div*/, [0xC8] KEY_UP, 67 | [0xC9] KEY_PGUP, [0xCB] KEY_LF, 68 | [0xCD] KEY_RT, [0xCF] KEY_END, 69 | [0xD0] KEY_DN, [0xD1] KEY_PGDN, 70 | [0xD2] KEY_INS, [0xD3] KEY_DEL 71 | }; 72 | 73 | static uint8_t 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 | [0xC7] KEY_HOME, [0x9C] '\n' /*KP_Enter*/, 87 | [0xB5] '/' /*KP_Div*/, [0xC8] KEY_UP, 88 | [0xC9] KEY_PGUP, [0xCB] KEY_LF, 89 | [0xCD] KEY_RT, [0xCF] KEY_END, 90 | [0xD0] KEY_DN, [0xD1] KEY_PGDN, 91 | [0xD2] KEY_INS, [0xD3] KEY_DEL 92 | }; 93 | 94 | #define C(x) (x - '@') 95 | 96 | static uint8_t ctlmap[256] = 97 | { 98 | NO, NO, NO, NO, NO, NO, NO, NO, 99 | NO, NO, NO, NO, NO, NO, NO, NO, 100 | C('Q'), C('W'), C('E'), C('R'), C('T'), C('Y'), C('U'), C('I'), 101 | C('O'), C('P'), NO, NO, '\r', NO, C('A'), C('S'), 102 | C('D'), C('F'), C('G'), C('H'), C('J'), C('K'), C('L'), NO, 103 | NO, NO, NO, C('\\'), C('Z'), C('X'), C('C'), C('V'), 104 | C('B'), C('N'), C('M'), NO, NO, C('/'), NO, NO, 105 | [0x97] KEY_HOME, 106 | [0xB5] C('/'), [0xC8] KEY_UP, 107 | [0xC9] KEY_PGUP, [0xCB] KEY_LF, 108 | [0xCD] KEY_RT, [0xCF] KEY_END, 109 | [0xD0] KEY_DN, [0xD1] KEY_PGDN, 110 | [0xD2] KEY_INS, [0xD3] KEY_DEL 111 | }; 112 | 113 | static uint8_t *charcode[4] = { 114 | normalmap, 115 | shiftmap, 116 | ctlmap, 117 | ctlmap 118 | }; 119 | 120 | /* 121 | * Get data from the keyboard. If we finish a character, return it. Else 0. 122 | * Return -1 if no data. 123 | */ 124 | static int 125 | kbd_proc_data(void) 126 | { 127 | int c; 128 | uint8_t data; 129 | static uint32_t shift; 130 | 131 | if ((inb(KBSTATP) & KBS_DIB) == 0) 132 | return -1; 133 | 134 | data = inb(KBDATAP); 135 | 136 | if (data == 0xE0) { 137 | // E0 escape character 138 | shift |= E0ESC; 139 | return 0; 140 | } else if (data & 0x80) { 141 | // Key released 142 | data = (shift & E0ESC ? data : data & 0x7F); 143 | shift &= ~(shiftcode[data] | E0ESC); 144 | return 0; 145 | } else if (shift & E0ESC) { 146 | // Last character was an E0 escape; or with 0x80 147 | data |= 0x80; 148 | shift &= ~E0ESC; 149 | } 150 | 151 | shift |= shiftcode[data]; 152 | shift ^= togglecode[data]; 153 | 154 | c = charcode[shift & (CTL | SHIFT)][data]; 155 | if (shift & CAPSLOCK) { 156 | if ('a' <= c && c <= 'z') 157 | c += 'A' - 'a'; 158 | else if ('A' <= c && c <= 'Z') 159 | c += 'a' - 'A'; 160 | } 161 | 162 | // Process special keys 163 | // Ctrl-Alt-Del: reboot 164 | if (!(~shift & (CTL | ALT)) && c == KEY_DEL) { 165 | cprintf("Rebooting!\n"); 166 | outb(0x92, 0x3); // courtesy of Chris Frost 167 | } 168 | 169 | return c; 170 | } 171 | 172 | void 173 | kbd_intr(void) 174 | { 175 | cons_intr(kbd_proc_data); 176 | } 177 | 178 | void 179 | kbd_init(void) 180 | { 181 | } 182 | 183 | 184 | -------------------------------------------------------------------------------- /dev/kbd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * PC keyboard input device driver. 3 | * 4 | * Copyright (c) 2010 Yale University. 5 | * Copyright (c) 1993, 1994, 1995 Charles Hannum. 6 | * Copyright (c) 1990 The Regents of the University of California. 7 | * See section "BSD License" in the file LICENSES for licensing terms. 8 | * 9 | * This code is derived from the NetBSD pcons driver, and in turn derived 10 | * from software contributed to Berkeley by William Jolitz and Don Ahn. 11 | * Adapted for PIOS by Bryan Ford at Yale University. 12 | */ 13 | 14 | #ifndef PIOS_KERN_KBD_H_ 15 | #define PIOS_KERN_KBD_H_ 16 | #ifndef PIOS_KERNEL 17 | # error "This is a kernel header; user programs should not #include it" 18 | #endif 19 | 20 | 21 | // Special keycodes 22 | #define KEY_HOME 0xE0 23 | #define KEY_END 0xE1 24 | #define KEY_UP 0xE2 25 | #define KEY_DN 0xE3 26 | #define KEY_LF 0xE4 27 | #define KEY_RT 0xE5 28 | #define KEY_PGUP 0xE6 29 | #define KEY_PGDN 0xE7 30 | #define KEY_INS 0xE8 31 | #define KEY_DEL 0xE9 32 | 33 | 34 | /* This is i8042reg.h + kbdreg.h from NetBSD. */ 35 | 36 | #define KBSTATP 0x64 /* kbd controller status port(I) */ 37 | #define KBS_DIB 0x01 /* kbd data in buffer */ 38 | #define KBS_IBF 0x02 /* kbd input buffer low */ 39 | #define KBS_WARM 0x04 /* kbd input buffer low */ 40 | #define KBS_OCMD 0x08 /* kbd output buffer has command */ 41 | #define KBS_NOSEC 0x10 /* kbd security lock not engaged */ 42 | #define KBS_TERR 0x20 /* kbd transmission error */ 43 | #define KBS_RERR 0x40 /* kbd receive error */ 44 | #define KBS_PERR 0x80 /* kbd parity error */ 45 | 46 | #define KBCMDP 0x64 /* kbd controller port(O) */ 47 | #define KBC_RAMREAD 0x20 /* read from RAM */ 48 | #define KBC_RAMWRITE 0x60 /* write to RAM */ 49 | #define KBC_AUXDISABLE 0xa7 /* disable auxiliary port */ 50 | #define KBC_AUXENABLE 0xa8 /* enable auxiliary port */ 51 | #define KBC_AUXTEST 0xa9 /* test auxiliary port */ 52 | #define KBC_KBDECHO 0xd2 /* echo to keyboard port */ 53 | #define KBC_AUXECHO 0xd3 /* echo to auxiliary port */ 54 | #define KBC_AUXWRITE 0xd4 /* write to auxiliary port */ 55 | #define KBC_SELFTEST 0xaa /* start self-test */ 56 | #define KBC_KBDTEST 0xab /* test keyboard port */ 57 | #define KBC_KBDDISABLE 0xad /* disable keyboard port */ 58 | #define KBC_KBDENABLE 0xae /* enable keyboard port */ 59 | #define KBC_PULSE0 0xfe /* pulse output bit 0 */ 60 | #define KBC_PULSE1 0xfd /* pulse output bit 1 */ 61 | #define KBC_PULSE2 0xfb /* pulse output bit 2 */ 62 | #define KBC_PULSE3 0xf7 /* pulse output bit 3 */ 63 | 64 | #define KBDATAP 0x60 /* kbd data port(I) */ 65 | #define KBOUTP 0x60 /* kbd data port(O) */ 66 | 67 | #define K_RDCMDBYTE 0x20 68 | #define K_LDCMDBYTE 0x60 69 | 70 | #define KC8_TRANS 0x40 /* convert to old scan codes */ 71 | #define KC8_MDISABLE 0x20 /* disable mouse */ 72 | #define KC8_KDISABLE 0x10 /* disable keyboard */ 73 | #define KC8_IGNSEC 0x08 /* ignore security lock */ 74 | #define KC8_CPU 0x04 /* exit from protected mode reset */ 75 | #define KC8_MENABLE 0x02 /* enable mouse interrupt */ 76 | #define KC8_KENABLE 0x01 /* enable keyboard interrupt */ 77 | #define CMDBYTE (KC8_TRANS|KC8_CPU|KC8_MENABLE|KC8_KENABLE) 78 | 79 | /* keyboard commands */ 80 | #define KBC_RESET 0xFF /* reset the keyboard */ 81 | #define KBC_RESEND 0xFE /* request the keyboard resend the last byte */ 82 | #define KBC_SETDEFAULT 0xF6 /* resets keyboard to its power-on defaults */ 83 | #define KBC_DISABLE 0xF5 /* as per KBC_SETDEFAULT, but also disable key scanning */ 84 | #define KBC_ENABLE 0xF4 /* enable key scanning */ 85 | #define KBC_TYPEMATIC 0xF3 /* set typematic rate and delay */ 86 | #define KBC_SETTABLE 0xF0 /* set scancode translation table */ 87 | #define KBC_MODEIND 0xED /* set mode indicators(i.e. LEDs) */ 88 | #define KBC_ECHO 0xEE /* request an echo from the keyboard */ 89 | 90 | /* keyboard responses */ 91 | #define KBR_EXTENDED 0xE0 /* extended key sequence */ 92 | #define KBR_RESEND 0xFE /* needs resend of command */ 93 | #define KBR_ACK 0xFA /* received a valid command */ 94 | #define KBR_OVERRUN 0x00 /* flooded */ 95 | #define KBR_FAILURE 0xFD /* diagnosic failure */ 96 | #define KBR_BREAK 0xF0 /* break code prefix - sent on key release */ 97 | #define KBR_RSTDONE 0xAA /* reset complete */ 98 | #define KBR_ECHO 0xEE /* echo response */ 99 | 100 | 101 | void kbd_init(void); 102 | void kbd_intenable(void); 103 | void kbd_intr(void); 104 | 105 | #endif /* PIOS_KERN_KBD_H_ */ 106 | -------------------------------------------------------------------------------- /dev/nvram.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Device driver code for accessing the PC's nonvolatile RAM (NVRAM), 3 | * which is part of the PC's real-time clock/calendar. 4 | * 5 | * Copyright (C) 1998 Exotec, Inc. 6 | * See section "MIT License" in the file LICENSES for licensing terms. 7 | * 8 | * Derived from the MIT Exokernel and JOS. 9 | * Adapted for PIOS by Bryan Ford at Yale University. 10 | */ 11 | 12 | #include 13 | 14 | #include 15 | 16 | 17 | unsigned 18 | nvram_read(unsigned reg) 19 | { 20 | outb(IO_RTC, reg); 21 | return inb(IO_RTC+1); 22 | } 23 | 24 | unsigned 25 | nvram_read16(unsigned r) 26 | { 27 | return nvram_read(r) | (nvram_read(r + 1) << 8); 28 | } 29 | 30 | void 31 | nvram_write(unsigned reg, unsigned datum) 32 | { 33 | outb(IO_RTC, reg); 34 | outb(IO_RTC+1, datum); 35 | } 36 | 37 | 38 | -------------------------------------------------------------------------------- /dev/nvram.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Definitions for the PC's nonvolatile RAM (NVRAM), 3 | * part of the PC's battery-backed real-time clock. 4 | * The kernel reads the NVRAM to detect how much memory we have. 5 | * 6 | * Copyright (C) 1998 Exotec, Inc. 7 | * See section "MIT License" in the file LICENSES for licensing terms. 8 | * 9 | * Derived from the MIT Exokernel and JOS. 10 | * Adapted for PIOS by Bryan Ford at Yale University. 11 | */ 12 | 13 | #ifndef PIOS_DEV_NVRAM_H 14 | #define PIOS_DEV_NVRAM_H 15 | #ifndef PIOS_KERNEL 16 | # error "This is a kernel header; user programs should not #include it" 17 | #endif 18 | 19 | #define IO_RTC 0x070 /* RTC port */ 20 | 21 | #define MC_NVRAM_START 0xe /* start of NVRAM: offset 14 */ 22 | #define MC_NVRAM_SIZE 50 /* 50 bytes of NVRAM */ 23 | 24 | /* NVRAM bytes 7 & 8: base memory size */ 25 | #define NVRAM_BASELO (MC_NVRAM_START + 7) /* low byte; RTC off. 0x15 */ 26 | #define NVRAM_BASEHI (MC_NVRAM_START + 8) /* high byte; RTC off. 0x16 */ 27 | 28 | /* NVRAM bytes 9 & 10: extended memory size */ 29 | #define NVRAM_EXTLO (MC_NVRAM_START + 9) /* low byte; RTC off. 0x17 */ 30 | #define NVRAM_EXTHI (MC_NVRAM_START + 10) /* high byte; RTC off. 0x18 */ 31 | 32 | /* NVRAM bytes 34 and 35: extended memory POSTed size */ 33 | #define NVRAM_PEXTLO (MC_NVRAM_START + 34) /* low byte; RTC off. 0x30 */ 34 | #define NVRAM_PEXTHI (MC_NVRAM_START + 35) /* high byte; RTC off. 0x31 */ 35 | 36 | /* NVRAM byte 36: current century. (please increment in Dec99!) */ 37 | #define NVRAM_CENTURY (MC_NVRAM_START + 36) /* RTC offset 0x32 */ 38 | 39 | 40 | // Read NVRAM registers 41 | unsigned nvram_read(unsigned reg); // read an 8-bit byte from NVRAM 42 | unsigned nvram_read16(unsigned reg); // read a 16-bit word from NVRAM 43 | 44 | // Write to NVRAM 45 | void nvram_write(unsigned reg, unsigned datum); 46 | 47 | 48 | #endif // !PIOS_DEV_NVRAM_H 49 | -------------------------------------------------------------------------------- /dev/serial.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Serial (COM) port I/O device driver. 3 | * 4 | * Copyright (c) 2010 Yale University. 5 | * Copyright (c) 1993, 1994, 1995 Charles Hannum. 6 | * Copyright (c) 1990 The Regents of the University of California. 7 | * See section "BSD License" in the file LICENSES for licensing terms. 8 | * 9 | * This code is derived from the NetBSD pcons driver, and in turn derived 10 | * from software contributed to Berkeley by William Jolitz and Don Ahn. 11 | * Adapted for PIOS by Bryan Ford at Yale University. 12 | */ 13 | 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | #include 20 | 21 | 22 | bool serial_exists; 23 | 24 | 25 | // Stupid I/O delay routine necessitated by historical PC design flaws 26 | static void 27 | delay(void) 28 | { 29 | inb(0x84); 30 | inb(0x84); 31 | inb(0x84); 32 | inb(0x84); 33 | } 34 | 35 | static int 36 | serial_proc_data(void) 37 | { 38 | if (!(inb(COM1+COM_LSR) & COM_LSR_DATA)) 39 | return -1; 40 | return inb(COM1+COM_RX); 41 | } 42 | 43 | void 44 | serial_intr(void) 45 | { 46 | if (serial_exists) 47 | cons_intr(serial_proc_data); 48 | } 49 | 50 | void 51 | serial_putc(int c) 52 | { 53 | if (!serial_exists) 54 | return; 55 | 56 | int i; 57 | for (i = 0; 58 | !(inb(COM1 + COM_LSR) & COM_LSR_TXRDY) && i < 12800; 59 | i++) 60 | delay(); 61 | 62 | outb(COM1 + COM_TX, c); 63 | } 64 | 65 | void 66 | serial_init(void) 67 | { 68 | // Turn off the FIFO 69 | outb(COM1+COM_FCR, 0); 70 | 71 | // Set speed; requires DLAB latch 72 | outb(COM1+COM_LCR, COM_LCR_DLAB); 73 | outb(COM1+COM_DLL, (uint8_t) (115200 / 9600)); 74 | outb(COM1+COM_DLM, 0); 75 | 76 | // 8 data bits, 1 stop bit, parity off; turn off DLAB latch 77 | outb(COM1+COM_LCR, COM_LCR_WLEN8 & ~COM_LCR_DLAB); 78 | 79 | // No modem controls 80 | outb(COM1+COM_MCR, 0); 81 | // Enable rcv interrupts 82 | outb(COM1+COM_IER, COM_IER_RDI); 83 | 84 | // Clear any preexisting overrun indications and interrupts 85 | // Serial port doesn't exist if COM_LSR returns 0xFF 86 | serial_exists = (inb(COM1+COM_LSR) != 0xFF); 87 | (void) inb(COM1+COM_IIR); 88 | (void) inb(COM1+COM_RX); 89 | } 90 | 91 | 92 | -------------------------------------------------------------------------------- /dev/serial.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Serial (COM) port I/O device driver. 3 | * 4 | * Copyright (c) 2010 Yale University. 5 | * Copyright (c) 1993, 1994, 1995 Charles Hannum. 6 | * Copyright (c) 1990 The Regents of the University of California. 7 | * See section "BSD License" in the file LICENSES for licensing terms. 8 | * 9 | * This code is derived from the NetBSD pcons driver, and in turn derived 10 | * from software contributed to Berkeley by William Jolitz and Don Ahn. 11 | * Adapted for PIOS by Bryan Ford at Yale University. 12 | */ 13 | 14 | #ifndef PIOS_KERN_SERIAL_H_ 15 | #define PIOS_KERN_SERIAL_H_ 16 | #ifndef PIOS_KERNEL 17 | # error "This is a kernel header; user programs should not #include it" 18 | #endif 19 | 20 | #include 21 | 22 | 23 | #define COM1 0x3F8 24 | 25 | #define COM_RX 0 // In: Receive buffer (DLAB=0) 26 | #define COM_TX 0 // Out: Transmit buffer (DLAB=0) 27 | #define COM_DLL 0 // Out: Divisor Latch Low (DLAB=1) 28 | #define COM_DLM 1 // Out: Divisor Latch High (DLAB=1) 29 | #define COM_IER 1 // Out: Interrupt Enable Register 30 | #define COM_IER_RDI 0x01 // Enable receiver data interrupt 31 | #define COM_IIR 2 // In: Interrupt ID Register 32 | #define COM_FCR 2 // Out: FIFO Control Register 33 | #define COM_LCR 3 // Out: Line Control Register 34 | #define COM_LCR_DLAB 0x80 // Divisor latch access bit 35 | #define COM_LCR_WLEN8 0x03 // Wordlength: 8 bits 36 | #define COM_MCR 4 // Out: Modem Control Register 37 | #define COM_MCR_RTS 0x02 // RTS complement 38 | #define COM_MCR_DTR 0x01 // DTR complement 39 | #define COM_MCR_OUT2 0x08 // Out2 complement 40 | #define COM_LSR 5 // In: Line Status Register 41 | #define COM_LSR_DATA 0x01 // Data available 42 | #define COM_LSR_TXRDY 0x20 // Transmit buffer avail 43 | #define COM_LSR_TSRE 0x40 // Transmitter off 44 | 45 | 46 | extern bool serial_exists; 47 | 48 | void serial_init(void); 49 | void serial_putc(int c); 50 | void serial_intenable(void); 51 | void serial_intr(void); // irq 4 52 | 53 | #endif /* PIOS_KERN_SERIAL_H_ */ 54 | -------------------------------------------------------------------------------- /dev/video.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Text-mode CGA/VGA display output device driver. 3 | * 4 | * Copyright (c) 2010 Yale University. 5 | * Copyright (c) 1993, 1994, 1995 Charles Hannum. 6 | * Copyright (c) 1990 The Regents of the University of California. 7 | * See section "BSD License" in the file LICENSES for licensing terms. 8 | * 9 | * This code is derived from the NetBSD pcons driver, and in turn derived 10 | * from software contributed to Berkeley by William Jolitz and Don Ahn. 11 | * Adapted for PIOS by Bryan Ford at Yale University. 12 | */ 13 | 14 | #include 15 | 16 | #include 17 | 18 | #include 19 | 20 | 21 | static unsigned addr_6845; 22 | static uint16_t *crt_buf; 23 | static uint16_t crt_pos; 24 | 25 | void 26 | video_init(void) 27 | { 28 | volatile uint16_t *cp; 29 | uint16_t was; 30 | unsigned pos; 31 | 32 | /* Get a pointer to the memory-mapped text display buffer. */ 33 | cp = (uint16_t*) mem_ptr(CGA_BUF); 34 | was = *cp; 35 | *cp = (uint16_t) 0xA55A; 36 | if (*cp != 0xA55A) { 37 | cp = (uint16_t*) mem_ptr(MONO_BUF); 38 | addr_6845 = MONO_BASE; 39 | } else { 40 | *cp = was; 41 | addr_6845 = CGA_BASE; 42 | } 43 | 44 | /* Extract cursor location */ 45 | outb(addr_6845, 14); 46 | pos = inb(addr_6845 + 1) << 8; 47 | outb(addr_6845, 15); 48 | pos |= inb(addr_6845 + 1); 49 | 50 | crt_buf = (uint16_t*) cp; 51 | crt_pos = pos; 52 | } 53 | 54 | 55 | 56 | void 57 | video_putc(int c) 58 | { 59 | // if no attribute given, then use black on white 60 | if (!(c & ~0xFF)) 61 | c |= 0x0700; 62 | 63 | switch (c & 0xff) { 64 | case '\b': 65 | if (crt_pos > 0) { 66 | crt_pos--; 67 | crt_buf[crt_pos] = (c & ~0xff) | ' '; 68 | } 69 | break; 70 | case '\n': 71 | crt_pos += CRT_COLS; 72 | /* fallthru */ 73 | case '\r': 74 | crt_pos -= (crt_pos % CRT_COLS); 75 | break; 76 | case '\t': 77 | video_putc(' '); 78 | video_putc(' '); 79 | video_putc(' '); 80 | video_putc(' '); 81 | video_putc(' '); 82 | break; 83 | default: 84 | crt_buf[crt_pos++] = c; /* write the character */ 85 | break; 86 | } 87 | 88 | // What is the purpose of this? 89 | if (crt_pos >= CRT_SIZE) { 90 | int i; 91 | 92 | memmove(crt_buf, crt_buf + CRT_COLS, 93 | (CRT_SIZE - CRT_COLS) * sizeof(uint16_t)); 94 | for (i = CRT_SIZE - CRT_COLS; i < CRT_SIZE; i++) 95 | crt_buf[i] = 0x0700 | ' '; 96 | crt_pos -= CRT_COLS; 97 | } 98 | 99 | /* move that little blinky thing */ 100 | outb(addr_6845, 14); 101 | outb(addr_6845 + 1, crt_pos >> 8); 102 | outb(addr_6845, 15); 103 | outb(addr_6845 + 1, crt_pos); 104 | } 105 | 106 | 107 | -------------------------------------------------------------------------------- /dev/video.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Text-mode CGA/VGA display output device driver. 3 | * 4 | * Copyright (c) 2010 Yale University. 5 | * Copyright (c) 1993, 1994, 1995 Charles Hannum. 6 | * Copyright (c) 1990 The Regents of the University of California. 7 | * See section "BSD License" in the file LICENSES for licensing terms. 8 | * 9 | * This code is derived from the NetBSD pcons driver, and in turn derived 10 | * from software contributed to Berkeley by William Jolitz and Don Ahn. 11 | * Adapted for PIOS by Bryan Ford at Yale University. 12 | */ 13 | 14 | #ifndef PIOS_KERN_VIDEO_H_ 15 | #define PIOS_KERN_VIDEO_H_ 16 | #ifndef PIOS_KERNEL 17 | # error "This is a kernel header; user programs should not #include it" 18 | #endif 19 | 20 | #include 21 | #include 22 | 23 | 24 | #define MONO_BASE 0x3B4 25 | #define MONO_BUF 0xB0000 26 | #define CGA_BASE 0x3D4 27 | #define CGA_BUF 0xB8000 28 | 29 | #define CRT_ROWS 25 30 | #define CRT_COLS 80 31 | #define CRT_SIZE (CRT_ROWS * CRT_COLS) 32 | 33 | 34 | void video_init(void); 35 | void video_putc(int c); 36 | 37 | 38 | #endif /* PIOS_KERN_VIDEO_H_ */ 39 | -------------------------------------------------------------------------------- /inc/assert.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Assertions and other debugging macros. 3 | * 4 | * Copyright (C) 1997 Massachusetts Institute of Technology 5 | * See section "MIT License" in the file LICENSES for licensing terms. 6 | * 7 | * Derived from the MIT Exokernel and JOS. 8 | * Adapted for PIOS by Bryan Ford at Yale University. 9 | */ 10 | 11 | #ifndef PIOS_INC_ASSERT_H 12 | #define PIOS_INC_ASSERT_H 13 | 14 | #include 15 | #include 16 | 17 | // The cputs() function is the basic debug-printing function in PIOS. 18 | // Implemented in kern/console.c (kernel) or lib/cputs.c (user-level code). 19 | void cputs(const char *str); 20 | #define CPUTS_MAX 256 // Max buffer length cputs() will accept 21 | 22 | void debug_warn(const char*, int, const char*, ...); 23 | void debug_panic(const char*, int, const char*, ...) gcc_noreturn; 24 | void debug_dump(const char*, int, const void *ptr, int size); 25 | 26 | #define warn(...) debug_warn(__FILE__, __LINE__, __VA_ARGS__) 27 | #define panic(...) debug_panic(__FILE__, __LINE__, __VA_ARGS__) 28 | #define dump(...) debug_dump(__FILE__, __LINE__, __VA_ARGS__) 29 | 30 | #ifndef NDEBUG 31 | #define assert(x) \ 32 | do { if (!(x)) panic("assertion failed: %s", #x); } while (0) 33 | #else 34 | #define assert(x) // do nothing 35 | #endif 36 | 37 | // static_assert(x) will generate a compile-time error if 'x' is false. 38 | #define static_assert(x) switch (x) case 0: case (x): 39 | 40 | #endif /* !PIOS_INC_ASSERT_H */ 41 | -------------------------------------------------------------------------------- /inc/cdefs.h: -------------------------------------------------------------------------------- 1 | // Convenience macros for using GCC-specific compiler features 2 | // that tend to be very useful in OS programming. 3 | #ifndef PIOS_INC_CDEFS_H 4 | #define PIOS_INC_CDEFS_H 5 | 6 | 7 | // Use this to align a variable or struct member at a given boundary. 8 | #define gcc_aligned(mult) __attribute__((aligned (mult))) 9 | 10 | // Use this to _prevent_ GCC from naturally aligning a structure member. 11 | #define gcc_packed __attribute__((packed)) 12 | 13 | // Functions declared always_inline will always be inlined, 14 | // even in code compiled without optimization. In contrast, 15 | // the regular "inline" attribute is just a hint and may not be observed. 16 | #define gcc_inline __inline __attribute__((always_inline)) 17 | 18 | // Conversely, this ensures that GCC does NOT inline a function. 19 | #define gcc_noinline __attribute__((noinline)) 20 | 21 | // Functions declared noreturn are not expected to return 22 | // (and GCC complains if you write a noreturn function that does). 23 | #define gcc_noreturn __attribute__((noreturn)) 24 | 25 | // Functions declared pure have no non-stack writes or other side-effects. 26 | // Those declared pure2 do not even read anything but their direct arguments. 27 | #define gcc_pure __attribute__((pure)) 28 | #define gcc_pure2 __attribute__((const)) 29 | 30 | #endif // PIOS_INC_CDEFS_H 31 | -------------------------------------------------------------------------------- /inc/elf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ELF (Executable and Linkable Format) definitions. 3 | * 4 | * Copyright (C) 1997 Massachusetts Institute of Technology 5 | * See section "MIT License" in the file LICENSES for licensing terms. 6 | * 7 | * Derived from the MIT Exokernel and JOS. 8 | */ 9 | 10 | #ifndef PIOS_INC_ELF_H 11 | #define PIOS_INC_ELF_H 12 | 13 | #define ELF_MAGIC 0x464C457FU /* "\x7FELF" in little endian */ 14 | 15 | // ELF header 16 | typedef struct elfhdf { 17 | uint32_t e_magic; // must equal ELF_MAGIC 18 | uint8_t e_elf[12]; 19 | uint16_t e_type; 20 | uint16_t e_machine; 21 | uint32_t e_version; 22 | uint32_t e_entry; 23 | uint32_t e_phoff; 24 | uint32_t e_shoff; 25 | uint32_t e_flags; 26 | uint16_t e_ehsize; 27 | uint16_t e_phentsize; 28 | uint16_t e_phnum; 29 | uint16_t e_shentsize; 30 | uint16_t e_shnum; 31 | uint16_t e_shstrndx; 32 | } elfhdr; 33 | 34 | // ELF program header 35 | typedef struct proghdr { 36 | uint32_t p_type; 37 | uint32_t p_offset; 38 | uint32_t p_va; 39 | uint32_t p_pa; 40 | uint32_t p_filesz; 41 | uint32_t p_memsz; 42 | uint32_t p_flags; 43 | uint32_t p_align; 44 | } proghdr; 45 | 46 | // ELF section header 47 | typedef struct sechdr { 48 | uint32_t sh_name; 49 | uint32_t sh_type; 50 | uint32_t sh_flags; 51 | uint32_t sh_addr; 52 | uint32_t sh_offset; 53 | uint32_t sh_size; 54 | uint32_t sh_link; 55 | uint32_t sh_info; 56 | uint32_t sh_addralign; 57 | uint32_t sh_entsize; 58 | } sechdr; 59 | 60 | // Values for proghdr::p_type 61 | #define ELF_PROG_LOAD 1 62 | 63 | // Flag bits for proghdr::p_flags 64 | #define ELF_PROG_FLAG_EXEC 1 65 | #define ELF_PROG_FLAG_WRITE 2 66 | #define ELF_PROG_FLAG_READ 4 67 | 68 | // Values for sechdr::sh_type 69 | #define ELF_SHT_NULL 0 70 | #define ELF_SHT_PROGBITS 1 71 | #define ELF_SHT_SYMTAB 2 72 | #define ELF_SHT_STRTAB 3 73 | 74 | // Values for sechdr::sh_name 75 | #define ELF_SHN_UNDEF 0 76 | 77 | #endif /* !PIOS_INC_ELF_H */ 78 | -------------------------------------------------------------------------------- /inc/mmu.h: -------------------------------------------------------------------------------- 1 | /* 2 | * x86 memory management unit (MMU) hardware definitions. 3 | * 4 | * Copyright (C) 1997 Massachusetts Institute of Technology 5 | * See section "MIT License" in the file LICENSES for licensing terms. 6 | * 7 | * Derived from the MIT Exokernel and JOS. 8 | */ 9 | 10 | #ifndef PIOS_INC_MMU_H 11 | #define PIOS_INC_MMU_H 12 | 13 | #include 14 | 15 | /* 16 | * This file contains definitions for the x86 memory management unit (MMU), 17 | * including paging- and segmentation-related data structures and constants, 18 | * the %cr0, %cr4, and %eflags registers, and traps. 19 | */ 20 | 21 | /* 22 | * 23 | * Part 1. Paging data structures and constants. 24 | * 25 | */ 26 | 27 | // A linear address 'la' has a three-part structure as follows: 28 | // 29 | // +--------10------+-------10-------+---------12----------+ 30 | // | Page Directory | Page Table | Offset within Page | 31 | // | Index | Index | | 32 | // +----------------+----------------+---------------------+ 33 | // \--- PDX(la) --/ \--- PTX(la) --/ \---- PGOFF(la) ----/ 34 | // \----------- PPN(la) -----------/ 35 | // 36 | // The PDX, PTX, PGOFF, and PPN macros decompose linear addresses as shown. 37 | // To construct a linear address la from PDX(la), PTX(la), and PGOFF(la), 38 | // use PGADDR(PDX(la), PTX(la), PGOFF(la)). 39 | 40 | // page number field of address 41 | #define PPN(la) (((uintptr_t) (la)) >> PTXSHIFT) 42 | #define VPN(la) PPN(la) // used to index into vpt[] 43 | 44 | // page directory index 45 | #define PDX(la) ((((uintptr_t) (la)) >> PDXSHIFT) & 0x3FF) 46 | #define VPD(la) PDX(la) // used to index into vpd[] 47 | 48 | // page table index 49 | #define PTX(la) ((((uintptr_t) (la)) >> PTXSHIFT) & 0x3FF) 50 | 51 | // linear address components 52 | #define PGADDR(la) ((uintptr_t) (la) & ~0xFFF) // address of page 53 | #define PGOFF(la) ((uintptr_t) (la) & 0xFFF) // offset in page 54 | 55 | #define PTADDR(la) ((uintptr_t) (la) & ~0x3FFFFF) // address of page table 56 | #define PTOFF(la) ((uintptr_t) (la) & 0x3FFFFF) // offset in page table 57 | 58 | // Page directory and page table constants. 59 | #define NPDENTRIES 1024 // PDEs per page directory 60 | #define NPTENTRIES 1024 // PTEs per page table 61 | 62 | #define PAGESIZE 4096 // bytes mapped by a page 63 | #define PAGESHIFT 12 // log2(PAGESIZE) 64 | 65 | #define PTSIZE (PAGESIZE*NPTENTRIES) // bytes mapped by a PDE 66 | #define PTSHIFT 22 // log2(PTSIZE) 67 | 68 | #define PTXSHIFT 12 // offset of PTX in a linear address 69 | #define PDXSHIFT 22 // offset of PDX in a linear address 70 | 71 | // Page table/directory entry flags. 72 | #define PTE_P 0x001 // Present 73 | #define PTE_W 0x002 // Writeable 74 | #define PTE_U 0x004 // User-accessible 75 | #define PTE_PWT 0x008 // Write-Through 76 | #define PTE_PCD 0x010 // Cache-Disable 77 | #define PTE_A 0x020 // Accessed 78 | #define PTE_D 0x040 // Dirty 79 | #define PTE_PS 0x080 // Page Size (only in PDEs) 80 | #define PTE_PAT 0x080 // Page Attribute Table (only in PTEs) 81 | #define PTE_G 0x100 // Global 82 | 83 | // The PTE_AVAIL bits aren't used by the kernel or interpreted by the 84 | // hardware, so user processes are allowed to set them arbitrarily. 85 | #define PTE_AVAIL 0xE00 // Available for software use 86 | 87 | // Only flags in PTE_USER may be used in system calls. 88 | #define PTE_USER (PTE_AVAIL | PTE_P | PTE_W | PTE_U) 89 | 90 | // Control Register flags 91 | #define CR0_PE 0x00000001 // Protection Enable 92 | #define CR0_MP 0x00000002 // Monitor coProcessor 93 | #define CR0_EM 0x00000004 // Emulation 94 | #define CR0_TS 0x00000008 // Task Switched 95 | #define CR0_ET 0x00000010 // Extension Type 96 | #define CR0_NE 0x00000020 // Numeric Errror 97 | #define CR0_WP 0x00010000 // Write Protect 98 | #define CR0_AM 0x00040000 // Alignment Mask 99 | #define CR0_NW 0x20000000 // Not Writethrough 100 | #define CR0_CD 0x40000000 // Cache Disable 101 | #define CR0_PG 0x80000000 // Paging 102 | 103 | #define CR4_VME 0x00000001 // V86 Mode Extensions 104 | #define CR4_PVI 0x00000002 // Protected-Mode Virtual Interrupts 105 | #define CR4_TSD 0x00000004 // Time Stamp Disable 106 | #define CR4_DE 0x00000008 // Debugging Extensions 107 | #define CR4_PSE 0x00000010 // Page Size Extensions 108 | #define CR4_PAE 0x00000020 // Physical Address Extension 109 | #define CR4_MCE 0x00000040 // Machine Check Enable 110 | #define CR4_PGE 0x00000080 // Page Global Enable 111 | #define CR4_PCE 0x00000100 // Performance counter enable 112 | #define CR4_OSFXSR 0x00000200 // SSE and FXSAVE/FXRSTOR enable 113 | #define CR4_OSXMMEXCPT 0x00000400 // Unmasked SSE FP exceptions 114 | 115 | // Page fault error codes 116 | #define PFE_PR 0x1 // Page fault caused by protection violation 117 | #define PFE_WR 0x2 // Page fault caused by a write 118 | #define PFE_U 0x4 // Page fault occured while in user mode 119 | 120 | 121 | /* 122 | * 123 | * Part 2. Segmentation data structures and constants. 124 | * 125 | */ 126 | 127 | #ifdef __ASSEMBLER__ 128 | 129 | /* 130 | * Macros to build GDT entries in assembly. 131 | */ 132 | #define SEG_NULL \ 133 | .word 0, 0; \ 134 | .byte 0, 0, 0, 0 135 | #define SEG(type,base,lim) \ 136 | .word (((lim) >> 12) & 0xffff), ((base) & 0xffff); \ 137 | .byte (((base) >> 16) & 0xff), (0x90 | (type)), \ 138 | (0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff) 139 | 140 | #else // not __ASSEMBLER__ 141 | 142 | #include 143 | 144 | // Segment Descriptors 145 | typedef struct segdesc { 146 | unsigned sd_lim_15_0 : 16; // Low bits of segment limit 147 | unsigned sd_base_15_0 : 16; // Low bits of segment base address 148 | unsigned sd_base_23_16 : 8; // Middle bits of segment base address 149 | unsigned sd_type : 4; // Segment type (see STS_ constants) 150 | unsigned sd_s : 1; // 0 = system, 1 = application 151 | unsigned sd_dpl : 2; // Descriptor Privilege Level 152 | unsigned sd_p : 1; // Present 153 | unsigned sd_lim_19_16 : 4; // High bits of segment limit 154 | unsigned sd_avl : 1; // Unused (available for software use) 155 | unsigned sd_rsv1 : 1; // Reserved 156 | unsigned sd_db : 1; // 0 = 16-bit segment, 1 = 32-bit segment 157 | unsigned sd_g : 1; // Granularity: limit scaled by 4K when set 158 | unsigned sd_base_31_24 : 8; // High bits of segment base address 159 | } segdesc; 160 | 161 | // Null segment 162 | #define SEGDESC_NULL (struct segdesc){ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } 163 | // Segment that is loadable but faults when used 164 | #define SEGDESC_FAULT (struct segdesc){ 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0 } 165 | // Normal segment 166 | #define SEGDESC32(app, type, base, lim, dpl) (struct segdesc) \ 167 | { ((lim) >> 12) & 0xffff, (base) & 0xffff, ((base) >> 16) & 0xff, \ 168 | (type), (app), (dpl), 1, (unsigned) (lim) >> 28, 0, 0, 1, 1, \ 169 | (unsigned) (base) >> 24 } 170 | #define SEGDESC16(app, type, base, lim, dpl) (struct segdesc) \ 171 | { (lim) & 0xffff, (base) & 0xffff, ((base) >> 16) & 0xff, \ 172 | (type), (app), (dpl), 1, (unsigned) (lim) >> 16, 0, 0, 1, 0, \ 173 | (unsigned) (base) >> 24 } 174 | 175 | #endif /* !__ASSEMBLER__ */ 176 | 177 | // Application segment type bits ('app' bit = 1) 178 | #define STA_X 0x8 // Executable segment 179 | #define STA_E 0x4 // Expand down (non-executable segments) 180 | #define STA_C 0x4 // Conforming code segment (executable only) 181 | #define STA_W 0x2 // Writeable (non-executable segments) 182 | #define STA_R 0x2 // Readable (executable segments) 183 | #define STA_A 0x1 // Accessed 184 | 185 | // System segment type bits ('app' bit = 0) 186 | #define STS_T16A 0x1 // Available 16-bit TSS 187 | #define STS_LDT 0x2 // Local Descriptor Table 188 | #define STS_T16B 0x3 // Busy 16-bit TSS 189 | #define STS_CG16 0x4 // 16-bit Call Gate 190 | #define STS_TG 0x5 // Task Gate / Coum Transmitions 191 | #define STS_IG16 0x6 // 16-bit Interrupt Gate 192 | #define STS_TG16 0x7 // 16-bit Trap Gate 193 | #define STS_T32A 0x9 // Available 32-bit TSS 194 | #define STS_T32B 0xB // Busy 32-bit TSS 195 | #define STS_CG32 0xC // 32-bit Call Gate 196 | #define STS_IG32 0xE // 32-bit Interrupt Gate 197 | #define STS_TG32 0xF // 32-bit Trap Gate 198 | 199 | 200 | /* 201 | * 202 | * Part 3. Traps. 203 | * 204 | */ 205 | 206 | #ifndef __ASSEMBLER__ 207 | 208 | // Task state segment format, as defined by the x86 architecture. 209 | typedef struct taskstate { 210 | uint32_t ts_link; // Old ts selector 211 | uintptr_t ts_esp0; // Stack pointers and segment selectors 212 | uint16_t ts_ss0; // after an increase in privilege level 213 | uint16_t ts_padding1; 214 | uintptr_t ts_esp1; 215 | uint16_t ts_ss1; 216 | uint16_t ts_padding2; 217 | uintptr_t ts_esp2; 218 | uint16_t ts_ss2; 219 | uint16_t ts_padding3; 220 | uintptr_t ts_cr3; // Page directory base 221 | uintptr_t ts_eip; // Saved state from last task switch 222 | uint32_t ts_eflags; 223 | uint32_t ts_eax; // More saved state (registers) 224 | uint32_t ts_ecx; 225 | uint32_t ts_edx; 226 | uint32_t ts_ebx; 227 | uintptr_t ts_esp; 228 | uintptr_t ts_ebp; 229 | uint32_t ts_esi; 230 | uint32_t ts_edi; 231 | uint16_t ts_es; // Even more saved state (segment selectors) 232 | uint16_t ts_padding4; 233 | uint16_t ts_cs; 234 | uint16_t ts_padding5; 235 | uint16_t ts_ss; 236 | uint16_t ts_padding6; 237 | uint16_t ts_ds; 238 | uint16_t ts_padding7; 239 | uint16_t ts_fs; 240 | uint16_t ts_padding8; 241 | uint16_t ts_gs; 242 | uint16_t ts_padding9; 243 | uint16_t ts_ldt; 244 | uint16_t ts_padding10; 245 | uint16_t ts_t; // Trap on task switch 246 | uint16_t ts_iomb; // I/O map base address 247 | } taskstate; 248 | 249 | // Gate descriptors for interrupts and traps 250 | typedef struct gatedesc { 251 | unsigned gd_off_15_0 : 16; // low 16 bits of offset in segment 252 | unsigned gd_ss : 16; // segment selector 253 | unsigned gd_args : 5; // # args, 0 for interrupt/trap gates 254 | unsigned gd_rsv1 : 3; // reserved(should be zero I guess) 255 | unsigned gd_type : 4; // type(STS_{TG,IG32,TG32}) 256 | unsigned gd_s : 1; // must be 0 (system) 257 | unsigned gd_dpl : 2; // descriptor(meaning new) privilege level 258 | unsigned gd_p : 1; // Present 259 | unsigned gd_off_31_16 : 16; // high bits of offset in segment 260 | } gatedesc; 261 | 262 | // Set up a normal interrupt/trap gate descriptor. 263 | // - istrap: 1 for a trap (= exception) gate, 0 for an interrupt gate. 264 | // - sel: Code segment selector for interrupt/trap handler 265 | // - off: Offset in code segment for interrupt/trap handler 266 | // - dpl: Descriptor Privilege Level - 267 | // the privilege level required for software to invoke 268 | // this interrupt/trap gate explicitly using an int instruction. 269 | #define SETGATE(gate, istrap, sel, off, dpl) \ 270 | { \ 271 | (gate).gd_off_15_0 = (uint32_t) (off) & 0xffff; \ 272 | (gate).gd_ss = (sel); \ 273 | (gate).gd_args = 0; \ 274 | (gate).gd_rsv1 = 0; \ 275 | (gate).gd_type = (istrap) ? STS_TG32 : STS_IG32; \ 276 | (gate).gd_s = 0; \ 277 | (gate).gd_dpl = (dpl); \ 278 | (gate).gd_p = 1; \ 279 | (gate).gd_off_31_16 = (uint32_t) (off) >> 16; \ 280 | } 281 | 282 | // Set up a call gate descriptor. 283 | #define SETCALLGATE(gate, ss, off, dpl) \ 284 | { \ 285 | (gate).gd_off_15_0 = (uint32_t) (off) & 0xffff; \ 286 | (gate).gd_ss = (ss); \ 287 | (gate).gd_args = 0; \ 288 | (gate).gd_rsv1 = 0; \ 289 | (gate).gd_type = STS_CG32; \ 290 | (gate).gd_s = 0; \ 291 | (gate).gd_dpl = (dpl); \ 292 | (gate).gd_p = 1; \ 293 | (gate).gd_off_31_16 = (uint32_t) (off) >> 16; \ 294 | } 295 | 296 | // Pseudo-descriptors used for LGDT, LLDT and LIDT instructions. 297 | struct pseudodesc { 298 | uint16_t pd_lim; // Limit 299 | uint32_t gcc_packed pd_base; // Base - NOT 4-byte aligned! 300 | }; 301 | typedef struct pseudodesc pseudodesc; 302 | 303 | #endif /* !__ASSEMBLER__ */ 304 | 305 | #endif /* !PIOS_INC_MMU_H */ 306 | -------------------------------------------------------------------------------- /inc/stdarg.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Variable argument (varargs) parsing macros compliant with the C standard. 3 | * 4 | * Copyright (c) 1991, 1993 The Regents of the University of California. 5 | * See section "BSD License" in the file LICENSES for licensing terms. 6 | * 7 | * This code is derived from JOS and from BSD. 8 | */ 9 | 10 | #ifndef PIOS_INC_STDARG_H 11 | #define PIOS_INC_STDARG_H 12 | 13 | typedef char *va_list; 14 | 15 | #define __va_size(type) \ 16 | (((sizeof(type) + sizeof(long) - 1) / sizeof(long)) * sizeof(long)) 17 | 18 | #define va_start(ap, last) \ 19 | ((ap) = (va_list)&(last) + __va_size(last)) 20 | 21 | #define va_arg(ap, type) \ 22 | (*(type *)((ap) += __va_size(type), (ap) - __va_size(type))) 23 | 24 | #define va_end(ap) ((void)0) 25 | 26 | #endif /* !PIOS_INC_STDARG_H */ 27 | -------------------------------------------------------------------------------- /inc/stdio.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Standard I/O definitions, mostly inline with the standard C/Unix API 3 | * (except for the PIOS-specific "console printing" routines cprintf/cputs, 4 | * which are intended for debugging purposes only). 5 | * 6 | * Copyright (C) 1997 Massachusetts Institute of Technology 7 | * See section "MIT License" in the file LICENSES for licensing terms. 8 | * 9 | * Derived from the MIT Exokernel and JOS. 10 | * Adapted for PIOS by Bryan Ford at Yale University. 11 | */ 12 | 13 | #ifndef PIOS_INC_STDIO_H 14 | #define PIOS_INC_STDIO_H 15 | 16 | #include 17 | #include 18 | 19 | #ifndef NULL 20 | #define NULL ((void *) 0) 21 | #endif /* !NULL */ 22 | 23 | // Primitive formatted printing functions: lib/printfmt.c 24 | void printfmt(void (*putch)(int, void*), void *putdat, const char *fmt, ...); 25 | void vprintfmt(void (*putch)(int, void*), void *putdat, 26 | const char *fmt, va_list); 27 | 28 | // Debug console output functions. 29 | // These are available in both the PIOS kernel and in user space, 30 | // but are implemented differently in user space and in the kernel. 31 | void cputs(const char *str); // lib/cputs.c or kern/cons.c 32 | int cprintf(const char *fmt, ...); // lib/cprintf.c 33 | int vcprintf(const char *fmt, va_list); // lib/cprintf.c 34 | 35 | #endif /* !PIOS_INC_STDIO_H */ 36 | -------------------------------------------------------------------------------- /inc/stdlib.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generic C standard library definitions. 3 | * 4 | * Copyright (C) 1997 Massachusetts Institute of Technology 5 | * See section "MIT License" in the file LICENSES for licensing terms. 6 | * 7 | * Derived from the MIT Exokernel and JOS. 8 | * Adapted for PIOS by Bryan Ford at Yale University. 9 | */ 10 | 11 | #ifndef PIOS_INC_STDLIB_H 12 | #define PIOS_INC_STDLIB_H 13 | 14 | #include 15 | #include 16 | 17 | #ifndef NULL 18 | #define NULL ((void *) 0) 19 | #endif /* !NULL */ 20 | 21 | 22 | // Process exit 23 | #define EXIT_SUCCESS 0 // Success status for exit() 24 | #define EXIT_FAILURE 1 // Failure status for exit() 25 | 26 | void exit(int status) gcc_noreturn; 27 | void abort(void) gcc_noreturn; 28 | 29 | 30 | 31 | #endif /* !PIOS_INC_STDLIB_H */ 32 | -------------------------------------------------------------------------------- /inc/string.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generic string-handling functions as defined in the C standard. 3 | * 4 | * Copyright (C) 1997 Massachusetts Institute of Technology 5 | * See section "MIT License" in the file LICENSES for licensing terms. 6 | * 7 | * Derived from the MIT Exokernel and JOS. 8 | * Adapted for PIOS by Bryan Ford at Yale University. 9 | */ 10 | 11 | #ifndef PIOS_INC_STRING_H 12 | #define PIOS_INC_STRING_H 13 | 14 | #include 15 | 16 | int strlen(const char *s); 17 | char * strcpy(char *dst, const char *src); 18 | char * strncpy(char *dst, const char *src, size_t size); 19 | size_t strlcpy(char *dst, const char *src, size_t size); 20 | int strcmp(const char *s1, const char *s2); 21 | int strncmp(const char *s1, const char *s2, size_t size); 22 | char * strchr(const char *s, char c); 23 | 24 | void * memset(void *dst, int c, size_t len); 25 | void * memcpy(void *dst, const void *src, size_t len); 26 | void * memmove(void *dst, const void *src, size_t len); 27 | int memcmp(const void *s1, const void *s2, size_t len); 28 | void * memchr(const void *str, int c, size_t len); 29 | 30 | long strtol(const char *s, char **endptr, int base); 31 | 32 | char * strerror(int err); 33 | 34 | #endif /* not PIOS_INC_STRING_H */ 35 | -------------------------------------------------------------------------------- /inc/trap.h: -------------------------------------------------------------------------------- 1 | /* 2 | * PIOS trap handling definitions. 3 | * 4 | * Copyright (C) 1997 Massachusetts Institute of Technology 5 | * See section "MIT License" in the file LICENSES for licensing terms. 6 | * 7 | * Derived from the MIT Exokernel and JOS. 8 | * Adapted for PIOS by Bryan Ford at Yale University. 9 | */ 10 | 11 | #ifndef PIOS_INC_TRAP_H 12 | #define PIOS_INC_TRAP_H 13 | 14 | #include 15 | 16 | 17 | // Trap numbers 18 | // These are processor defined: 19 | #define T_DIVIDE 0 // divide error 20 | #define T_DEBUG 1 // debug exception 21 | #define T_NMI 2 // non-maskable interrupt 22 | #define T_BRKPT 3 // breakpoint 23 | #define T_OFLOW 4 // overflow 24 | #define T_BOUND 5 // bounds check 25 | #define T_ILLOP 6 // illegal opcode 26 | #define T_DEVICE 7 // device not available 27 | #define T_DBLFLT 8 // double fault 28 | /* #define T_COPROC 9 */ // reserved (not generated by recent processors) 29 | #define T_TSS 10 // invalid task switch segment 30 | #define T_SEGNP 11 // segment not present 31 | #define T_STACK 12 // stack exception 32 | #define T_GPFLT 13 // general protection fault 33 | #define T_PGFLT 14 // page fault 34 | /* #define T_RES 15 */ // reserved 35 | #define T_FPERR 16 // floating point error 36 | #define T_ALIGN 17 // aligment check 37 | #define T_MCHK 18 // machine check 38 | #define T_SIMD 19 // SIMD floating point exception 39 | #define T_SECEV 30 // Security-sensitive event 40 | 41 | #define T_IRQ0 32 // Legacy ISA hardware interrupts: IRQ0-15. 42 | 43 | // The rest are arbitrarily chosen, but with care not to overlap 44 | // processor defined exceptions or ISA hardware interrupt vectors. 45 | #define T_SYSCALL 48 // System call 46 | 47 | // We use these vectors to receive local per-CPU interrupts 48 | #define T_LTIMER 49 // Local APIC timer interrupt 49 | #define T_LERROR 50 // Local APIC error interrupt 50 | 51 | #define T_DEFAULT 500 // Unused trap vectors produce this value 52 | #define T_ICNT 501 // Child process instruction count expired 53 | 54 | // ISA hardware IRQ numbers. We receive these as (T_IRQ0 + IRQ_WHATEVER) 55 | #define IRQ_TIMER 0 // 8253 Programmable Interval Timer (PIT) 56 | #define IRQ_KBD 1 // Keyboard interrupt 57 | #define IRQ_SERIAL 4 // Serial (COM) interrupt 58 | #define IRQ_SPURIOUS 7 // Spurious interrupt 59 | #define IRQ_IDE 14 // IDE disk controller interrupt 60 | 61 | #ifndef __ASSEMBLER__ 62 | 63 | #include 64 | 65 | 66 | // General registers in the format pushed by PUSHA instruction. 67 | // We use this instruction to push the general registers only for convenience: 68 | // modern kernels generally avoid it and save the registers manually, 69 | // because that's just as fast or faster and they get to choose 70 | // exactly which registers to save and where. 71 | typedef struct pushregs { 72 | uint32_t edi; 73 | uint32_t esi; 74 | uint32_t ebp; 75 | uint32_t oesp; /* Useless */ 76 | uint32_t ebx; 77 | uint32_t edx; 78 | uint32_t ecx; 79 | uint32_t eax; 80 | } pushregs; 81 | 82 | 83 | // This struct represents the format of the trap frames 84 | // that get pushed on the kernel stack by the processor 85 | // in conjunction with the interrupt/trap entry code in trapasm.S. 86 | // All interrupts and traps use this same format, 87 | // although not all fields are always used: 88 | // e.g., the error code (err) applies only to some traps, 89 | // and the processor pushes esp and ss 90 | // only when taking a trap from user mode (privilege level >0). 91 | typedef struct trapframe { 92 | 93 | // registers and other info we push manually in trapasm.S 94 | pushregs regs; 95 | uint16_t gs; uint16_t padding_gs; 96 | uint16_t fs; uint16_t padding_fs; 97 | uint16_t es; uint16_t padding_es; 98 | uint16_t ds; uint16_t padding_ds; 99 | uint32_t trapno; 100 | 101 | // format from here on determined by x86 hardware architecture 102 | uint32_t err; 103 | uintptr_t eip; 104 | uint16_t cs; uint16_t padding_cs; 105 | uint32_t eflags; 106 | 107 | // rest included only when crossing rings, e.g., user to kernel 108 | uintptr_t esp; 109 | uint16_t ss; uint16_t padding_ss; 110 | } trapframe; 111 | 112 | // size of trapframe pushed when called from user and kernel mode, respectively 113 | #define trapframe_usize sizeof(trapframe) // full trapframe struct 114 | #define trapframe_ksize (sizeof(trapframe) - 8) // no esp, ss, padding4 115 | 116 | 117 | // Floating-point/MMX/XMM register save area format, 118 | // in the layout defined by the processor's FXSAVE/FXRSTOR instructions. 119 | typedef gcc_aligned(16) struct fxsave { 120 | uint16_t fcw; // byte 0 121 | uint16_t fsw; 122 | uint16_t ftw; 123 | uint16_t fop; 124 | uint32_t fpu_ip; 125 | uint16_t cs; 126 | uint16_t reserved1; 127 | uint32_t fpu_dp; // byte 16 128 | uint16_t ds; 129 | uint16_t reserved2; 130 | uint32_t mxcsr; 131 | uint32_t mxcsr_mask; 132 | uint8_t st_mm[8][16]; // byte 32: x87/MMX registers 133 | uint8_t xmm[8][16]; // byte 160: XMM registers 134 | uint8_t reserved3[11][16]; // byte 288: reserved area 135 | uint8_t available[3][16]; // byte 464: available to OS 136 | } fxsave; 137 | 138 | 139 | #endif /* !__ASSEMBLER__ */ 140 | 141 | // Must equal 'sizeof(struct trapframe)'. 142 | // A static_assert in kern/trap.c checks this. 143 | #define SIZEOF_STRUCT_TRAPFRAME 0x4c 144 | 145 | #endif /* !PIOS_INC_TRAP_H */ 146 | -------------------------------------------------------------------------------- /inc/types.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generic type definitions and macros used throughout PIOS. 3 | * Most are C/Unix standard, with a few PIOS-specific exceptions. 4 | * 5 | * Copyright (c) 1982, 1986, 1991, 1993 6 | * The Regents of the University of California. All rights reserved. 7 | * (c) UNIX System Laboratories, Inc. 8 | * See section "BSD License" in the file LICENSES for licensing terms. 9 | * 10 | * All or some portions of this file are derived from material licensed 11 | * to the University of California by American Telephone and Telegraph 12 | * Co. or Unix System Laboratories, Inc. and are reproduced herein with 13 | * the permission of UNIX System Laboratories, Inc. 14 | * Adapted for PIOS by Bryan Ford at Yale University. 15 | */ 16 | 17 | #ifndef PIOS_INC_TYPES_H 18 | #define PIOS_INC_TYPES_H 19 | 20 | #ifndef NULL 21 | #define NULL ((void*) 0) 22 | #endif 23 | 24 | // Represents true-or-false values 25 | typedef int bool; 26 | #define false 0 27 | #define true 1 28 | 29 | // Explicitly-sized versions of integer types 30 | typedef signed char int8_t; 31 | typedef unsigned char uint8_t; 32 | typedef short int16_t; 33 | typedef unsigned short uint16_t; 34 | typedef int int32_t; 35 | typedef unsigned int uint32_t; 36 | typedef long long int64_t; 37 | typedef unsigned long long uint64_t; 38 | 39 | // Pointers and addresses are 32 bits long. 40 | // We use pointer types to represent virtual addresses, 41 | // and [u]intptr_t to represent the numerical values of virtual addresses. 42 | typedef int intptr_t; // pointer-size signed integer 43 | typedef unsigned uintptr_t; // pointer-size unsigned integer 44 | typedef int ptrdiff_t; // difference between pointers 45 | 46 | // size_t is used for memory object sizes, and ssize_t is a signed analog. 47 | typedef unsigned size_t; 48 | typedef int ssize_t; 49 | 50 | // intmax_t and uintmax_t represent the maximum-size integers supported. 51 | typedef long long intmax_t; 52 | typedef unsigned long long uintmax_t; 53 | 54 | // Floating-point types matching the size at which the compiler 55 | // actually evaluates floating-point expressions of a given type. (math.h) 56 | typedef double double_t; 57 | typedef float float_t; 58 | 59 | // Unix API compatibility types 60 | typedef int off_t; // file offsets and lengths 61 | typedef int pid_t; // process IDs 62 | typedef int ino_t; // file inode numbers 63 | typedef int mode_t; // file mode flags 64 | 65 | // Efficient min and max operations 66 | #define MIN(_a, _b) \ 67 | ({ \ 68 | typeof(_a) __a = (_a); \ 69 | typeof(_b) __b = (_b); \ 70 | __a <= __b ? __a : __b; \ 71 | }) 72 | #define MAX(_a, _b) \ 73 | ({ \ 74 | typeof(_a) __a = (_a); \ 75 | typeof(_b) __b = (_b); \ 76 | __a >= __b ? __a : __b; \ 77 | }) 78 | 79 | // Rounding operations (efficient when n is a power of 2) 80 | // Round down to the nearest multiple of n 81 | #define ROUNDDOWN(a, n) \ 82 | ({ \ 83 | uint32_t __a = (uint32_t) (a); \ 84 | (typeof(a)) (__a - __a % (n)); \ 85 | }) 86 | // Round up to the nearest multiple of n 87 | #define ROUNDUP(a, n) \ 88 | ({ \ 89 | uint32_t __n = (uint32_t) (n); \ 90 | (typeof(a)) (ROUNDDOWN((uint32_t) (a) + __n - 1, __n)); \ 91 | }) 92 | 93 | // Return the offset of 'member' relative to the beginning of a struct type 94 | #define offsetof(type, member) ((size_t) (&((type*)0)->member)) 95 | 96 | // Make the compiler think a value is getting used, even if it isn't. 97 | #define USED(x) (void)(x) 98 | 99 | 100 | #endif /* !PIOS_INC_TYPES_H */ 101 | -------------------------------------------------------------------------------- /inc/x86.h: -------------------------------------------------------------------------------- 1 | /* 2 | * x86 processor-specific definitions and utility functions. 3 | * 4 | * Copyright (C) 1997 Massachusetts Institute of Technology 5 | * See section "MIT License" in the file LICENSES for licensing terms. 6 | * 7 | * Derived from the MIT Exokernel and JOS. 8 | * Adapted for PIOS by Bryan Ford at Yale University. 9 | */ 10 | 11 | #ifndef PIOS_INC_X86_H 12 | #define PIOS_INC_X86_H 13 | 14 | #include 15 | #include 16 | 17 | 18 | // EFLAGS register 19 | #define FL_CF 0x00000001 // Carry Flag 20 | #define FL_PF 0x00000004 // Parity Flag 21 | #define FL_AF 0x00000010 // Auxiliary carry Flag 22 | #define FL_ZF 0x00000040 // Zero Flag 23 | #define FL_SF 0x00000080 // Sign Flag 24 | #define FL_TF 0x00000100 // Trap Flag 25 | #define FL_IF 0x00000200 // Interrupt Flag 26 | #define FL_DF 0x00000400 // Direction Flag 27 | #define FL_OF 0x00000800 // Overflow Flag 28 | #define FL_IOPL_MASK 0x00003000 // I/O Privilege Level bitmask 29 | #define FL_IOPL_0 0x00000000 // IOPL == 0 30 | #define FL_IOPL_1 0x00001000 // IOPL == 1 31 | #define FL_IOPL_2 0x00002000 // IOPL == 2 32 | #define FL_IOPL_3 0x00003000 // IOPL == 3 33 | #define FL_NT 0x00004000 // Nested Task 34 | #define FL_RF 0x00010000 // Resume Flag 35 | #define FL_VM 0x00020000 // Virtual 8086 mode 36 | #define FL_AC 0x00040000 // Alignment Check 37 | #define FL_VIF 0x00080000 // Virtual Interrupt Flag 38 | #define FL_VIP 0x00100000 // Virtual Interrupt Pending 39 | #define FL_ID 0x00200000 // ID flag 40 | 41 | 42 | // Struct containing information returned by the CPUID instruction 43 | typedef struct cpuinfo { 44 | uint32_t eax; 45 | uint32_t ebx; 46 | uint32_t edx; 47 | uint32_t ecx; 48 | } cpuinfo; 49 | 50 | 51 | 52 | static gcc_inline void 53 | breakpoint(void) 54 | { 55 | __asm __volatile("int3"); 56 | } 57 | 58 | static gcc_inline uint8_t 59 | inb(int port) 60 | { 61 | uint8_t data; 62 | __asm __volatile("inb %w1,%0" : "=a" (data) : "d" (port)); 63 | return data; 64 | } 65 | 66 | static gcc_inline void 67 | insb(int port, void *addr, int cnt) 68 | { 69 | __asm __volatile("cld\n\trepne\n\tinsb" : 70 | "=D" (addr), "=c" (cnt) : 71 | "d" (port), "0" (addr), "1" (cnt) : 72 | "memory", "cc"); 73 | } 74 | 75 | static gcc_inline uint16_t 76 | inw(int port) 77 | { 78 | uint16_t data; 79 | __asm __volatile("inw %w1,%0" : "=a" (data) : "d" (port)); 80 | return data; 81 | } 82 | 83 | static gcc_inline void 84 | insw(int port, void *addr, int cnt) 85 | { 86 | __asm __volatile("cld\n\trepne\n\tinsw" : 87 | "=D" (addr), "=c" (cnt) : 88 | "d" (port), "0" (addr), "1" (cnt) : 89 | "memory", "cc"); 90 | } 91 | 92 | static gcc_inline uint32_t 93 | inl(int port) 94 | { 95 | uint32_t data; 96 | __asm __volatile("inl %w1,%0" : "=a" (data) : "d" (port)); 97 | return data; 98 | } 99 | 100 | static gcc_inline void 101 | insl(int port, void *addr, int cnt) 102 | { 103 | __asm __volatile("cld\n\trepne\n\tinsl" : 104 | "=D" (addr), "=c" (cnt) : 105 | "d" (port), "0" (addr), "1" (cnt) : 106 | "memory", "cc"); 107 | } 108 | 109 | static gcc_inline void 110 | outb(int port, uint8_t data) 111 | { 112 | __asm __volatile("outb %0,%w1" : : "a" (data), "d" (port)); 113 | } 114 | 115 | static gcc_inline void 116 | outsb(int port, const void *addr, int cnt) 117 | { 118 | __asm __volatile("cld\n\trepne\n\toutsb" : 119 | "=S" (addr), "=c" (cnt) : 120 | "d" (port), "0" (addr), "1" (cnt) : 121 | "cc"); 122 | } 123 | 124 | static gcc_inline void 125 | outw(int port, uint16_t data) 126 | { 127 | __asm __volatile("outw %0,%w1" : : "a" (data), "d" (port)); 128 | } 129 | 130 | static gcc_inline void 131 | outsw(int port, const void *addr, int cnt) 132 | { 133 | __asm __volatile("cld\n\trepne\n\toutsw" : 134 | "=S" (addr), "=c" (cnt) : 135 | "d" (port), "0" (addr), "1" (cnt) : 136 | "cc"); 137 | } 138 | 139 | static gcc_inline void 140 | outsl(int port, const void *addr, int cnt) 141 | { 142 | __asm __volatile("cld\n\trepne\n\toutsl" : 143 | "=S" (addr), "=c" (cnt) : 144 | "d" (port), "0" (addr), "1" (cnt) : 145 | "cc"); 146 | } 147 | 148 | static gcc_inline void 149 | outl(int port, uint32_t data) 150 | { 151 | __asm __volatile("outl %0,%w1" : : "a" (data), "d" (port)); 152 | } 153 | 154 | static gcc_inline void 155 | invlpg(void *addr) 156 | { 157 | __asm __volatile("invlpg (%0)" : : "r" (addr) : "memory"); 158 | } 159 | 160 | static gcc_inline void 161 | lidt(void *p) 162 | { 163 | __asm __volatile("lidt (%0)" : : "r" (p)); 164 | } 165 | 166 | static gcc_inline void 167 | lldt(uint16_t sel) 168 | { 169 | __asm __volatile("lldt %0" : : "r" (sel)); 170 | } 171 | 172 | static gcc_inline void 173 | ltr(uint16_t sel) 174 | { 175 | __asm __volatile("ltr %0" : : "r" (sel)); 176 | } 177 | 178 | static gcc_inline void 179 | lcr0(uint32_t val) 180 | { 181 | __asm __volatile("movl %0,%%cr0" : : "r" (val)); 182 | } 183 | 184 | static gcc_inline uint32_t 185 | rcr0(void) 186 | { 187 | uint32_t val; 188 | __asm __volatile("movl %%cr0,%0" : "=r" (val)); 189 | return val; 190 | } 191 | 192 | static gcc_inline uint32_t 193 | rcr2(void) 194 | { 195 | uint32_t val; 196 | __asm __volatile("movl %%cr2,%0" : "=r" (val)); 197 | return val; 198 | } 199 | 200 | static gcc_inline void 201 | lcr3(uint32_t val) 202 | { 203 | __asm __volatile("movl %0,%%cr3" : : "r" (val)); 204 | } 205 | 206 | static gcc_inline uint32_t 207 | rcr3(void) 208 | { 209 | uint32_t val; 210 | __asm __volatile("movl %%cr3,%0" : "=r" (val)); 211 | return val; 212 | } 213 | 214 | static gcc_inline void 215 | lcr4(uint32_t val) 216 | { 217 | __asm __volatile("movl %0,%%cr4" : : "r" (val)); 218 | } 219 | 220 | static gcc_inline uint32_t 221 | rcr4(void) 222 | { 223 | uint32_t cr4; 224 | __asm __volatile("movl %%cr4,%0" : "=r" (cr4)); 225 | return cr4; 226 | } 227 | 228 | static gcc_inline void 229 | tlbflush(void) 230 | { 231 | uint32_t cr3; 232 | __asm __volatile("movl %%cr3,%0" : "=r" (cr3)); 233 | __asm __volatile("movl %0,%%cr3" : : "r" (cr3)); 234 | } 235 | 236 | static gcc_inline uint32_t 237 | read_eflags(void) 238 | { 239 | uint32_t eflags; 240 | __asm __volatile("pushfl; popl %0" : "=rm" (eflags)); 241 | return eflags; 242 | } 243 | 244 | static gcc_inline void 245 | write_eflags(uint32_t eflags) 246 | { 247 | __asm __volatile("pushl %0; popfl" : : "rm" (eflags)); 248 | } 249 | 250 | static gcc_inline uint32_t 251 | read_ebp(void) 252 | { 253 | uint32_t ebp; 254 | __asm __volatile("movl %%ebp,%0" : "=rm" (ebp)); 255 | return ebp; 256 | } 257 | 258 | static gcc_inline uint32_t 259 | read_esp(void) 260 | { 261 | uint32_t esp; 262 | __asm __volatile("movl %%esp,%0" : "=rm" (esp)); 263 | return esp; 264 | } 265 | 266 | static gcc_inline uint16_t 267 | read_cs(void) 268 | { 269 | uint16_t cs; 270 | __asm __volatile("movw %%cs,%0" : "=rm" (cs)); 271 | return cs; 272 | } 273 | 274 | // Atomically set *addr to newval and return the old value of *addr. 275 | static inline uint32_t 276 | xchg(volatile uint32_t *addr, uint32_t newval) 277 | { 278 | uint32_t result; 279 | 280 | // The + in "+m" denotes a read-modify-write operand. 281 | asm volatile("lock; xchgl %0, %1" : 282 | "+m" (*addr), "=a" (result) : 283 | "1" (newval) : 284 | "cc"); 285 | return result; 286 | } 287 | 288 | // Atomically add incr to *addr. 289 | static inline void 290 | lockadd(volatile int32_t *addr, int32_t incr) 291 | { 292 | asm volatile("lock; addl %1,%0" : "+m" (*addr) : "r" (incr) : "cc"); 293 | } 294 | 295 | // Atomically add incr to *addr and return true if the result is zero. 296 | static inline uint8_t 297 | lockaddz(volatile int32_t *addr, int32_t incr) 298 | { 299 | uint8_t zero; 300 | asm volatile("lock; addl %2,%0; setzb %1" 301 | : "+m" (*addr), "=rm" (zero) 302 | : "r" (incr) 303 | : "cc"); 304 | return zero; 305 | } 306 | 307 | // Atomically add incr to *addr and return the old value of *addr. 308 | static inline int32_t 309 | xadd(volatile uint32_t *addr, int32_t incr) 310 | { 311 | int32_t result; 312 | 313 | // The + in "+m" denotes a read-modify-write operand. 314 | asm volatile("lock; xaddl %0, %1" : 315 | "+m" (*addr), "=a" (result) : 316 | "1" (incr) : 317 | "cc"); 318 | return result; 319 | } 320 | 321 | static inline void 322 | pause(void) 323 | { 324 | asm volatile("pause" : : : "memory"); 325 | } 326 | 327 | static gcc_inline void 328 | cpuid(uint32_t idx, cpuinfo *info) 329 | { 330 | asm volatile("cpuid" 331 | : "=a" (info->eax), "=b" (info->ebx), 332 | "=c" (info->ecx), "=d" (info->edx) 333 | : "a" (idx)); 334 | } 335 | 336 | static gcc_inline uint64_t 337 | rdtsc(void) 338 | { 339 | uint64_t tsc; 340 | asm volatile("rdtsc" : "=A" (tsc)); 341 | return tsc; 342 | } 343 | 344 | // Enable external device interrupts. 345 | static gcc_inline void 346 | sti(void) 347 | { 348 | asm volatile("sti"); 349 | } 350 | 351 | // Disable external device interrupts. 352 | static gcc_inline void 353 | cli(void) 354 | { 355 | asm volatile("cli"); 356 | } 357 | 358 | 359 | 360 | #endif /* !PIOS_INC_X86_H */ 361 | -------------------------------------------------------------------------------- /kern/Makefrag: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile fragment for the kernel. 3 | # This is NOT a complete makefile; 4 | # you must run GNU make in the top-level directory 5 | # where the GNUmakefile is located. 6 | # 7 | # Copyright (C) 2003 Massachusetts Institute of Technology 8 | # See section "MIT License" in the file LICENSES for licensing terms. 9 | # Primary authors: Bryan Ford, Eddie Kohler 10 | # 11 | 12 | OBJDIRS += kern 13 | 14 | # entry.S must be first, so that it's the first code in the text segment!!! 15 | # 16 | # We also snatch the use of a few handy source files 17 | # from the lib directory, to avoid gratuitous code duplication. 18 | KERN_SRCFILES := kern/entry.S \ 19 | kern/init.c \ 20 | kern/cons.c \ 21 | kern/debug.c \ 22 | kern/mem.c \ 23 | kern/cpu.c \ 24 | kern/trap.c \ 25 | kern/trapasm.S \ 26 | kern/mp.c \ 27 | kern/spinlock.c \ 28 | kern/proc.c \ 29 | kern/syscall.c \ 30 | kern/pmap.c \ 31 | kern/file.c \ 32 | kern/net.c \ 33 | dev/video.c \ 34 | dev/kbd.c \ 35 | dev/serial.c \ 36 | dev/pic.c \ 37 | dev/nvram.c \ 38 | dev/lapic.c \ 39 | dev/ioapic.c \ 40 | dev/pci.c \ 41 | dev/e100.c \ 42 | lib/printfmt.c \ 43 | lib/cprintf.c \ 44 | lib/sprintf.c \ 45 | lib/string.c 46 | 47 | # Build files only if they exist. 48 | KERN_SRCFILES := $(wildcard $(KERN_SRCFILES)) 49 | 50 | 51 | 52 | # Binary program images to embed within the kernel. 53 | 54 | # Kernel object files generated from C (.c) and assembly (.S) source files 55 | KERN_OBJFILES := $(patsubst %.c, $(OBJDIR)/%.o, $(KERN_SRCFILES)) 56 | KERN_OBJFILES := $(patsubst %.S, $(OBJDIR)/%.o, $(KERN_OBJFILES)) 57 | 58 | # The kernel needs to link in a few source files from the PIOS C library, 59 | # but we want to build a special "kernel version" of those source files 60 | # so they can conditionally compile based on the PIOS_KERNEL cpp symbol. 61 | # Therefore, compile the kernel versions of library sources into obj/kern. 62 | KERN_OBJFILES := $(patsubst $(OBJDIR)/lib/%, $(OBJDIR)/kern/%, $(KERN_OBJFILES)) 63 | 64 | # All binary files to be linked into the kernel will come from the objdir. 65 | KERN_BINFILES := $(patsubst %, $(OBJDIR)/%, $(KERN_BINFILES)) 66 | 67 | # Rules describing how to build kernel object files 68 | $(OBJDIR)/kern/%.o: kern/%.c 69 | @echo + cc $< 70 | @mkdir -p $(@D) 71 | $(V)$(CC) $(KERN_CFLAGS) -c -o $@ $< 72 | 73 | $(OBJDIR)/kern/%.o: kern/%.S 74 | @echo + as $< 75 | @mkdir -p $(@D) 76 | $(V)$(CC) $(KERN_CFLAGS) -c -o $@ $< 77 | 78 | $(OBJDIR)/dev/%.o: dev/%.c 79 | @echo + cc $< 80 | @mkdir -p $(@D) 81 | $(V)$(CC) $(KERN_CFLAGS) -c -o $@ $< 82 | 83 | # Compile kernel versions of C library sources into the obj/kern directory 84 | # (see the note above about KERN_OBJFILES). 85 | $(OBJDIR)/kern/%.o: lib/%.c 86 | @echo + cc $< 87 | @mkdir -p $(@D) 88 | $(V)$(CC) $(KERN_CFLAGS) -c -o $@ $< 89 | 90 | # How to link the kernel itself from its object and binary files. 91 | $(OBJDIR)/kern/kernel: $(KERN_OBJFILES) $(KERN_BINFILES) 92 | @echo + ld $@ 93 | $(V)$(LD) -o $@ $(KERN_LDFLAGS) $(KERN_OBJFILES) $(KERN_LDLIBS) \ 94 | -b binary $(KERN_BINFILES) 95 | $(V)$(OBJDUMP) -S $@ > $@.asm 96 | $(V)$(NM) -n $@ > $@.sym 97 | 98 | # How to build the kernel disk image 99 | $(OBJDIR)/kern/kernel.img: $(OBJDIR)/kern/kernel $(OBJDIR)/boot/bootblock 100 | @echo + mk $@ 101 | $(V)dd if=/dev/zero of=$(OBJDIR)/kern/kernel.img~ count=10000 2>/dev/null 102 | $(V)dd if=$(OBJDIR)/boot/bootblock of=$(OBJDIR)/kern/kernel.img~ conv=notrunc 2>/dev/null 103 | $(V)dd if=$(OBJDIR)/kern/kernel of=$(OBJDIR)/kern/kernel.img~ seek=1 conv=notrunc 2>/dev/null 104 | $(V)mv $(OBJDIR)/kern/kernel.img~ $(OBJDIR)/kern/kernel.img 105 | 106 | 107 | all: $(OBJDIR)/kern/kernel.img 108 | 109 | grub: $(OBJDIR)/pios-grub 110 | 111 | $(OBJDIR)/pios-grub: $(OBJDIR)/kern/kernel 112 | @echo + oc $@ 113 | $(V)$(OBJCOPY) --adjust-vma=0x10000000 $^ $@ 114 | 115 | # Create VMware disk image to run under VMware, VirtualBox, etc. 116 | %.vmdk: %.img 117 | qemu-img convert -f raw -O vmdk $*.img $*.vmdk 118 | 119 | # Run PIOS under VirtualBox 120 | vbox: $(OBJDIR)/kern/kernel.vmdk vbox-stop 121 | @VBoxManage storageattach PIOS --storagectl "IDE Controller" --port 0 \ 122 | --device 0 --type hdd --medium `/bin/pwd`/obj/kern/kernel.vmdk 123 | VBoxManage startvm PIOS 124 | 125 | # Stop VirtualBox's PIOS virtual machine 126 | vbox-stop: 127 | @VBoxManage controlvm PIOS poweroff >/dev/null 2>&1 || true 128 | @until VBoxManage showvminfo PIOS -machinereadable | \ 129 | grep -q 'VMState="poweroff"' ; do echo waiting; sleep 1; done 130 | @VBoxManage storageattach PIOS --storagectl "IDE Controller" --port 0 \ 131 | --device 0 --type hdd --medium none >/dev/null 2>&1 || true 132 | @VBoxManage closemedium disk `/bin/pwd`/obj/kern/kernel.vmdk \ 133 | >/dev/null 2>&1 || true 134 | -------------------------------------------------------------------------------- /kern/cons.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Main console driver for PIOS, which manages lower-level console devices 3 | * such as video (dev/video.*), keyboard (dev/kbd.*), and serial (dev/serial.*) 4 | * 5 | * Copyright (c) 2010 Yale University. 6 | * Copyright (c) 1993, 1994, 1995 Charles Hannum. 7 | * Copyright (c) 1990 The Regents of the University of California. 8 | * See section "BSD License" in the file LICENSES for licensing terms. 9 | * 10 | * This code is derived from the NetBSD pcons driver, and in turn derived 11 | * from software contributed to Berkeley by William Jolitz and Don Ahn. 12 | * Adapted for PIOS by Bryan Ford at Yale University. 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | void cons_intr(int (*proc)(void)); 30 | static void cons_putc(int c); 31 | 32 | 33 | /***** General device-independent console code *****/ 34 | // Here we manage the console input buffer, 35 | // where we stash characters received from the keyboard or serial port 36 | // whenever the corresponding interrupt occurs. 37 | 38 | #define CONSBUFSIZE 512 39 | 40 | static struct { 41 | uint8_t buf[CONSBUFSIZE]; 42 | uint32_t rpos; 43 | uint32_t wpos; 44 | } cons; 45 | 46 | 47 | // called by device interrupt routines to feed input characters 48 | // into the circular console input buffer. 49 | void 50 | cons_intr(int (*proc)(void)) 51 | { 52 | int c; 53 | 54 | while ((c = (*proc)()) != -1) { 55 | if (c == 0) 56 | continue; 57 | cons.buf[cons.wpos++] = c; 58 | if (cons.wpos == CONSBUFSIZE) 59 | cons.wpos = 0; 60 | } 61 | } 62 | 63 | // return the next input character from the console, or 0 if none waiting 64 | int 65 | cons_getc(void) 66 | { 67 | int c; 68 | 69 | // poll for any pending input characters, 70 | // so that this function works even when interrupts are disabled 71 | // (e.g., when called from the kernel monitor). 72 | serial_intr(); 73 | kbd_intr(); 74 | 75 | // grab the next character from the input buffer. 76 | if (cons.rpos != cons.wpos) { 77 | c = cons.buf[cons.rpos++]; 78 | if (cons.rpos == CONSBUFSIZE) 79 | cons.rpos = 0; 80 | return c; 81 | } 82 | return 0; 83 | } 84 | 85 | // output a character to the console 86 | static void 87 | cons_putc(int c) 88 | { 89 | serial_putc(c); 90 | video_putc(c); 91 | } 92 | 93 | // initialize the console devices 94 | void 95 | cons_init(void) 96 | { 97 | if (!cpu_onboot()) // only do once, on the boot CPU 98 | return; 99 | 100 | video_init(); 101 | kbd_init(); 102 | serial_init(); 103 | 104 | if (!serial_exists) 105 | warn("Serial port does not exist!\n"); 106 | } 107 | 108 | 109 | // `High'-level console I/O. Used by readline and cprintf. 110 | void 111 | cputs(const char *str) 112 | { 113 | char ch; 114 | while (*str) 115 | cons_putc(*str++); 116 | } 117 | 118 | 119 | -------------------------------------------------------------------------------- /kern/cons.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Main console driver for PIOS, which manages lower-level console devices 3 | * such as video (dev/video.*), keyboard (dev/kbd.*), and serial (dev/serial.*) 4 | * 5 | * Copyright (c) 2010 Yale University. 6 | * Copyright (c) 1993, 1994, 1995 Charles Hannum. 7 | * Copyright (c) 1990 The Regents of the University of California. 8 | * See section "BSD License" in the file LICENSES for licensing terms. 9 | * 10 | * This code is derived from the NetBSD pcons driver, and in turn derived 11 | * from software contributed to Berkeley by William Jolitz and Don Ahn. 12 | * Adapted for PIOS by Bryan Ford at Yale University. 13 | */ 14 | 15 | #ifndef PIOS_KERN_CONSOLE_H_ 16 | #define PIOS_KERN_CONSOLE_H_ 17 | #ifndef PIOS_KERNEL 18 | # error "This is a kernel header; user programs should not #include it" 19 | #endif 20 | 21 | #include 22 | 23 | 24 | #define DEBUG_TRACEFRAMES 10 25 | 26 | struct iocons; 27 | 28 | 29 | 30 | void cons_init(void); 31 | 32 | // Called by device interrupt routines to feed input characters 33 | // into the circular console input buffer. 34 | // Device-specific code supplies 'proc', which polls for a character 35 | // and returns that character or 0 if no more available from device. 36 | void cons_intr(int (*proc)(void)); 37 | 38 | // Called by init() when the kernel is ready to receive console interrupts. 39 | void cons_intenable(void); 40 | 41 | 42 | #endif /* PIOS_KERN_CONSOLE_H_ */ 43 | -------------------------------------------------------------------------------- /kern/cpu.c: -------------------------------------------------------------------------------- 1 | /* 2 | * CPU setup and management of key protected-mode data structures, 3 | * such as global descriptor table (GDT) and task state segment (TSS). 4 | * 5 | * Copyright (C) 2010 Yale University. 6 | * See section "MIT License" in the file LICENSES for licensing terms. 7 | * 8 | * Primary author: Bryan Ford 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | 19 | 20 | cpu cpu_boot = { 21 | 22 | // Global descriptor table for bootstrap CPU. 23 | // The GDTs for other CPUs are copied from this and fixed up. 24 | // 25 | // The kernel and user segments are identical except for the DPL. 26 | // To load the SS register, the CPL must equal the DPL. Thus, 27 | // we must duplicate the segments for the user and the kernel. 28 | // 29 | // The only descriptor that differs across CPUs is the TSS descriptor. 30 | // 31 | gdt: { 32 | // 0x0 - unused (always faults: for trapping NULL far pointers) 33 | [0] = SEGDESC_NULL, 34 | 35 | // 0x08 - kernel code segment 36 | [CPU_GDT_KCODE >> 3] = SEGDESC32(1, STA_X | STA_R, 0x0, 37 | 0xffffffff, 0), 38 | 39 | // 0x10 - kernel data segment 40 | [CPU_GDT_KDATA >> 3] = SEGDESC32(1, STA_W, 0x0, 41 | 0xffffffff, 0), 42 | }, 43 | 44 | magic: CPU_MAGIC 45 | }; 46 | 47 | 48 | void cpu_init() 49 | { 50 | cpu *c = cpu_cur(); 51 | 52 | // Load the GDT 53 | struct pseudodesc gdt_pd = { 54 | sizeof(c->gdt) - 1, (uint32_t) c->gdt }; 55 | asm volatile("lgdt %0" : : "m" (gdt_pd)); 56 | 57 | // Reload all segment registers. 58 | asm volatile("movw %%ax,%%gs" :: "a" (CPU_GDT_UDATA|3)); 59 | asm volatile("movw %%ax,%%fs" :: "a" (CPU_GDT_UDATA|3)); 60 | asm volatile("movw %%ax,%%es" :: "a" (CPU_GDT_KDATA)); 61 | asm volatile("movw %%ax,%%ds" :: "a" (CPU_GDT_KDATA)); 62 | asm volatile("movw %%ax,%%ss" :: "a" (CPU_GDT_KDATA)); 63 | asm volatile("ljmp %0,$1f\n 1:\n" :: "i" (CPU_GDT_KCODE)); // reload CS 64 | 65 | // We don't need an LDT. 66 | asm volatile("lldt %%ax" :: "a" (0)); 67 | } 68 | 69 | 70 | -------------------------------------------------------------------------------- /kern/cpu.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Per-CPU kernel state structures. 3 | * 4 | * Copyright (C) 2010 Yale University. 5 | * See section "MIT License" in the file LICENSES for licensing terms. 6 | * 7 | * Primary author: Bryan Ford 8 | */ 9 | #ifndef PIOS_KERN_SEG_H 10 | #define PIOS_KERN_SEG_H 11 | #ifndef PIOS_KERNEL 12 | # error "This is a kernel header; user programs should not #include it" 13 | #endif 14 | 15 | 16 | // Global segment descriptor numbers used by the kernel 17 | #define CPU_GDT_NULL 0x00 // null descriptor (required by x86 processor) 18 | #define CPU_GDT_KCODE 0x08 // kernel text 19 | #define CPU_GDT_KDATA 0x10 // kernel data 20 | #define CPU_GDT_UCODE 0x18 // user text 21 | #define CPU_GDT_UDATA 0x20 // user data 22 | #define CPU_GDT_UDTLS 0x28 // user thread local storage data segment 23 | #define CPU_GDT_TSS 0x30 // task state segment 24 | #define CPU_GDT_NDESC 7 // number of GDT entries used, including null 25 | 26 | 27 | #ifndef __ASSEMBLER__ 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | 36 | // Per-CPU kernel state structure. 37 | // Exactly one page (4096 bytes) in size. 38 | typedef struct cpu { 39 | // Since the x86 processor finds the TSS from a descriptor in the GDT, 40 | // each processor needs its own TSS segment descriptor in some GDT. 41 | // We could have a single, "global" GDT with multiple TSS descriptors, 42 | // but it's easier just to have a separate fixed-size GDT per CPU. 43 | segdesc gdt[CPU_GDT_NDESC]; 44 | 45 | // Each CPU needs its own TSS, 46 | // because when the processor switches from lower to higher privilege, 47 | // it loads a new stack pointer (ESP) and stack segment (SS) 48 | // for the higher privilege level from this task state structure. 49 | taskstate tss; 50 | 51 | // When non-NULL, all traps get diverted to this handler. 52 | gcc_noreturn void (*recover)(trapframe *tf, void *recoverdata); 53 | void *recoverdata; 54 | 55 | // Magic verification tag (CPU_MAGIC) to help detect corruption, 56 | // e.g., if the CPU's ring 0 stack overflows down onto the cpu struct. 57 | uint32_t magic; 58 | 59 | // Low end (growth limit) of the kernel stack. 60 | char kstacklo[1]; 61 | 62 | // High end (starting point) of the kernel stack. 63 | char gcc_aligned(PAGESIZE) kstackhi[0]; 64 | } cpu; 65 | 66 | #define CPU_MAGIC 0x98765432 // cpu.magic should always = this 67 | 68 | 69 | // We have one statically-allocated cpu struct representing the boot CPU; 70 | // others get chained onto this via cpu_boot.next as we find them. 71 | extern cpu cpu_boot; 72 | 73 | #define cpu_disabled(c) 0 74 | 75 | // Find the CPU struct representing the current CPU. 76 | // It always resides at the bottom of the page containing the CPU's stack. 77 | static inline cpu * 78 | cpu_cur() { 79 | cpu *c = (cpu*)ROUNDDOWN(read_esp(), PAGESIZE); 80 | assert(c->magic == CPU_MAGIC); 81 | return c; 82 | } 83 | 84 | // Returns true if we're running on the bootstrap CPU. 85 | static inline int 86 | cpu_onboot() { 87 | return cpu_cur() == &cpu_boot; 88 | } 89 | 90 | 91 | // Set up the current CPU's private register state such as GDT and TSS. 92 | // Assumes the cpu struct for this CPU is basically initialized 93 | // and that we're running on the cpu's correct kernel stack. 94 | void cpu_init(void); 95 | 96 | // Allocate an additional cpu struct representing a non-bootstrap processor, 97 | // and chain it onto the list of all CPUs. 98 | cpu *cpu_alloc(void); 99 | 100 | // Get any additional processors booted up and running. 101 | void cpu_bootothers(void); 102 | 103 | #endif // ! __ASSEMBLER__ 104 | 105 | #endif // PIOS_KERN_CPU_H 106 | -------------------------------------------------------------------------------- /kern/debug.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Kernel debugging support. 3 | * Called throughout the kernel, especially by assert() macro. 4 | * 5 | * Copyright (C) 2010 Yale University. 6 | * See section "MIT License" in the file LICENSES for licensing terms. 7 | * 8 | * Primary author: Bryan Ford 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | 21 | // Variable panicstr contains argument to first call to panic; used as flag 22 | // to indicate that the kernel has already called panic and avoid recursion. 23 | static const char *panicstr; 24 | 25 | // Panic is called on unresolvable fatal errors. 26 | // It prints "panic: mesg", and then enters the kernel monitor. 27 | void 28 | debug_panic(const char *file, int line, const char *fmt,...) 29 | { 30 | va_list ap; 31 | int i; 32 | 33 | // Avoid infinite recursion if we're panicking from kernel mode. 34 | if ((read_cs() & 3) == 0) { 35 | if (panicstr) 36 | goto dead; 37 | panicstr = fmt; 38 | } 39 | 40 | // First print the requested message 41 | va_start(ap, fmt); 42 | cprintf("kernel panic at %s:%d: ", file, line); 43 | vcprintf(fmt, ap); 44 | cprintf("\n"); 45 | va_end(ap); 46 | 47 | // Then print a backtrace of the kernel call chain 48 | uint32_t eips[DEBUG_TRACEFRAMES]; 49 | debug_trace(read_ebp(), eips); 50 | for (i = 0; i < DEBUG_TRACEFRAMES && eips[i] != 0; i++) 51 | cprintf(" from %08x\n", eips[i]); 52 | 53 | dead: 54 | done(); // enter infinite loop (see kern/init.c) 55 | } 56 | 57 | /* like panic, but don't */ 58 | void 59 | debug_warn(const char *file, int line, const char *fmt,...) 60 | { 61 | va_list ap; 62 | 63 | va_start(ap, fmt); 64 | cprintf("kernel warning at %s:%d: ", file, line); 65 | vcprintf(fmt, ap); 66 | cprintf("\n"); 67 | va_end(ap); 68 | } 69 | 70 | // Record the current call stack in eips[] by following the %ebp chain. 71 | void gcc_noinline 72 | debug_trace(uint32_t ebp, uint32_t eips[DEBUG_TRACEFRAMES]) 73 | { 74 | panic("debug_trace not implemented"); 75 | } 76 | 77 | 78 | static void gcc_noinline f3(int r, uint32_t *e) { debug_trace(read_ebp(), e); } 79 | static void gcc_noinline f2(int r, uint32_t *e) { r & 2 ? f3(r,e) : f3(r,e); } 80 | static void gcc_noinline f1(int r, uint32_t *e) { r & 1 ? f2(r,e) : f2(r,e); } 81 | 82 | // Test the backtrace implementation for correct operation 83 | void 84 | debug_check(void) 85 | { 86 | uint32_t eips[4][DEBUG_TRACEFRAMES]; 87 | int r, i; 88 | 89 | // produce several related backtraces... 90 | for (i = 0; i < 4; i++) 91 | f1(i, eips[i]); 92 | 93 | // ...and make sure they come out correctly. 94 | for (r = 0; r < 4; r++) 95 | for (i = 0; i < DEBUG_TRACEFRAMES; i++) { 96 | assert((eips[r][i] != 0) == (i < 5)); 97 | if (i >= 2) 98 | assert(eips[r][i] == eips[0][i]); 99 | } 100 | assert(eips[0][0] == eips[1][0]); 101 | assert(eips[2][0] == eips[3][0]); 102 | assert(eips[1][0] != eips[2][0]); 103 | assert(eips[0][1] == eips[2][1]); 104 | assert(eips[1][1] == eips[3][1]); 105 | assert(eips[0][1] != eips[1][1]); 106 | 107 | cprintf("debug_check() succeeded!\n"); 108 | } 109 | 110 | -------------------------------------------------------------------------------- /kern/debug.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Kernel debugging support. 3 | * 4 | * Copyright (C) 2010 Yale University. 5 | * See section "MIT License" in the file LICENSES for licensing terms. 6 | * 7 | * Primary author: Bryan Ford 8 | */ 9 | 10 | #ifndef PIOS_KERN_DEBUG_H_ 11 | #define PIOS_KERN_DEBUG_H_ 12 | #ifndef PIOS_KERNEL 13 | # error "This is a kernel header; user programs should not #include it" 14 | #endif 15 | 16 | #include 17 | #include 18 | 19 | 20 | #define DEBUG_TRACEFRAMES 10 21 | 22 | 23 | void debug_warn(const char*, int, const char*, ...); 24 | void debug_panic(const char*, int, const char*, ...) gcc_noreturn; 25 | void debug_trace(uint32_t ebp, uint32_t eips[DEBUG_TRACEFRAMES]); 26 | void debug_check(void); 27 | 28 | #endif /* PIOS_KERN_DEBUG_H_ */ 29 | -------------------------------------------------------------------------------- /kern/entry.S: -------------------------------------------------------------------------------- 1 | /* 2 | * Kernel entrypoint and stack setup. 3 | * 4 | * Copyright (C) 1997 Massachusetts Institute of Technology 5 | * See section "MIT License" in the file LICENSES for licensing terms. 6 | * 7 | * Derived from the MIT Exokernel and JOS. 8 | * Adapted for PIOS by Bryan Ford at Yale University. 9 | */ 10 | 11 | 12 | 13 | #define MULTIBOOT_PAGE_ALIGN (1<<0) 14 | #define MULTIBOOT_MEMORY_INFO (1<<1) 15 | #define MULTIBOOT_HEADER_MAGIC (0x1BADB002) 16 | #define MULTIBOOT_HEADER_FLAGS (MULTIBOOT_MEMORY_INFO | MULTIBOOT_PAGE_ALIGN) 17 | #define CHECKSUM (-(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)) 18 | 19 | ################################################################### 20 | # entry point 21 | ################################################################### 22 | 23 | .text 24 | 25 | # The Multiboot header 26 | .align 4 27 | .long MULTIBOOT_HEADER_MAGIC 28 | .long MULTIBOOT_HEADER_FLAGS 29 | .long CHECKSUM 30 | 31 | .globl start,_start 32 | start: _start: 33 | movw $0x1234,0x472 # warm boot BIOS flag 34 | 35 | # Clear the frame pointer register (EBP) 36 | # so that once we get into debugging C code, 37 | # stack backtraces will be terminated properly. 38 | movl $0x0,%ebp # nuke frame pointer 39 | 40 | # Set the stack pointer 41 | movl $(cpu_boot+4096),%esp 42 | 43 | # now to C code 44 | call init 45 | 46 | # Should never get here, but in case we do, just spin. 47 | spin: jmp spin 48 | 49 | 50 | -------------------------------------------------------------------------------- /kern/init.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Kernel initialization. 3 | * 4 | * Copyright (C) 1997 Massachusetts Institute of Technology 5 | * See section "MIT License" in the file LICENSES for licensing terms. 6 | * 7 | * Derived from the MIT Exokernel and JOS. 8 | * Adapted for PIOS by Bryan Ford at Yale University. 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | 24 | 25 | // User-mode stack for user(), below, to run on. 26 | static char gcc_aligned(16) user_stack[PAGESIZE]; 27 | 28 | #define ROOTEXE_START _binary_obj_user_sh_start 29 | 30 | // Lab 3: ELF executable containing root process, linked into the kernel 31 | #ifndef ROOTEXE_START 32 | #endif 33 | extern char ROOTEXE_START[]; 34 | 35 | 36 | // Called first from entry.S on the bootstrap processor, 37 | // and later from boot/bootother.S on all other processors. 38 | // As a rule, "init" functions in PIOS are called once on EACH processor. 39 | void 40 | init(void) 41 | { 42 | extern char start[], edata[], end[]; 43 | 44 | // Before anything else, complete the ELF loading process. 45 | // Clear all uninitialized global data (BSS) in our program, 46 | // ensuring that all static/global variables start out zero. 47 | if (cpu_onboot()) 48 | memset(edata, 0, end - edata); 49 | 50 | // Initialize the console. 51 | // Can't call cprintf until after we do this! 52 | cons_init(); 53 | 54 | // Lab 1: test cprintf and debug_trace 55 | cprintf("1234 decimal is %o octal!\n", 1234); 56 | debug_check(); 57 | 58 | // Initialize and load the bootstrap CPU's GDT, TSS, and IDT. 59 | cpu_init(); 60 | trap_init(); 61 | 62 | // Physical memory detection/initialization. 63 | // Can't call mem_alloc until after we do this! 64 | mem_init(); 65 | 66 | 67 | // Lab 1: change this so it enters user() in user mode, 68 | // running on the user_stack declared above, 69 | // instead of just calling user() directly. 70 | user(); 71 | } 72 | 73 | // This is the first function that gets run in user mode (ring 3). 74 | // It acts as PIOS's "root process", 75 | // of which all other processes are descendants. 76 | void 77 | user() 78 | { 79 | cprintf("in user()\n"); 80 | assert(read_esp() > (uint32_t) &user_stack[0]); 81 | assert(read_esp() < (uint32_t) &user_stack[sizeof(user_stack)]); 82 | 83 | // Check that we're in user mode and can handle traps from there. 84 | trap_check_user(); 85 | 86 | done(); 87 | } 88 | 89 | // This is a function that we call when the kernel is "done" - 90 | // it just puts the processor into an infinite loop. 91 | // We make this a function so that we can set a breakpoints on it. 92 | // Our grade scripts use this breakpoint to know when to stop QEMU. 93 | void gcc_noreturn 94 | done() 95 | { 96 | while (1) 97 | ; // just spin 98 | } 99 | 100 | -------------------------------------------------------------------------------- /kern/init.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Kernel initialization. 3 | * 4 | * Copyright (C) 1997 Massachusetts Institute of Technology 5 | * See section "MIT License" in the file LICENSES for licensing terms. 6 | * 7 | * Derived from the MIT Exokernel and JOS. 8 | * Adapted for PIOS by Bryan Ford at Yale University. 9 | */ 10 | 11 | #ifndef PIOS_KERN_INIT_H 12 | #define PIOS_KERN_INIT_H 13 | #ifndef PIOS_KERNEL 14 | # error "This is a kernel header; user programs should not #include it" 15 | #endif 16 | 17 | #include 18 | 19 | 20 | // Called on each processor to initialize the kernel. 21 | void init(void); 22 | 23 | // First function run in user mode (only on one processor) 24 | void user(void); 25 | 26 | // Called when there is no more work left to do in the system. 27 | // The grading scripts trap calls to this to know when to stop. 28 | void done(void) gcc_noreturn; 29 | 30 | 31 | #endif /* !PIOS_KERN_INIT_H */ 32 | -------------------------------------------------------------------------------- /kern/mem.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Physical memory management. 3 | * 4 | * Copyright (C) 1997 Massachusetts Institute of Technology 5 | * See section "MIT License" in the file LICENSES for licensing terms. 6 | * 7 | * Derived from the MIT Exokernel and JOS. 8 | * Adapted for PIOS by Bryan Ford at Yale University. 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | 19 | #include 20 | 21 | 22 | size_t mem_max; // Maximum physical address 23 | size_t mem_npage; // Total number of physical memory pages 24 | 25 | pageinfo *mem_pageinfo; // Metadata array indexed by page number 26 | 27 | pageinfo *mem_freelist; // Start of free page list 28 | 29 | 30 | void mem_check(void); 31 | 32 | void 33 | mem_init(void) 34 | { 35 | if (!cpu_onboot()) // only do once, on the boot CPU 36 | return; 37 | 38 | // Determine how much base (<640K) and extended (>1MB) memory 39 | // is available in the system (in bytes), 40 | // by reading the PC's BIOS-managed nonvolatile RAM (NVRAM). 41 | // The NVRAM tells us how many kilobytes there are. 42 | // Since the count is 16 bits, this gives us up to 64MB of RAM; 43 | // additional RAM beyond that would have to be detected another way. 44 | size_t basemem = ROUNDDOWN(nvram_read16(NVRAM_BASELO)*1024, PAGESIZE); 45 | size_t extmem = ROUNDDOWN(nvram_read16(NVRAM_EXTLO)*1024, PAGESIZE); 46 | 47 | warn("Assuming we have 1GB of memory!"); 48 | extmem = 1024*1024*1024 - MEM_EXT; // assume 1GB total memory 49 | 50 | // The maximum physical address is the top of extended memory. 51 | mem_max = MEM_EXT + extmem; 52 | 53 | // Compute the total number of physical pages (including I/O holes) 54 | mem_npage = mem_max / PAGESIZE; 55 | 56 | cprintf("Physical memory: %dK available, ", (int)(mem_max/1024)); 57 | cprintf("base = %dK, extended = %dK\n", 58 | (int)(basemem/1024), (int)(extmem/1024)); 59 | 60 | 61 | // Insert code here to: 62 | // (1) allocate physical memory for the mem_pageinfo array, 63 | // making it big enough to hold mem_npage entries. 64 | // (2) add all pageinfo structs in the array representing 65 | // available memory that is not in use for other purposes. 66 | // 67 | // For step (2), here is some incomplete/incorrect example code 68 | // that simply marks all mem_npage pages as free. 69 | // Which memory is actually free? 70 | // 1) Reserve page 0 for the real-mode IDT and BIOS structures 71 | // (do not allow this page to be used for anything else). 72 | // 2) Reserve page 1 for the AP bootstrap code (boot/bootother.S). 73 | // 3) Mark the rest of base memory as free. 74 | // 4) Then comes the IO hole [MEM_IO, MEM_EXT). 75 | // Mark it as in-use so that it can never be allocated. 76 | // 5) Then extended memory [MEM_EXT, ...). 77 | // Some of it is in use, some is free. 78 | // Which pages hold the kernel and the pageinfo array? 79 | // Hint: the linker places the kernel (see start and end above), 80 | // but YOU decide where to place the pageinfo array. 81 | // Change the code to reflect this. 82 | pageinfo **freetail = &mem_freelist; 83 | int i; 84 | for (i = 0; i < mem_npage; i++) { 85 | // A free page has no references to it. 86 | mem_pageinfo[i].refcount = 0; 87 | 88 | // Add the page to the end of the free list. 89 | *freetail = &mem_pageinfo[i]; 90 | freetail = &mem_pageinfo[i].free_next; 91 | } 92 | *freetail = NULL; // null-terminate the freelist 93 | 94 | // ...and remove this when you're ready. 95 | panic("mem_init() not implemented"); 96 | 97 | // Check to make sure the page allocator seems to work correctly. 98 | mem_check(); 99 | } 100 | 101 | // 102 | // Allocates a physical page from the page free list. 103 | // Does NOT set the contents of the physical page to zero - 104 | // the caller must do that if necessary. 105 | // 106 | // RETURNS 107 | // - a pointer to the page's pageinfo struct if successful 108 | // - NULL if no available physical pages. 109 | // 110 | // Hint: pi->refs should not be incremented 111 | // Hint: be sure to use proper mutual exclusion for multiprocessor operation. 112 | pageinfo * 113 | mem_alloc(void) 114 | { 115 | // Fill this function in 116 | // Fill this function in. 117 | panic("mem_alloc not implemented."); 118 | } 119 | 120 | // 121 | // Return a page to the free list, given its pageinfo pointer. 122 | // (This function should only be called when pp->pp_ref reaches 0.) 123 | // 124 | void 125 | mem_free(pageinfo *pi) 126 | { 127 | // Fill this function in. 128 | panic("mem_free not implemented."); 129 | } 130 | 131 | // 132 | // Check the physical page allocator (mem_alloc(), mem_free()) 133 | // for correct operation after initialization via mem_init(). 134 | // 135 | void 136 | mem_check() 137 | { 138 | pageinfo *pp, *pp0, *pp1, *pp2; 139 | pageinfo *fl; 140 | int i; 141 | 142 | // if there's a page that shouldn't be on 143 | // the free list, try to make sure it 144 | // eventually causes trouble. 145 | int freepages = 0; 146 | for (pp = mem_freelist; pp != 0; pp = pp->free_next) { 147 | memset(mem_pi2ptr(pp), 0x97, 128); 148 | freepages++; 149 | } 150 | cprintf("mem_check: %d free pages\n", freepages); 151 | assert(freepages < mem_npage); // can't have more free than total! 152 | assert(freepages > 16000); // make sure it's in the right ballpark 153 | 154 | // should be able to allocate three pages 155 | pp0 = pp1 = pp2 = 0; 156 | pp0 = mem_alloc(); assert(pp0 != 0); 157 | pp1 = mem_alloc(); assert(pp1 != 0); 158 | pp2 = mem_alloc(); assert(pp2 != 0); 159 | 160 | assert(pp0); 161 | assert(pp1 && pp1 != pp0); 162 | assert(pp2 && pp2 != pp1 && pp2 != pp0); 163 | assert(mem_pi2phys(pp0) < mem_npage*PAGESIZE); 164 | assert(mem_pi2phys(pp1) < mem_npage*PAGESIZE); 165 | assert(mem_pi2phys(pp2) < mem_npage*PAGESIZE); 166 | 167 | // temporarily steal the rest of the free pages 168 | fl = mem_freelist; 169 | mem_freelist = 0; 170 | 171 | // should be no free memory 172 | assert(mem_alloc() == 0); 173 | 174 | // free and re-allocate? 175 | mem_free(pp0); 176 | mem_free(pp1); 177 | mem_free(pp2); 178 | pp0 = pp1 = pp2 = 0; 179 | pp0 = mem_alloc(); assert(pp0 != 0); 180 | pp1 = mem_alloc(); assert(pp1 != 0); 181 | pp2 = mem_alloc(); assert(pp2 != 0); 182 | assert(pp0); 183 | assert(pp1 && pp1 != pp0); 184 | assert(pp2 && pp2 != pp1 && pp2 != pp0); 185 | assert(mem_alloc() == 0); 186 | 187 | // give free list back 188 | mem_freelist = fl; 189 | 190 | // free the pages we took 191 | mem_free(pp0); 192 | mem_free(pp1); 193 | mem_free(pp2); 194 | 195 | cprintf("mem_check() succeeded!\n"); 196 | } 197 | 198 | -------------------------------------------------------------------------------- /kern/mem.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Physical memory management definitions. 3 | * 4 | * Copyright (C) 1997 Massachusetts Institute of Technology 5 | * See section "MIT License" in the file LICENSES for licensing terms. 6 | * 7 | * Derived from the MIT Exokernel and JOS. 8 | * Adapted for PIOS by Bryan Ford at Yale University. 9 | */ 10 | 11 | #ifndef PIOS_KERN_MEM_H 12 | #define PIOS_KERN_MEM_H 13 | #ifndef PIOS_KERNEL 14 | # error "This is a kernel header; user programs should not #include it" 15 | #endif 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | 23 | // At physical address MEM_IO (640K) there is a 384K hole for I/O. 24 | // The hole ends at physical address MEM_EXT, where extended memory begins. 25 | #define MEM_IO 0x0A0000 26 | #define MEM_EXT 0x100000 27 | 28 | 29 | // Given a physical address, 30 | // return a C pointer the kernel can use to access it. 31 | // This macro does nothing in PIOS because physical memory 32 | // is mapped into the kernel's virtual address space at address 0, 33 | // but this is not the case for many other systems such as JOS or Linux, 34 | // which must do some translation here (usually just adding an offset). 35 | #define mem_ptr(physaddr) ((void*)(physaddr)) 36 | 37 | // The converse to the above: given a C pointer, return a physical address. 38 | #define mem_phys(ptr) ((uint32_t)(ptr)) 39 | 40 | 41 | // A pageinfo struct holds metadata on how a particular physical page is used. 42 | // On boot we allocate a big array of pageinfo structs, one per physical page. 43 | // This could be a union instead of a struct, 44 | // since only one member is used for a given page state (free, allocated) - 45 | // but that might make debugging a bit more challenging. 46 | typedef struct pageinfo { 47 | struct pageinfo *free_next; // Next page number on free list 48 | int32_t refcount; // Reference count on allocated pages 49 | } pageinfo; 50 | 51 | 52 | // The pmem module sets up the following globals during mem_init(). 53 | extern size_t mem_max; // Maximum physical address 54 | extern size_t mem_npage; // Total number of physical memory pages 55 | extern pageinfo *mem_pageinfo; // Metadata array indexed by page number 56 | 57 | // Convert between pageinfo pointers, page indexes, and physical page addresses 58 | #define mem_phys2pi(phys) (&mem_pageinfo[(phys)/PAGESIZE]) 59 | #define mem_pi2phys(pi) (((pi)-mem_pageinfo) * PAGESIZE) 60 | #define mem_ptr2pi(ptr) (mem_phys2pi(mem_phys(ptr))) 61 | #define mem_pi2ptr(pi) (mem_ptr(mem_pi2phys(pi))) 62 | 63 | 64 | // The linker defines these special symbols to mark the start and end of 65 | // the program's entire linker-arranged memory region, 66 | // including the program's code, data, and bss sections. 67 | // Use these to avoid treating kernel code/data pages as free memory! 68 | extern char start[], end[]; 69 | 70 | 71 | // Detect available physical memory and initialize the mem_pageinfo array. 72 | void mem_init(void); 73 | 74 | // Allocate a physical page and return a pointer to its pageinfo struct. 75 | // Returns NULL if no more physical pages are available. 76 | pageinfo *mem_alloc(void); 77 | 78 | // Return a physical page to the free list. 79 | void mem_free(pageinfo *pi); 80 | 81 | 82 | 83 | // Atomically increment the reference count on a page. 84 | static gcc_inline void 85 | mem_incref(pageinfo *pi) 86 | { 87 | assert(pi > &mem_pageinfo[1] && pi < &mem_pageinfo[mem_npage]); 88 | assert(pi < mem_ptr2pi(start) || pi > mem_ptr2pi(end-1)); 89 | 90 | lockadd(&pi->refcount, 1); 91 | } 92 | 93 | // Atomically decrement the reference count on a page, 94 | // freeing the page with the provided function if there are no more refs. 95 | static gcc_inline void 96 | mem_decref(pageinfo* pi, void (*freefun)(pageinfo *pi)) 97 | { 98 | assert(pi > &mem_pageinfo[1] && pi < &mem_pageinfo[mem_npage]); 99 | assert(pi < mem_ptr2pi(start) || pi > mem_ptr2pi(end-1)); 100 | 101 | if (lockaddz(&pi->refcount, -1)) 102 | freefun(pi); 103 | assert(pi->refcount >= 0); 104 | } 105 | 106 | 107 | #endif /* !PIOS_KERN_MEM_H */ 108 | -------------------------------------------------------------------------------- /kern/trap.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Processor trap handling. 3 | * 4 | * Copyright (C) 1997 Massachusetts Institute of Technology 5 | * See section "MIT License" in the file LICENSES for licensing terms. 6 | * 7 | * Derived from the MIT Exokernel and JOS. 8 | * Adapted for PIOS by Bryan Ford at Yale University. 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | 21 | // Interrupt descriptor table. Must be built at run time because 22 | // shifted function addresses can't be represented in relocation records. 23 | static struct gatedesc idt[256]; 24 | 25 | // This "pseudo-descriptor" is needed only by the LIDT instruction, 26 | // to specify both the size and address of th IDT at once. 27 | static struct pseudodesc idt_pd = { 28 | sizeof(idt) - 1, (uint32_t) idt 29 | }; 30 | 31 | 32 | static void 33 | trap_init_idt(void) 34 | { 35 | extern segdesc gdt[]; 36 | 37 | panic("trap_init() not implemented."); 38 | } 39 | 40 | void 41 | trap_init(void) 42 | { 43 | // The first time we get called on the bootstrap processor, 44 | // initialize the IDT. Other CPUs will share the same IDT. 45 | if (cpu_onboot()) 46 | trap_init_idt(); 47 | 48 | // Load the IDT into this processor's IDT register. 49 | asm volatile("lidt %0" : : "m" (idt_pd)); 50 | 51 | // Check for the correct IDT and trap handler operation. 52 | if (cpu_onboot()) 53 | trap_check_kernel(); 54 | } 55 | 56 | const char *trap_name(int trapno) 57 | { 58 | static const char * const excnames[] = { 59 | "Divide error", 60 | "Debug", 61 | "Non-Maskable Interrupt", 62 | "Breakpoint", 63 | "Overflow", 64 | "BOUND Range Exceeded", 65 | "Invalid Opcode", 66 | "Device Not Available", 67 | "Double Fault", 68 | "Coprocessor Segment Overrun", 69 | "Invalid TSS", 70 | "Segment Not Present", 71 | "Stack Fault", 72 | "General Protection", 73 | "Page Fault", 74 | "(unknown trap)", 75 | "x87 FPU Floating-Point Error", 76 | "Alignment Check", 77 | "Machine-Check", 78 | "SIMD Floating-Point Exception" 79 | }; 80 | 81 | if (trapno < sizeof(excnames)/sizeof(excnames[0])) 82 | return excnames[trapno]; 83 | return "(unknown trap)"; 84 | } 85 | 86 | void 87 | trap_print_regs(pushregs *regs) 88 | { 89 | cprintf(" edi 0x%08x\n", regs->edi); 90 | cprintf(" esi 0x%08x\n", regs->esi); 91 | cprintf(" ebp 0x%08x\n", regs->ebp); 92 | // cprintf(" oesp 0x%08x\n", regs->oesp); don't print - useless 93 | cprintf(" ebx 0x%08x\n", regs->ebx); 94 | cprintf(" edx 0x%08x\n", regs->edx); 95 | cprintf(" ecx 0x%08x\n", regs->ecx); 96 | cprintf(" eax 0x%08x\n", regs->eax); 97 | } 98 | 99 | void 100 | trap_print(trapframe *tf) 101 | { 102 | cprintf("TRAP frame at %p\n", tf); 103 | trap_print_regs(&tf->regs); 104 | cprintf(" es 0x----%04x\n", tf->es); 105 | cprintf(" ds 0x----%04x\n", tf->ds); 106 | cprintf(" trap 0x%08x %s\n", tf->trapno, trap_name(tf->trapno)); 107 | cprintf(" err 0x%08x\n", tf->err); 108 | cprintf(" eip 0x%08x\n", tf->eip); 109 | cprintf(" cs 0x----%04x\n", tf->cs); 110 | cprintf(" flag 0x%08x\n", tf->eflags); 111 | cprintf(" esp 0x%08x\n", tf->esp); 112 | cprintf(" ss 0x----%04x\n", tf->ss); 113 | } 114 | 115 | void gcc_noreturn 116 | trap(trapframe *tf) 117 | { 118 | // The user-level environment may have set the DF flag, 119 | // and some versions of GCC rely on DF being clear. 120 | asm volatile("cld" ::: "cc"); 121 | 122 | // If this trap was anticipated, just use the designated handler. 123 | cpu *c = cpu_cur(); 124 | if (c->recover) 125 | c->recover(tf, c->recoverdata); 126 | 127 | trap_print(tf); 128 | panic("unhandled trap"); 129 | } 130 | 131 | 132 | // Helper function for trap_check_recover(), below: 133 | // handles "anticipated" traps by simply resuming at a new EIP. 134 | static void gcc_noreturn 135 | trap_check_recover(trapframe *tf, void *recoverdata) 136 | { 137 | trap_check_args *args = recoverdata; 138 | tf->eip = (uint32_t) args->reip; // Use recovery EIP on return 139 | args->trapno = tf->trapno; // Return trap number 140 | trap_return(tf); 141 | } 142 | 143 | // Check for correct handling of traps from kernel mode. 144 | // Called on the boot CPU after trap_init() and trap_setup(). 145 | void 146 | trap_check_kernel(void) 147 | { 148 | assert((read_cs() & 3) == 0); // better be in kernel mode! 149 | 150 | cpu *c = cpu_cur(); 151 | c->recover = trap_check_recover; 152 | trap_check(&c->recoverdata); 153 | c->recover = NULL; // No more mr. nice-guy; traps are real again 154 | 155 | cprintf("trap_check_kernel() succeeded!\n"); 156 | } 157 | 158 | // Check for correct handling of traps from user mode. 159 | // Called from user() in kern/init.c, only in lab 1. 160 | // We assume the "current cpu" is always the boot cpu; 161 | // this true only because lab 1 doesn't start any other CPUs. 162 | void 163 | trap_check_user(void) 164 | { 165 | assert((read_cs() & 3) == 3); // better be in user mode! 166 | 167 | cpu *c = &cpu_boot; // cpu_cur doesn't work from user mode! 168 | c->recover = trap_check_recover; 169 | trap_check(&c->recoverdata); 170 | c->recover = NULL; // No more mr. nice-guy; traps are real again 171 | 172 | cprintf("trap_check_user() succeeded!\n"); 173 | } 174 | 175 | void after_div0(); 176 | void after_breakpoint(); 177 | void after_overflow(); 178 | void after_bound(); 179 | void after_illegal(); 180 | void after_gpfault(); 181 | void after_priv(); 182 | 183 | // Multi-purpose trap checking function. 184 | void 185 | trap_check(void **argsp) 186 | { 187 | volatile int cookie = 0xfeedface; 188 | volatile trap_check_args args; 189 | *argsp = (void*)&args; // provide args needed for trap recovery 190 | 191 | // Try a divide by zero trap. 192 | // Be careful when using && to take the address of a label: 193 | // some versions of GCC (4.4.2 at least) will incorrectly try to 194 | // eliminate code it thinks is _only_ reachable via such a pointer. 195 | args.reip = after_div0; 196 | asm volatile("div %0,%0; after_div0:" : : "r" (0)); 197 | assert(args.trapno == T_DIVIDE); 198 | 199 | // Make sure we got our correct stack back with us. 200 | // The asm ensures gcc uses ebp/esp to get the cookie. 201 | asm volatile("" : : : "eax","ebx","ecx","edx","esi","edi"); 202 | assert(cookie == 0xfeedface); 203 | 204 | // Breakpoint trap 205 | args.reip = after_breakpoint; 206 | asm volatile("int3; after_breakpoint:"); 207 | assert(args.trapno == T_BRKPT); 208 | 209 | // Overflow trap 210 | args.reip = after_overflow; 211 | asm volatile("addl %0,%0; into; after_overflow:" : : "r" (0x70000000)); 212 | assert(args.trapno == T_OFLOW); 213 | 214 | // Bounds trap 215 | args.reip = after_bound; 216 | int bounds[2] = { 1, 3 }; 217 | asm volatile("boundl %0,%1; after_bound:" : : "r" (0), "m" (bounds[0])); 218 | assert(args.trapno == T_BOUND); 219 | 220 | // Illegal instruction trap 221 | args.reip = after_illegal; 222 | asm volatile("ud2; after_illegal:"); // guaranteed to be undefined 223 | assert(args.trapno == T_ILLOP); 224 | 225 | // General protection fault due to invalid segment load 226 | args.reip = after_gpfault; 227 | asm volatile("movl %0,%%fs; after_gpfault:" : : "r" (-1)); 228 | assert(args.trapno == T_GPFLT); 229 | 230 | // General protection fault due to privilege violation 231 | if (read_cs() & 3) { 232 | args.reip = after_priv; 233 | asm volatile("lidt %0; after_priv:" : : "m" (idt_pd)); 234 | assert(args.trapno == T_GPFLT); 235 | } 236 | 237 | // Make sure our stack cookie is still with us 238 | assert(cookie == 0xfeedface); 239 | 240 | *argsp = NULL; // recovery mechanism not needed anymore 241 | } 242 | 243 | -------------------------------------------------------------------------------- /kern/trap.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Processor trap handling module definitions. 3 | * 4 | * Copyright (C) 1997 Massachusetts Institute of Technology 5 | * See section "MIT License" in the file LICENSES for licensing terms. 6 | * 7 | * Derived from the MIT Exokernel and JOS. 8 | * Adapted for PIOS by Bryan Ford at Yale University. 9 | */ 10 | 11 | #ifndef PIOS_KERN_TRAP_H 12 | #define PIOS_KERN_TRAP_H 13 | #ifndef PIOS_KERNEL 14 | # error "This is a kernel header; user programs should not #include it" 15 | #endif 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | 22 | // Arguments that trap_check() passes to trap recovery code, 23 | // so that the latter can resume the trapping code 24 | // at the appropriate point and return the trap number. 25 | // There are two versions of the trap recovery code: 26 | // one in kern/trap.c (used twice), the other in kern/proc.c. 27 | typedef struct trap_check_args { 28 | void *reip; // In: EIP at which to resume trapping code 29 | int trapno; // Out: trap number from trapframe 30 | } trap_check_args; 31 | 32 | 33 | // Initialize the trap-handling module and the processor's IDT. 34 | void trap_init(void); 35 | 36 | // Return a string constant describing a given trap number, 37 | // or "(unknown trap)" if not known. 38 | const char *trap_name(int trapno); 39 | 40 | // Pretty-print the general-purpose register save area in a trapframe. 41 | void trap_print_regs(pushregs *regs); 42 | 43 | // Pretty-print the entire contents of a trapframe to the console. 44 | void trap_print(trapframe *tf); 45 | 46 | void trap(trapframe *tf) gcc_noreturn; 47 | void trap_return(trapframe *tf) gcc_noreturn; 48 | 49 | // Check for correct operation of trap handling. 50 | void trap_check_kernel(void); 51 | void trap_check_user(void); 52 | void trap_check(void **argsp); 53 | 54 | #endif /* PIOS_KERN_TRAP_H */ 55 | -------------------------------------------------------------------------------- /kern/trapasm.S: -------------------------------------------------------------------------------- 1 | /* 2 | * Trap handler entry and exit code, written in assembly language. 3 | * 4 | * Copyright (C) 1997 Massachusetts Institute of Technology 5 | * See section "MIT License" in the file LICENSES for licensing terms. 6 | * 7 | * Derived from the MIT Exokernel and JOS. 8 | * Adapted for PIOS by Bryan Ford at Yale University. 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | 17 | 18 | /* The TRAPHANDLER macro defines a globally-visible function for handling 19 | * a trap. It pushes a trap number onto the stack, then jumps to _alltraps. 20 | * Use TRAPHANDLER for traps where the CPU automatically pushes an error code. 21 | */ 22 | #define TRAPHANDLER(name, num) \ 23 | .globl name; /* define global symbol for 'name' */ \ 24 | .type name, @function; /* symbol type is function */ \ 25 | .align 2; /* align function definition */ \ 26 | name: /* function starts here */ \ 27 | pushl $(num); \ 28 | jmp _alltraps 29 | 30 | /* Use TRAPHANDLER_NOEC for traps where the CPU doesn't push an error code. 31 | * It pushes a 0 in place of the error code, so the trap frame has the same 32 | * format in either case. 33 | */ 34 | #define TRAPHANDLER_NOEC(name, num) \ 35 | .globl name; \ 36 | .type name, @function; \ 37 | .align 2; \ 38 | name: \ 39 | pushl $0; \ 40 | pushl $(num); \ 41 | jmp _alltraps 42 | 43 | .text 44 | 45 | /* 46 | * Lab 1: Your code here for generating entry points for the different traps. 47 | */ 48 | 49 | /* 50 | * Lab 1: Your code here for _alltraps 51 | */ 52 | 53 | 54 | 55 | // 56 | // Trap return code. 57 | // C code in the kernel will call this function to return from a trap, 58 | // providing the 59 | // Restore the CPU state from a given trapframe struct 60 | // and return from the trap using the processor's 'iret' instruction. 61 | // This function does not return to the caller, 62 | // since the new CPU state this function loads 63 | // replaces the caller's stack pointer and other registers. 64 | // 65 | .globl trap_return 66 | .type trap_return,@function 67 | .p2align 4, 0x90 /* 16-byte alignment, nop filled */ 68 | trap_return: 69 | /* 70 | * Lab 1: Your code here for trap_return 71 | */ 72 | 1: jmp 1b // just spin 73 | 74 | -------------------------------------------------------------------------------- /lib/cprintf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Implementation of cprintf console output for user environments, 3 | * based on printfmt() and cputs(). 4 | * 5 | * cprintf is a debugging facility, not a generic output facility. 6 | * It is very important that it always go to the console, especially when 7 | * debugging file descriptor code! 8 | * 9 | * Copyright (c) 1986, 1988, 1991, 1993 10 | * The Regents of the University of California. All rights reserved. 11 | * (c) UNIX System Laboratories, Inc. 12 | * See section "BSD License" in the file LICENSES for licensing terms. 13 | * 14 | * All or some portions of this file are derived from material licensed 15 | * to the University of California by American Telephone and Telegraph 16 | * Co. or Unix System Laboratories, Inc. and are reproduced herein with 17 | * the permission of UNIX System Laboratories, Inc. 18 | * 19 | * Adapted for PIOS by Bryan Ford at Yale University. 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | 28 | #define CPUTS_MAX 256 // Max buffer length cputs will accept 29 | // Collect up to CPUTS_MAX-1 characters into a buffer 30 | // and perform ONE system call to print all of them, 31 | // in order to make the lines output to the console atomic 32 | // and prevent interrupts from causing context switches 33 | // in the middle of a console output line and such. 34 | struct printbuf { 35 | int idx; // current buffer index 36 | int cnt; // total bytes printed so far 37 | char buf[CPUTS_MAX]; 38 | }; 39 | 40 | 41 | static void 42 | putch(int ch, struct printbuf *b) 43 | { 44 | b->buf[b->idx++] = ch; 45 | if (b->idx == CPUTS_MAX-1) { 46 | b->buf[b->idx] = 0; 47 | cputs(b->buf); 48 | b->idx = 0; 49 | } 50 | b->cnt++; 51 | } 52 | 53 | int 54 | vcprintf(const char *fmt, va_list ap) 55 | { 56 | struct printbuf b; 57 | 58 | b.idx = 0; 59 | b.cnt = 0; 60 | vprintfmt((void*)putch, &b, fmt, ap); 61 | 62 | b.buf[b.idx] = 0; 63 | cputs(b.buf); 64 | 65 | return b.cnt; 66 | } 67 | 68 | int 69 | cprintf(const char *fmt, ...) 70 | { 71 | va_list ap; 72 | int cnt; 73 | 74 | va_start(ap, fmt); 75 | cnt = vcprintf(fmt, ap); 76 | va_end(ap); 77 | 78 | return cnt; 79 | } 80 | 81 | -------------------------------------------------------------------------------- /lib/printfmt.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Stripped-down primitive printf-style formatting routines, 3 | * used in common by printf, sprintf, fprintf, etc. 4 | * This code is also used by both the kernel and user programs. 5 | * 6 | * Copyright (c) 1986, 1988, 1991, 1993 7 | * The Regents of the University of California. All rights reserved. 8 | * (c) UNIX System Laboratories, Inc. 9 | * See section "BSD License" in the file LICENSES for licensing terms. 10 | * 11 | * All or some portions of this file are derived from material licensed 12 | * to the University of California by American Telephone and Telegraph 13 | * Co. or Unix System Laboratories, Inc. and are reproduced herein with 14 | * the permission of UNIX System Laboratories, Inc. 15 | * 16 | * Adapted for PIOS by Bryan Ford at Yale University. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | typedef struct printstate { 26 | void (*putch)(int ch, void *putdat); // character output function 27 | void *putdat; // data for above function 28 | int padc; // left pad character, ' ' or '0' 29 | int width; // field width, -1=none 30 | int prec; // numeric precision or string length, -1=none 31 | int signc; // sign character: '+', '-', ' ', or -1=none 32 | int flags; // flags below 33 | int base; // base for numeric output 34 | } printstate; 35 | 36 | #define F_L 0x01 // (at least) one 'l' specified 37 | #define F_LL 0x02 // (at least) two 'l's specified 38 | #define F_ALT 0x04 // '#' alternate format flag specified 39 | #define F_DOT 0x08 // '.' separating width from precision seen 40 | #define F_RPAD 0x10 // '-' indiciating right padding seen 41 | 42 | // Get an unsigned int of various possible sizes from a varargs list, 43 | // depending on the lflag parameter. 44 | static uintmax_t 45 | getuint(printstate *st, va_list *ap) 46 | { 47 | if (st->flags & F_LL) 48 | return va_arg(*ap, unsigned long long); 49 | else if (st->flags & F_L) 50 | return va_arg(*ap, unsigned long); 51 | else 52 | return va_arg(*ap, unsigned int); 53 | } 54 | 55 | // Same as getuint but signed - can't use getuint 56 | // because of sign extension 57 | static intmax_t 58 | getint(printstate *st, va_list *ap) 59 | { 60 | if (st->flags & F_LL) 61 | return va_arg(*ap, long long); 62 | else if (st->flags & F_L) 63 | return va_arg(*ap, long); 64 | else 65 | return va_arg(*ap, int); 66 | } 67 | 68 | // Print padding characters, and an optional sign before a number. 69 | static void 70 | putpad(printstate *st) 71 | { 72 | while (--st->width >= 0) 73 | st->putch(st->padc, st->putdat); 74 | } 75 | 76 | // Print a string with a specified maximum length (-1=unlimited), 77 | // with any appropriate left or right field padding. 78 | static void 79 | putstr(printstate *st, const char *str, int maxlen) 80 | { 81 | const char *lim; // find where the string actually ends 82 | if (maxlen < 0) 83 | lim = strchr(str, 0); // find the terminating null 84 | else if ((lim = memchr(str, 0, maxlen)) == NULL) 85 | lim = str + maxlen; 86 | st->width -= (lim-str); // deduct string length from field width 87 | 88 | if (!(st->flags & F_RPAD)) // print left-side padding 89 | putpad(st); // (also leaves st->width == 0) 90 | while (str < lim) { 91 | char ch = *str++; 92 | st->putch(ch, st->putdat); 93 | } 94 | putpad(st); // print right-side padding 95 | } 96 | 97 | // Generate a number (base <= 16) in reverse order into a string buffer. 98 | static char * 99 | genint(printstate *st, char *p, uintmax_t num) 100 | { 101 | // first recursively print all preceding (more significant) digits 102 | if (num >= st->base) 103 | p = genint(st, p, num / st->base); // output higher digits 104 | else if (st->signc >= 0) 105 | *p++ = st->signc; // output leading sign 106 | *p++ = "0123456789abcdef"[num % st->base]; // output this digit 107 | return p; 108 | } 109 | 110 | // Print an integer with any appropriate field padding. 111 | static void 112 | putint(printstate *st, uintmax_t num, int base) 113 | { 114 | char buf[30], *p = buf; // big enough for any 64-bit int in octal 115 | st->base = base; // select base for genint 116 | p = genint(st, p, num); // output to the string buffer 117 | putstr(st, buf, p-buf); // print it with left/right padding 118 | } 119 | 120 | #ifndef PIOS_KERNEL // the kernel doesn't need or want floating-point 121 | // Print the integer part of a floating-point number 122 | static char * 123 | genfint(printstate *st, char *p, double num) 124 | { 125 | if (num >= 10.0) 126 | p = genfint(st, p, num / 10.0); // recursively print higher digits 127 | else if (st->signc >= 0) 128 | *p++ = st->signc; // optional sign before first digit 129 | *p++ = '0' + (int)fmod(num, 10.0); // output this digit 130 | return p; 131 | } 132 | 133 | static char * 134 | genfrac(printstate *st, char *p, double num, int fmtch) 135 | { 136 | *p++ = '.'; // start with the '.' 137 | int rdig = st->prec < 0 ? 6 : st->prec; // digits to the right of the '.' 138 | num -= floor(num); // get the fractional part only 139 | while (rdig-- > 0) { // output 'rdig' fractional digits 140 | num *= 10.0; 141 | int dig = (int)num; 142 | *p++ = '0' + dig; 143 | num -= dig; 144 | } 145 | if (tolower(fmtch) == 'g') // %g format removes trailing zeros 146 | while (p[-1] == '0') 147 | p--; 148 | if (p[-1] == '.' && !(st->flags & F_ALT)) 149 | p--; // no '.' if nothing after it, unless '#' 150 | return p; 151 | } 152 | 153 | // Print a floating-point number in simple '%f' floating-point notation. 154 | static void 155 | putfloat(printstate *st, double num, int l10, int fmtch) 156 | { 157 | char buf[MAX(l10,0) + st->prec + 10], *p = buf; // big enough output buffer 158 | p = genfint(st, p, num); // sign and integer part 159 | p = genfrac(st, p, num, fmtch); // '.' and fractional part 160 | putstr(st, buf, p-buf); // print it with padding 161 | } 162 | 163 | // Print a floating-point number in exponential '%e' notation. 164 | static void 165 | putflexp(printstate *st, double num, int l10, int fmtch) 166 | { 167 | num *= pow(10, -l10); // shift num to correct position 168 | 169 | char buf[st->prec + 20], *p = buf; // big enough output buffer 170 | p = genfint(st, p, num); // generate sign and integer part 171 | p = genfrac(st, p, num, fmtch); // generate '.' and fractional part 172 | 173 | *p++ = isupper(fmtch) ? 'E' : 'e'; // generate exponent 174 | st->signc = '+'; 175 | if (l10 < 0) 176 | l10 = -l10, st->signc = '-'; 177 | p = genint(st, p, l10 / 10); // at least 2 digits 178 | *p++ = '0' + l10 % 10; 179 | 180 | putstr(st, buf, p-buf); // print it all with field padding 181 | } 182 | 183 | // Print a floating-point number in general '%g' notation. 184 | static void 185 | putflgen(printstate *st, double num, int l10, int fmtch) 186 | { 187 | // The precision in the format string counts significant figures. 188 | int sigfigs = (st->prec < 0) ? 6 : (st->prec == 0) ? 1 : st->prec; 189 | if (l10 < -4 || l10 >= st->prec) { // Use exponential notation 190 | st->prec = sigfigs-1; 191 | putflexp(st, num, l10, fmtch); 192 | } else { // Use simple decimal notation 193 | st->prec -= l10 + 1; 194 | putfloat(st, num, l10, fmtch); 195 | } 196 | } 197 | 198 | // Print a floating point infinity or NaN 199 | static void 200 | putfinf(printstate *st, const char *str) 201 | { 202 | char buf[10], *p = buf; 203 | if (st->signc >= 0) 204 | *p++ = st->signc; // leading sign 205 | strcpy(p, str); 206 | putstr(st, buf, -1); 207 | } 208 | #endif // ! PIOS_KERNEL 209 | 210 | // Main function to format and print a string. 211 | void 212 | vprintfmt(void (*putch)(int, void*), void *putdat, const char *fmt, va_list ap) 213 | { 214 | register int ch, err; 215 | 216 | printstate st = { .putch = putch, .putdat = putdat }; 217 | while (1) { 218 | while ((ch = *(unsigned char *) fmt++) != '%') { 219 | if (ch == '\0') 220 | return; 221 | putch(ch, putdat); 222 | } 223 | 224 | // Process a %-escape sequence 225 | st.padc = ' '; 226 | st.width = -1; 227 | st.prec = -1; 228 | st.signc = -1; 229 | st.flags = 0; 230 | st.base = 10; 231 | uintmax_t num; 232 | reswitch: 233 | switch (ch = *(unsigned char *) fmt++) { 234 | 235 | // modifier flags 236 | case '-': // pad on the right instead of the left 237 | st.flags |= F_RPAD; 238 | goto reswitch; 239 | 240 | case '+': // prefix positive numeric values with a '+' sign 241 | st.signc = '+'; 242 | goto reswitch; 243 | 244 | case ' ': // prefix signless numeric values with a space 245 | if (st.signc < 0) // (but only if no '+' is specified) 246 | st.signc = ' '; 247 | goto reswitch; 248 | 249 | // width or precision field 250 | case '0': 251 | if (!(st.flags & F_DOT)) 252 | st.padc = '0'; // pad with 0's instead of spaces 253 | case '1': case '2': case '3': case '4': 254 | case '5': case '6': case '7': case '8': case '9': 255 | for (st.prec = 0; ; ++fmt) { 256 | st.prec = st.prec * 10 + ch - '0'; 257 | ch = *fmt; 258 | if (ch < '0' || ch > '9') 259 | break; 260 | } 261 | goto gotprec; 262 | 263 | case '*': 264 | st.prec = va_arg(ap, int); 265 | gotprec: 266 | if (!(st.flags & F_DOT)) { // haven't seen a '.' yet? 267 | st.width = st.prec; // then it's a field width 268 | st.prec = -1; 269 | } 270 | goto reswitch; 271 | 272 | case '.': 273 | st.flags |= F_DOT; 274 | goto reswitch; 275 | 276 | case '#': 277 | st.flags |= F_ALT; 278 | goto reswitch; 279 | 280 | // long flag (doubled for long long) 281 | case 'l': 282 | st.flags |= (st.flags & F_L) ? F_LL : F_L; 283 | goto reswitch; 284 | 285 | // character 286 | case 'c': 287 | putch(va_arg(ap, int), putdat); 288 | break; 289 | 290 | // string 291 | case 's': { 292 | const char *s; 293 | if ((s = va_arg(ap, char *)) == NULL) 294 | s = "(null)"; 295 | putstr(&st, s, st.prec); 296 | break; 297 | } 298 | 299 | // (signed) decimal 300 | case 'd': 301 | num = getint(&st, &ap); 302 | if ((intmax_t) num < 0) { 303 | num = -(intmax_t) num; 304 | st.signc = '-'; 305 | } 306 | putint(&st, num, 10); 307 | break; 308 | 309 | // unsigned decimal 310 | case 'u': 311 | putint(&st, getuint(&st, &ap), 10); 312 | break; 313 | 314 | // (unsigned) octal 315 | case 'o': 316 | // Replace this with your code. 317 | putch('X', putdat); 318 | putch('X', putdat); 319 | putch('X', putdat); 320 | break; 321 | 322 | // (unsigned) hexadecimal 323 | case 'x': 324 | putint(&st, getuint(&st, &ap), 16); 325 | break; 326 | 327 | // pointer 328 | case 'p': 329 | putch('0', putdat); 330 | putch('x', putdat); 331 | putint(&st, (uintptr_t) va_arg(ap, void *), 16); 332 | break; 333 | 334 | #ifndef PIOS_KERNEL 335 | // floating-point 336 | case 'f': case 'F': 337 | case 'e': case 'E': // XXX should be different from %f 338 | case 'g': case 'G': { // XXX should be different from %f 339 | int variant = tolower(ch); // which format variant? 340 | double val = va_arg(ap, double); // number to print 341 | if (val < 0) { // handle the sign 342 | val = -val; 343 | st.signc = '-'; 344 | } 345 | if (isinf(val)) // handle infinities 346 | putfinf(&st, isupper(ch) ? "INF" : "inf"); 347 | else if (isnan(val)) // handle NANs 348 | putfinf(&st, isupper(ch) ? "NAN" : "nan"); 349 | else if (variant == 'f') // simple decimal format 350 | putfloat(&st, val, floor(log10(val)), ch); 351 | else if (variant == 'e') // exponential format 352 | putflexp(&st, val, floor(log10(val)), ch); 353 | else if (variant == 'g') // general/mixed format 354 | putflgen(&st, val, floor(log10(val)), ch); 355 | break; 356 | } 357 | #endif // ! PIOS_KERNEL 358 | 359 | // escaped '%' character 360 | case '%': 361 | putch(ch, putdat); 362 | break; 363 | 364 | // unrecognized escape sequence - just print it literally 365 | default: 366 | putch('%', putdat); 367 | for (fmt--; fmt[-1] != '%'; fmt--) 368 | /* do nothing */; 369 | break; 370 | } 371 | } 372 | } 373 | 374 | -------------------------------------------------------------------------------- /lib/string.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Basic string routines. Not hardware optimized, but not shabby. 3 | * 4 | * Copyright (C) 1997 Massachusetts Institute of Technology 5 | * See section "MIT License" in the file LICENSES for licensing terms. 6 | * 7 | * Derived from the MIT Exokernel and JOS. 8 | */ 9 | 10 | #include 11 | 12 | // Using assembly for memset/memmove 13 | // makes some difference on real hardware, 14 | // but it makes an even bigger difference on bochs. 15 | // Primespipe runs 3x faster this way. 16 | #define ASM 1 17 | 18 | int 19 | strlen(const char *s) 20 | { 21 | int n; 22 | 23 | for (n = 0; *s != '\0'; s++) 24 | n++; 25 | return n; 26 | } 27 | 28 | char * 29 | strcpy(char *dst, const char *src) 30 | { 31 | char *ret; 32 | 33 | ret = dst; 34 | while ((*dst++ = *src++) != '\0') 35 | /* do nothing */; 36 | return ret; 37 | } 38 | 39 | char * 40 | strncpy(char *dst, const char *src, size_t size) 41 | { 42 | size_t i; 43 | char *ret; 44 | 45 | ret = dst; 46 | for (i = 0; i < size; i++) { 47 | *dst++ = *src; 48 | // If strlen(src) < size, null-pad 'dst' out to 'size' chars 49 | if (*src != '\0') 50 | src++; 51 | } 52 | return ret; 53 | } 54 | 55 | size_t 56 | strlcpy(char *dst, const char *src, size_t size) 57 | { 58 | char *dst_in; 59 | 60 | dst_in = dst; 61 | if (size > 0) { 62 | while (--size > 0 && *src != '\0') 63 | *dst++ = *src++; 64 | *dst = '\0'; 65 | } 66 | return dst - dst_in; 67 | } 68 | 69 | int 70 | strcmp(const char *p, const char *q) 71 | { 72 | while (*p && *p == *q) 73 | p++, q++; 74 | return (int) ((unsigned char) *p - (unsigned char) *q); 75 | } 76 | 77 | int 78 | strncmp(const char *p, const char *q, size_t n) 79 | { 80 | while (n > 0 && *p && *p == *q) 81 | n--, p++, q++; 82 | if (n == 0) 83 | return 0; 84 | else 85 | return (int) ((unsigned char) *p - (unsigned char) *q); 86 | } 87 | 88 | // Return a pointer to the first occurrence of 'c' in 's', 89 | // or a null pointer if the string has no 'c'. 90 | char * 91 | strchr(const char *s, char c) 92 | { 93 | while (*s != c) 94 | if (*s++ == 0) 95 | return NULL; 96 | return (char *) s; 97 | } 98 | 99 | #if ASM 100 | void * 101 | memset(void *v, int c, size_t n) 102 | { 103 | char *p; 104 | 105 | if (n == 0) 106 | return v; 107 | if ((int)v%4 == 0 && n%4 == 0) { 108 | c &= 0xFF; 109 | c = (c<<24)|(c<<16)|(c<<8)|c; 110 | asm volatile("cld; rep stosl\n" 111 | :: "D" (v), "a" (c), "c" (n/4) 112 | : "cc", "memory"); 113 | } else 114 | asm volatile("cld; rep stosb\n" 115 | :: "D" (v), "a" (c), "c" (n) 116 | : "cc", "memory"); 117 | return v; 118 | } 119 | 120 | void * 121 | memmove(void *dst, const void *src, size_t n) 122 | { 123 | const char *s; 124 | char *d; 125 | 126 | s = src; 127 | d = dst; 128 | if (s < d && s + n > d) { 129 | s += n; 130 | d += n; 131 | if ((int)s%4 == 0 && (int)d%4 == 0 && n%4 == 0) 132 | asm volatile("std; rep movsl\n" 133 | :: "D" (d-4), "S" (s-4), "c" (n/4) : "cc", "memory"); 134 | else 135 | asm volatile("std; rep movsb\n" 136 | :: "D" (d-1), "S" (s-1), "c" (n) : "cc", "memory"); 137 | // Some versions of GCC rely on DF being clear 138 | asm volatile("cld" ::: "cc"); 139 | } else { 140 | if ((int)s%4 == 0 && (int)d%4 == 0 && n%4 == 0) 141 | asm volatile("cld; rep movsl\n" 142 | :: "D" (d), "S" (s), "c" (n/4) : "cc", "memory"); 143 | else 144 | asm volatile("cld; rep movsb\n" 145 | :: "D" (d), "S" (s), "c" (n) : "cc", "memory"); 146 | } 147 | return dst; 148 | } 149 | 150 | #else 151 | 152 | void * 153 | memset(void *v, int c, size_t n) 154 | { 155 | char *p; 156 | int m; 157 | 158 | p = v; 159 | m = n; 160 | while (--m >= 0) 161 | *p++ = c; 162 | 163 | return v; 164 | } 165 | 166 | void * 167 | memmove(void *dst, const void *src, size_t n) 168 | { 169 | const char *s; 170 | char *d; 171 | 172 | s = src; 173 | d = dst; 174 | if (s < d && s + n > d) { 175 | s += n; 176 | d += n; 177 | while (n-- > 0) 178 | *--d = *--s; 179 | } else 180 | while (n-- > 0) 181 | *d++ = *s++; 182 | 183 | return dst; 184 | } 185 | #endif 186 | 187 | void * 188 | memcpy(void *dst, const void *src, size_t n) 189 | { 190 | return memmove(dst, src, n); 191 | } 192 | 193 | int 194 | memcmp(const void *v1, const void *v2, size_t n) 195 | { 196 | const uint8_t *s1 = (const uint8_t *) v1; 197 | const uint8_t *s2 = (const uint8_t *) v2; 198 | 199 | while (n-- > 0) { 200 | if (*s1 != *s2) 201 | return (int) *s1 - (int) *s2; 202 | s1++, s2++; 203 | } 204 | 205 | return 0; 206 | } 207 | 208 | void * 209 | memchr(const void *s, int c, size_t n) 210 | { 211 | const void *ends = (const char *) s + n; 212 | for (; s < ends; s++) 213 | if (*(const unsigned char *) s == (unsigned char) c) 214 | return (void *) s; 215 | return NULL; 216 | } 217 | 218 | 219 | -------------------------------------------------------------------------------- /misc/gccprefix.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Find the appropriate prefix to add to gcc/binutils commands. 4 | # Used by our GNUmakefile to set the GCCPREFIX variable. 5 | # We look for the following toolchains, in decreasing preference order: 6 | # 7 | # 1. pios-* (pios-gcc, pios-ld, etc.) 8 | # 2. i386-elf-* (i386-elf-gcc, i386-elf-ld, etc.) 9 | # 3. no prefix ONLY if the system default GCC supports elf32-i386 10 | # 11 | 12 | if pios-objdump -i 2>&1 | grep '^elf32-i386$' >/dev/null 2>&1; then 13 | echo 'pios-' 14 | elif i386-elf-objdump -i 2>&1 | grep '^elf32-i386$' >/dev/null 2>&1; then 15 | echo 'i386-elf-' 16 | elif objdump -i 2>&1 | grep 'elf32-i386' >/dev/null 2>&1; then 17 | echo '' 18 | else 19 | echo "***" 1>&2 20 | echo "*** Error: Can't find an i386-elf version of GCC/binutils." 1>&2 21 | echo "*** Is the directory with i386-elf-gcc in your PATH?" 1>&2 22 | echo "*** If your i386-elf toolchain is installed with a command" 1>&2 23 | echo "*** prefix other than 'i386-elf-', set your GCCPREFIX" 1>&2 24 | echo "*** environment variable to that prefix and try again." 1>&2 25 | echo "*** To turn off this error, run 'gmake GCCPREFIX= ...'." 1>&2 26 | echo "***" 1>&2 27 | exit 1 28 | fi 29 | 30 | -------------------------------------------------------------------------------- /misc/grade-functions.sh: -------------------------------------------------------------------------------- 1 | verbose=false 2 | 3 | if [ "x$1" = "x-v" ] 4 | then 5 | verbose=true 6 | out=/dev/stdout 7 | err=/dev/stderr 8 | else 9 | out=/dev/null 10 | err=/dev/null 11 | fi 12 | 13 | if gmake --version >/dev/null 2>&1; then make=gmake; else make=make; fi 14 | 15 | pts=5 16 | timeout=30 17 | preservefs=n 18 | qemu=`$SHELL misc/which-qemu.sh` 19 | brkfn=done 20 | in=/dev/null 21 | 22 | echo_n () { 23 | # suns can't echo -n, and Mac OS X can't echo "x\c" 24 | # assume argument has no doublequotes 25 | awk 'BEGIN { printf("'"$*"'"); }' /dev/null` 40 | ( 41 | ulimit -t $timeout 42 | exec $qemu -nographic $qemuopts -serial stdio -monitor null \ 43 | -no-reboot $qemuextra 44 | ) <$in >grade-out 2>$err & 45 | PID=$! 46 | 47 | # Wait for QEMU to start 48 | sleep 1 49 | 50 | if [ "$brkfn" ]; then 51 | # Find the address of the function $brkfn in the kernel. 52 | brkaddr=`grep " $brkfn\$" obj/kern/kernel.sym | sed -e's/ .*$//g'` 53 | 54 | ( 55 | echo "target remote localhost:$port" 56 | echo "br *0x$brkaddr" 57 | echo c 58 | ) > grade-gdb-in 59 | gdb -batch -nx -x grade-gdb-in > /dev/null 2>&1 60 | rm grade-gdb-in 61 | 62 | # Make sure QEMU is dead. On OS X, exiting gdb 63 | # doesn't always exit QEMU. 64 | kill $PID > /dev/null 2>&1 65 | fi 66 | } 67 | 68 | passfailmsg () { 69 | msg="$1" 70 | shift 71 | if [ $# -gt 0 ]; then 72 | msg="$msg," 73 | fi 74 | 75 | t1=`date +%s.%N 2>/dev/null` 76 | time=`echo "scale=1; ($t1-$t0)/1" | sed 's/.N/.0/g' | bc 2>/dev/null` 77 | 78 | echo $msg "$@" "(${time}s)" 79 | } 80 | 81 | pass () { 82 | passfailmsg OK "$@" 83 | score=`expr $pts + $score` 84 | } 85 | 86 | fail () { 87 | passfailmsg WRONG "$@" 88 | } 89 | 90 | greptest () { 91 | echo_n "$1" 92 | if grep "$2" grade-out >/dev/null 93 | then 94 | pass 95 | else 96 | fail 97 | fi 98 | } 99 | 100 | # Grep for a multi-line pattern 101 | grmltest () { 102 | echo_n "$1" 103 | if tr /dev/null 104 | then 105 | pass 106 | else 107 | fail 108 | fi 109 | } 110 | 111 | -------------------------------------------------------------------------------- /misc/grade-lab1.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | qemuopts="-hda obj/kern/kernel.img" 4 | . misc/grade-functions.sh 5 | 6 | 7 | $make 8 | run 9 | 10 | score=0 11 | 12 | pts=10; greptest "Printf: " "1234 decimal is 2322 octal!" 13 | pts=15; greptest "Backtrace: " "debug_check() succeeded!" 14 | pts=25; greptest "Traps: " "trap_check_kernel() succeeded!" 15 | pts=25; greptest "User mode: " "trap_check_user() succeeded!" 16 | pts=25; greptest "Memory: " "mem_check() succeeded!" 17 | 18 | echo "Score: $score/100" 19 | 20 | if [ $score -lt 100 ]; then 21 | exit 1 22 | fi 23 | -------------------------------------------------------------------------------- /misc/mergedep.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # Copyright 2003 Bryan Ford 3 | # Distributed under the GNU General Public License. 4 | # 5 | # Usage: mergedep [ ...] 6 | # 7 | # This script merges the contents of all specified 8 | # on the command line into the single file , 9 | # which may or may not previously exist. 10 | # Dependencies in the will override 11 | # any existing dependencies for the same targets in . 12 | # The are deleted after is updated. 13 | # 14 | # The are typically generated by GCC with the -MD option, 15 | # and the is typically included from a Makefile, 16 | # as shown here for GNU 'make': 17 | # 18 | # .deps: $(wildcard *.d) 19 | # perl mergedep $@ $^ 20 | # -include .deps 21 | # 22 | # This script properly handles multiple dependencies per , 23 | # including dependencies having no target, 24 | # so it is compatible with GCC3's -MP option. 25 | # 26 | 27 | sub readdeps { 28 | my $filename = shift; 29 | 30 | open(DEPFILE, $filename) or return 0; 31 | while () { 32 | if (/([^:]*):([^\\:]*)([\\]?)$/) { 33 | my $target = $1; 34 | my $deplines = $2; 35 | my $slash = $3; 36 | while ($slash ne '') { 37 | $_ = ; 38 | defined($_) or die 39 | "Unterminated dependency in $filename"; 40 | /(^[ \t][^\\]*)([\\]?)$/ or die 41 | "Bad continuation line in $filename"; 42 | $deplines = "$deplines\\\n$1"; 43 | $slash = $2; 44 | } 45 | #print "DEPENDENCY [[$target]]: [[$deplines]]\n"; 46 | $dephash{$target} = $deplines; 47 | } elsif (/^[#]?[ \t]*$/) { 48 | # ignore blank lines and comments 49 | } else { 50 | die "Bad dependency line in $filename: $_"; 51 | } 52 | } 53 | close DEPFILE; 54 | return 1; 55 | } 56 | 57 | 58 | if ($#ARGV < 0) { 59 | print "Usage: mergedep [ ..]\n"; 60 | exit(1); 61 | } 62 | 63 | %dephash = (); 64 | 65 | # Read the main dependency file 66 | $maindeps = $ARGV[0]; 67 | readdeps($maindeps); 68 | 69 | # Read and merge in the new dependency files 70 | foreach $i (1 .. $#ARGV) { 71 | readdeps($ARGV[$i]) or die "Can't open $ARGV[$i]"; 72 | } 73 | 74 | # Update the main dependency file 75 | open(DEPFILE, ">$maindeps.tmp") or die "Can't open output file $maindeps.tmp"; 76 | foreach $target (keys %dephash) { 77 | print DEPFILE "$target:$dephash{$target}"; 78 | } 79 | close DEPFILE; 80 | rename("$maindeps.tmp", "$maindeps") or die "Can't overwrite $maindeps"; 81 | 82 | # Finally, delete the new dependency files 83 | foreach $i (1 .. $#ARGV) { 84 | unlink($ARGV[$i]) or print "Error removing $ARGV[$i]\n"; 85 | } 86 | 87 | -------------------------------------------------------------------------------- /misc/which-qemu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Try to find an appropriate version of QEMU to use. 4 | # Use the version in the /c/cs422/tools/bin directory first (XXX local hack!) 5 | # Otherwise, look in a couple typical places. 6 | 7 | if test -x /c/cs422/tools/bin/qemu; then 8 | echo /c/cs422/tools/bin/qemu 9 | exit 10 | elif which qemu > /dev/null; then 11 | echo qemu 12 | exit 13 | else 14 | # Find the appropriate binary in the Mac OS X distribution 15 | qemu=/Applications/Q.app/Contents/MacOS/i386-softmmu.app/Contents/MacOS/i386-softmmu; \ 16 | if test -x $qemu; then 17 | echo $qemu 18 | exit 19 | fi 20 | fi 21 | 22 | echo "***" 1>&2 23 | echo "*** Error: Couldn't find a working QEMU executable." 1>&2 24 | echo "*** Is the directory containing the qemu binary in your PATH" 1>&2 25 | echo "*** or have you tried setting the QEMU variable in conf/env.mk?" 1>&2 26 | echo "***" 1>&2 27 | 28 | exit 1 29 | --------------------------------------------------------------------------------