├── .dir-locals.el ├── .gdbinit.tmpl ├── .gitignore ├── CODING ├── GNUmakefile ├── README.md ├── boot ├── Makefrag ├── boot.S ├── main.c └── sign.pl ├── conf └── env.mk ├── fs ├── Makefrag ├── bc.c ├── fs.c ├── fs.h ├── fsformat.c ├── ide.c ├── motd ├── newmotd ├── serv.c └── test.c ├── grade-functions.sh ├── grade-lab1.sh ├── grade-lab2.sh ├── grade-lab3.sh ├── grade-lab4.sh ├── grade-lab5.sh ├── inc ├── COPYRIGHT ├── args.h ├── assert.h ├── elf.h ├── env.h ├── error.h ├── fd.h ├── fs.h ├── kbdreg.h ├── lib.h ├── memlayout.h ├── mmu.h ├── queue.h ├── stab.h ├── stdarg.h ├── stdio.h ├── string.h ├── syscall.h ├── trap.h ├── types.h └── x86.h ├── kern ├── COPYRIGHT ├── Makefrag ├── console.c ├── console.h ├── cpu.h ├── entry.S ├── entrypgdir.c ├── env.c ├── env.h ├── init.c ├── kclock.c ├── kclock.h ├── kdebug.c ├── kdebug.h ├── kernel.ld ├── lapic.c ├── monitor.c ├── monitor.h ├── mpconfig.c ├── mpentry.S ├── picirq.c ├── picirq.h ├── pmap.c ├── pmap.h ├── printf.c ├── sched.c ├── sched.h ├── spinlock.c ├── spinlock.h ├── syscall.c ├── syscall.h ├── trap.c ├── trap.h └── trapentry.S ├── lib ├── Makefrag ├── args.c ├── console.c ├── entry.S ├── exit.c ├── fd.c ├── file.c ├── fork.c ├── fprintf.c ├── ipc.c ├── libmain.c ├── pageref.c ├── panic.c ├── pfentry.S ├── pgfault.c ├── printf.c ├── printfmt.c ├── readline.c ├── spawn.c ├── string.c └── syscall.c ├── mergedep.pl └── user ├── Makefrag ├── badsegment.c ├── breakpoint.c ├── buggyhello.c ├── buggyhello2.c ├── divzero.c ├── dumbfork.c ├── evilhello.c ├── fairness.c ├── faultalloc.c ├── faultallocbad.c ├── faultbadhandler.c ├── faultdie.c ├── faultevilhandler.c ├── faultnostack.c ├── faultread.c ├── faultreadkernel.c ├── faultregs.c ├── faultwrite.c ├── faultwritekernel.c ├── forktree.c ├── hello.c ├── icode.c ├── idle.c ├── init.c ├── pingpong.c ├── pingpongs.c ├── primes.c ├── softint.c ├── spawnhello.c ├── spawninit.c ├── spin.c ├── stresssched.c ├── testbss.c ├── testfile.c ├── user.ld ├── writemotd.c └── yield.c /.dir-locals.el: -------------------------------------------------------------------------------- 1 | ((nil 2 | (indent-tabs-mode . t) 3 | (tab-width . 8)) 4 | (c-mode 5 | (c-file-style . "bsd") 6 | (c-basic-offset . 8)) 7 | (shell-mode 8 | (sh-basic-offset . 8) 9 | (sh-indentation . 8)) 10 | ) 11 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /obj 2 | /jos.in 3 | /jos.log 4 | /jos.out 5 | /jos.out.* 6 | /jos.cmd 7 | /.gdbinit 8 | /wget.log 9 | /slirp.cap 10 | /qemu.out 11 | /qemu.log 12 | /lab?-handin.tar.gz 13 | /lab?/ 14 | /sol?/ 15 | /conf/lab.mk 16 | -------------------------------------------------------------------------------- /CODING: -------------------------------------------------------------------------------- 1 | JOS 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 | The included .dir-locals.el file will automatically set up the basic 37 | indentation style in Emacs. 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | mit-jos 2 | ======= 3 | 4 | MIT JOS 6.828 2011 -------------------------------------------------------------------------------- /boot/Makefrag: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile fragment for the JOS 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 | 8 | OBJDIRS += boot 9 | 10 | BOOT_OBJS := $(OBJDIR)/boot/boot.o $(OBJDIR)/boot/main.o 11 | 12 | $(OBJDIR)/boot/%.o: boot/%.c 13 | @echo + cc -Os $< 14 | @mkdir -p $(@D) 15 | $(V)$(CC) -nostdinc $(KERN_CFLAGS) -Os -c -o $@ $< 16 | 17 | $(OBJDIR)/boot/%.o: boot/%.S 18 | @echo + as $< 19 | @mkdir -p $(@D) 20 | $(V)$(CC) -nostdinc $(KERN_CFLAGS) -c -o $@ $< 21 | 22 | $(OBJDIR)/boot/main.o: boot/main.c 23 | @echo + cc -Os $< 24 | $(V)$(CC) -nostdinc $(KERN_CFLAGS) -Os -c -o $(OBJDIR)/boot/main.o boot/main.c 25 | 26 | $(OBJDIR)/boot/boot: $(BOOT_OBJS) 27 | @echo + ld boot/boot 28 | $(V)$(LD) $(LDFLAGS) -N -e start -Ttext 0x7C00 -o $@.out $^ 29 | $(V)$(OBJDUMP) -S $@.out >$@.asm 30 | $(V)$(OBJCOPY) -S -O binary -j .text $@.out $@ 31 | $(V)perl boot/sign.pl $(OBJDIR)/boot/boot 32 | 33 | -------------------------------------------------------------------------------- /boot/boot.S: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | # Start the CPU: switch to 32-bit protected mode, jump into C. 4 | # The BIOS loads this code from the first sector of the hard disk into 5 | # memory at physical address 0x7c00 and starts executing in real mode 6 | # with %cs=0 %ip=7c00. 7 | 8 | .set PROT_MODE_CSEG, 0x8 # kernel code segment selector 9 | .set PROT_MODE_DSEG, 0x10 # kernel data segment selector 10 | .set CR0_PE_ON, 0x1 # protected mode enable flag 11 | 12 | .globl start 13 | start: 14 | .code16 # Assemble for 16-bit mode 15 | cli # Disable interrupts 16 | cld # String operations increment 17 | 18 | # Set up the important data segment registers (DS, ES, SS). 19 | xorw %ax,%ax # Segment number zero 20 | movw %ax,%ds # -> Data Segment 21 | movw %ax,%es # -> Extra Segment 22 | movw %ax,%ss # -> Stack Segment 23 | 24 | # Enable A20: 25 | # For backwards compatibility with the earliest PCs, physical 26 | # address line 20 is tied low, so that addresses higher than 27 | # 1MB wrap around to zero by default. This code undoes this. 28 | seta20.1: 29 | inb $0x64,%al # Wait for not busy 30 | testb $0x2,%al 31 | jnz seta20.1 32 | 33 | movb $0xd1,%al # 0xd1 -> port 0x64 34 | outb %al,$0x64 35 | 36 | seta20.2: 37 | inb $0x64,%al # Wait for not busy 38 | testb $0x2,%al 39 | jnz seta20.2 40 | 41 | movb $0xdf,%al # 0xdf -> port 0x60 42 | outb %al,$0x60 43 | 44 | # Switch from real to protected mode, using a bootstrap GDT 45 | # and segment translation that makes virtual addresses 46 | # identical to their physical addresses, so that the 47 | # effective memory map does not change during the switch. 48 | lgdt gdtdesc 49 | movl %cr0, %eax 50 | orl $CR0_PE_ON, %eax 51 | movl %eax, %cr0 52 | 53 | # Jump to next instruction, but in 32-bit code segment. 54 | # Switches processor into 32-bit mode. 55 | ljmp $PROT_MODE_CSEG, $protcseg 56 | 57 | .code32 # Assemble for 32-bit mode 58 | protcseg: 59 | # Set up the protected-mode data segment registers 60 | movw $PROT_MODE_DSEG, %ax # Our data segment selector 61 | movw %ax, %ds # -> DS: Data Segment 62 | movw %ax, %es # -> ES: Extra Segment 63 | movw %ax, %fs # -> FS 64 | movw %ax, %gs # -> GS 65 | movw %ax, %ss # -> SS: Stack Segment 66 | 67 | # Set up the stack pointer and call into C. 68 | movl $start, %esp 69 | call bootmain 70 | 71 | # If bootmain returns (it shouldn't), loop. 72 | spin: 73 | jmp spin 74 | 75 | # Bootstrap GDT 76 | .p2align 2 # force 4 byte alignment 77 | gdt: 78 | SEG_NULL # null seg 79 | SEG(STA_X|STA_R, 0x0, 0xffffffff) # code seg 80 | SEG(STA_W, 0x0, 0xffffffff) # data seg 81 | 82 | gdtdesc: 83 | .word 0x17 # sizeof(gdt) - 1 84 | .long gdt # address gdt 85 | 86 | -------------------------------------------------------------------------------- /boot/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /********************************************************************** 5 | * This a dirt simple boot loader, whose sole job is to boot 6 | * an ELF kernel image from the first IDE hard disk. 7 | * 8 | * DISK LAYOUT 9 | * * This program(boot.S and main.c) is the bootloader. It should 10 | * be stored in the first sector of the disk. 11 | * 12 | * * The 2nd sector onward holds the kernel image. 13 | * 14 | * * The kernel image must be in ELF format. 15 | * 16 | * BOOT UP STEPS 17 | * * when the CPU boots it loads the BIOS into memory and executes it 18 | * 19 | * * the BIOS intializes devices, sets of the interrupt routines, and 20 | * reads the first sector of the boot device(e.g., hard-drive) 21 | * into memory and jumps to it. 22 | * 23 | * * Assuming this boot loader is stored in the first sector of the 24 | * hard-drive, this code takes over... 25 | * 26 | * * control starts in boot.S -- which sets up protected mode, 27 | * and a stack so C code then run, then calls bootmain() 28 | * 29 | * * bootmain() in this file takes over, reads in the kernel and jumps to it. 30 | **********************************************************************/ 31 | 32 | #define SECTSIZE 512 33 | #define ELFHDR ((struct Elf *) 0x10000) // scratch space 34 | 35 | void readsect(void*, uint32_t); 36 | void readseg(uint32_t, uint32_t, uint32_t); 37 | 38 | void 39 | bootmain(void) 40 | { 41 | struct Proghdr *ph, *eph; 42 | 43 | // read 1st page off disk 44 | readseg((uint32_t) ELFHDR, SECTSIZE*8, 0); 45 | 46 | // is this a valid ELF? 47 | if (ELFHDR->e_magic != ELF_MAGIC) 48 | goto bad; 49 | 50 | // load each program segment (ignores ph flags) 51 | ph = (struct Proghdr *) ((uint8_t *) ELFHDR + ELFHDR->e_phoff); 52 | eph = ph + ELFHDR->e_phnum; 53 | for (; ph < eph; ph++) 54 | // p_pa is the load address of this segment (as well 55 | // as the physical address) 56 | readseg(ph->p_pa, ph->p_memsz, ph->p_offset); 57 | 58 | // call the entry point from the ELF header 59 | // note: does not return! 60 | ((void (*)(void)) (ELFHDR->e_entry))(); 61 | 62 | bad: 63 | outw(0x8A00, 0x8A00); 64 | outw(0x8A00, 0x8E00); 65 | while (1) 66 | /* do nothing */; 67 | } 68 | 69 | // Read 'count' bytes at 'offset' from kernel into physical address 'pa'. 70 | // Might copy more than asked 71 | void 72 | readseg(uint32_t pa, uint32_t count, uint32_t offset) 73 | { 74 | uint32_t end_pa; 75 | 76 | end_pa = pa + count; 77 | 78 | // round down to sector boundary 79 | pa &= ~(SECTSIZE - 1); 80 | 81 | // translate from bytes to sectors, and kernel starts at sector 1 82 | offset = (offset / SECTSIZE) + 1; 83 | 84 | // If this is too slow, we could read lots of sectors at a time. 85 | // We'd write more to memory than asked, but it doesn't matter -- 86 | // we load in increasing order. 87 | while (pa < end_pa) { 88 | // Since we haven't enabled paging yet and we're using 89 | // an identity segment mapping (see boot.S), we can 90 | // use physical addresses directly. This won't be the 91 | // case once JOS enables the MMU. 92 | readsect((uint8_t*) pa, offset); 93 | pa += SECTSIZE; 94 | offset++; 95 | } 96 | } 97 | 98 | void 99 | waitdisk(void) 100 | { 101 | // wait for disk reaady 102 | while ((inb(0x1F7) & 0xC0) != 0x40) 103 | /* do nothing */; 104 | } 105 | 106 | void 107 | readsect(void *dst, uint32_t offset) 108 | { 109 | // wait for disk to be ready 110 | waitdisk(); 111 | 112 | outb(0x1F2, 1); // count = 1 113 | outb(0x1F3, offset); 114 | outb(0x1F4, offset >> 8); 115 | outb(0x1F5, offset >> 16); 116 | outb(0x1F6, (offset >> 24) | 0xE0); 117 | outb(0x1F7, 0x20); // cmd 0x20 - read sectors 118 | 119 | // wait for disk to be ready 120 | waitdisk(); 121 | 122 | // read a sector 123 | insl(0x1F0, dst, SECTSIZE/4); 124 | } 125 | 126 | -------------------------------------------------------------------------------- /boot/sign.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | open(BB, $ARGV[0]) || die "open $ARGV[0]: $!"; 4 | 5 | binmode BB; 6 | my $buf; 7 | read(BB, $buf, 1000); 8 | $n = length($buf); 9 | 10 | if($n > 510){ 11 | print STDERR "boot block too large: $n bytes (max 510)\n"; 12 | exit 1; 13 | } 14 | 15 | print STDERR "boot block is $n bytes (max 510)\n"; 16 | 17 | $buf .= "\0" x (510-$n); 18 | $buf .= "\x55\xAA"; 19 | 20 | open(BB, ">$ARGV[0]") || die "open >$ARGV[0]: $!"; 21 | binmode BB; 22 | print BB $buf; 23 | close BB; 24 | -------------------------------------------------------------------------------- /conf/env.mk: -------------------------------------------------------------------------------- 1 | # env.mk - configuration variables for the JOS lab 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-jos-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 | -------------------------------------------------------------------------------- /fs/Makefrag: -------------------------------------------------------------------------------- 1 | 2 | OBJDIRS += fs 3 | 4 | FSOFILES := $(OBJDIR)/fs/ide.o \ 5 | $(OBJDIR)/fs/bc.o \ 6 | $(OBJDIR)/fs/fs.o \ 7 | $(OBJDIR)/fs/serv.o \ 8 | $(OBJDIR)/fs/test.o \ 9 | 10 | USERAPPS := $(OBJDIR)/user/init 11 | 12 | FSIMGTXTFILES := fs/newmotd \ 13 | fs/motd 14 | 15 | 16 | 17 | FSIMGFILES := $(FSIMGTXTFILES) $(USERAPPS) 18 | 19 | $(OBJDIR)/fs/%.o: fs/%.c fs/fs.h inc/lib.h 20 | @echo + cc[USER] $< 21 | @mkdir -p $(@D) 22 | $(V)$(CC) -nostdinc $(USER_CFLAGS) -c -o $@ $< 23 | 24 | $(OBJDIR)/fs/fs: $(FSOFILES) $(OBJDIR)/lib/entry.o $(OBJDIR)/lib/libjos.a user/user.ld 25 | @echo + ld $@ 26 | $(V)mkdir -p $(@D) 27 | $(V)$(LD) -o $@ $(ULDFLAGS) $(LDFLAGS) -nostdlib \ 28 | $(OBJDIR)/lib/entry.o $(FSOFILES) \ 29 | -L$(OBJDIR)/lib -ljos $(GCC_LIB) 30 | $(V)$(OBJDUMP) -S $@ >$@.asm 31 | 32 | # How to build the file system image 33 | $(OBJDIR)/fs/fsformat: fs/fsformat.c 34 | @echo + mk $(OBJDIR)/fs/fsformat 35 | $(V)mkdir -p $(@D) 36 | $(V)$(NCC) $(NATIVE_CFLAGS) -o $(OBJDIR)/fs/fsformat fs/fsformat.c 37 | 38 | $(OBJDIR)/fs/clean-fs.img: $(OBJDIR)/fs/fsformat $(FSIMGFILES) 39 | @echo + mk $(OBJDIR)/fs/clean-fs.img 40 | $(V)mkdir -p $(@D) 41 | $(V)$(OBJDIR)/fs/fsformat $(OBJDIR)/fs/clean-fs.img 1024 $(FSIMGFILES) 42 | 43 | $(OBJDIR)/fs/fs.img: $(OBJDIR)/fs/clean-fs.img 44 | @echo + cp $(OBJDIR)/fs/clean-fs.img $@ 45 | $(V)cp $(OBJDIR)/fs/clean-fs.img $@ 46 | 47 | all: $(OBJDIR)/fs/fs.img 48 | 49 | #all: $(addsuffix .sym, $(USERAPPS)) 50 | 51 | #all: $(addsuffix .asm, $(USERAPPS)) 52 | 53 | -------------------------------------------------------------------------------- /fs/bc.c: -------------------------------------------------------------------------------- 1 | 2 | #include "fs.h" 3 | 4 | // Return the virtual address of this disk block. 5 | void* 6 | diskaddr(uint32_t blockno) 7 | { 8 | if (blockno == 0 || (super && blockno >= super->s_nblocks)) 9 | panic("bad block number %08x in diskaddr", blockno); 10 | return (char*) (DISKMAP + blockno * BLKSIZE); 11 | } 12 | 13 | // Is this virtual address mapped? 14 | bool 15 | va_is_mapped(void *va) 16 | { 17 | return (vpd[PDX(va)] & PTE_P) && (vpt[PGNUM(va)] & PTE_P); 18 | } 19 | 20 | // Is this virtual address dirty? 21 | bool 22 | va_is_dirty(void *va) 23 | { 24 | return (vpt[PGNUM(va)] & PTE_D) != 0; 25 | } 26 | 27 | // Fault any disk block that is read or written in to memory by 28 | // loading it from disk. 29 | // Hint: Use ide_read and BLKSECTS. 30 | static void 31 | bc_pgfault(struct UTrapframe *utf) 32 | { 33 | void *addr = (void *) utf->utf_fault_va; 34 | uint32_t blockno = ((uint32_t)addr - DISKMAP) / BLKSIZE; 35 | int r; 36 | 37 | // Check that the fault was within the block cache region 38 | if (addr < (void*)DISKMAP || addr >= (void*)(DISKMAP + DISKSIZE)) 39 | panic("page fault in FS: eip %08x, va %08x, err %04x", 40 | utf->utf_eip, addr, utf->utf_err); 41 | 42 | // Sanity check the block number. 43 | if (super && blockno >= super->s_nblocks) 44 | panic("reading non-existent block %08x\n", blockno); 45 | 46 | // Allocate a page in the disk map region, read the contents 47 | // of the block from the disk into that page, and mark the 48 | // page not-dirty (since reading the data from disk will mark 49 | // the page dirty). 50 | // 51 | // LAB 5: Your code here 52 | void *pgva = ROUNDDOWN(addr, BLKSIZE); 53 | if (sys_page_alloc(0, pgva, PTE_U | PTE_W | PTE_P)) 54 | panic("bc_pgfault: no phys mem"); 55 | 56 | uint32_t secno = blockno * BLKSECTS; 57 | if (ide_read(secno, pgva, BLKSECTS)) 58 | panic("bc_pgfault: ide read error"); 59 | 60 | // Mark the page not-dirty. 61 | sys_page_map(0, pgva, 0, pgva, vpt[PGNUM(pgva)] & PTE_SYSCALL); 62 | assert(!va_is_dirty(pgva)); 63 | 64 | // panic("bc_pgfault not implemented"); 65 | 66 | // Check that the block we read was allocated. (exercise for 67 | // the reader: why do we do this *after* reading the block 68 | // in?) 69 | if (bitmap && block_is_free(blockno)) 70 | panic("reading free block %08x\n", blockno); 71 | } 72 | 73 | // Flush the contents of the block containing VA out to disk if 74 | // necessary, then clear the PTE_D bit using sys_page_map. 75 | // If the block is not in the block cache or is not dirty, does 76 | // nothing. 77 | // Hint: Use va_is_mapped, va_is_dirty, and ide_write. 78 | // Hint: Use the PTE_SYSCALL constant when calling sys_page_map. 79 | // Hint: Don't forget to round addr down. 80 | void 81 | flush_block(void *addr) 82 | { 83 | uint32_t blockno = ((uint32_t)addr - DISKMAP) / BLKSIZE; 84 | 85 | if (addr < (void*)DISKMAP || addr >= (void*)(DISKMAP + DISKSIZE)) 86 | panic("flush_block of bad va %08x", addr); 87 | 88 | // LAB 5: Your code here. 89 | void *pgva = ROUNDDOWN(addr, BLKSIZE); 90 | pte_t pte = vpt[PGNUM(pgva)]; 91 | 92 | if (!va_is_mapped(pgva) || !va_is_dirty(pgva)) 93 | return; 94 | 95 | uint32_t secno = blockno * BLKSECTS; 96 | if (ide_write(secno, pgva, BLKSECTS)) 97 | panic("flush_block: ide write error"); 98 | 99 | if (sys_page_map(0, pgva, 0, pgva, pte & PTE_SYSCALL)) 100 | panic("flush_block: map error"); 101 | 102 | // panic("flush_block not implemented"); 103 | } 104 | 105 | // Test that the block cache works, by smashing the superblock and 106 | // reading it back. 107 | static void 108 | check_bc(void) 109 | { 110 | struct Super backup; 111 | 112 | // back up super block 113 | memmove(&backup, diskaddr(1), sizeof backup); 114 | 115 | // smash it 116 | strcpy(diskaddr(1), "OOPS!\n"); 117 | flush_block(diskaddr(1)); 118 | assert(va_is_mapped(diskaddr(1))); 119 | assert(!va_is_dirty(diskaddr(1))); 120 | 121 | // clear it out 122 | sys_page_unmap(0, diskaddr(1)); 123 | assert(!va_is_mapped(diskaddr(1))); 124 | 125 | // read it back in 126 | assert(strcmp(diskaddr(1), "OOPS!\n") == 0); 127 | 128 | // fix it 129 | memmove(diskaddr(1), &backup, sizeof backup); 130 | flush_block(diskaddr(1)); 131 | 132 | cprintf("block cache is good\n"); 133 | } 134 | 135 | void 136 | bc_init(void) 137 | { 138 | set_pgfault_handler(bc_pgfault); 139 | check_bc(); 140 | } 141 | 142 | -------------------------------------------------------------------------------- /fs/fs.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define SECTSIZE 512 // bytes per disk sector 5 | #define BLKSECTS (BLKSIZE / SECTSIZE) // sectors per block 6 | 7 | /* Disk block n, when in memory, is mapped into the file system 8 | * server's address space at DISKMAP + (n*BLKSIZE). */ 9 | #define DISKMAP 0x10000000 10 | 11 | /* Maximum disk size we can handle (3GB) */ 12 | #define DISKSIZE 0xC0000000 13 | 14 | struct Super *super; // superblock 15 | uint32_t *bitmap; // bitmap blocks mapped in memory 16 | 17 | /* ide.c */ 18 | bool ide_probe_disk1(void); 19 | void ide_set_disk(int diskno); 20 | int ide_read(uint32_t secno, void *dst, size_t nsecs); 21 | int ide_write(uint32_t secno, const void *src, size_t nsecs); 22 | 23 | /* bc.c */ 24 | void* diskaddr(uint32_t blockno); 25 | bool va_is_mapped(void *va); 26 | bool va_is_dirty(void *va); 27 | void flush_block(void *addr); 28 | void bc_init(void); 29 | 30 | /* fs.c */ 31 | void fs_init(void); 32 | int file_get_block(struct File *f, uint32_t file_blockno, char **pblk); 33 | int file_create(const char *path, struct File **f); 34 | int file_open(const char *path, struct File **f); 35 | ssize_t file_read(struct File *f, void *buf, size_t count, off_t offset); 36 | int file_write(struct File *f, const void *buf, size_t count, off_t offset); 37 | int file_set_size(struct File *f, off_t newsize); 38 | void file_flush(struct File *f); 39 | int file_remove(const char *path); 40 | void fs_sync(void); 41 | 42 | /* int map_block(uint32_t); */ 43 | bool block_is_free(uint32_t blockno); 44 | int alloc_block(void); 45 | 46 | /* test.c */ 47 | void fs_test(void); 48 | 49 | -------------------------------------------------------------------------------- /fs/fsformat.c: -------------------------------------------------------------------------------- 1 | /* 2 | * JOS file system format 3 | */ 4 | 5 | // We don't actually want to define off_t! 6 | #define off_t xxx_off_t 7 | #define bool xxx_bool 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #undef off_t 21 | #undef bool 22 | 23 | // Prevent inc/types.h, included from inc/fs.h, 24 | // from attempting to redefine types defined in the host's inttypes.h. 25 | #define JOS_INC_TYPES_H 26 | // Typedef the types that inc/mmu.h needs. 27 | typedef uint32_t physaddr_t; 28 | typedef uint32_t off_t; 29 | typedef int bool; 30 | 31 | #include 32 | #include 33 | 34 | #define ROUNDUP(n, v) ((n) - 1 + (v) - ((n) - 1) % (v)) 35 | #define MAX_DIR_ENTS 128 36 | 37 | struct Dir 38 | { 39 | struct File *f; 40 | struct File *ents; 41 | int n; 42 | }; 43 | 44 | uint32_t nblocks; 45 | char *diskmap, *diskpos; 46 | struct Super *super; 47 | uint32_t *bitmap; 48 | 49 | void 50 | panic(const char *fmt, ...) 51 | { 52 | va_list ap; 53 | 54 | va_start(ap, fmt); 55 | vfprintf(stderr, fmt, ap); 56 | va_end(ap); 57 | fputc('\n', stderr); 58 | abort(); 59 | } 60 | 61 | void 62 | readn(int f, void *out, size_t n) 63 | { 64 | size_t p = 0; 65 | while (p < n) { 66 | size_t m = read(f, out + p, n - p); 67 | if (m < 0) 68 | panic("read: %s", strerror(errno)); 69 | if (m == 0) 70 | panic("read: Unexpected EOF"); 71 | p += m; 72 | } 73 | } 74 | 75 | uint32_t 76 | blockof(void *pos) 77 | { 78 | return ((char*)pos - diskmap) / BLKSIZE; 79 | } 80 | 81 | void * 82 | alloc(uint32_t bytes) 83 | { 84 | void *start = diskpos; 85 | diskpos += ROUNDUP(bytes, BLKSIZE); 86 | if (blockof(diskpos) >= nblocks) 87 | panic("out of disk blocks"); 88 | return start; 89 | } 90 | 91 | void 92 | opendisk(const char *name) 93 | { 94 | int r, diskfd, nbitblocks; 95 | 96 | if ((diskfd = open(name, O_RDWR | O_CREAT, 0666)) < 0) 97 | panic("open %s: %s", name, strerror(errno)); 98 | 99 | if ((r = ftruncate(diskfd, 0)) < 0 100 | || (r = ftruncate(diskfd, nblocks * BLKSIZE)) < 0) 101 | panic("truncate %s: %s", name, strerror(errno)); 102 | 103 | if ((diskmap = mmap(NULL, nblocks * BLKSIZE, PROT_READ|PROT_WRITE, 104 | MAP_SHARED, diskfd, 0)) == MAP_FAILED) 105 | panic("mmap %s: %s", name, strerror(errno)); 106 | 107 | close(diskfd); 108 | 109 | diskpos = diskmap; 110 | alloc(BLKSIZE); 111 | super = alloc(BLKSIZE); 112 | super->s_magic = FS_MAGIC; 113 | super->s_nblocks = nblocks; 114 | super->s_root.f_type = FTYPE_DIR; 115 | strcpy(super->s_root.f_name, "/"); 116 | 117 | nbitblocks = (nblocks + BLKBITSIZE - 1) / BLKBITSIZE; 118 | bitmap = alloc(nbitblocks * BLKSIZE); 119 | memset(bitmap, 0xFF, nbitblocks * BLKSIZE); 120 | } 121 | 122 | void 123 | finishdisk(void) 124 | { 125 | int r, i; 126 | 127 | for (i = 0; i < blockof(diskpos); ++i) 128 | bitmap[i/32] &= ~(1<<(i%32)); 129 | 130 | if ((r = msync(diskmap, nblocks * BLKSIZE, MS_SYNC)) < 0) 131 | panic("msync: %s", strerror(errno)); 132 | } 133 | 134 | void 135 | finishfile(struct File *f, uint32_t start, uint32_t len) 136 | { 137 | int i; 138 | f->f_size = len; 139 | len = ROUNDUP(len, BLKSIZE); 140 | for (i = 0; i < len / BLKSIZE && i < NDIRECT; ++i) 141 | f->f_direct[i] = start + i; 142 | if (i == NDIRECT) { 143 | uint32_t *ind = alloc(BLKSIZE); 144 | f->f_indirect = blockof(ind); 145 | for (; i < len / BLKSIZE; ++i) 146 | ind[i - NDIRECT] = start + i; 147 | } 148 | } 149 | 150 | void 151 | startdir(struct File *f, struct Dir *dout) 152 | { 153 | dout->f = f; 154 | dout->ents = malloc(MAX_DIR_ENTS * sizeof *dout->ents); 155 | dout->n = 0; 156 | } 157 | 158 | struct File * 159 | diradd(struct Dir *d, uint32_t type, const char *name) 160 | { 161 | struct File *out = &d->ents[d->n++]; 162 | if (d->n > MAX_DIR_ENTS) 163 | panic("too many directory entries"); 164 | strcpy(out->f_name, name); 165 | out->f_type = type; 166 | return out; 167 | } 168 | 169 | void 170 | finishdir(struct Dir *d) 171 | { 172 | int size = d->n * sizeof(struct File); 173 | struct File *start = alloc(size); 174 | memmove(start, d->ents, size); 175 | finishfile(d->f, blockof(start), ROUNDUP(size, BLKSIZE)); 176 | free(d->ents); 177 | d->ents = NULL; 178 | } 179 | 180 | void 181 | writefile(struct Dir *dir, const char *name) 182 | { 183 | int r, fd; 184 | struct File *f; 185 | struct stat st; 186 | const char *last; 187 | char *start; 188 | 189 | if ((fd = open(name, O_RDONLY)) < 0) 190 | panic("open %s: %s", name, strerror(errno)); 191 | if ((r = fstat(fd, &st)) < 0) 192 | panic("stat %s: %s", name, strerror(errno)); 193 | if (!S_ISREG(st.st_mode)) 194 | panic("%s is not a regular file", name); 195 | if (st.st_size >= MAXFILESIZE) 196 | panic("%s too large", name); 197 | 198 | last = strrchr(name, '/'); 199 | if (last) 200 | last++; 201 | else 202 | last = name; 203 | 204 | f = diradd(dir, FTYPE_REG, last); 205 | start = alloc(st.st_size); 206 | readn(fd, start, st.st_size); 207 | finishfile(f, blockof(start), st.st_size); 208 | close(fd); 209 | } 210 | 211 | void 212 | usage(void) 213 | { 214 | fprintf(stderr, "Usage: fsformat fs.img NBLOCKS files...\n"); 215 | exit(2); 216 | } 217 | 218 | int 219 | main(int argc, char **argv) 220 | { 221 | int i; 222 | char *s; 223 | struct Dir root; 224 | 225 | assert(BLKSIZE % sizeof(struct File) == 0); 226 | 227 | if (argc < 3) 228 | usage(); 229 | 230 | nblocks = strtol(argv[2], &s, 0); 231 | if (*s || s == argv[2] || nblocks < 2 || nblocks > 1024) 232 | usage(); 233 | 234 | opendisk(argv[1]); 235 | 236 | startdir(&super->s_root, &root); 237 | for (i = 3; i < argc; i++) 238 | writefile(&root, argv[i]); 239 | finishdir(&root); 240 | 241 | finishdisk(); 242 | return 0; 243 | } 244 | 245 | -------------------------------------------------------------------------------- /fs/ide.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Minimal PIO-based (non-interrupt-driven) IDE driver code. 3 | * For information about what all this IDE/ATA magic means, 4 | * see the materials available on the class references page. 5 | */ 6 | 7 | #include "fs.h" 8 | #include 9 | 10 | #define IDE_BSY 0x80 11 | #define IDE_DRDY 0x40 12 | #define IDE_DF 0x20 13 | #define IDE_ERR 0x01 14 | 15 | static int diskno = 1; 16 | 17 | static int 18 | ide_wait_ready(bool check_error) 19 | { 20 | int r; 21 | 22 | while (((r = inb(0x1F7)) & (IDE_BSY|IDE_DRDY)) != IDE_DRDY) 23 | /* do nothing */; 24 | 25 | if (check_error && (r & (IDE_DF|IDE_ERR)) != 0) 26 | return -1; 27 | return 0; 28 | } 29 | 30 | bool 31 | ide_probe_disk1(void) 32 | { 33 | int r, x; 34 | 35 | // wait for Device 0 to be ready 36 | ide_wait_ready(0); 37 | 38 | // switch to Device 1 39 | outb(0x1F6, 0xE0 | (1<<4)); 40 | 41 | // check for Device 1 to be ready for a while 42 | for (x = 0; 43 | x < 1000 && ((r = inb(0x1F7)) & (IDE_BSY|IDE_DF|IDE_ERR)) != 0; 44 | x++) 45 | /* do nothing */; 46 | 47 | // switch back to Device 0 48 | outb(0x1F6, 0xE0 | (0<<4)); 49 | 50 | cprintf("Device 1 presence: %d\n", (x < 1000)); 51 | return (x < 1000); 52 | } 53 | 54 | void 55 | ide_set_disk(int d) 56 | { 57 | if (d != 0 && d != 1) 58 | panic("bad disk number"); 59 | diskno = d; 60 | } 61 | 62 | int 63 | ide_read(uint32_t secno, void *dst, size_t nsecs) 64 | { 65 | int r; 66 | 67 | assert(nsecs <= 256); 68 | 69 | ide_wait_ready(0); 70 | 71 | outb(0x1F2, nsecs); 72 | outb(0x1F3, secno & 0xFF); 73 | outb(0x1F4, (secno >> 8) & 0xFF); 74 | outb(0x1F5, (secno >> 16) & 0xFF); 75 | outb(0x1F6, 0xE0 | ((diskno&1)<<4) | ((secno>>24)&0x0F)); 76 | outb(0x1F7, 0x20); // CMD 0x20 means read sector 77 | 78 | for (; nsecs > 0; nsecs--, dst += SECTSIZE) { 79 | if ((r = ide_wait_ready(1)) < 0) 80 | return r; 81 | insl(0x1F0, dst, SECTSIZE/4); 82 | } 83 | 84 | return 0; 85 | } 86 | 87 | int 88 | ide_write(uint32_t secno, const void *src, size_t nsecs) 89 | { 90 | int r; 91 | 92 | assert(nsecs <= 256); 93 | 94 | ide_wait_ready(0); 95 | 96 | outb(0x1F2, nsecs); 97 | outb(0x1F3, secno & 0xFF); 98 | outb(0x1F4, (secno >> 8) & 0xFF); 99 | outb(0x1F5, (secno >> 16) & 0xFF); 100 | outb(0x1F6, 0xE0 | ((diskno&1)<<4) | ((secno>>24)&0x0F)); 101 | outb(0x1F7, 0x30); // CMD 0x30 means write sector 102 | 103 | for (; nsecs > 0; nsecs--, src += SECTSIZE) { 104 | if ((r = ide_wait_ready(1)) < 0) 105 | return r; 106 | outsl(0x1F0, src, SECTSIZE/4); 107 | } 108 | 109 | return 0; 110 | } 111 | 112 | -------------------------------------------------------------------------------- /fs/motd: -------------------------------------------------------------------------------- 1 | This is /motd, the message of the day. 2 | 3 | Welcome to the JOS kernel, now with a file system! 4 | 5 | -------------------------------------------------------------------------------- /fs/newmotd: -------------------------------------------------------------------------------- 1 | This is the NEW message of the day! 2 | 3 | -------------------------------------------------------------------------------- /fs/test.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include "fs.h" 6 | 7 | static char *msg = "This is the NEW message of the day!\n\n"; 8 | 9 | void 10 | fs_test(void) 11 | { 12 | struct File *f; 13 | int r; 14 | char *blk; 15 | uint32_t *bits; 16 | 17 | // back up bitmap 18 | if ((r = sys_page_alloc(0, (void*) PGSIZE, PTE_P|PTE_U|PTE_W)) < 0) 19 | panic("sys_page_alloc: %e", r); 20 | bits = (uint32_t*) PGSIZE; 21 | memmove(bits, bitmap, PGSIZE); 22 | // allocate block 23 | if ((r = alloc_block()) < 0) 24 | panic("alloc_block: %e", r); 25 | // check that block was free 26 | assert(bits[r/32] & (1 << (r%32))); 27 | // and is not free any more 28 | assert(!(bitmap[r/32] & (1 << (r%32)))); 29 | cprintf("alloc_block is good\n"); 30 | 31 | if ((r = file_open("/not-found", &f)) < 0 && r != -E_NOT_FOUND) 32 | panic("file_open /not-found: %e", r); 33 | else if (r == 0) 34 | panic("file_open /not-found succeeded!"); 35 | if ((r = file_open("/newmotd", &f)) < 0) 36 | panic("file_open /newmotd: %e", r); 37 | cprintf("file_open is good\n"); 38 | 39 | if ((r = file_get_block(f, 0, &blk)) < 0) 40 | panic("file_get_block: %e", r); 41 | if (strcmp(blk, msg) != 0) 42 | panic("file_get_block returned wrong data"); 43 | cprintf("file_get_block is good\n"); 44 | 45 | *(volatile char*)blk = *(volatile char*)blk; 46 | assert((vpt[PGNUM(blk)] & PTE_D)); 47 | file_flush(f); 48 | assert(!(vpt[PGNUM(blk)] & PTE_D)); 49 | cprintf("file_flush is good\n"); 50 | 51 | if ((r = file_set_size(f, 0)) < 0) 52 | panic("file_set_size: %e", r); 53 | assert(f->f_direct[0] == 0); 54 | assert(!(vpt[PGNUM(f)] & PTE_D)); 55 | cprintf("file_truncate is good\n"); 56 | 57 | if ((r = file_set_size(f, strlen(msg))) < 0) 58 | panic("file_set_size 2: %e", r); 59 | assert(!(vpt[PGNUM(f)] & PTE_D)); 60 | if ((r = file_get_block(f, 0, &blk)) < 0) 61 | panic("file_get_block 2: %e", r); 62 | strcpy(blk, msg); 63 | assert((vpt[PGNUM(blk)] & PTE_D)); 64 | file_flush(f); 65 | assert(!(vpt[PGNUM(blk)] & PTE_D)); 66 | assert(!(vpt[PGNUM(f)] & PTE_D)); 67 | cprintf("file rewrite is good\n"); 68 | } 69 | -------------------------------------------------------------------------------- /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 | # 16 | # QEMU 17 | # 18 | 19 | timeout=30 20 | preservefs=n 21 | qemu=`$make -s --no-print-directory print-qemu` 22 | gdbport=`$make -s --no-print-directory print-gdbport` 23 | brkfn=readline 24 | 25 | echo_n () { 26 | # suns can't echo -n, and Mac OS X can't echo "x\c" 27 | # assume argument has no doublequotes 28 | awk 'BEGIN { printf("'"$*"'"); }' &2 43 | fi 44 | 45 | t0=`date +%s.%N 2>/dev/null` 46 | ( 47 | ulimit -t $timeout 48 | exec $qemucommand 49 | ) >$out 2>$err & 50 | PID=$! 51 | 52 | # Wait for QEMU to start 53 | sleep 1 54 | 55 | if [ "$brkfn" ]; then 56 | # Find the address of the kernel $brkfn function, 57 | # which is typically what the kernel monitor uses to 58 | # read commands interactively. 59 | brkaddr=`grep " $brkfn\$" obj/kern/kernel.sym | sed -e's/ .*$//g'` 60 | 61 | ( 62 | echo "target remote localhost:$gdbport" 63 | echo "br *0x$brkaddr" 64 | echo c 65 | ) > jos.in 66 | gdb -batch -nx -x jos.in > /dev/null 2>&1 67 | 68 | # Make sure QEMU is dead. On OS X, exiting gdb 69 | # doesn't always exit QEMU. 70 | kill $PID > /dev/null 2>&1 71 | fi 72 | } 73 | 74 | # 75 | # Scoring 76 | # 77 | 78 | pts=5 79 | part=0 80 | partpos=0 81 | total=0 82 | totalpos=0 83 | 84 | showpart () { 85 | echo "Part $1 score: $part/$partpos" 86 | echo 87 | total=`expr $total + $part` 88 | totalpos=`expr $totalpos + $partpos` 89 | part=0 90 | partpos=0 91 | } 92 | 93 | showfinal () { 94 | total=`expr $total + $part` 95 | totalpos=`expr $totalpos + $partpos` 96 | echo "Score: $total/$totalpos" 97 | if [ $total -lt $totalpos ]; then 98 | exit 1 99 | fi 100 | } 101 | 102 | passfailmsg () { 103 | msg="$1" 104 | shift 105 | if [ $# -gt 0 ]; then 106 | msg="$msg," 107 | fi 108 | 109 | t1=`date +%s.%N 2>/dev/null` 110 | time=`echo "scale=1; ($t1-$t0)/1" | sed 's/.N/.0/g' | bc 2>/dev/null` 111 | 112 | echo $msg "$@" "(${time}s)" 113 | } 114 | 115 | pass () { 116 | passfailmsg OK "$@" 117 | part=`expr $part + $pts` 118 | partpos=`expr $partpos + $pts` 119 | } 120 | 121 | fail () { 122 | passfailmsg WRONG "$@" 123 | partpos=`expr $partpos + $pts` 124 | if $verbose; then 125 | exit 1 126 | fi 127 | } 128 | 129 | 130 | # 131 | # User tests 132 | # 133 | 134 | # Usage: runtest 135 | runtest () { 136 | perl -e "print '$1: '" 137 | rm -f obj/kern/init.o obj/kern/kernel obj/kern/kernel.img 138 | [ "$preservefs" = y ] || rm -f obj/fs/fs.img 139 | if $verbose 140 | then 141 | echo "$make $2... " 142 | fi 143 | $make $2 >$out 144 | if [ $? -ne 0 ] 145 | then 146 | rm -f obj/kern/init.o 147 | echo $make $2 failed 148 | exit 1 149 | fi 150 | # We just built a weird init.o that runs a specific test. As 151 | # a result, 'make qemu' will run the last graded test and 152 | # 'make clean; make qemu' will run the user-specified 153 | # environment. Remove our weird init.o to fix this. 154 | rm -f obj/kern/init.o 155 | run 156 | 157 | # Give qemu some more time to run (for asynchronous mode). 158 | # This way, we get the small 1 second wait for most tests 159 | # and a longer wait (5 seconds) in case qemu needs that 160 | # time to load. 161 | if [ ! -s jos.out ] 162 | then 163 | sleep 4 164 | fi 165 | 166 | if [ ! -s jos.out ] 167 | then 168 | fail > /dev/null # Still increment number of possible points 169 | echo 'no jos.out' 170 | else 171 | shift 172 | shift 173 | check=$1 174 | shift 175 | $check "$@" 176 | fi 177 | } 178 | 179 | quicktest () { 180 | perl -e "print '$1: '" 181 | shift 182 | checkregexps "$@" 183 | } 184 | 185 | checkregexps () { 186 | okay=yes 187 | 188 | not=false 189 | for i 190 | do 191 | if [ "x$i" = "x!" ] 192 | then 193 | not=true 194 | elif $not 195 | then 196 | if egrep "^$i\$" jos.out >/dev/null 197 | then 198 | echo "got unexpected line '$i'" 199 | okay=no 200 | fi 201 | not=false 202 | else 203 | egrep "^$i\$" jos.out >/dev/null 204 | if [ $? -ne 0 ] 205 | then 206 | echo "missing '$i'" 207 | okay=no 208 | fi 209 | not=false 210 | fi 211 | done 212 | if [ "$okay" = "yes" ] 213 | then 214 | pass 215 | else 216 | fail 217 | fi 218 | } 219 | 220 | # Usage: runtest1 [-tag ] [-dir ] [-Ddef...] [-check checkfn] checkargs... 221 | runtest1 () { 222 | tag= 223 | dir=user 224 | check=checkregexps 225 | while true; do 226 | if [ $1 = -tag ] 227 | then 228 | tag=$2 229 | elif [ $1 = -dir ] 230 | then 231 | dir=$2 232 | else 233 | break 234 | fi 235 | shift 236 | shift 237 | done 238 | prog=$1 239 | shift 240 | if [ "x$tag" = x ] 241 | then 242 | tag=$prog 243 | fi 244 | runtest1_defs= 245 | while expr "x$1" : 'x-D.*' >/dev/null; do 246 | runtest1_defs="DEFS+='$1' $runtest1_defs" 247 | shift 248 | done 249 | if [ "x$1" = "x-check" ]; then 250 | check=$2 251 | shift 252 | shift 253 | fi 254 | runtest "$tag" "DEFS='-DTEST=${dir}_${prog}' $runtest1_defs" "$check" "$@" 255 | } 256 | 257 | -------------------------------------------------------------------------------- /grade-lab1.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | qemuopts="-hda obj/kern/kernel.img" 4 | . ./grade-functions.sh 5 | 6 | 7 | $make 8 | 9 | check () { 10 | pts=20 11 | echo_n "Printf: " 12 | if grep "6828 decimal is 15254 octal!" jos.out >/dev/null 13 | then 14 | pass 15 | else 16 | fail 17 | fi 18 | 19 | pts=10 20 | echo "Backtrace:" 21 | args=`grep "ebp f01.* eip f0100.* args" jos.out | awk '{ print $6 }'` 22 | cnt=`echo $args | grep '^00000000 00000000 00000001 00000002 00000003 00000004 00000005' | wc -w` 23 | echo_n " Count " 24 | if [ $cnt -eq 8 ] 25 | then 26 | pass 27 | else 28 | fail 29 | fi 30 | 31 | cnt=`grep "ebp f01.* eip f0100.* args" jos.out | awk 'BEGIN { FS = ORS = " " } 32 | { print $6 } 33 | END { printf("\n") }' | grep '^00000000 00000000 00000001 00000002 00000003 00000004 00000005' | wc -w` 34 | echo_n " Args " 35 | if [ $cnt -eq 8 ]; then 36 | pass 37 | else 38 | fail "($args)" 39 | fi 40 | 41 | syms=`grep "kern/init.c:[0-9]*: *test_backtrace[+]" jos.out` 42 | symcnt=`grep "kern/init.c:[0-9]*: *test_backtrace[+]" jos.out | wc -l` 43 | echo_n " Symbols " 44 | if [ $symcnt -eq 6 ]; then 45 | pass 46 | else 47 | fail "($syms)" 48 | fi 49 | } 50 | 51 | run 52 | check 53 | 54 | showfinal 55 | -------------------------------------------------------------------------------- /grade-lab2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | qemuopts="-hda obj/kern/kernel.img" 4 | . ./grade-functions.sh 5 | 6 | 7 | $make 8 | 9 | check () { 10 | pts=20 11 | echo_n "Physical page allocator: " 12 | if grep "check_page_alloc() succeeded!" jos.out >/dev/null 13 | then 14 | pass 15 | else 16 | fail 17 | fi 18 | 19 | pts=20 20 | echo_n "Page management: " 21 | if grep "check_page() succeeded!" jos.out >/dev/null 22 | then 23 | pass 24 | else 25 | fail 26 | fi 27 | 28 | pts=20 29 | echo_n "Kernel page directory: " 30 | if grep "check_kern_pgdir() succeeded!" jos.out >/dev/null 31 | then 32 | pass 33 | else 34 | fail 35 | fi 36 | 37 | pts=10 38 | echo_n "Page management 2: " 39 | if grep "check_page_installed_pgdir() succeeded!" jos.out >/dev/null 40 | then 41 | pass 42 | else 43 | fail 44 | fi 45 | } 46 | 47 | run 48 | check 49 | 50 | showfinal 51 | -------------------------------------------------------------------------------- /grade-lab3.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | qemuopts="-hda obj/kern/kernel.img" 4 | . ./grade-functions.sh 5 | 6 | 7 | $make 8 | 9 | # the [00001000] tags should have [] in them, but that's 10 | # a regular expression reserved character, and i'll be damned if 11 | # I can figure out how many \ i need to add to get through 12 | # however many times the shell interprets this string. sigh. 13 | 14 | pts=10 15 | 16 | runtest1 divzero \ 17 | ! '1/0 is ........!' \ 18 | 'Incoming TRAP frame at 0xefbfff..' \ 19 | 'TRAP frame at 0xf.......' \ 20 | ' trap 0x00000000 Divide error' \ 21 | ' eip 0x008.....' \ 22 | ' ss 0x----0023' \ 23 | '.00001000. free env 00001000' 24 | 25 | runtest1 softint \ 26 | 'Welcome to the JOS kernel monitor!' \ 27 | 'Incoming TRAP frame at 0xefbfffbc' \ 28 | 'TRAP frame at 0xf.......' \ 29 | ' trap 0x0000000d General Protection' \ 30 | ' eip 0x008.....' \ 31 | ' ss 0x----0023' \ 32 | '.00001000. free env 00001000' 33 | 34 | runtest1 badsegment \ 35 | 'Incoming TRAP frame at 0xefbfffbc' \ 36 | 'TRAP frame at 0xf.......' \ 37 | ' trap 0x0000000d General Protection' \ 38 | ' err 0x00000028' \ 39 | ' eip 0x008.....' \ 40 | ' ss 0x----0023' \ 41 | '.00001000. free env 00001000' 42 | 43 | showpart A 44 | 45 | pts=5 46 | 47 | runtest1 faultread \ 48 | ! 'I read ........ from location 0!' \ 49 | '.00001000. user fault va 00000000 ip 008.....' \ 50 | 'Incoming TRAP frame at 0xefbfffbc' \ 51 | 'TRAP frame at 0xf.......' \ 52 | ' trap 0x0000000e Page Fault' \ 53 | ' err 0x00000004.*' \ 54 | '.00001000. free env 00001000' 55 | 56 | runtest1 faultreadkernel \ 57 | ! 'I read ........ from location 0xf0100000!' \ 58 | '.00001000. user fault va f0100000 ip 008.....' \ 59 | 'Incoming TRAP frame at 0xefbfffbc' \ 60 | 'TRAP frame at 0xf.......' \ 61 | ' trap 0x0000000e Page Fault' \ 62 | ' err 0x00000005.*' \ 63 | '.00001000. free env 00001000' \ 64 | 65 | runtest1 faultwrite \ 66 | '.00001000. user fault va 00000000 ip 008.....' \ 67 | 'Incoming TRAP frame at 0xefbfffbc' \ 68 | 'TRAP frame at 0xf.......' \ 69 | ' trap 0x0000000e Page Fault' \ 70 | ' err 0x00000006.*' \ 71 | '.00001000. free env 00001000' 72 | 73 | runtest1 faultwritekernel \ 74 | '.00001000. user fault va f0100000 ip 008.....' \ 75 | 'Incoming TRAP frame at 0xefbfffbc' \ 76 | 'TRAP frame at 0xf.......' \ 77 | ' trap 0x0000000e Page Fault' \ 78 | ' err 0x00000007.*' \ 79 | '.00001000. free env 00001000' 80 | 81 | 82 | runtest1 breakpoint \ 83 | 'Welcome to the JOS kernel monitor!' \ 84 | 'Incoming TRAP frame at 0xefbfffbc' \ 85 | 'TRAP frame at 0xf.......' \ 86 | ' trap 0x00000003 Breakpoint' \ 87 | ' eip 0x008.....' \ 88 | ' ss 0x----0023' \ 89 | ! '.00001000. free env 00001000' 90 | 91 | runtest1 testbss \ 92 | 'Making sure bss works right...' \ 93 | 'Yes, good. Now doing a wild write off the end...' \ 94 | '.00001000. user fault va 00c..... ip 008.....' \ 95 | '.00001000. free env 00001000' 96 | 97 | runtest1 hello \ 98 | '.00000000. new env 00001000' \ 99 | 'hello, world' \ 100 | 'i am environment 00001000' \ 101 | '.00001000. exiting gracefully' \ 102 | '.00001000. free env 00001000' \ 103 | 'Destroyed the only environment - nothing more to do!' 104 | 105 | runtest1 buggyhello \ 106 | '.00001000. user_mem_check assertion failure for va 00000001' \ 107 | '.00001000. free env 00001000' 108 | 109 | runtest1 buggyhello2 \ 110 | '.00001000. user_mem_check assertion failure for va 0....000' \ 111 | '.00001000. free env 00001000' \ 112 | ! 'hello, world' 113 | 114 | runtest1 evilhello \ 115 | '.00001000. user_mem_check assertion failure for va f0100...' \ 116 | '.00001000. free env 00001000' 117 | 118 | showpart B 119 | 120 | showfinal 121 | -------------------------------------------------------------------------------- /grade-lab4.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | qemuopts="-hda obj/kern/kernel.img" 4 | . ./grade-functions.sh 5 | 6 | 7 | $make 8 | 9 | timeout=10 10 | E0='07' 11 | E1='08' 12 | E2='09' 13 | E3='0a' 14 | E4='0b' 15 | E5='0c' 16 | E6='0d' 17 | E7='0e' 18 | 19 | runtest1 dumbfork \ 20 | ".00000000. new env 00001000" \ 21 | ".00000000. new env 000010$E1" \ 22 | "0: I am the parent." \ 23 | "9: I am the parent." \ 24 | "0: I am the child." \ 25 | "9: I am the child." \ 26 | "19: I am the child." \ 27 | ".000010$E1. exiting gracefully" \ 28 | ".000010$E1. free env 000010$E1" \ 29 | ".000010$E2. exiting gracefully" \ 30 | ".000010$E2. free env 000010$E2" 31 | 32 | showpart A 33 | 34 | runtest1 faultread \ 35 | ! "I read ........ from location 0." \ 36 | ".000010$E1. user fault va 00000000 ip 008....." \ 37 | "TRAP frame at 0xf....... from CPU ." \ 38 | " trap 0x0000000e Page Fault" \ 39 | " err 0x00000004.*" \ 40 | ".000010$E1. free env 000010$E1" 41 | 42 | runtest1 faultwrite \ 43 | ".000010$E1. user fault va 00000000 ip 008....." \ 44 | "TRAP frame at 0xf....... from CPU ." \ 45 | " trap 0x0000000e Page Fault" \ 46 | " err 0x00000006.*" \ 47 | ".000010$E1. free env 000010$E1" 48 | 49 | runtest1 faultdie \ 50 | "i faulted at va deadbeef, err 6" \ 51 | ".000010$E1. exiting gracefully" \ 52 | ".000010$E1. free env 000010$E1" 53 | 54 | runtest1 faultregs \ 55 | "Registers in UTrapframe OK" \ 56 | ! "Registers in UTrapframe MISMATCH" \ 57 | "Registers after page-fault OK" \ 58 | ! "Registers after page-fault MISMATCH" 59 | 60 | runtest1 faultalloc \ 61 | "fault deadbeef" \ 62 | "this string was faulted in at deadbeef" \ 63 | "fault cafebffe" \ 64 | "fault cafec000" \ 65 | "this string was faulted in at cafebffe" \ 66 | ".000010$E1. exiting gracefully" \ 67 | ".000010$E1. free env 000010$E1" 68 | 69 | runtest1 faultallocbad \ 70 | ".000010$E1. user_mem_check assertion failure for va deadbeef" \ 71 | ".000010$E1. free env 000010$E1" 72 | 73 | runtest1 faultnostack \ 74 | ".000010$E1. user_mem_check assertion failure for va eebfff.." \ 75 | ".000010$E1. free env 000010$E1" 76 | 77 | runtest1 faultbadhandler \ 78 | ".000010$E1. user_mem_check assertion failure for va (deadb|eebfe)..." \ 79 | ".000010$E1. free env 000010$E1" 80 | 81 | runtest1 faultevilhandler \ 82 | ".000010$E1. user_mem_check assertion failure for va (f0100|eebfe)..." \ 83 | ".000010$E1. free env 000010$E1" 84 | 85 | runtest1 forktree \ 86 | "....: I am .0." \ 87 | "....: I am .1." \ 88 | "....: I am .000." \ 89 | "....: I am .100." \ 90 | "....: I am .110." \ 91 | "....: I am .111." \ 92 | "....: I am .011." \ 93 | "....: I am .001." \ 94 | ".000010$E1. exiting gracefully" \ 95 | ".000010$E2. exiting gracefully" \ 96 | ".0000200.. exiting gracefully" \ 97 | ".0000200.. free env 0000200." 98 | 99 | showpart B 100 | 101 | runtest1 spin \ 102 | ".00000000. new env 00001000" \ 103 | ".00000000. new env 000010$E1" \ 104 | "I am the parent. Forking the child..." \ 105 | ".000010$E1. new env 000010$E2" \ 106 | "I am the parent. Running the child..." \ 107 | "I am the child. Spinning..." \ 108 | "I am the parent. Killing the child..." \ 109 | ".000010$E1. destroying 000010$E2" \ 110 | ".000010$E1. free env 000010$E2" \ 111 | ".000010$E1. exiting gracefully" \ 112 | ".000010$E1. free env 000010$E1" 113 | 114 | qemuopts="$qemuopts -smp 2" 115 | 116 | runtest1 stresssched \ 117 | ".000010... stresssched on CPU 0" \ 118 | ".000010... stresssched on CPU 1" \ 119 | ! ".*ran on two CPUs at once" 120 | 121 | runtest1 pingpong \ 122 | ".00000000. new env 00001000" \ 123 | ".00000000. new env 000010$E1" \ 124 | ".000010$E1. new env 000010$E2" \ 125 | "send 0 from 10$E1 to 10$E2" \ 126 | "10$E2 got 0 from 10$E1" \ 127 | "10$E1 got 1 from 10$E2" \ 128 | "10$E2 got 8 from 10$E1" \ 129 | "10$E1 got 9 from 10$E2" \ 130 | "10$E2 got 10 from 10$E1" \ 131 | ".000010$E1. exiting gracefully" \ 132 | ".000010$E1. free env 000010$E1" \ 133 | ".000010$E2. exiting gracefully" \ 134 | ".000010$E2. free env 000010$E2" \ 135 | 136 | timeout=30 137 | runtest1 primes \ 138 | ".00000000. new env 00001000" \ 139 | ".00000000. new env 000010$E1" \ 140 | ".000010$E1. new env 000010$E2" \ 141 | "CPU .: 2 .000010$E2. new env 000010$E3" \ 142 | "CPU .: 3 .000010$E3. new env 000010$E4" \ 143 | "CPU .: 5 .000010$E4. new env 000010$E5" \ 144 | "CPU .: 7 .000010$E5. new env 000010$E6" \ 145 | "CPU .: 11 .000010$E6. new env 000010$E7" \ 146 | "CPU .: 1877 .00001128. new env 00001129" 147 | 148 | showpart C 149 | 150 | showfinal 151 | -------------------------------------------------------------------------------- /grade-lab5.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | qemuopts="-hda obj/kern/kernel.img -hdb obj/fs/fs.img" 4 | . ./grade-functions.sh 5 | 6 | 7 | $make 8 | 9 | qemuopts_orig="$qemuopts" 10 | qemuopts="$qemuopts -snapshot" 11 | 12 | runtest1 -tag 'fs i/o [fs]' hello \ 13 | 'FS can do I/O' \ 14 | ! 'idle loop can do I/O' \ 15 | 16 | quicktest 'check_bc [fs]' \ 17 | 'block cache is good' \ 18 | 19 | quicktest 'check_super [fs]' \ 20 | 'superblock is good' \ 21 | 22 | quicktest 'check_bitmap [fs]' \ 23 | 'bitmap is good' \ 24 | 25 | quicktest 'alloc_block [fs]' \ 26 | 'alloc_block is good' \ 27 | 28 | quicktest 'file_open [fs]' \ 29 | 'file_open is good' \ 30 | 31 | quicktest 'file_get_block [fs]' \ 32 | 'file_get_block is good' \ 33 | 34 | quicktest 'file_flush/file_truncate/file rewrite [fs]' \ 35 | 'file_flush is good' \ 36 | 'file_truncate is good' \ 37 | 'file rewrite is good' \ 38 | 39 | runtest1 -tag 'lib/file.c [testfile]' testfile \ 40 | 'serve_open is good' \ 41 | 'file_stat is good' \ 42 | 'file_close is good' \ 43 | 'stale fileid is good' \ 44 | 45 | quicktest 'file_read [testfile]' \ 46 | 'file_read is good' 47 | 48 | quicktest 'file_write [testfile]' \ 49 | 'file_write is good' 50 | 51 | quicktest 'file_read after file_write [testfile]' \ 52 | 'file_read after file_write is good' 53 | 54 | quicktest 'open [testfile]' \ 55 | 'open is good' \ 56 | 57 | quicktest 'large file [testfile]' \ 58 | 'large file is good' 59 | 60 | qemuopts="$qemuopts_orig" 61 | 62 | pts=10 63 | runtest1 -tag 'motd display [writemotd]' writemotd \ 64 | 'OLD MOTD' \ 65 | 'This is /motd, the message of the day.' \ 66 | 'NEW MOTD' \ 67 | 'This is the NEW message of the day!' \ 68 | 69 | preservefs=y 70 | runtest1 -tag 'motd change [writemotd]' writemotd \ 71 | 'OLD MOTD' \ 72 | 'This is the NEW message of the day!' \ 73 | 'NEW MOTD' \ 74 | ! 'This is /motd, the message of the day.' \ 75 | 76 | pts=15 77 | preservefs=n 78 | runtest1 -tag 'spawn via icode [icode]' icode \ 79 | 'icode: read /motd' \ 80 | 'This is /motd, the message of the day.' \ 81 | 'icode: spawn /init' \ 82 | 'init: running' \ 83 | 'init: data seems okay' \ 84 | 'icode: exiting' \ 85 | 'init: bss seems okay' \ 86 | "init: args: 'init' 'initarg1' 'initarg2'" \ 87 | 'init: exiting' \ 88 | 89 | showfinal 90 | -------------------------------------------------------------------------------- /inc/args.h: -------------------------------------------------------------------------------- 1 | #ifndef JOS_INC_ARGS_H 2 | #define JOS_INC_ARGS_H 3 | 4 | struct Argstate; 5 | 6 | // JOS command-line parsing functions. 7 | 8 | // Initializes the Argstate buffer from argc and argv. 9 | // (Note: it is safe to use a 'const char **' for argv.) 10 | void argstart(int *argc, char **argv, struct Argstate *args); 11 | 12 | // Returns the next flag in the argument list, 13 | // or -1 if there are no more flags. 14 | // 15 | // Flags stop at a non-flag (anything that doesn't start with '-'), 16 | // at the end of the argument list, before "-", or after "--", 17 | // whichever comes first. Any "--" argument is not returned. 18 | // 19 | // Consumes arguments from the argc/argv array passed in to argstart. 20 | // If you argstart with an argc/argv array of ["sh", "-i", "foo"], 21 | // the first call to argnext will return 'i' and change the array to 22 | // ["sh", "foo"]. Thus, when argnext returns -1, the argc/argv array 23 | // contains just the non-flag arguments. 24 | int argnext(struct Argstate *); 25 | 26 | // Returns the next value for the current flag, or 0 if it has no value. 27 | // For example, given an argument list ["-fval1", "val2", "val3"], 28 | // a call to argnext() will return 'f', after which repeated calls to 29 | // argnextvalue will return "val1", "val2", and "val3". 30 | // Consumes arguments from the argc/argv array. 31 | char *argnextvalue(struct Argstate *); 32 | 33 | // Returns the current flag's value, or 0 if it has no value. 34 | // Behaves like argnextvalue, except that repeated calls to argvalue will 35 | // return the same value. 36 | char *argvalue(struct Argstate *); 37 | 38 | // Example: 39 | // 40 | // #include 41 | // 42 | // void 43 | // umain(int argc, char **argv) 44 | // { 45 | // int i; 46 | // struct Argstate args; 47 | // 48 | // argstart(&argc, argv, &args); 49 | // while ((i = argnext(&args)) >= 0) 50 | // switch (i) { 51 | // case 'r': 52 | // case 'x': 53 | // cprintf("'-%c' flag\n", i); 54 | // break; 55 | // case 'f': 56 | // cprintf("'-f %s' flag\n", argvalue(&args)); 57 | // break; 58 | // default: 59 | // cprintf("unknown flag\n"); 60 | // } 61 | // 62 | // for (i = 1; i < argc; i++) 63 | // cprintf("argument '%s'\n", argv[i]); 64 | // } 65 | // 66 | // If this program is run with the arguments 67 | // ["-rx", "-f", "foo", "--", "-r", "duh"] 68 | // it will print out 69 | // '-r' flag 70 | // '-x' flag 71 | // '-f foo' flag 72 | // argument '-r' 73 | // argument 'duh' 74 | 75 | struct Argstate { 76 | int *argc; 77 | const char **argv; 78 | const char *curarg; 79 | const char *argvalue; 80 | }; 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /inc/assert.h: -------------------------------------------------------------------------------- 1 | /* See COPYRIGHT for copyright information. */ 2 | 3 | #ifndef JOS_INC_ASSERT_H 4 | #define JOS_INC_ASSERT_H 5 | 6 | #include 7 | 8 | void _warn(const char*, int, const char*, ...); 9 | void _panic(const char*, int, const char*, ...) __attribute__((noreturn)); 10 | 11 | #define warn(...) _warn(__FILE__, __LINE__, __VA_ARGS__) 12 | #define panic(...) _panic(__FILE__, __LINE__, __VA_ARGS__) 13 | 14 | #define assert(x) \ 15 | do { if (!(x)) panic("assertion failed: %s", #x); } while (0) 16 | 17 | // static_assert(x) will generate a compile-time error if 'x' is false. 18 | #define static_assert(x) switch (x) case 0: case (x): 19 | 20 | #endif /* !JOS_INC_ASSERT_H */ 21 | -------------------------------------------------------------------------------- /inc/elf.h: -------------------------------------------------------------------------------- 1 | #ifndef JOS_INC_ELF_H 2 | #define JOS_INC_ELF_H 3 | 4 | #define ELF_MAGIC 0x464C457FU /* "\x7FELF" in little endian */ 5 | 6 | struct Elf { 7 | uint32_t e_magic; // must equal ELF_MAGIC 8 | uint8_t e_elf[12]; 9 | uint16_t e_type; 10 | uint16_t e_machine; 11 | uint32_t e_version; 12 | uint32_t e_entry; 13 | uint32_t e_phoff; 14 | uint32_t e_shoff; 15 | uint32_t e_flags; 16 | uint16_t e_ehsize; 17 | uint16_t e_phentsize; 18 | uint16_t e_phnum; 19 | uint16_t e_shentsize; 20 | uint16_t e_shnum; 21 | uint16_t e_shstrndx; 22 | }; 23 | 24 | struct Proghdr { 25 | uint32_t p_type; 26 | uint32_t p_offset; 27 | uint32_t p_va; 28 | uint32_t p_pa; 29 | uint32_t p_filesz; 30 | uint32_t p_memsz; 31 | uint32_t p_flags; 32 | uint32_t p_align; 33 | }; 34 | 35 | struct Secthdr { 36 | uint32_t sh_name; 37 | uint32_t sh_type; 38 | uint32_t sh_flags; 39 | uint32_t sh_addr; 40 | uint32_t sh_offset; 41 | uint32_t sh_size; 42 | uint32_t sh_link; 43 | uint32_t sh_info; 44 | uint32_t sh_addralign; 45 | uint32_t sh_entsize; 46 | }; 47 | 48 | // Values for Proghdr::p_type 49 | #define ELF_PROG_LOAD 1 50 | 51 | // Flag bits for Proghdr::p_flags 52 | #define ELF_PROG_FLAG_EXEC 1 53 | #define ELF_PROG_FLAG_WRITE 2 54 | #define ELF_PROG_FLAG_READ 4 55 | 56 | // Values for Secthdr::sh_type 57 | #define ELF_SHT_NULL 0 58 | #define ELF_SHT_PROGBITS 1 59 | #define ELF_SHT_SYMTAB 2 60 | #define ELF_SHT_STRTAB 3 61 | 62 | // Values for Secthdr::sh_name 63 | #define ELF_SHN_UNDEF 0 64 | 65 | #endif /* !JOS_INC_ELF_H */ 66 | -------------------------------------------------------------------------------- /inc/env.h: -------------------------------------------------------------------------------- 1 | /* See COPYRIGHT for copyright information. */ 2 | 3 | #ifndef JOS_INC_ENV_H 4 | #define JOS_INC_ENV_H 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | typedef int32_t envid_t; 11 | 12 | // An environment ID 'envid_t' has three parts: 13 | // 14 | // +1+---------------21-----------------+--------10--------+ 15 | // |0| Uniqueifier | Environment | 16 | // | | | Index | 17 | // +------------------------------------+------------------+ 18 | // \--- ENVX(eid) --/ 19 | // 20 | // The environment index ENVX(eid) equals the environment's offset in the 21 | // 'envs[]' array. The uniqueifier distinguishes environments that were 22 | // created at different times, but share the same environment index. 23 | // 24 | // All real environments are greater than 0 (so the sign bit is zero). 25 | // envid_ts less than 0 signify errors. The envid_t == 0 is special, and 26 | // stands for the current environment. 27 | 28 | #define LOG2NENV 10 29 | #define NENV (1 << LOG2NENV) 30 | #define ENVX(envid) ((envid) & (NENV - 1)) 31 | 32 | // Values of env_status in struct Env 33 | enum { 34 | ENV_FREE = 0, 35 | ENV_DYING, 36 | ENV_RUNNABLE, 37 | ENV_RUNNING, 38 | ENV_NOT_RUNNABLE 39 | }; 40 | 41 | // Special environment types 42 | enum EnvType { 43 | ENV_TYPE_USER = 0, 44 | ENV_TYPE_IDLE, 45 | ENV_TYPE_FS, // File system server 46 | }; 47 | 48 | struct Env { 49 | struct Trapframe env_tf; // Saved registers 50 | struct Env *env_link; // Next free Env 51 | envid_t env_id; // Unique environment identifier 52 | envid_t env_parent_id; // env_id of this env's parent 53 | enum EnvType env_type; // Indicates special system environments 54 | unsigned env_status; // Status of the environment 55 | uint32_t env_runs; // Number of times environment has run 56 | int env_cpunum; // The CPU that the env is running on 57 | 58 | // Address space 59 | pde_t *env_pgdir; // Kernel virtual address of page dir 60 | 61 | // Exception handling 62 | void *env_pgfault_upcall; // Page fault upcall entry point 63 | 64 | // Lab 4 IPC 65 | bool env_ipc_recving; // Env is blocked receiving 66 | void *env_ipc_dstva; // VA at which to map received page 67 | uint32_t env_ipc_value; // Data value sent to us 68 | envid_t env_ipc_from; // envid of the sender 69 | int env_ipc_perm; // Perm of page mapping received 70 | }; 71 | 72 | #endif // !JOS_INC_ENV_H 73 | -------------------------------------------------------------------------------- /inc/error.h: -------------------------------------------------------------------------------- 1 | /* See COPYRIGHT for copyright information. */ 2 | 3 | #ifndef JOS_INC_ERROR_H 4 | #define JOS_INC_ERROR_H 5 | 6 | enum { 7 | // Kernel error codes -- keep in sync with list in lib/printfmt.c. 8 | E_UNSPECIFIED = 1, // Unspecified or unknown problem 9 | E_BAD_ENV = 2, // Environment doesn't exist or otherwise 10 | // cannot be used in requested action 11 | E_INVAL = 3, // Invalid parameter 12 | E_NO_MEM = 4, // Request failed due to memory shortage 13 | E_NO_FREE_ENV = 5, // Attempt to create a new environment beyond 14 | // the maximum allowed 15 | E_FAULT = 6, // Memory fault 16 | 17 | E_IPC_NOT_RECV = 7, // Attempt to send to env that is not recving 18 | E_EOF = 8, // Unexpected end of file 19 | 20 | // File system error codes -- only seen in user-level 21 | E_NO_DISK = 9, // No free space left on disk 22 | E_MAX_OPEN = 10, // Too many files are open 23 | E_NOT_FOUND = 11, // File or block not found 24 | E_BAD_PATH = 12, // Bad path 25 | E_FILE_EXISTS = 13, // File already exists 26 | E_NOT_EXEC = 14, // File not a valid executable 27 | E_NOT_SUPP = 15, // Operation not supported 28 | 29 | MAXERROR 30 | }; 31 | 32 | #endif // !JOS_INC_ERROR_H */ 33 | -------------------------------------------------------------------------------- /inc/fd.h: -------------------------------------------------------------------------------- 1 | // Public definitions for the POSIX-like file descriptor emulation layer 2 | // that our user-land support library implements for the use of applications. 3 | // See the code in the lib directory for the implementation details. 4 | 5 | #ifndef JOS_INC_FD_H 6 | #define JOS_INC_FD_H 7 | 8 | #include 9 | #include 10 | 11 | struct Fd; 12 | struct Stat; 13 | struct Dev; 14 | 15 | // Per-device-class file descriptor operations 16 | struct Dev { 17 | int dev_id; 18 | const char *dev_name; 19 | ssize_t (*dev_read)(struct Fd *fd, void *buf, size_t len); 20 | ssize_t (*dev_write)(struct Fd *fd, const void *buf, size_t len); 21 | int (*dev_close)(struct Fd *fd); 22 | int (*dev_stat)(struct Fd *fd, struct Stat *stat); 23 | int (*dev_trunc)(struct Fd *fd, off_t length); 24 | }; 25 | 26 | struct FdFile { 27 | int id; 28 | }; 29 | 30 | struct Fd { 31 | int fd_dev_id; 32 | off_t fd_offset; 33 | int fd_omode; 34 | union { 35 | // File server files 36 | struct FdFile fd_file; 37 | }; 38 | }; 39 | 40 | struct Stat { 41 | char st_name[MAXNAMELEN]; 42 | off_t st_size; 43 | int st_isdir; 44 | struct Dev *st_dev; 45 | }; 46 | 47 | char* fd2data(struct Fd *fd); 48 | int fd2num(struct Fd *fd); 49 | int fd_alloc(struct Fd **fd_store); 50 | int fd_close(struct Fd *fd, bool must_exist); 51 | int fd_lookup(int fdnum, struct Fd **fd_store); 52 | int dev_lookup(int devid, struct Dev **dev_store); 53 | 54 | extern struct Dev devfile; 55 | 56 | #endif // not JOS_INC_FD_H 57 | -------------------------------------------------------------------------------- /inc/fs.h: -------------------------------------------------------------------------------- 1 | // See COPYRIGHT for copyright information. 2 | 3 | #ifndef JOS_INC_FS_H 4 | #define JOS_INC_FS_H 5 | 6 | #include 7 | #include 8 | 9 | // File nodes (both in-memory and on-disk) 10 | 11 | // Bytes per file system block - same as page size 12 | #define BLKSIZE PGSIZE 13 | #define BLKBITSIZE (BLKSIZE * 8) 14 | 15 | // Maximum size of a filename (a single path component), including null 16 | // Must be a multiple of 4 17 | #define MAXNAMELEN 128 18 | 19 | // Maximum size of a complete pathname, including null 20 | #define MAXPATHLEN 1024 21 | 22 | // Number of block pointers in a File descriptor 23 | #define NDIRECT 10 24 | // Number of direct block pointers in an indirect block 25 | #define NINDIRECT (BLKSIZE / 4) 26 | 27 | #define MAXFILESIZE ((NDIRECT + NINDIRECT) * BLKSIZE) 28 | 29 | struct File { 30 | char f_name[MAXNAMELEN]; // filename 31 | off_t f_size; // file size in bytes 32 | uint32_t f_type; // file type 33 | 34 | // Block pointers. 35 | // A block is allocated iff its value is != 0. 36 | uint32_t f_direct[NDIRECT]; // direct blocks 37 | uint32_t f_indirect; // indirect block 38 | 39 | // Pad out to 256 bytes; must do arithmetic in case we're compiling 40 | // fsformat on a 64-bit machine. 41 | uint8_t f_pad[256 - MAXNAMELEN - 8 - 4*NDIRECT - 4]; 42 | } __attribute__((packed)); // required only on some 64-bit machines 43 | 44 | // An inode block contains exactly BLKFILES 'struct File's 45 | #define BLKFILES (BLKSIZE / sizeof(struct File)) 46 | 47 | // File types 48 | #define FTYPE_REG 0 // Regular file 49 | #define FTYPE_DIR 1 // Directory 50 | 51 | 52 | // File system super-block (both in-memory and on-disk) 53 | 54 | #define FS_MAGIC 0x4A0530AE // related vaguely to 'J\0S!' 55 | 56 | struct Super { 57 | uint32_t s_magic; // Magic number: FS_MAGIC 58 | uint32_t s_nblocks; // Total number of blocks on disk 59 | struct File s_root; // Root directory node 60 | }; 61 | 62 | // Definitions for requests from clients to file system 63 | enum { 64 | FSREQ_OPEN = 1, 65 | FSREQ_SET_SIZE, 66 | // Read returns a Fsret_read on the request page 67 | FSREQ_READ, 68 | FSREQ_WRITE, 69 | // Stat returns a Fsret_stat on the request page 70 | FSREQ_STAT, 71 | FSREQ_FLUSH, 72 | FSREQ_REMOVE, 73 | FSREQ_SYNC 74 | }; 75 | 76 | union Fsipc { 77 | struct Fsreq_open { 78 | char req_path[MAXPATHLEN]; 79 | int req_omode; 80 | } open; 81 | struct Fsreq_set_size { 82 | int req_fileid; 83 | off_t req_size; 84 | } set_size; 85 | struct Fsreq_read { 86 | int req_fileid; 87 | size_t req_n; 88 | } read; 89 | struct Fsret_read { 90 | char ret_buf[PGSIZE]; 91 | } readRet; 92 | struct Fsreq_write { 93 | int req_fileid; 94 | size_t req_n; 95 | char req_buf[PGSIZE - (sizeof(int) + sizeof(size_t))]; 96 | } write; 97 | struct Fsreq_stat { 98 | int req_fileid; 99 | } stat; 100 | struct Fsret_stat { 101 | char ret_name[MAXNAMELEN]; 102 | off_t ret_size; 103 | int ret_isdir; 104 | } statRet; 105 | struct Fsreq_flush { 106 | int req_fileid; 107 | } flush; 108 | struct Fsreq_remove { 109 | char req_path[MAXPATHLEN]; 110 | } remove; 111 | 112 | // Ensure Fsipc is one page 113 | char _pad[PGSIZE]; 114 | }; 115 | 116 | #endif /* !JOS_INC_FS_H */ 117 | -------------------------------------------------------------------------------- /inc/kbdreg.h: -------------------------------------------------------------------------------- 1 | #ifndef JOS_KBDREG_H 2 | #define JOS_KBDREG_H 3 | 4 | // Special keycodes 5 | #define KEY_HOME 0xE0 6 | #define KEY_END 0xE1 7 | #define KEY_UP 0xE2 8 | #define KEY_DN 0xE3 9 | #define KEY_LF 0xE4 10 | #define KEY_RT 0xE5 11 | #define KEY_PGUP 0xE6 12 | #define KEY_PGDN 0xE7 13 | #define KEY_INS 0xE8 14 | #define KEY_DEL 0xE9 15 | 16 | 17 | /* This is i8042reg.h + kbdreg.h from NetBSD. */ 18 | 19 | #define KBSTATP 0x64 /* kbd controller status port(I) */ 20 | #define KBS_DIB 0x01 /* kbd data in buffer */ 21 | #define KBS_IBF 0x02 /* kbd input buffer low */ 22 | #define KBS_WARM 0x04 /* kbd input buffer low */ 23 | #define KBS_OCMD 0x08 /* kbd output buffer has command */ 24 | #define KBS_NOSEC 0x10 /* kbd security lock not engaged */ 25 | #define KBS_TERR 0x20 /* kbd transmission error */ 26 | #define KBS_RERR 0x40 /* kbd receive error */ 27 | #define KBS_PERR 0x80 /* kbd parity error */ 28 | 29 | #define KBCMDP 0x64 /* kbd controller port(O) */ 30 | #define KBC_RAMREAD 0x20 /* read from RAM */ 31 | #define KBC_RAMWRITE 0x60 /* write to RAM */ 32 | #define KBC_AUXDISABLE 0xa7 /* disable auxiliary port */ 33 | #define KBC_AUXENABLE 0xa8 /* enable auxiliary port */ 34 | #define KBC_AUXTEST 0xa9 /* test auxiliary port */ 35 | #define KBC_KBDECHO 0xd2 /* echo to keyboard port */ 36 | #define KBC_AUXECHO 0xd3 /* echo to auxiliary port */ 37 | #define KBC_AUXWRITE 0xd4 /* write to auxiliary port */ 38 | #define KBC_SELFTEST 0xaa /* start self-test */ 39 | #define KBC_KBDTEST 0xab /* test keyboard port */ 40 | #define KBC_KBDDISABLE 0xad /* disable keyboard port */ 41 | #define KBC_KBDENABLE 0xae /* enable keyboard port */ 42 | #define KBC_PULSE0 0xfe /* pulse output bit 0 */ 43 | #define KBC_PULSE1 0xfd /* pulse output bit 1 */ 44 | #define KBC_PULSE2 0xfb /* pulse output bit 2 */ 45 | #define KBC_PULSE3 0xf7 /* pulse output bit 3 */ 46 | 47 | #define KBDATAP 0x60 /* kbd data port(I) */ 48 | #define KBOUTP 0x60 /* kbd data port(O) */ 49 | 50 | #define K_RDCMDBYTE 0x20 51 | #define K_LDCMDBYTE 0x60 52 | 53 | #define KC8_TRANS 0x40 /* convert to old scan codes */ 54 | #define KC8_MDISABLE 0x20 /* disable mouse */ 55 | #define KC8_KDISABLE 0x10 /* disable keyboard */ 56 | #define KC8_IGNSEC 0x08 /* ignore security lock */ 57 | #define KC8_CPU 0x04 /* exit from protected mode reset */ 58 | #define KC8_MENABLE 0x02 /* enable mouse interrupt */ 59 | #define KC8_KENABLE 0x01 /* enable keyboard interrupt */ 60 | #define CMDBYTE (KC8_TRANS|KC8_CPU|KC8_MENABLE|KC8_KENABLE) 61 | 62 | /* keyboard commands */ 63 | #define KBC_RESET 0xFF /* reset the keyboard */ 64 | #define KBC_RESEND 0xFE /* request the keyboard resend the last byte */ 65 | #define KBC_SETDEFAULT 0xF6 /* resets keyboard to its power-on defaults */ 66 | #define KBC_DISABLE 0xF5 /* as per KBC_SETDEFAULT, but also disable key scanning */ 67 | #define KBC_ENABLE 0xF4 /* enable key scanning */ 68 | #define KBC_TYPEMATIC 0xF3 /* set typematic rate and delay */ 69 | #define KBC_SETTABLE 0xF0 /* set scancode translation table */ 70 | #define KBC_MODEIND 0xED /* set mode indicators(i.e. LEDs) */ 71 | #define KBC_ECHO 0xEE /* request an echo from the keyboard */ 72 | 73 | /* keyboard responses */ 74 | #define KBR_EXTENDED 0xE0 /* extended key sequence */ 75 | #define KBR_RESEND 0xFE /* needs resend of command */ 76 | #define KBR_ACK 0xFA /* received a valid command */ 77 | #define KBR_OVERRUN 0x00 /* flooded */ 78 | #define KBR_FAILURE 0xFD /* diagnosic failure */ 79 | #define KBR_BREAK 0xF0 /* break code prefix - sent on key release */ 80 | #define KBR_RSTDONE 0xAA /* reset complete */ 81 | #define KBR_ECHO 0xEE /* echo response */ 82 | 83 | #endif /* !JOS_KBDREG_H */ 84 | -------------------------------------------------------------------------------- /inc/lib.h: -------------------------------------------------------------------------------- 1 | // Main public header file for our user-land support library, 2 | // whose code lives in the lib directory. 3 | // This library is roughly our OS's version of a standard C library, 4 | // and is intended to be linked into all user-mode applications 5 | // (NOT the kernel or boot loader). 6 | 7 | #ifndef JOS_INC_LIB_H 8 | #define JOS_INC_LIB_H 1 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #define USED(x) (void)(x) 25 | 26 | // main user program 27 | void umain(int argc, char **argv); 28 | 29 | // libmain.c or entry.S 30 | extern const char *binaryname; 31 | extern const volatile struct Env *thisenv; 32 | extern const volatile struct Env envs[NENV]; 33 | extern const volatile struct Page pages[]; 34 | 35 | // exit.c 36 | void exit(void); 37 | 38 | // pgfault.c 39 | void set_pgfault_handler(void (*handler)(struct UTrapframe *utf)); 40 | 41 | // readline.c 42 | char* readline(const char *buf); 43 | 44 | // syscall.c 45 | void sys_cputs(const char *string, size_t len); 46 | int sys_cgetc(void); 47 | envid_t sys_getenvid(void); 48 | int sys_env_destroy(envid_t); 49 | void sys_yield(void); 50 | static envid_t sys_exofork(void); 51 | int sys_env_set_status(envid_t env, int status); 52 | int sys_env_set_trapframe(envid_t env, struct Trapframe *tf); 53 | int sys_env_set_pgfault_upcall(envid_t env, void *upcall); 54 | int sys_page_alloc(envid_t env, void *pg, int perm); 55 | int sys_page_map(envid_t src_env, void *src_pg, 56 | envid_t dst_env, void *dst_pg, int perm); 57 | int sys_page_unmap(envid_t env, void *pg); 58 | int sys_ipc_try_send(envid_t to_env, uint32_t value, void *pg, int perm); 59 | int sys_ipc_recv(void *rcv_pg); 60 | 61 | // This must be inlined. Exercise for reader: why? 62 | static __inline envid_t __attribute__((always_inline)) 63 | sys_exofork(void) 64 | { 65 | envid_t ret; 66 | __asm __volatile("int %2" 67 | : "=a" (ret) 68 | : "a" (SYS_exofork), 69 | "i" (T_SYSCALL) 70 | ); 71 | return ret; 72 | } 73 | 74 | // ipc.c 75 | void ipc_send(envid_t to_env, uint32_t value, void *pg, int perm); 76 | int32_t ipc_recv(envid_t *from_env_store, void *pg, int *perm_store); 77 | envid_t ipc_find_env(enum EnvType type); 78 | 79 | // fork.c 80 | #define PTE_SHARE 0x400 81 | envid_t fork(void); 82 | envid_t sfork(void); // Challenge! 83 | 84 | // fd.c 85 | int close(int fd); 86 | ssize_t read(int fd, void *buf, size_t nbytes); 87 | ssize_t write(int fd, const void *buf, size_t nbytes); 88 | int seek(int fd, off_t offset); 89 | void close_all(void); 90 | ssize_t readn(int fd, void *buf, size_t nbytes); 91 | int dup(int oldfd, int newfd); 92 | int fstat(int fd, struct Stat *statbuf); 93 | int stat(const char *path, struct Stat *statbuf); 94 | 95 | // file.c 96 | int open(const char *path, int mode); 97 | int ftruncate(int fd, off_t size); 98 | int remove(const char *path); 99 | int sync(void); 100 | 101 | // pageref.c 102 | int pageref(void *addr); 103 | 104 | 105 | // spawn.c 106 | envid_t spawn(const char *program, const char **argv); 107 | envid_t spawnl(const char *program, const char *arg0, ...); 108 | 109 | 110 | /* File open modes */ 111 | #define O_RDONLY 0x0000 /* open for reading only */ 112 | #define O_WRONLY 0x0001 /* open for writing only */ 113 | #define O_RDWR 0x0002 /* open for reading and writing */ 114 | #define O_ACCMODE 0x0003 /* mask for above modes */ 115 | 116 | #define O_CREAT 0x0100 /* create if nonexistent */ 117 | #define O_TRUNC 0x0200 /* truncate to zero length */ 118 | #define O_EXCL 0x0400 /* error if already exists */ 119 | #define O_MKDIR 0x0800 /* create directory, not regular file */ 120 | 121 | #endif // !JOS_INC_LIB_H 122 | -------------------------------------------------------------------------------- /inc/stab.h: -------------------------------------------------------------------------------- 1 | #ifndef JOS_STAB_H 2 | #define JOS_STAB_H 3 | #include 4 | 5 | // 6 | // STABS debugging info 7 | 8 | // The JOS kernel debugger can understand some debugging information 9 | // in the STABS format. For more information on this format, see 10 | // http://sourceware.org/gdb/onlinedocs/stabs.html 11 | 12 | // The constants below define some symbol types used by various debuggers 13 | // and compilers. JOS uses the N_SO, N_SOL, N_FUN, and N_SLINE types. 14 | 15 | #define N_GSYM 0x20 // global symbol 16 | #define N_FNAME 0x22 // F77 function name 17 | #define N_FUN 0x24 // procedure name 18 | #define N_STSYM 0x26 // data segment variable 19 | #define N_LCSYM 0x28 // bss segment variable 20 | #define N_MAIN 0x2a // main function name 21 | #define N_PC 0x30 // global Pascal symbol 22 | #define N_RSYM 0x40 // register variable 23 | #define N_SLINE 0x44 // text segment line number 24 | #define N_DSLINE 0x46 // data segment line number 25 | #define N_BSLINE 0x48 // bss segment line number 26 | #define N_SSYM 0x60 // structure/union element 27 | #define N_SO 0x64 // main source file name 28 | #define N_LSYM 0x80 // stack variable 29 | #define N_BINCL 0x82 // include file beginning 30 | #define N_SOL 0x84 // included source file name 31 | #define N_PSYM 0xa0 // parameter variable 32 | #define N_EINCL 0xa2 // include file end 33 | #define N_ENTRY 0xa4 // alternate entry point 34 | #define N_LBRAC 0xc0 // left bracket 35 | #define N_EXCL 0xc2 // deleted include file 36 | #define N_RBRAC 0xe0 // right bracket 37 | #define N_BCOMM 0xe2 // begin common 38 | #define N_ECOMM 0xe4 // end common 39 | #define N_ECOML 0xe8 // end common (local name) 40 | #define N_LENG 0xfe // length of preceding entry 41 | 42 | // Entries in the STABS table are formatted as follows. 43 | struct Stab { 44 | uint32_t n_strx; // index into string table of name 45 | uint8_t n_type; // type of symbol 46 | uint8_t n_other; // misc info (usually empty) 47 | uint16_t n_desc; // description field 48 | uintptr_t n_value; // value of symbol 49 | }; 50 | 51 | #endif /* !JOS_STAB_H */ 52 | -------------------------------------------------------------------------------- /inc/stdarg.h: -------------------------------------------------------------------------------- 1 | /* $NetBSD: stdarg.h,v 1.12 1995/12/25 23:15:31 mycroft Exp $ */ 2 | 3 | #ifndef JOS_INC_STDARG_H 4 | #define JOS_INC_STDARG_H 5 | 6 | typedef __builtin_va_list va_list; 7 | 8 | #define va_start(ap, last) __builtin_va_start(ap, last) 9 | 10 | #define va_arg(ap, type) __builtin_va_arg(ap, type) 11 | 12 | #define va_end(ap) __builtin_va_end(ap) 13 | 14 | #endif /* !JOS_INC_STDARG_H */ 15 | -------------------------------------------------------------------------------- /inc/stdio.h: -------------------------------------------------------------------------------- 1 | #ifndef JOS_INC_STDIO_H 2 | #define JOS_INC_STDIO_H 3 | 4 | #include 5 | 6 | #ifndef NULL 7 | #define NULL ((void *) 0) 8 | #endif /* !NULL */ 9 | 10 | // lib/stdio.c 11 | void cputchar(int c); 12 | int getchar(void); 13 | int iscons(int fd); 14 | 15 | // lib/printfmt.c 16 | void printfmt(void (*putch)(int, void*), void *putdat, const char *fmt, ...); 17 | void vprintfmt(void (*putch)(int, void*), void *putdat, const char *fmt, va_list); 18 | int snprintf(char *str, int size, const char *fmt, ...); 19 | int vsnprintf(char *str, int size, const char *fmt, va_list); 20 | 21 | // lib/printf.c 22 | int cprintf(const char *fmt, ...); 23 | int vcprintf(const char *fmt, va_list); 24 | 25 | // lib/fprintf.c 26 | int printf(const char *fmt, ...); 27 | int fprintf(int fd, const char *fmt, ...); 28 | int vfprintf(int fd, const char *fmt, va_list); 29 | 30 | // lib/readline.c 31 | char* readline(const char *prompt); 32 | 33 | #endif /* !JOS_INC_STDIO_H */ 34 | -------------------------------------------------------------------------------- /inc/string.h: -------------------------------------------------------------------------------- 1 | #ifndef JOS_INC_STRING_H 2 | #define JOS_INC_STRING_H 3 | 4 | #include 5 | 6 | int strlen(const char *s); 7 | int strnlen(const char *s, size_t size); 8 | char * strcpy(char *dst, const char *src); 9 | char * strncpy(char *dst, const char *src, size_t size); 10 | char * strcat(char *dst, const char *src); 11 | size_t strlcpy(char *dst, const char *src, size_t size); 12 | int strcmp(const char *s1, const char *s2); 13 | int strncmp(const char *s1, const char *s2, size_t size); 14 | char * strchr(const char *s, char c); 15 | char * strfind(const char *s, char c); 16 | 17 | void * memset(void *dst, int c, size_t len); 18 | /* no memcpy - use memmove instead */ 19 | void * memmove(void *dst, const void *src, size_t len); 20 | int memcmp(const void *s1, const void *s2, size_t len); 21 | void * memfind(const void *s, int c, size_t len); 22 | 23 | long strtol(const char *s, char **endptr, int base); 24 | 25 | #endif /* not JOS_INC_STRING_H */ 26 | -------------------------------------------------------------------------------- /inc/syscall.h: -------------------------------------------------------------------------------- 1 | #ifndef JOS_INC_SYSCALL_H 2 | #define JOS_INC_SYSCALL_H 3 | 4 | /* system call numbers */ 5 | enum { 6 | SYS_cputs = 0, 7 | SYS_cgetc, 8 | SYS_getenvid, 9 | SYS_env_destroy, 10 | SYS_page_alloc, 11 | SYS_page_map, 12 | SYS_page_unmap, 13 | SYS_exofork, 14 | SYS_env_set_status, 15 | SYS_env_set_trapframe, 16 | SYS_env_set_pgfault_upcall, 17 | SYS_yield, 18 | SYS_ipc_try_send, 19 | SYS_ipc_recv, 20 | NSYSCALLS 21 | }; 22 | 23 | #endif /* !JOS_INC_SYSCALL_H */ 24 | -------------------------------------------------------------------------------- /inc/trap.h: -------------------------------------------------------------------------------- 1 | #ifndef JOS_INC_TRAP_H 2 | #define JOS_INC_TRAP_H 3 | 4 | // Trap numbers 5 | // These are processor defined: 6 | #define T_DIVIDE 0 // divide error 7 | #define T_DEBUG 1 // debug exception 8 | #define T_NMI 2 // non-maskable interrupt 9 | #define T_BRKPT 3 // breakpoint 10 | #define T_OFLOW 4 // overflow 11 | #define T_BOUND 5 // bounds check 12 | #define T_ILLOP 6 // illegal opcode 13 | #define T_DEVICE 7 // device not available 14 | #define T_DBLFLT 8 // double fault 15 | /* #define T_COPROC 9 */ // reserved (not generated by recent processors) 16 | #define T_TSS 10 // invalid task switch segment 17 | #define T_SEGNP 11 // segment not present 18 | #define T_STACK 12 // stack exception 19 | #define T_GPFLT 13 // general protection fault 20 | #define T_PGFLT 14 // page fault 21 | /* #define T_RES 15 */ // reserved 22 | #define T_FPERR 16 // floating point error 23 | #define T_ALIGN 17 // aligment check 24 | #define T_MCHK 18 // machine check 25 | #define T_SIMDERR 19 // SIMD floating point error 26 | 27 | // These are arbitrarily chosen, but with care not to overlap 28 | // processor defined exceptions or interrupt vectors. 29 | #define T_SYSCALL 48 // system call 30 | #define T_DEFAULT 500 // catchall 31 | 32 | #define IRQ_OFFSET 32 // IRQ 0 corresponds to int IRQ_OFFSET 33 | 34 | // Hardware IRQ numbers. We receive these as (IRQ_OFFSET+IRQ_WHATEVER) 35 | #define IRQ_TIMER 0 36 | #define IRQ_KBD 1 37 | #define IRQ_SERIAL 4 38 | #define IRQ_SPURIOUS 7 39 | #define IRQ_IDE 14 40 | #define IRQ_ERROR 19 41 | 42 | #ifndef __ASSEMBLER__ 43 | 44 | #include 45 | 46 | struct PushRegs { 47 | /* registers as pushed by pusha */ 48 | uint32_t reg_edi; 49 | uint32_t reg_esi; 50 | uint32_t reg_ebp; 51 | uint32_t reg_oesp; /* Useless */ 52 | uint32_t reg_ebx; 53 | uint32_t reg_edx; 54 | uint32_t reg_ecx; 55 | uint32_t reg_eax; 56 | } __attribute__((packed)); 57 | 58 | struct Trapframe { 59 | struct PushRegs tf_regs; 60 | uint16_t tf_es; 61 | uint16_t tf_padding1; 62 | uint16_t tf_ds; 63 | uint16_t tf_padding2; 64 | uint32_t tf_trapno; 65 | /* below here defined by x86 hardware */ 66 | uint32_t tf_err; 67 | uintptr_t tf_eip; 68 | uint16_t tf_cs; 69 | uint16_t tf_padding3; 70 | uint32_t tf_eflags; 71 | /* below here only when crossing rings, such as from user to kernel */ 72 | uintptr_t tf_esp; 73 | uint16_t tf_ss; 74 | uint16_t tf_padding4; 75 | } __attribute__((packed)); 76 | 77 | struct UTrapframe { 78 | /* information about the fault */ 79 | uint32_t utf_fault_va; /* va for T_PGFLT, 0 otherwise */ 80 | uint32_t utf_err; 81 | /* trap-time return state */ 82 | struct PushRegs utf_regs; 83 | uintptr_t utf_eip; 84 | uint32_t utf_eflags; 85 | /* the trap-time stack to return to */ 86 | uintptr_t utf_esp; 87 | } __attribute__((packed)); 88 | 89 | #endif /* !__ASSEMBLER__ */ 90 | 91 | #endif /* !JOS_INC_TRAP_H */ 92 | -------------------------------------------------------------------------------- /inc/types.h: -------------------------------------------------------------------------------- 1 | #ifndef JOS_INC_TYPES_H 2 | #define JOS_INC_TYPES_H 3 | 4 | #ifndef NULL 5 | #define NULL ((void*) 0) 6 | #endif 7 | 8 | // Represents true-or-false values 9 | typedef int bool; 10 | 11 | // Explicitly-sized versions of integer types 12 | typedef __signed char int8_t; 13 | typedef unsigned char uint8_t; 14 | typedef short int16_t; 15 | typedef unsigned short uint16_t; 16 | typedef int int32_t; 17 | typedef unsigned int uint32_t; 18 | typedef long long int64_t; 19 | typedef unsigned long long uint64_t; 20 | 21 | // Pointers and addresses are 32 bits long. 22 | // We use pointer types to represent virtual addresses, 23 | // uintptr_t to represent the numerical values of virtual addresses, 24 | // and physaddr_t to represent physical addresses. 25 | typedef int32_t intptr_t; 26 | typedef uint32_t uintptr_t; 27 | typedef uint32_t physaddr_t; 28 | 29 | // Page numbers are 32 bits long. 30 | typedef uint32_t ppn_t; 31 | 32 | // size_t is used for memory object sizes. 33 | typedef uint32_t size_t; 34 | // ssize_t is a signed version of ssize_t, used in case there might be an 35 | // error return. 36 | typedef int32_t ssize_t; 37 | 38 | // off_t is used for file offsets and lengths. 39 | typedef int32_t off_t; 40 | 41 | // Efficient min and max operations 42 | #define MIN(_a, _b) \ 43 | ({ \ 44 | typeof(_a) __a = (_a); \ 45 | typeof(_b) __b = (_b); \ 46 | __a <= __b ? __a : __b; \ 47 | }) 48 | #define MAX(_a, _b) \ 49 | ({ \ 50 | typeof(_a) __a = (_a); \ 51 | typeof(_b) __b = (_b); \ 52 | __a >= __b ? __a : __b; \ 53 | }) 54 | 55 | // Rounding operations (efficient when n is a power of 2) 56 | // Round down to the nearest multiple of n 57 | #define ROUNDDOWN(a, n) \ 58 | ({ \ 59 | uint32_t __a = (uint32_t) (a); \ 60 | (typeof(a)) (__a - __a % (n)); \ 61 | }) 62 | // Round up to the nearest multiple of n 63 | #define ROUNDUP(a, n) \ 64 | ({ \ 65 | uint32_t __n = (uint32_t) (n); \ 66 | (typeof(a)) (ROUNDDOWN((uint32_t) (a) + __n - 1, __n)); \ 67 | }) 68 | 69 | // Return the offset of 'member' relative to the beginning of a struct type 70 | #define offsetof(type, member) ((size_t) (&((type*)0)->member)) 71 | 72 | #endif /* !JOS_INC_TYPES_H */ 73 | -------------------------------------------------------------------------------- /kern/Makefrag: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile fragment for JOS 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 | 8 | OBJDIRS += kern 9 | 10 | KERN_LDFLAGS := $(LDFLAGS) -T kern/kernel.ld -nostdlib 11 | 12 | # entry.S must be first, so that it's the first code in the text segment!!! 13 | # 14 | # We also snatch the use of a couple handy source files 15 | # from the lib directory, to avoid gratuitous code duplication. 16 | KERN_SRCFILES := kern/entry.S \ 17 | kern/entrypgdir.c \ 18 | kern/init.c \ 19 | kern/console.c \ 20 | kern/monitor.c \ 21 | kern/pmap.c \ 22 | kern/env.c \ 23 | kern/kclock.c \ 24 | kern/picirq.c \ 25 | kern/printf.c \ 26 | kern/trap.c \ 27 | kern/trapentry.S \ 28 | kern/sched.c \ 29 | kern/syscall.c \ 30 | kern/kdebug.c \ 31 | lib/printfmt.c \ 32 | lib/readline.c \ 33 | lib/string.c 34 | 35 | # Source files for LAB4 36 | KERN_SRCFILES += kern/mpentry.S \ 37 | kern/mpconfig.c \ 38 | kern/lapic.c \ 39 | kern/spinlock.c 40 | 41 | # Only build files if they exist. 42 | KERN_SRCFILES := $(wildcard $(KERN_SRCFILES)) 43 | 44 | # Binary program images to embed within the kernel. 45 | # Binary files for LAB3 46 | KERN_BINFILES := user/hello \ 47 | user/buggyhello \ 48 | user/buggyhello2 \ 49 | user/evilhello \ 50 | user/testbss \ 51 | user/divzero \ 52 | user/breakpoint \ 53 | user/softint \ 54 | user/badsegment \ 55 | user/faultread \ 56 | user/faultreadkernel \ 57 | user/faultwrite \ 58 | user/faultwritekernel 59 | 60 | # Binary files for LAB4 61 | KERN_BINFILES += user/idle \ 62 | user/yield \ 63 | user/dumbfork \ 64 | user/stresssched \ 65 | user/faultdie \ 66 | user/faultregs \ 67 | user/faultalloc \ 68 | user/faultallocbad \ 69 | user/faultnostack \ 70 | user/faultbadhandler \ 71 | user/faultevilhandler \ 72 | user/forktree \ 73 | user/spin \ 74 | user/fairness \ 75 | user/pingpong \ 76 | user/pingpongs \ 77 | user/primes 78 | # Binary files for LAB5 79 | KERN_BINFILES += user/testfile \ 80 | user/writemotd \ 81 | user/icode \ 82 | fs/fs 83 | 84 | KERN_OBJFILES := $(patsubst %.c, $(OBJDIR)/%.o, $(KERN_SRCFILES)) 85 | KERN_OBJFILES := $(patsubst %.S, $(OBJDIR)/%.o, $(KERN_OBJFILES)) 86 | KERN_OBJFILES := $(patsubst $(OBJDIR)/lib/%, $(OBJDIR)/kern/%, $(KERN_OBJFILES)) 87 | 88 | KERN_BINFILES := $(patsubst %, $(OBJDIR)/%, $(KERN_BINFILES)) 89 | 90 | # How to build kernel object files 91 | $(OBJDIR)/kern/%.o: kern/%.c 92 | @echo + cc $< 93 | @mkdir -p $(@D) 94 | $(V)$(CC) -nostdinc $(KERN_CFLAGS) -c -o $@ $< 95 | 96 | $(OBJDIR)/kern/%.o: kern/%.S 97 | @echo + as $< 98 | @mkdir -p $(@D) 99 | $(V)$(CC) -nostdinc $(KERN_CFLAGS) -c -o $@ $< 100 | 101 | $(OBJDIR)/kern/%.o: lib/%.c 102 | @echo + cc $< 103 | @mkdir -p $(@D) 104 | $(V)$(CC) -nostdinc $(KERN_CFLAGS) -c -o $@ $< 105 | 106 | # How to build the kernel itself 107 | $(OBJDIR)/kern/kernel: $(KERN_OBJFILES) $(KERN_BINFILES) kern/kernel.ld 108 | @echo + ld $@ 109 | $(V)$(LD) -o $@ $(KERN_LDFLAGS) $(KERN_OBJFILES) $(GCC_LIB) -b binary $(KERN_BINFILES) 110 | $(V)$(OBJDUMP) -S $@ > $@.asm 111 | $(V)$(NM) -n $@ > $@.sym 112 | 113 | # How to build the kernel disk image 114 | $(OBJDIR)/kern/kernel.img: $(OBJDIR)/kern/kernel $(OBJDIR)/boot/boot 115 | @echo + mk $@ 116 | $(V)dd if=/dev/zero of=$(OBJDIR)/kern/kernel.img~ count=10000 2>/dev/null 117 | $(V)dd if=$(OBJDIR)/boot/boot of=$(OBJDIR)/kern/kernel.img~ conv=notrunc 2>/dev/null 118 | $(V)dd if=$(OBJDIR)/kern/kernel of=$(OBJDIR)/kern/kernel.img~ seek=1 conv=notrunc 2>/dev/null 119 | $(V)mv $(OBJDIR)/kern/kernel.img~ $(OBJDIR)/kern/kernel.img 120 | 121 | all: $(OBJDIR)/kern/kernel.img 122 | 123 | grub: $(OBJDIR)/jos-grub 124 | 125 | $(OBJDIR)/jos-grub: $(OBJDIR)/kern/kernel 126 | @echo + oc $@ 127 | $(V)$(OBJCOPY) --adjust-vma=0x10000000 $^ $@ 128 | -------------------------------------------------------------------------------- /kern/console.h: -------------------------------------------------------------------------------- 1 | /* See COPYRIGHT for copyright information. */ 2 | 3 | #ifndef _CONSOLE_H_ 4 | #define _CONSOLE_H_ 5 | #ifndef JOS_KERNEL 6 | # error "This is a JOS kernel header; user programs should not #include it" 7 | #endif 8 | 9 | #include 10 | 11 | #define MONO_BASE 0x3B4 12 | #define MONO_BUF 0xB0000 13 | #define CGA_BASE 0x3D4 14 | #define CGA_BUF 0xB8000 15 | 16 | #define CRT_ROWS 25 17 | #define CRT_COLS 80 18 | #define CRT_SIZE (CRT_ROWS * CRT_COLS) 19 | 20 | void cons_init(void); 21 | int cons_getc(void); 22 | 23 | void kbd_intr(void); // irq 1 24 | void serial_intr(void); // irq 4 25 | 26 | #endif /* _CONSOLE_H_ */ 27 | -------------------------------------------------------------------------------- /kern/cpu.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef JOS_INC_CPU_H 3 | #define JOS_INC_CPU_H 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | // Maximum number of CPUs 11 | #define NCPU 8 12 | 13 | // Values of status in struct Cpu 14 | enum { 15 | CPU_UNUSED = 0, 16 | CPU_STARTED, 17 | }; 18 | 19 | // Per-CPU state 20 | struct Cpu { 21 | uint8_t cpu_id; // Local APIC ID; index into cpus[] below 22 | volatile unsigned cpu_status; // The status of the CPU 23 | struct Env *cpu_env; // The currently-running environment. 24 | struct Taskstate cpu_ts; // Used by x86 to find stack for interrupt 25 | }; 26 | 27 | // Initialized in mpconfig.c 28 | extern struct Cpu cpus[NCPU]; 29 | extern int ncpu; // Total number of CPUs in the system 30 | extern struct Cpu *bootcpu; // The boot-strap processor (BSP) 31 | extern volatile uint32_t *lapic; // MMIO address to access the local-APIC 32 | 33 | // Per-CPU kernel stacks 34 | extern unsigned char percpu_kstacks[NCPU][KSTKSIZE]; 35 | 36 | int cpunum(void); 37 | #define thiscpu (&cpus[cpunum()]) 38 | 39 | void mp_init(void); 40 | void lapic_init(void); 41 | void lapic_startap(uint8_t apicid, uint32_t addr); 42 | void lapic_eoi(void); 43 | void lapic_ipi(int vector); 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /kern/entry.S: -------------------------------------------------------------------------------- 1 | /* See COPYRIGHT for copyright information. */ 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | # Shift Right Logical 8 | #define SRL(val, shamt) (((val) >> (shamt)) & ~(-1 << (32 - (shamt)))) 9 | 10 | 11 | ################################################################### 12 | # The kernel (this code) is linked at address ~(KERNBASE + 1 Meg), 13 | # but the bootloader loads it at address ~1 Meg. 14 | # 15 | # RELOC(x) maps a symbol x from its link address to its actual 16 | # location in physical memory (its load address). 17 | ################################################################### 18 | 19 | #define RELOC(x) ((x) - KERNBASE) 20 | 21 | #define MULTIBOOT_HEADER_MAGIC (0x1BADB002) 22 | #define MULTIBOOT_HEADER_FLAGS (0) 23 | #define CHECKSUM (-(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)) 24 | 25 | ################################################################### 26 | # entry point 27 | ################################################################### 28 | 29 | .text 30 | 31 | # The Multiboot header 32 | .align 4 33 | .long MULTIBOOT_HEADER_MAGIC 34 | .long MULTIBOOT_HEADER_FLAGS 35 | .long CHECKSUM 36 | 37 | # '_start' specifies the ELF entry point. Since we haven't set up 38 | # virtual memory when the bootloader enters this code, we need the 39 | # bootloader to jump to the *physical* address of the entry point. 40 | .globl _start 41 | _start = RELOC(entry) 42 | 43 | .globl entry 44 | entry: 45 | movw $0x1234,0x472 # warm boot 46 | 47 | # We haven't set up virtual memory yet, so we're running from 48 | # the physical address the boot loader loaded the kernel at: 1MB 49 | # (plus a few bytes). However, the C code is linked to run at 50 | # KERNBASE+1MB. Hence, we set up a trivial page directory that 51 | # translates virtual addresses [KERNBASE, KERNBASE+4MB) to 52 | # physical addresses [0, 4MB). This 4MB region will be suffice 53 | # until we set up our real page table in mem_init in lab 2. 54 | 55 | # Load the physical address of entry_pgdir into cr3. entry_pgdir 56 | # is defined in entrypgdir.c. 57 | movl $(RELOC(entry_pgdir)), %eax 58 | movl %eax, %cr3 59 | # Turn on paging. 60 | movl %cr0, %eax 61 | orl $(CR0_PE|CR0_PG|CR0_WP), %eax 62 | movl %eax, %cr0 63 | 64 | # Now paging is enabled, but we're still running at a low EIP 65 | # (why is this okay?). Jump up above KERNBASE before entering 66 | # C code. 67 | mov $relocated, %eax 68 | jmp *%eax 69 | relocated: 70 | 71 | # Clear the frame pointer register (EBP) 72 | # so that once we get into debugging C code, 73 | # stack backtraces will be terminated properly. 74 | movl $0x0,%ebp # nuke frame pointer 75 | 76 | # Set the stack pointer 77 | movl $(bootstacktop),%esp 78 | 79 | # now to C code 80 | call i386_init 81 | 82 | # Should never get here, but in case we do, just spin. 83 | spin: jmp spin 84 | 85 | 86 | .data 87 | ################################################################### 88 | # boot stack 89 | ################################################################### 90 | .p2align PGSHIFT # force page alignment 91 | .globl bootstack 92 | bootstack: 93 | .space KSTKSIZE 94 | .globl bootstacktop 95 | bootstacktop: 96 | 97 | -------------------------------------------------------------------------------- /kern/env.h: -------------------------------------------------------------------------------- 1 | /* See COPYRIGHT for copyright information. */ 2 | 3 | #ifndef JOS_KERN_ENV_H 4 | #define JOS_KERN_ENV_H 5 | 6 | #include 7 | #include 8 | 9 | extern struct Env *envs; // All environments 10 | #define curenv (thiscpu->cpu_env) // Current environment 11 | extern struct Segdesc gdt[]; 12 | 13 | void env_init(void); 14 | void env_init_percpu(void); 15 | int env_alloc(struct Env **e, envid_t parent_id); 16 | void env_free(struct Env *e); 17 | void env_create(uint8_t *binary, size_t size, enum EnvType type); 18 | void env_destroy(struct Env *e); // Does not return if e == curenv 19 | 20 | int envid2env(envid_t envid, struct Env **env_store, bool checkperm); 21 | // The following two functions do not return 22 | void env_run(struct Env *e) __attribute__((noreturn)); 23 | void env_pop_tf(struct Trapframe *tf) __attribute__((noreturn)); 24 | 25 | // Without this extra macro, we couldn't pass macros like TEST to 26 | // ENV_CREATE because of the C pre-processor's argument prescan rule. 27 | #define ENV_PASTE3(x, y, z) x ## y ## z 28 | 29 | #define ENV_CREATE(x, type) \ 30 | do { \ 31 | extern uint8_t ENV_PASTE3(_binary_obj_, x, _start)[], \ 32 | ENV_PASTE3(_binary_obj_, x, _size)[]; \ 33 | env_create(ENV_PASTE3(_binary_obj_, x, _start), \ 34 | (int)ENV_PASTE3(_binary_obj_, x, _size), \ 35 | type); \ 36 | } while (0) 37 | 38 | #endif // !JOS_KERN_ENV_H 39 | -------------------------------------------------------------------------------- /kern/init.c: -------------------------------------------------------------------------------- 1 | /* See COPYRIGHT for copyright information. */ 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | static void boot_aps(void); 19 | 20 | 21 | void 22 | i386_init(void) 23 | { 24 | extern char edata[], end[]; 25 | 26 | // Before doing anything else, complete the ELF loading process. 27 | // Clear the uninitialized global data (BSS) section of our program. 28 | // This ensures that all static/global variables start out zero. 29 | memset(edata, 0, end - edata); 30 | 31 | // Initialize the console. 32 | // Can't call cprintf until after we do this! 33 | cons_init(); 34 | 35 | cprintf("6828 decimal is %o octal!\n", 6828); 36 | 37 | // Lab 2 memory management initialization functions 38 | mem_init(); 39 | 40 | // Lab 3 user environment initialization functions 41 | env_init(); 42 | trap_init(); 43 | 44 | // Lab 4 multiprocessor initialization functions 45 | mp_init(); 46 | lapic_init(); 47 | 48 | // Lab 4 multitasking initialization functions 49 | pic_init(); 50 | 51 | // Acquire the big kernel lock before waking up APs 52 | // Your code here: 53 | lock_kernel(); 54 | 55 | // Starting non-boot CPUs 56 | boot_aps(); 57 | 58 | // Should always have idle processes at first. 59 | int i; 60 | for (i = 0; i < NCPU; i++) 61 | ENV_CREATE(user_idle, ENV_TYPE_IDLE); 62 | 63 | // Start fs. 64 | ENV_CREATE(fs_fs, ENV_TYPE_FS); 65 | 66 | #if defined(TEST) 67 | // Don't touch -- used by grading script! 68 | ENV_CREATE(TEST, ENV_TYPE_USER); 69 | #else 70 | // Touch all you want. 71 | // ENV_CREATE(user_writemotd, ENV_TYPE_USER); 72 | // ENV_CREATE(user_testfile, ENV_TYPE_USER); 73 | ENV_CREATE(user_icode, ENV_TYPE_USER); 74 | #endif // TEST* 75 | 76 | // Schedule and run the first user environment! 77 | sched_yield(); 78 | } 79 | 80 | // While boot_aps is booting a given CPU, it communicates the per-core 81 | // stack pointer that should be loaded by mpentry.S to that CPU in 82 | // this variable. 83 | void *mpentry_kstack; 84 | 85 | // Start the non-boot (AP) processors. 86 | static void 87 | boot_aps(void) 88 | { 89 | extern unsigned char mpentry_start[], mpentry_end[]; 90 | void *code; 91 | struct Cpu *c; 92 | 93 | // Write entry code to unused memory at MPENTRY_PADDR 94 | code = KADDR(MPENTRY_PADDR); 95 | memmove(code, mpentry_start, mpentry_end - mpentry_start); 96 | 97 | // Boot each AP one at a time 98 | for (c = cpus; c < cpus + ncpu; c++) { 99 | if (c == cpus + cpunum()) // We've started already. 100 | continue; 101 | 102 | // Tell mpentry.S what stack to use 103 | mpentry_kstack = percpu_kstacks[c - cpus] + KSTKSIZE; 104 | // Start the CPU at mpentry_start 105 | lapic_startap(c->cpu_id, PADDR(code)); 106 | // Wait for the CPU to finish some basic setup in mp_main() 107 | while(c->cpu_status != CPU_STARTED) 108 | ; 109 | } 110 | } 111 | 112 | // Setup code for APs 113 | void 114 | mp_main(void) 115 | { 116 | // We are in high EIP now, safe to switch to kern_pgdir 117 | lcr3(PADDR(kern_pgdir)); 118 | cprintf("SMP: CPU %d starting\n", cpunum()); 119 | 120 | lapic_init(); 121 | env_init_percpu(); 122 | trap_init_percpu(); 123 | xchg(&thiscpu->cpu_status, CPU_STARTED); // tell boot_aps() we're up 124 | 125 | // Now that we have finished some basic setup, call sched_yield() 126 | // to start running processes on this CPU. But make sure that 127 | // only one CPU can enter the scheduler at a time! 128 | // 129 | // Your code here: 130 | lock_kernel(); 131 | sched_yield(); 132 | 133 | // Remove this after you finish Exercise 4 134 | // for (;;); 135 | } 136 | 137 | /* 138 | * Variable panicstr contains argument to first call to panic; used as flag 139 | * to indicate that the kernel has already called panic. 140 | */ 141 | const char *panicstr; 142 | 143 | /* 144 | * Panic is called on unresolvable fatal errors. 145 | * It prints "panic: mesg", and then enters the kernel monitor. 146 | */ 147 | void 148 | _panic(const char *file, int line, const char *fmt,...) 149 | { 150 | va_list ap; 151 | 152 | if (panicstr) 153 | goto dead; 154 | panicstr = fmt; 155 | 156 | // Be extra sure that the machine is in as reasonable state 157 | __asm __volatile("cli; cld"); 158 | 159 | va_start(ap, fmt); 160 | cprintf("kernel panic on CPU %d at %s:%d: ", cpunum(), file, line); 161 | vcprintf(fmt, ap); 162 | cprintf("\n"); 163 | va_end(ap); 164 | 165 | dead: 166 | /* break into the kernel monitor */ 167 | while (1) 168 | monitor(NULL); 169 | } 170 | 171 | /* like panic, but don't */ 172 | void 173 | _warn(const char *file, int line, const char *fmt,...) 174 | { 175 | va_list ap; 176 | 177 | va_start(ap, fmt); 178 | cprintf("kernel warning at %s:%d: ", file, line); 179 | vcprintf(fmt, ap); 180 | cprintf("\n"); 181 | va_end(ap); 182 | } 183 | -------------------------------------------------------------------------------- /kern/kclock.c: -------------------------------------------------------------------------------- 1 | /* See COPYRIGHT for copyright information. */ 2 | 3 | /* Support for reading the NVRAM from the real-time clock. */ 4 | 5 | #include 6 | 7 | #include 8 | 9 | 10 | unsigned 11 | mc146818_read(unsigned reg) 12 | { 13 | outb(IO_RTC, reg); 14 | return inb(IO_RTC+1); 15 | } 16 | 17 | void 18 | mc146818_write(unsigned reg, unsigned datum) 19 | { 20 | outb(IO_RTC, reg); 21 | outb(IO_RTC+1, datum); 22 | } 23 | -------------------------------------------------------------------------------- /kern/kclock.h: -------------------------------------------------------------------------------- 1 | /* See COPYRIGHT for copyright information. */ 2 | 3 | #ifndef JOS_KERN_KCLOCK_H 4 | #define JOS_KERN_KCLOCK_H 5 | #ifndef JOS_KERNEL 6 | # error "This is a JOS kernel header; user programs should not #include it" 7 | #endif 8 | 9 | #define IO_RTC 0x070 /* RTC port */ 10 | 11 | #define MC_NVRAM_START 0xe /* start of NVRAM: offset 14 */ 12 | #define MC_NVRAM_SIZE 50 /* 50 bytes of NVRAM */ 13 | 14 | /* NVRAM bytes 7 & 8: base memory size */ 15 | #define NVRAM_BASELO (MC_NVRAM_START + 7) /* low byte; RTC off. 0x15 */ 16 | #define NVRAM_BASEHI (MC_NVRAM_START + 8) /* high byte; RTC off. 0x16 */ 17 | 18 | /* NVRAM bytes 9 & 10: extended memory size */ 19 | #define NVRAM_EXTLO (MC_NVRAM_START + 9) /* low byte; RTC off. 0x17 */ 20 | #define NVRAM_EXTHI (MC_NVRAM_START + 10) /* high byte; RTC off. 0x18 */ 21 | 22 | /* NVRAM bytes 34 and 35: extended memory POSTed size */ 23 | #define NVRAM_PEXTLO (MC_NVRAM_START + 34) /* low byte; RTC off. 0x30 */ 24 | #define NVRAM_PEXTHI (MC_NVRAM_START + 35) /* high byte; RTC off. 0x31 */ 25 | 26 | /* NVRAM byte 36: current century. (please increment in Dec99!) */ 27 | #define NVRAM_CENTURY (MC_NVRAM_START + 36) /* RTC offset 0x32 */ 28 | 29 | unsigned mc146818_read(unsigned reg); 30 | void mc146818_write(unsigned reg, unsigned datum); 31 | 32 | #endif // !JOS_KERN_KCLOCK_H 33 | -------------------------------------------------------------------------------- /kern/kdebug.h: -------------------------------------------------------------------------------- 1 | #ifndef JOS_KERN_KDEBUG_H 2 | #define JOS_KERN_KDEBUG_H 3 | 4 | #include 5 | 6 | // Debug information about a particular instruction pointer 7 | struct Eipdebuginfo { 8 | const char *eip_file; // Source code filename for EIP 9 | int eip_line; // Source code linenumber for EIP 10 | 11 | const char *eip_fn_name; // Name of function containing EIP 12 | // - Note: not null terminated! 13 | int eip_fn_namelen; // Length of function name 14 | uintptr_t eip_fn_addr; // Address of start of function 15 | int eip_fn_narg; // Number of function arguments 16 | }; 17 | 18 | int debuginfo_eip(uintptr_t eip, struct Eipdebuginfo *info); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /kern/kernel.ld: -------------------------------------------------------------------------------- 1 | /* Simple linker script for the JOS kernel. 2 | See the GNU ld 'info' manual ("info ld") to learn the syntax. */ 3 | 4 | OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") 5 | OUTPUT_ARCH(i386) 6 | ENTRY(_start) 7 | 8 | SECTIONS 9 | { 10 | /* Link the kernel at this address: "." means the current address */ 11 | . = 0xF0100000; 12 | 13 | /* AT(...) gives the load address of this section, which tells 14 | the boot loader where to load the kernel in physical memory */ 15 | .text : AT(0x100000) { 16 | *(.text .stub .text.* .gnu.linkonce.t.*) 17 | } 18 | 19 | PROVIDE(etext = .); /* Define the 'etext' symbol to this value */ 20 | 21 | .rodata : { 22 | *(.rodata .rodata.* .gnu.linkonce.r.*) 23 | } 24 | 25 | /* Include debugging information in kernel memory */ 26 | .stab : { 27 | PROVIDE(__STAB_BEGIN__ = .); 28 | *(.stab); 29 | PROVIDE(__STAB_END__ = .); 30 | BYTE(0) /* Force the linker to allocate space 31 | for this section */ 32 | } 33 | 34 | .stabstr : { 35 | PROVIDE(__STABSTR_BEGIN__ = .); 36 | *(.stabstr); 37 | PROVIDE(__STABSTR_END__ = .); 38 | BYTE(0) /* Force the linker to allocate space 39 | for this section */ 40 | } 41 | 42 | /* Adjust the address for the data segment to the next page */ 43 | . = ALIGN(0x1000); 44 | 45 | /* The data segment */ 46 | .data : { 47 | *(.data) 48 | } 49 | 50 | PROVIDE(edata = .); 51 | 52 | .bss : { 53 | *(.bss) 54 | } 55 | 56 | PROVIDE(end = .); 57 | 58 | /DISCARD/ : { 59 | *(.eh_frame .note.GNU-stack) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /kern/lapic.c: -------------------------------------------------------------------------------- 1 | // The local APIC manages internal (non-I/O) interrupts. 2 | // See Chapter 8 & Appendix C of Intel processor manual volume 3. 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | // Local APIC registers, divided by 4 for use as uint32_t[] indices. 14 | #define ID (0x0020/4) // ID 15 | #define VER (0x0030/4) // Version 16 | #define TPR (0x0080/4) // Task Priority 17 | #define EOI (0x00B0/4) // EOI 18 | #define SVR (0x00F0/4) // Spurious Interrupt Vector 19 | #define ENABLE 0x00000100 // Unit Enable 20 | #define ESR (0x0280/4) // Error Status 21 | #define ICRLO (0x0300/4) // Interrupt Command 22 | #define INIT 0x00000500 // INIT/RESET 23 | #define STARTUP 0x00000600 // Startup IPI 24 | #define DELIVS 0x00001000 // Delivery status 25 | #define ASSERT 0x00004000 // Assert interrupt (vs deassert) 26 | #define DEASSERT 0x00000000 27 | #define LEVEL 0x00008000 // Level triggered 28 | #define BCAST 0x00080000 // Send to all APICs, including self. 29 | #define OTHERS 0x000C0000 // Send to all APICs, excluding self. 30 | #define BUSY 0x00001000 31 | #define FIXED 0x00000000 32 | #define ICRHI (0x0310/4) // Interrupt Command [63:32] 33 | #define TIMER (0x0320/4) // Local Vector Table 0 (TIMER) 34 | #define X1 0x0000000B // divide counts by 1 35 | #define PERIODIC 0x00020000 // Periodic 36 | #define PCINT (0x0340/4) // Performance Counter LVT 37 | #define LINT0 (0x0350/4) // Local Vector Table 1 (LINT0) 38 | #define LINT1 (0x0360/4) // Local Vector Table 2 (LINT1) 39 | #define ERROR (0x0370/4) // Local Vector Table 3 (ERROR) 40 | #define MASKED 0x00010000 // Interrupt masked 41 | #define TICR (0x0380/4) // Timer Initial Count 42 | #define TCCR (0x0390/4) // Timer Current Count 43 | #define TDCR (0x03E0/4) // Timer Divide Configuration 44 | 45 | volatile uint32_t *lapic; // Initialized in mp.c 46 | 47 | static void 48 | lapicw(int index, int value) 49 | { 50 | lapic[index] = value; 51 | lapic[ID]; // wait for write to finish, by reading 52 | } 53 | 54 | void 55 | lapic_init(void) 56 | { 57 | if (!lapic) 58 | return; 59 | 60 | // Enable local APIC; set spurious interrupt vector. 61 | lapicw(SVR, ENABLE | (IRQ_OFFSET + IRQ_SPURIOUS)); 62 | 63 | // The timer repeatedly counts down at bus frequency 64 | // from lapic[TICR] and then issues an interrupt. 65 | // If we cared more about precise timekeeping, 66 | // TICR would be calibrated using an external time source. 67 | lapicw(TDCR, X1); 68 | lapicw(TIMER, PERIODIC | (IRQ_OFFSET + IRQ_TIMER)); 69 | lapicw(TICR, 10000000); 70 | 71 | // Leave LINT0 of the BSP enabled so that it can get 72 | // interrupts from the 8259A chip. 73 | // 74 | // According to Intel MP Specification, the BIOS should initialize 75 | // BSP's local APIC in Virtual Wire Mode, in which 8259A's 76 | // INTR is virtually connected to BSP's LINTIN0. In this mode, 77 | // we do not need to program the IOAPIC. 78 | if (thiscpu != bootcpu) 79 | lapicw(LINT0, MASKED); 80 | 81 | // Disable NMI (LINT1) on all CPUs 82 | lapicw(LINT1, MASKED); 83 | 84 | // Disable performance counter overflow interrupts 85 | // on machines that provide that interrupt entry. 86 | if (((lapic[VER]>>16) & 0xFF) >= 4) 87 | lapicw(PCINT, MASKED); 88 | 89 | // Map error interrupt to IRQ_ERROR. 90 | lapicw(ERROR, IRQ_OFFSET + IRQ_ERROR); 91 | 92 | // Clear error status register (requires back-to-back writes). 93 | lapicw(ESR, 0); 94 | lapicw(ESR, 0); 95 | 96 | // Ack any outstanding interrupts. 97 | lapicw(EOI, 0); 98 | 99 | // Send an Init Level De-Assert to synchronize arbitration ID's. 100 | lapicw(ICRHI, 0); 101 | lapicw(ICRLO, BCAST | INIT | LEVEL); 102 | while(lapic[ICRLO] & DELIVS) 103 | ; 104 | 105 | // Enable interrupts on the APIC (but not on the processor). 106 | lapicw(TPR, 0); 107 | } 108 | 109 | int 110 | cpunum(void) 111 | { 112 | if (lapic) 113 | return lapic[ID] >> 24; 114 | return 0; 115 | } 116 | 117 | // Acknowledge interrupt. 118 | void 119 | lapic_eoi(void) 120 | { 121 | if (lapic) 122 | lapicw(EOI, 0); 123 | } 124 | 125 | // Spin for a given number of microseconds. 126 | // On real hardware would want to tune this dynamically. 127 | static void 128 | microdelay(int us) 129 | { 130 | } 131 | 132 | #define IO_RTC 0x70 133 | 134 | // Start additional processor running entry code at addr. 135 | // See Appendix B of MultiProcessor Specification. 136 | void 137 | lapic_startap(uint8_t apicid, uint32_t addr) 138 | { 139 | int i; 140 | uint16_t *wrv; 141 | 142 | // "The BSP must initialize CMOS shutdown code to 0AH 143 | // and the warm reset vector (DWORD based at 40:67) to point at 144 | // the AP startup code prior to the [universal startup algorithm]." 145 | outb(IO_RTC, 0xF); // offset 0xF is shutdown code 146 | outb(IO_RTC+1, 0x0A); 147 | wrv = (uint16_t *)KADDR((0x40 << 4 | 0x67)); // Warm reset vector 148 | wrv[0] = 0; 149 | wrv[1] = addr >> 4; 150 | 151 | // "Universal startup algorithm." 152 | // Send INIT (level-triggered) interrupt to reset other CPU. 153 | lapicw(ICRHI, apicid << 24); 154 | lapicw(ICRLO, INIT | LEVEL | ASSERT); 155 | microdelay(200); 156 | lapicw(ICRLO, INIT | LEVEL); 157 | microdelay(100); // should be 10ms, but too slow in Bochs! 158 | 159 | // Send startup IPI (twice!) to enter code. 160 | // Regular hardware is supposed to only accept a STARTUP 161 | // when it is in the halted state due to an INIT. So the second 162 | // should be ignored, but it is part of the official Intel algorithm. 163 | // Bochs complains about the second one. Too bad for Bochs. 164 | for (i = 0; i < 2; i++) { 165 | lapicw(ICRHI, apicid << 24); 166 | lapicw(ICRLO, STARTUP | (addr >> 12)); 167 | microdelay(200); 168 | } 169 | } 170 | 171 | void 172 | lapic_ipi(int vector) 173 | { 174 | lapicw(ICRLO, OTHERS | FIXED | vector); 175 | while (lapic[ICRLO] & DELIVS) 176 | ; 177 | } 178 | -------------------------------------------------------------------------------- /kern/monitor.h: -------------------------------------------------------------------------------- 1 | #ifndef JOS_KERN_MONITOR_H 2 | #define JOS_KERN_MONITOR_H 3 | #ifndef JOS_KERNEL 4 | # error "This is a JOS kernel header; user programs should not #include it" 5 | #endif 6 | 7 | struct Trapframe; 8 | 9 | // Activate the kernel monitor, 10 | // optionally providing a trap frame indicating the current state 11 | // (NULL if none). 12 | void monitor(struct Trapframe *tf); 13 | 14 | // Functions implementing monitor commands. 15 | int mon_help(int argc, char **argv, struct Trapframe *tf); 16 | int mon_kerninfo(int argc, char **argv, struct Trapframe *tf); 17 | int mon_backtrace(int argc, char **argv, struct Trapframe *tf); 18 | int mon_showmap(int argc, char **argv, struct Trapframe *tf); 19 | int mon_showpp(int argc, char **argv, struct Trapframe *tf); 20 | int mon_showvp(int argc, char **argv, struct Trapframe *tf); 21 | 22 | #endif // !JOS_KERN_MONITOR_H 23 | -------------------------------------------------------------------------------- /kern/mpconfig.c: -------------------------------------------------------------------------------- 1 | // Search for and parse the multiprocessor configuration table 2 | // See http://developer.intel.com/design/pentium/datashts/24201606.pdf 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | struct Cpu cpus[NCPU]; 14 | struct Cpu *bootcpu; 15 | int ismp; 16 | int ncpu; 17 | 18 | // Per-CPU kernel stacks 19 | unsigned char percpu_kstacks[NCPU][KSTKSIZE] 20 | __attribute__ ((aligned(PGSIZE))); 21 | 22 | 23 | // See MultiProcessor Specification Version 1.[14] 24 | 25 | struct mp { // floating pointer [MP 4.1] 26 | uint8_t signature[4]; // "_MP_" 27 | physaddr_t physaddr; // phys addr of MP config table 28 | uint8_t length; // 1 29 | uint8_t specrev; // [14] 30 | uint8_t checksum; // all bytes must add up to 0 31 | uint8_t type; // MP system config type 32 | uint8_t imcrp; 33 | uint8_t reserved[3]; 34 | } __attribute__((__packed__)); 35 | 36 | struct mpconf { // configuration table header [MP 4.2] 37 | uint8_t signature[4]; // "PCMP" 38 | uint16_t length; // total table length 39 | uint8_t version; // [14] 40 | uint8_t checksum; // all bytes must add up to 0 41 | uint8_t product[20]; // product id 42 | physaddr_t oemtable; // OEM table pointer 43 | uint16_t oemlength; // OEM table length 44 | uint16_t entry; // entry count 45 | physaddr_t lapicaddr; // address of local APIC 46 | uint16_t xlength; // extended table length 47 | uint8_t xchecksum; // extended table checksum 48 | uint8_t reserved; 49 | uint8_t entries[0]; // table entries 50 | } __attribute__((__packed__)); 51 | 52 | struct mpproc { // processor table entry [MP 4.3.1] 53 | uint8_t type; // entry type (0) 54 | uint8_t apicid; // local APIC id 55 | uint8_t version; // local APIC version 56 | uint8_t flags; // CPU flags 57 | uint8_t signature[4]; // CPU signature 58 | uint32_t feature; // feature flags from CPUID instruction 59 | uint8_t reserved[8]; 60 | } __attribute__((__packed__)); 61 | 62 | // mpproc flags 63 | #define MPPROC_BOOT 0x02 // This mpproc is the bootstrap processor 64 | 65 | // Table entry types 66 | #define MPPROC 0x00 // One per processor 67 | #define MPBUS 0x01 // One per bus 68 | #define MPIOAPIC 0x02 // One per I/O APIC 69 | #define MPIOINTR 0x03 // One per bus interrupt source 70 | #define MPLINTR 0x04 // One per system interrupt source 71 | 72 | static uint8_t 73 | sum(void *addr, int len) 74 | { 75 | int i, sum; 76 | 77 | sum = 0; 78 | for (i = 0; i < len; i++) 79 | sum += ((uint8_t *)addr)[i]; 80 | return sum; 81 | } 82 | 83 | // Look for an MP structure in the len bytes at physical address addr. 84 | static struct mp * 85 | mpsearch1(physaddr_t a, int len) 86 | { 87 | struct mp *mp = KADDR(a), *end = KADDR(a + len); 88 | 89 | for (; mp < end; mp++) 90 | if (memcmp(mp->signature, "_MP_", 4) == 0 && 91 | sum(mp, sizeof(*mp)) == 0) 92 | return mp; 93 | return NULL; 94 | } 95 | 96 | // Search for the MP Floating Pointer Structure, which according to 97 | // [MP 4] is in one of the following three locations: 98 | // 1) in the first KB of the EBDA; 99 | // 2) if there is no EBDA, in the last KB of system base memory; 100 | // 3) in the BIOS ROM between 0xE0000 and 0xFFFFF. 101 | static struct mp * 102 | mpsearch(void) 103 | { 104 | uint8_t *bda; 105 | uint32_t p; 106 | struct mp *mp; 107 | 108 | static_assert(sizeof(*mp) == 16); 109 | 110 | // The BIOS data area lives in 16-bit segment 0x40. 111 | bda = (uint8_t *) KADDR(0x40 << 4); 112 | 113 | // [MP 4] The 16-bit segment of the EBDA is in the two bytes 114 | // starting at byte 0x0E of the BDA. 0 if not present. 115 | if ((p = *(uint16_t *) (bda + 0x0E))) { 116 | p <<= 4; // Translate from segment to PA 117 | if ((mp = mpsearch1(p, 1024))) 118 | return mp; 119 | } else { 120 | // The size of base memory, in KB is in the two bytes 121 | // starting at 0x13 of the BDA. 122 | p = *(uint16_t *) (bda + 0x13) * 1024; 123 | if ((mp = mpsearch1(p - 1024, 1024))) 124 | return mp; 125 | } 126 | return mpsearch1(0xF0000, 0x10000); 127 | } 128 | 129 | // Search for an MP configuration table. For now, don't accept the 130 | // default configurations (physaddr == 0). 131 | // Check for the correct signature, checksum, and version. 132 | static struct mpconf * 133 | mpconfig(struct mp **pmp) 134 | { 135 | struct mpconf *conf; 136 | struct mp *mp; 137 | 138 | if ((mp = mpsearch()) == 0) 139 | return NULL; 140 | if (mp->physaddr == 0 || mp->type != 0) { 141 | cprintf("SMP: Default configurations not implemented\n"); 142 | return NULL; 143 | } 144 | conf = (struct mpconf *) KADDR(mp->physaddr); 145 | if (memcmp(conf, "PCMP", 4) != 0) { 146 | cprintf("SMP: Incorrect MP configuration table signature\n"); 147 | return NULL; 148 | } 149 | if (sum(conf, conf->length) != 0) { 150 | cprintf("SMP: Bad MP configuration checksum\n"); 151 | return NULL; 152 | } 153 | if (conf->version != 1 && conf->version != 4) { 154 | cprintf("SMP: Unsupported MP version %d\n", conf->version); 155 | return NULL; 156 | } 157 | if (sum((uint8_t *)conf + conf->length, conf->xlength) != conf->xchecksum) { 158 | cprintf("SMP: Bad MP configuration extended checksum\n"); 159 | return NULL; 160 | } 161 | *pmp = mp; 162 | return conf; 163 | } 164 | 165 | void 166 | mp_init(void) 167 | { 168 | struct mp *mp; 169 | struct mpconf *conf; 170 | struct mpproc *proc; 171 | uint8_t *p; 172 | unsigned int i; 173 | 174 | bootcpu = &cpus[0]; 175 | if ((conf = mpconfig(&mp)) == 0) 176 | return; 177 | ismp = 1; 178 | lapic = (uint32_t *)conf->lapicaddr; 179 | 180 | for (p = conf->entries, i = 0; i < conf->entry; i++) { 181 | switch (*p) { 182 | case MPPROC: 183 | proc = (struct mpproc *)p; 184 | if (proc->flags & MPPROC_BOOT) 185 | bootcpu = &cpus[ncpu]; 186 | if (ncpu < NCPU) { 187 | cpus[ncpu].cpu_id = ncpu; 188 | ncpu++; 189 | } else { 190 | cprintf("SMP: too many CPUs, CPU %d disabled\n", 191 | proc->apicid); 192 | } 193 | p += sizeof(struct mpproc); 194 | continue; 195 | case MPBUS: 196 | case MPIOAPIC: 197 | case MPIOINTR: 198 | case MPLINTR: 199 | p += 8; 200 | continue; 201 | default: 202 | cprintf("mpinit: unknown config type %x\n", *p); 203 | ismp = 0; 204 | i = conf->entry; 205 | } 206 | } 207 | 208 | bootcpu->cpu_status = CPU_STARTED; 209 | if (!ismp) { 210 | // Didn't like what we found; fall back to no MP. 211 | ncpu = 1; 212 | lapic = NULL; 213 | cprintf("SMP: configuration not found, SMP disabled\n"); 214 | return; 215 | } 216 | cprintf("SMP: CPU %d found %d CPU(s)\n", bootcpu->cpu_id, ncpu); 217 | 218 | if (mp->imcrp) { 219 | // [MP 3.2.6.1] If the hardware implements PIC mode, 220 | // switch to getting interrupts from the LAPIC. 221 | cprintf("SMP: Setting IMCR to switch from PIC mode to symmetric I/O mode\n"); 222 | outb(0x22, 0x70); // Select IMCR 223 | outb(0x23, inb(0x23) | 1); // Mask external interrupts. 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /kern/mpentry.S: -------------------------------------------------------------------------------- 1 | /* See COPYRIGHT for copyright information. */ 2 | 3 | #include 4 | #include 5 | 6 | ################################################################### 7 | # entry point for APs 8 | ################################################################### 9 | 10 | # Each non-boot CPU ("AP") is started up in response to a STARTUP 11 | # IPI from the boot CPU. Section B.4.2 of the Multi-Processor 12 | # Specification says that the AP will start in real mode with CS:IP 13 | # set to XY00:0000, where XY is an 8-bit value sent with the 14 | # STARTUP. Thus this code must start at a 4096-byte boundary. 15 | # 16 | # Because this code sets DS to zero, it must run from an address in 17 | # the low 2^16 bytes of physical memory. 18 | # 19 | # boot_aps() (in init.c) copies this code to MPENTRY_PADDR (which 20 | # satisfies the above restrictions). Then, for each AP, it stores the 21 | # address of the pre-allocated per-core stack in mpentry_kstack, sends 22 | # the STARTUP IPI, and waits for this code to acknowledge that it has 23 | # started (which happens in mp_main in init.c). 24 | # 25 | # This code is similar to boot/boot.S except that 26 | # - it does not need to enable A20 27 | # - it uses MPBOOTPHYS to calculate absolute addresses of its 28 | # symbols, rather than relying on the linker to fill them 29 | 30 | #define RELOC(x) ((x) - KERNBASE) 31 | #define MPBOOTPHYS(s) ((s) - mpentry_start + MPENTRY_PADDR) 32 | 33 | .set PROT_MODE_CSEG, 0x8 # kernel code segment selector 34 | .set PROT_MODE_DSEG, 0x10 # kernel data segment selector 35 | 36 | .code16 37 | .globl mpentry_start 38 | mpentry_start: 39 | cli 40 | 41 | xorw %ax, %ax 42 | movw %ax, %ds 43 | movw %ax, %es 44 | movw %ax, %ss 45 | 46 | lgdt MPBOOTPHYS(gdtdesc) 47 | movl %cr0, %eax 48 | orl $CR0_PE, %eax 49 | movl %eax, %cr0 50 | 51 | ljmpl $(PROT_MODE_CSEG), $(MPBOOTPHYS(start32)) 52 | 53 | .code32 54 | start32: 55 | movw $(PROT_MODE_DSEG), %ax 56 | movw %ax, %ds 57 | movw %ax, %es 58 | movw %ax, %ss 59 | movw $0, %ax 60 | movw %ax, %fs 61 | movw %ax, %gs 62 | 63 | # Set up initial page table. We cannot use kern_pgdir yet because 64 | # we are still running at a low EIP. 65 | movl $(RELOC(entry_pgdir)), %eax 66 | movl %eax, %cr3 67 | # Turn on paging. 68 | movl %cr0, %eax 69 | orl $(CR0_PE|CR0_PG|CR0_WP), %eax 70 | movl %eax, %cr0 71 | 72 | # Switch to the per-cpu stack allocated in mem_init() 73 | movl mpentry_kstack, %esp 74 | movl $0x0, %ebp # nuke frame pointer 75 | 76 | # Call mp_main(). (Exercise for the reader: why the indirect call?) 77 | movl $mp_main, %eax 78 | call *%eax 79 | 80 | # If mp_main returns (it shouldn't), loop. 81 | spin: 82 | jmp spin 83 | 84 | # Bootstrap GDT 85 | .p2align 2 # force 4 byte alignment 86 | gdt: 87 | SEG_NULL # null seg 88 | SEG(STA_X|STA_R, 0x0, 0xffffffff) # code seg 89 | SEG(STA_W, 0x0, 0xffffffff) # data seg 90 | 91 | gdtdesc: 92 | .word 0x17 # sizeof(gdt) - 1 93 | .long MPBOOTPHYS(gdt) # address gdt 94 | 95 | .globl mpentry_end 96 | mpentry_end: 97 | nop 98 | -------------------------------------------------------------------------------- /kern/picirq.c: -------------------------------------------------------------------------------- 1 | /* See COPYRIGHT for copyright information. */ 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | 9 | // Current IRQ mask. 10 | // Initial IRQ mask has interrupt 2 enabled (for slave 8259A). 11 | uint16_t irq_mask_8259A = 0xFFFF & ~(1<> 8)); 80 | cprintf("enabled interrupts:"); 81 | for (i = 0; i < 16; i++) 82 | if (~mask & (1< 21 | #include 22 | 23 | extern uint16_t irq_mask_8259A; 24 | void pic_init(void); 25 | void irq_setmask_8259A(uint16_t mask); 26 | #endif // !__ASSEMBLER__ 27 | 28 | #endif // !JOS_KERN_PICIRQ_H 29 | -------------------------------------------------------------------------------- /kern/pmap.h: -------------------------------------------------------------------------------- 1 | /* See COPYRIGHT for copyright information. */ 2 | 3 | #ifndef JOS_KERN_PMAP_H 4 | #define JOS_KERN_PMAP_H 5 | #ifndef JOS_KERNEL 6 | # error "This is a JOS kernel header; user programs should not #include it" 7 | #endif 8 | 9 | #include 10 | #include 11 | struct Env; 12 | 13 | extern char bootstacktop[], bootstack[]; 14 | 15 | extern struct Page *pages; 16 | extern size_t npages; 17 | 18 | extern pde_t *kern_pgdir; 19 | 20 | 21 | /* This macro takes a kernel virtual address -- an address that points above 22 | * KERNBASE, where the machine's maximum 256MB of physical memory is mapped -- 23 | * and returns the corresponding physical address. It panics if you pass it a 24 | * non-kernel virtual address. 25 | */ 26 | #define PADDR(kva) _paddr(__FILE__, __LINE__, kva) 27 | 28 | static inline physaddr_t 29 | _paddr(const char *file, int line, void *kva) 30 | { 31 | if ((uint32_t)kva < KERNBASE) 32 | _panic(file, line, "PADDR called with invalid kva %08lx", kva); 33 | return (physaddr_t)kva - KERNBASE; 34 | } 35 | 36 | /* This macro takes a physical address and returns the corresponding kernel 37 | * virtual address. It panics if you pass an invalid physical address. */ 38 | #define KADDR(pa) _kaddr(__FILE__, __LINE__, pa) 39 | 40 | static inline void* 41 | _kaddr(const char *file, int line, physaddr_t pa) 42 | { 43 | if (PGNUM(pa) >= npages) 44 | _panic(file, line, "KADDR called with invalid pa %08lx", pa); 45 | return (void *)(pa + KERNBASE); 46 | } 47 | 48 | 49 | enum { 50 | // For page_alloc, zero the returned physical page. 51 | ALLOC_ZERO = 1<<0, 52 | }; 53 | 54 | void mem_init(void); 55 | 56 | void page_init(void); 57 | struct Page *page_alloc(int alloc_flags); 58 | void page_free(struct Page *pp); 59 | int page_insert(pde_t *pgdir, struct Page *pp, void *va, int perm); 60 | void page_remove(pde_t *pgdir, void *va); 61 | struct Page *page_lookup(pde_t *pgdir, void *va, pte_t **pte_store); 62 | void page_decref(struct Page *pp); 63 | 64 | void tlb_invalidate(pde_t *pgdir, void *va); 65 | 66 | int user_mem_check(struct Env *env, const void *va, size_t len, int perm); 67 | void user_mem_assert(struct Env *env, const void *va, size_t len, int perm); 68 | 69 | static inline physaddr_t 70 | page2pa(struct Page *pp) 71 | { 72 | return (pp - pages) << PGSHIFT; 73 | } 74 | 75 | static inline struct Page* 76 | pa2page(physaddr_t pa) 77 | { 78 | if (PGNUM(pa) >= npages) 79 | panic("pa2page called with invalid pa"); 80 | return &pages[PGNUM(pa)]; 81 | } 82 | 83 | static inline void* 84 | page2kva(struct Page *pp) 85 | { 86 | return KADDR(page2pa(pp)); 87 | } 88 | 89 | pte_t *pgdir_walk(pde_t *pgdir, const void *va, int create); 90 | 91 | #endif /* !JOS_KERN_PMAP_H */ 92 | -------------------------------------------------------------------------------- /kern/printf.c: -------------------------------------------------------------------------------- 1 | // Simple implementation of cprintf console output for the kernel, 2 | // based on printfmt() and the kernel console's cputchar(). 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | static void 10 | putch(int ch, int *cnt) 11 | { 12 | cputchar(ch); 13 | *cnt++; 14 | } 15 | 16 | int 17 | vcprintf(const char *fmt, va_list ap) 18 | { 19 | int cnt = 0; 20 | 21 | vprintfmt((void*)putch, &cnt, fmt, ap); 22 | return cnt; 23 | } 24 | 25 | int 26 | cprintf(const char *fmt, ...) 27 | { 28 | va_list ap; 29 | int cnt; 30 | 31 | va_start(ap, fmt); 32 | cnt = vcprintf(fmt, ap); 33 | va_end(ap); 34 | 35 | return cnt; 36 | } 37 | 38 | -------------------------------------------------------------------------------- /kern/sched.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | // Choose a user environment to run and run it. 9 | void 10 | sched_yield(void) 11 | { 12 | struct Env *idle; 13 | int i; 14 | 15 | // Implement simple round-robin scheduling. 16 | // 17 | // Search through 'envs' for an ENV_RUNNABLE environment in 18 | // circular fashion starting just after the env this CPU was 19 | // last running. Switch to the first such environment found. 20 | // 21 | // If no envs are runnable, but the environment previously 22 | // running on this CPU is still ENV_RUNNING, it's okay to 23 | // choose that environment. 24 | // 25 | // Never choose an environment that's currently running on 26 | // another CPU (env_status == ENV_RUNNING) and never choose an 27 | // idle environment (env_type == ENV_TYPE_IDLE). If there are 28 | // no runnable environments, simply drop through to the code 29 | // below to switch to this CPU's idle environment. 30 | 31 | // LAB 4: Your code here. 32 | 33 | // Find the environment this CPU was last running. 34 | struct Env *curr = thiscpu->cpu_env; 35 | int idx = curr ? ENVX(curr->env_id) % NENV : 0; 36 | for (i = 1; i < NENV; i++) { 37 | idx = (idx + 1) % NENV; 38 | 39 | if (envs[idx].env_type == ENV_TYPE_IDLE) 40 | continue; 41 | 42 | if (envs[idx].env_status == ENV_RUNNABLE) 43 | env_run(&envs[idx]); 44 | } 45 | 46 | if (curr && curr->env_status == ENV_RUNNING) { 47 | // If not found and current environment is running, then continue running. 48 | env_run(curr); 49 | } 50 | 51 | // For debugging and testing purposes, if there are no 52 | // runnable environments other than the idle environments, 53 | // drop into the kernel monitor. 54 | for (i = 0; i < NENV; i++) { 55 | if (envs[i].env_type != ENV_TYPE_IDLE && 56 | (envs[i].env_status == ENV_RUNNABLE || 57 | envs[i].env_status == ENV_RUNNING)) 58 | break; 59 | } 60 | if (i == NENV) { 61 | cprintf("No more runnable environments!\n"); 62 | while (1) 63 | monitor(NULL); 64 | } 65 | 66 | // Run this CPU's idle environment when nothing else is runnable. 67 | idle = &envs[cpunum()]; 68 | if (!(idle->env_status == ENV_RUNNABLE || idle->env_status == ENV_RUNNING)) 69 | panic("CPU %d: No idle environment!", cpunum()); 70 | env_run(idle); 71 | } 72 | -------------------------------------------------------------------------------- /kern/sched.h: -------------------------------------------------------------------------------- 1 | /* See COPYRIGHT for copyright information. */ 2 | 3 | #ifndef JOS_KERN_SCHED_H 4 | #define JOS_KERN_SCHED_H 5 | #ifndef JOS_KERNEL 6 | # error "This is a JOS kernel header; user programs should not #include it" 7 | #endif 8 | 9 | // This function does not return. 10 | void sched_yield(void) __attribute__((noreturn)); 11 | 12 | #endif // !JOS_KERN_SCHED_H 13 | -------------------------------------------------------------------------------- /kern/spinlock.c: -------------------------------------------------------------------------------- 1 | // Mutual exclusion spin locks. 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | // The big kernel lock 13 | struct spinlock kernel_lock = { 14 | #ifdef DEBUG_SPINLOCK 15 | .name = "kernel_lock" 16 | #endif 17 | }; 18 | 19 | #ifdef DEBUG_SPINLOCK 20 | // Record the current call stack in pcs[] by following the %ebp chain. 21 | static void 22 | get_caller_pcs(uint32_t pcs[]) 23 | { 24 | uint32_t *ebp; 25 | int i; 26 | 27 | ebp = (uint32_t *)read_ebp(); 28 | for (i = 0; i < 10; i++){ 29 | if (ebp == 0 || ebp < (uint32_t *)ULIM 30 | || ebp >= (uint32_t *)IOMEMBASE) 31 | break; 32 | pcs[i] = ebp[1]; // saved %eip 33 | ebp = (uint32_t *)ebp[0]; // saved %ebp 34 | } 35 | for (; i < 10; i++) 36 | pcs[i] = 0; 37 | } 38 | 39 | // Check whether this CPU is holding the lock. 40 | static int 41 | holding(struct spinlock *lock) 42 | { 43 | return lock->locked && lock->cpu == thiscpu; 44 | } 45 | #endif 46 | 47 | void 48 | __spin_initlock(struct spinlock *lk, char *name) 49 | { 50 | lk->locked = 0; 51 | #ifdef DEBUG_SPINLOCK 52 | lk->name = name; 53 | lk->cpu = 0; 54 | #endif 55 | } 56 | 57 | // Acquire the lock. 58 | // Loops (spins) until the lock is acquired. 59 | // Holding a lock for a long time may cause 60 | // other CPUs to waste time spinning to acquire it. 61 | void 62 | spin_lock(struct spinlock *lk) 63 | { 64 | #ifdef DEBUG_SPINLOCK 65 | if (holding(lk)) 66 | panic("CPU %d cannot acquire %s: already holding", cpunum(), lk->name); 67 | #endif 68 | 69 | // The xchg is atomic. 70 | // It also serializes, so that reads after acquire are not 71 | // reordered before it. 72 | while (xchg(&lk->locked, 1) != 0) 73 | asm volatile ("pause"); 74 | 75 | // Record info about lock acquisition for debugging. 76 | #ifdef DEBUG_SPINLOCK 77 | lk->cpu = thiscpu; 78 | get_caller_pcs(lk->pcs); 79 | #endif 80 | } 81 | 82 | // Release the lock. 83 | void 84 | spin_unlock(struct spinlock *lk) 85 | { 86 | #ifdef DEBUG_SPINLOCK 87 | if (!holding(lk)) { 88 | int i; 89 | uint32_t pcs[10]; 90 | // Nab the acquiring EIP chain before it gets released 91 | memmove(pcs, lk->pcs, sizeof pcs); 92 | cprintf("CPU %d cannot release %s: held by CPU %d\nAcquired at:", 93 | cpunum(), lk->name, lk->cpu->cpu_id); 94 | for (i = 0; i < 10 && pcs[i]; i++) { 95 | struct Eipdebuginfo info; 96 | if (debuginfo_eip(pcs[i], &info) >= 0) 97 | cprintf(" %08x %s:%d: %.*s+%x\n", pcs[i], 98 | info.eip_file, info.eip_line, 99 | info.eip_fn_namelen, info.eip_fn_name, 100 | pcs[i] - info.eip_fn_addr); 101 | else 102 | cprintf(" %08x\n", pcs[i]); 103 | } 104 | panic("spin_unlock"); 105 | } 106 | 107 | lk->pcs[0] = 0; 108 | lk->cpu = 0; 109 | #endif 110 | 111 | // The xchg serializes, so that reads before release are 112 | // not reordered after it. The 1996 PentiumPro manual (Volume 3, 113 | // 7.2) says reads can be carried out speculatively and in 114 | // any order, which implies we need to serialize here. 115 | // But the 2007 Intel 64 Architecture Memory Ordering White 116 | // Paper says that Intel 64 and IA-32 will not move a load 117 | // after a store. So lock->locked = 0 would work here. 118 | // The xchg being asm volatile ensures gcc emits it after 119 | // the above assignments (and after the critical section). 120 | xchg(&lk->locked, 0); 121 | } 122 | -------------------------------------------------------------------------------- /kern/spinlock.h: -------------------------------------------------------------------------------- 1 | #ifndef JOS_INC_SPINLOCK_H 2 | #define JOS_INC_SPINLOCK_H 3 | 4 | #include 5 | 6 | // Comment this to disable spinlock debugging 7 | #define DEBUG_SPINLOCK 8 | 9 | // Mutual exclusion lock. 10 | struct spinlock { 11 | unsigned locked; // Is the lock held? 12 | 13 | #ifdef DEBUG_SPINLOCK 14 | // For debugging: 15 | char *name; // Name of lock. 16 | struct Cpu *cpu; // The CPU holding the lock. 17 | uintptr_t pcs[10]; // The call stack (an array of program counters) 18 | // that locked the lock. 19 | #endif 20 | }; 21 | 22 | void __spin_initlock(struct spinlock *lk, char *name); 23 | void spin_lock(struct spinlock *lk); 24 | void spin_unlock(struct spinlock *lk); 25 | 26 | #define spin_initlock(lock) __spin_initlock(lock, #lock) 27 | 28 | extern struct spinlock kernel_lock; 29 | 30 | static inline void 31 | lock_kernel(void) 32 | { 33 | spin_lock(&kernel_lock); 34 | } 35 | 36 | static inline void 37 | unlock_kernel(void) 38 | { 39 | spin_unlock(&kernel_lock); 40 | 41 | // Normally we wouldn't need to do this, but QEMU only runs 42 | // one CPU at a time and has a long time-slice. Without the 43 | // pause, this CPU is likely to reacquire the lock before 44 | // another CPU has even been given a chance to acquire it. 45 | asm volatile("pause"); 46 | } 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /kern/syscall.h: -------------------------------------------------------------------------------- 1 | #ifndef JOS_KERN_SYSCALL_H 2 | #define JOS_KERN_SYSCALL_H 3 | #ifndef JOS_KERNEL 4 | # error "This is a JOS kernel header; user programs should not #include it" 5 | #endif 6 | 7 | #include 8 | 9 | int32_t syscall(uint32_t num, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5); 10 | 11 | #endif /* !JOS_KERN_SYSCALL_H */ 12 | -------------------------------------------------------------------------------- /kern/trap.h: -------------------------------------------------------------------------------- 1 | /* See COPYRIGHT for copyright information. */ 2 | 3 | #ifndef JOS_KERN_TRAP_H 4 | #define JOS_KERN_TRAP_H 5 | #ifndef JOS_KERNEL 6 | # error "This is a JOS kernel header; user programs should not #include it" 7 | #endif 8 | 9 | #include 10 | #include 11 | 12 | /* The kernel's interrupt descriptor table */ 13 | extern struct Gatedesc idt[]; 14 | extern struct Pseudodesc idt_pd; 15 | 16 | void trap_init(void); 17 | void trap_init_percpu(void); 18 | void print_regs(struct PushRegs *regs); 19 | void print_trapframe(struct Trapframe *tf); 20 | void page_fault_handler(struct Trapframe *); 21 | void backtrace(struct Trapframe *); 22 | 23 | #endif /* JOS_KERN_TRAP_H */ 24 | -------------------------------------------------------------------------------- /kern/trapentry.S: -------------------------------------------------------------------------------- 1 | /* See COPYRIGHT for copyright information. */ 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | 10 | ################################################################### 11 | # exceptions/interrupts 12 | ################################################################### 13 | 14 | /* TRAPHANDLER defines a globally-visible function for handling a trap. 15 | * It pushes a trap number onto the stack, then jumps to _alltraps. 16 | * Use TRAPHANDLER for traps where the CPU automatically pushes an error code. 17 | * 18 | * You shouldn't call a TRAPHANDLER function from C, but you may 19 | * need to _declare_ one in C (for instance, to get a function pointer 20 | * during IDT setup). You can declare the function with 21 | * void NAME(); 22 | * where NAME is the argument passed to TRAPHANDLER. 23 | */ 24 | #define TRAPHANDLER(name, num) \ 25 | .globl name; /* define global symbol for 'name' */ \ 26 | .type name, @function; /* symbol type is function */ \ 27 | .align 2; /* align function definition */ \ 28 | name: /* function starts here */ \ 29 | pushl $(num); \ 30 | jmp _alltraps 31 | 32 | /* Use TRAPHANDLER_NOEC for traps where the CPU doesn't push an error code. 33 | * It pushes a 0 in place of the error code, so the trap frame has the same 34 | * format in either case. 35 | */ 36 | #define TRAPHANDLER_NOEC(name, num) \ 37 | .globl name; \ 38 | .type name, @function; \ 39 | .align 2; \ 40 | name: \ 41 | pushl $0; \ 42 | pushl $(num); \ 43 | jmp _alltraps 44 | 45 | #define H(N) handler##N 46 | #define TY(N) TRAPHANDLER(H(N), N) 47 | #define TN(N) TRAPHANDLER_NOEC(H(N), N) 48 | #define L(N) .long H(N) 49 | #define S(N) .long 0 /* skip */ 50 | 51 | .text 52 | 53 | /* 54 | * Lab 3: Your code here for generating entry points for the different traps. 55 | */ 56 | TN(0) 57 | TN(1) 58 | TN(2) 59 | TN(3) 60 | TN(4) 61 | TN(5) 62 | TN(6) 63 | TN(7) 64 | TY(8) 65 | // TN(9) /* reserved */ 66 | TY(10) 67 | TY(11) 68 | TY(12) 69 | TY(13) 70 | TY(14) 71 | // TN(15) /* reserved */ 72 | TN(16) 73 | TY(17) 74 | TN(18) 75 | TN(19) 76 | 77 | /* 16 IRQs */ 78 | TN(32) 79 | TN(33) 80 | TN(34) 81 | TN(35) 82 | TN(36) 83 | TN(37) 84 | TN(38) 85 | TN(39) 86 | TN(40) 87 | TN(41) 88 | TN(42) 89 | TN(43) 90 | TN(44) 91 | TN(45) 92 | TN(46) 93 | TN(47) 94 | 95 | TN(48) 96 | 97 | /* 98 | * Lab 3: Your code here for _alltraps 99 | */ 100 | .global _alltraps 101 | _alltraps: 102 | // Build struct Trapframe 103 | pushl %ds 104 | pushl %es 105 | pushal 106 | 107 | movl $GD_KD, %eax 108 | movw %ax, %ds 109 | movw %ax, %es 110 | 111 | pushl %esp /* struct Trapframe * as argument */ 112 | call trap 113 | /* the call will never return */ 114 | 115 | -------------------------------------------------------------------------------- /lib/Makefrag: -------------------------------------------------------------------------------- 1 | OBJDIRS += lib 2 | 3 | LIB_SRCFILES := lib/console.c \ 4 | lib/libmain.c \ 5 | lib/exit.c \ 6 | lib/panic.c \ 7 | lib/printf.c \ 8 | lib/printfmt.c \ 9 | lib/readline.c \ 10 | lib/string.c \ 11 | lib/syscall.c 12 | 13 | LIB_SRCFILES := $(LIB_SRCFILES) \ 14 | lib/pgfault.c \ 15 | lib/pfentry.S \ 16 | lib/fork.c \ 17 | lib/ipc.c 18 | 19 | LIB_SRCFILES := $(LIB_SRCFILES) \ 20 | lib/args.c \ 21 | lib/fd.c \ 22 | lib/file.c \ 23 | lib/fprintf.c \ 24 | lib/pageref.c \ 25 | lib/spawn.c 26 | 27 | 28 | LIB_OBJFILES := $(patsubst lib/%.c, $(OBJDIR)/lib/%.o, $(LIB_SRCFILES)) 29 | LIB_OBJFILES := $(patsubst lib/%.S, $(OBJDIR)/lib/%.o, $(LIB_OBJFILES)) 30 | 31 | $(OBJDIR)/lib/%.o: lib/%.c 32 | @echo + cc[USER] $< 33 | @mkdir -p $(@D) 34 | $(V)$(CC) -nostdinc $(USER_CFLAGS) -c -o $@ $< 35 | 36 | $(OBJDIR)/lib/%.o: lib/%.S 37 | @echo + as[USER] $< 38 | @mkdir -p $(@D) 39 | $(V)$(CC) -nostdinc $(USER_CFLAGS) -c -o $@ $< 40 | 41 | $(OBJDIR)/lib/libjos.a: $(LIB_OBJFILES) 42 | @echo + ar $@ 43 | $(V)$(AR) r $@ $(LIB_OBJFILES) 44 | -------------------------------------------------------------------------------- /lib/args.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void 5 | argstart(int *argc, char **argv, struct Argstate *args) 6 | { 7 | args->argc = argc; 8 | args->argv = (const char **) argv; 9 | args->curarg = (*argc > 1 && argv ? "" : 0); 10 | args->argvalue = 0; 11 | } 12 | 13 | int 14 | argnext(struct Argstate *args) 15 | { 16 | int arg; 17 | 18 | args->argvalue = 0; 19 | 20 | // Done processing arguments if args->curarg == 0 21 | if (args->curarg == 0) 22 | return -1; 23 | 24 | if (!*args->curarg) { 25 | // Need to process the next argument 26 | // Check for end of argument list 27 | if (*args->argc == 1 28 | || args->argv[1][0] != '-' 29 | || args->argv[1][1] == '\0') 30 | goto endofargs; 31 | // Shift arguments down one 32 | args->curarg = args->argv[1] + 1; 33 | memmove(args->argv + 1, args->argv + 2, sizeof(const char *) * (*args->argc - 1)); 34 | (*args->argc)--; 35 | // Check for "--": end of argument list 36 | if (args->curarg[0] == '-' && args->curarg[1] == '\0') 37 | goto endofargs; 38 | } 39 | 40 | arg = (unsigned char) *args->curarg; 41 | args->curarg++; 42 | return arg; 43 | 44 | endofargs: 45 | args->curarg = 0; 46 | return -1; 47 | } 48 | 49 | char * 50 | argvalue(struct Argstate *args) 51 | { 52 | return (char*) (args->argvalue ? args->argvalue : argnextvalue(args)); 53 | } 54 | 55 | char * 56 | argnextvalue(struct Argstate *args) 57 | { 58 | if (!args->curarg) 59 | return 0; 60 | if (*args->curarg) { 61 | args->argvalue = args->curarg; 62 | args->curarg = ""; 63 | } else if (*args->argc > 1) { 64 | args->argvalue = args->argv[1]; 65 | memmove(args->argv + 1, args->argv + 2, sizeof(const char *) * (*args->argc - 1)); 66 | (*args->argc)--; 67 | } else { 68 | args->argvalue = 0; 69 | args->curarg = 0; 70 | } 71 | return (char*) args->argvalue; 72 | } 73 | 74 | -------------------------------------------------------------------------------- /lib/console.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | void 6 | cputchar(int ch) 7 | { 8 | char c = ch; 9 | 10 | // Unlike standard Unix's putchar, 11 | // the cputchar function _always_ outputs to the system console. 12 | sys_cputs(&c, 1); 13 | } 14 | 15 | int 16 | getchar(void) 17 | { 18 | int r; 19 | // sys_cgetc does not block, but getchar should. 20 | while ((r = sys_cgetc()) == 0) 21 | sys_yield(); 22 | return r; 23 | } 24 | 25 | 26 | -------------------------------------------------------------------------------- /lib/entry.S: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | .data 5 | // Define the global symbols 'envs', 'pages', 'vpt', and 'vpd' 6 | // so that they can be used in C as if they were ordinary global arrays. 7 | .globl envs 8 | .set envs, UENVS 9 | .globl pages 10 | .set pages, UPAGES 11 | .globl vpt 12 | .set vpt, UVPT 13 | .globl vpd 14 | .set vpd, (UVPT+(UVPT>>12)*4) 15 | 16 | 17 | // Entrypoint - this is where the kernel (or our parent environment) 18 | // starts us running when we are initially loaded into a new environment. 19 | .text 20 | .globl _start 21 | _start: 22 | // See if we were started with arguments on the stack 23 | cmpl $USTACKTOP, %esp 24 | jne args_exist 25 | 26 | // If not, push dummy argc/argv arguments. 27 | // This happens when we are loaded by the kernel, 28 | // because the kernel does not know about passing arguments. 29 | pushl $0 30 | pushl $0 31 | 32 | args_exist: 33 | call libmain 34 | 1: jmp 1b 35 | 36 | -------------------------------------------------------------------------------- /lib/exit.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | void 5 | exit(void) 6 | { 7 | close_all(); 8 | sys_env_destroy(0); 9 | } 10 | 11 | -------------------------------------------------------------------------------- /lib/file.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define debug 0 6 | 7 | union Fsipc fsipcbuf __attribute__((aligned(PGSIZE))); 8 | 9 | // Send an inter-environment request to the file server, and wait for 10 | // a reply. The request body should be in fsipcbuf, and parts of the 11 | // response may be written back to fsipcbuf. 12 | // type: request code, passed as the simple integer IPC value. 13 | // dstva: virtual address at which to receive reply page, 0 if none. 14 | // Returns result from the file server. 15 | static int 16 | fsipc(unsigned type, void *dstva) 17 | { 18 | static envid_t fsenv; 19 | if (fsenv == 0) 20 | fsenv = ipc_find_env(ENV_TYPE_FS); 21 | 22 | static_assert(sizeof(fsipcbuf) == PGSIZE); 23 | 24 | if (debug) 25 | cprintf("[%08x] fsipc %d %08x\n", thisenv->env_id, type, *(uint32_t *)&fsipcbuf); 26 | 27 | ipc_send(fsenv, type, &fsipcbuf, PTE_P | PTE_W | PTE_U); 28 | return ipc_recv(NULL, dstva, NULL); 29 | } 30 | 31 | static int devfile_flush(struct Fd *fd); 32 | static ssize_t devfile_read(struct Fd *fd, void *buf, size_t n); 33 | static ssize_t devfile_write(struct Fd *fd, const void *buf, size_t n); 34 | static int devfile_stat(struct Fd *fd, struct Stat *stat); 35 | static int devfile_trunc(struct Fd *fd, off_t newsize); 36 | 37 | struct Dev devfile = 38 | { 39 | .dev_id = 'f', 40 | .dev_name = "file", 41 | .dev_read = devfile_read, 42 | .dev_write = devfile_write, 43 | .dev_close = devfile_flush, 44 | .dev_stat = devfile_stat, 45 | .dev_trunc = devfile_trunc 46 | }; 47 | 48 | // Open a file (or directory). 49 | // 50 | // Returns: 51 | // The file descriptor index on success 52 | // -E_BAD_PATH if the path is too long (>= MAXPATHLEN) 53 | // < 0 for other errors. 54 | int 55 | open(const char *path, int mode) 56 | { 57 | // Find an unused file descriptor page using fd_alloc. 58 | // Then send a file-open request to the file server. 59 | // Include 'path' and 'omode' in request, 60 | // and map the returned file descriptor page 61 | // at the appropriate fd address. 62 | // FSREQ_OPEN returns 0 on success, < 0 on failure. 63 | // 64 | // (fd_alloc does not allocate a page, it just returns an 65 | // unused fd address. Do you need to allocate a page?) 66 | // 67 | // Return the file descriptor index. 68 | // If any step after fd_alloc fails, use fd_close to free the 69 | // file descriptor. 70 | 71 | // LAB 5: Your code here. 72 | 73 | // check path length. 74 | if (strlen(path) >= MAXPATHLEN) 75 | return -E_BAD_PATH; 76 | 77 | // allocate a new fd. 78 | struct Fd *fd; 79 | int r = fd_alloc(&fd); 80 | if (r < 0) 81 | return r; 82 | 83 | // try to allocate a page for this fd. 84 | r = sys_page_alloc(thisenv->env_id, fd, PTE_U | PTE_P | PTE_W); 85 | if (r < 0) { 86 | fd_close(fd, 0); 87 | return r; 88 | } 89 | 90 | // make a request. 91 | fsipcbuf.open.req_omode = mode; 92 | strcpy(fsipcbuf.open.req_path, path); 93 | 94 | // send the request. 95 | r = fsipc(FSREQ_OPEN, fd); 96 | if (r < 0) { 97 | // to suppress the warning generated from fd_close(). 98 | fd->fd_dev_id = devfile.dev_id; 99 | 100 | fd_close(fd, 0); 101 | return r; 102 | } 103 | 104 | return fd2num(fd); 105 | 106 | // panic("open not implemented"); 107 | } 108 | 109 | // Flush the file descriptor. After this the fileid is invalid. 110 | // 111 | // This function is called by fd_close. fd_close will take care of 112 | // unmapping the FD page from this environment. Since the server uses 113 | // the reference counts on the FD pages to detect which files are 114 | // open, unmapping it is enough to free up server-side resources. 115 | // Other than that, we just have to make sure our changes are flushed 116 | // to disk. 117 | static int 118 | devfile_flush(struct Fd *fd) 119 | { 120 | fsipcbuf.flush.req_fileid = fd->fd_file.id; 121 | return fsipc(FSREQ_FLUSH, NULL); 122 | } 123 | 124 | // Read at most 'n' bytes from 'fd' at the current position into 'buf'. 125 | // 126 | // Returns: 127 | // The number of bytes successfully read. 128 | // < 0 on error. 129 | static ssize_t 130 | devfile_read(struct Fd *fd, void *buf, size_t n) 131 | { 132 | // Make an FSREQ_READ request to the file system server after 133 | // filling fsipcbuf.read with the request arguments. The 134 | // bytes read will be written back to fsipcbuf by the file 135 | // system server. 136 | // LAB 5: Your code here 137 | 138 | // Make request. 139 | struct Fsreq_read *req = &fsipcbuf.read; 140 | req->req_fileid = fd->fd_file.id; 141 | req->req_n = n; 142 | 143 | // Send request. 144 | ssize_t rddsz = fsipc(FSREQ_READ, NULL); 145 | if (rddsz > 0) 146 | memmove(buf, fsipcbuf.readRet.ret_buf, rddsz); 147 | 148 | return rddsz; 149 | // panic("devfile_read not implemented"); 150 | } 151 | 152 | // Write at most 'n' bytes from 'buf' to 'fd' at the current seek position. 153 | // 154 | // Returns: 155 | // The number of bytes successfully written. 156 | // < 0 on error. 157 | static ssize_t 158 | devfile_write(struct Fd *fd, const void *buf, size_t n) 159 | { 160 | // Make an FSREQ_WRITE request to the file system server. Be 161 | // careful: fsipcbuf.write.req_buf is only so large, but 162 | // remember that write is always allowed to write *fewer* 163 | // bytes than requested. 164 | // LAB 5: Your code here 165 | 166 | // Make request. 167 | struct Fsreq_write *req = &fsipcbuf.write; 168 | req->req_fileid = fd->fd_file.id; 169 | 170 | // Make sure not exceed size of req_buf. 171 | size_t mvsz = sizeof(req->req_buf); 172 | if (n < mvsz) 173 | mvsz = n; 174 | req->req_n = mvsz; 175 | 176 | // Copy the bytes to write. 177 | memmove(req->req_buf, buf, mvsz); 178 | 179 | // Send request. 180 | ssize_t wrnsz = fsipc(FSREQ_WRITE, NULL); 181 | 182 | return wrnsz; 183 | 184 | // panic("devfile_write not implemented"); 185 | } 186 | 187 | static int 188 | devfile_stat(struct Fd *fd, struct Stat *st) 189 | { 190 | int r; 191 | 192 | fsipcbuf.stat.req_fileid = fd->fd_file.id; 193 | if ((r = fsipc(FSREQ_STAT, NULL)) < 0) 194 | return r; 195 | strcpy(st->st_name, fsipcbuf.statRet.ret_name); 196 | st->st_size = fsipcbuf.statRet.ret_size; 197 | st->st_isdir = fsipcbuf.statRet.ret_isdir; 198 | return 0; 199 | } 200 | 201 | // Truncate or extend an open file to 'size' bytes 202 | static int 203 | devfile_trunc(struct Fd *fd, off_t newsize) 204 | { 205 | fsipcbuf.set_size.req_fileid = fd->fd_file.id; 206 | fsipcbuf.set_size.req_size = newsize; 207 | return fsipc(FSREQ_SET_SIZE, NULL); 208 | } 209 | 210 | // Delete a file 211 | int 212 | remove(const char *path) 213 | { 214 | if (strlen(path) >= MAXPATHLEN) 215 | return -E_BAD_PATH; 216 | strcpy(fsipcbuf.remove.req_path, path); 217 | return fsipc(FSREQ_REMOVE, NULL); 218 | } 219 | 220 | // Synchronize disk with buffer cache 221 | int 222 | sync(void) 223 | { 224 | // Ask the file server to update the disk 225 | // by writing any dirty blocks in the buffer cache. 226 | 227 | return fsipc(FSREQ_SYNC, NULL); 228 | } 229 | 230 | -------------------------------------------------------------------------------- /lib/fork.c: -------------------------------------------------------------------------------- 1 | // implement fork from user space 2 | 3 | #include 4 | #include 5 | 6 | // PTE_COW marks copy-on-write page table entries. 7 | // It is one of the bits explicitly allocated to user processes (PTE_AVAIL). 8 | #define PTE_COW 0x800 9 | 10 | // 11 | // Custom page fault handler - if faulting page is copy-on-write, 12 | // map in our own private writable copy. 13 | // 14 | static void 15 | pgfault(struct UTrapframe *utf) 16 | { 17 | void *addr = (void *) utf->utf_fault_va; 18 | uint32_t err = utf->utf_err; 19 | int r; 20 | 21 | // Check that the faulting access was (1) a write, and (2) to a 22 | // copy-on-write page. If not, panic. 23 | // Hint: 24 | // Use the read-only page table mappings at vpt 25 | // (see ). 26 | 27 | // LAB 4: Your code here. 28 | 29 | // Comment this out, otherwise user/prime would make the screen mass. 30 | // cprintf("Well, I don't quite understand how vpt and vpd works!\n"); 31 | 32 | // check if access is write and to a copy-on-write page. 33 | pte_t pte = vpt[PGNUM(addr)]; 34 | if (!(err & FEC_WR) || !(pte & PTE_COW)) 35 | panic("pgfault: faulting access not write or not to a copy-on-write page"); 36 | 37 | // Allocate a new page, map it at a temporary location (PFTEMP), 38 | // copy the data from the old page to the new page, then move the new 39 | // page to the old page's address. 40 | // Hint: 41 | // You should make three system calls. 42 | // No need to explicitly delete the old page's mapping. 43 | 44 | // LAB 4: Your code here. 45 | if (sys_page_alloc(0, PFTEMP, PTE_W | PTE_U | PTE_P)) 46 | panic("pgfault: no phys mem"); 47 | 48 | // copy data to the new page from the source page. 49 | void *fltpg_addr = (void *)ROUNDDOWN(addr, PGSIZE); 50 | memmove(PFTEMP, fltpg_addr, PGSIZE); 51 | 52 | // change mapping for the faulting page. 53 | if (sys_page_map(0, 54 | PFTEMP, 55 | 0, 56 | fltpg_addr, 57 | PTE_W | PTE_U | PTE_P)) 58 | panic("pgfault: map error"); 59 | 60 | // panic("pgfault not implemented"); 61 | } 62 | 63 | // 64 | // Map our virtual page pn (address pn*PGSIZE) into the target envid 65 | // at the same virtual address. If the page is writable or copy-on-write, 66 | // the new mapping must be created copy-on-write, and then our mapping must be 67 | // marked copy-on-write as well. (Exercise: Why do we need to mark ours 68 | // copy-on-write again if it was already copy-on-write at the beginning of 69 | // this function?) 70 | // 71 | // Returns: 0 on success, < 0 on error. 72 | // It is also OK to panic on error. 73 | // 74 | static int 75 | duppage(envid_t envid, unsigned pn) 76 | { 77 | int r; 78 | 79 | // LAB 4: Your code here. 80 | 81 | pte_t pte = vpt[pn]; 82 | void *va = (void *)(pn << PGSHIFT); 83 | 84 | // If the page is writable or copy-on-write, 85 | // the mapping must be copy-on-write , 86 | // otherwise the new environment could change this page. 87 | if ((pte & PTE_W) || (pte & PTE_COW)) { 88 | if (sys_page_map(0, 89 | va, 90 | envid, 91 | va, 92 | PTE_COW | PTE_U | PTE_P)) 93 | panic("duppage: map cow error"); 94 | 95 | // Change permission of the page in this environment to copy-on-write. 96 | // Otherwise the new environment would see the change in this environment. 97 | if (sys_page_map(0, 98 | va, 99 | 0, 100 | va, 101 | PTE_COW | PTE_U | PTE_P)) 102 | panic("duppage: change perm error"); 103 | } else if (sys_page_map(0, 104 | va, 105 | envid, 106 | va, 107 | PTE_U | PTE_P)) 108 | panic("duppage: map ro error"); 109 | 110 | //panic("duppage not implemented"); 111 | return 0; 112 | } 113 | 114 | // 115 | // User-level fork with copy-on-write. 116 | // Set up our page fault handler appropriately. 117 | // Create a child. 118 | // Copy our address space and page fault handler setup to the child. 119 | // Then mark the child as runnable and return. 120 | // 121 | // Returns: child's envid to the parent, 0 to the child, < 0 on error. 122 | // It is also OK to panic on error. 123 | // 124 | // Hint: 125 | // Use vpd, vpt, and duppage. 126 | // Remember to fix "thisenv" in the child process. 127 | // Neither user exception stack should ever be marked copy-on-write, 128 | // so you must allocate a new page for the child's user exception stack. 129 | // 130 | envid_t 131 | fork(void) 132 | { 133 | // LAB 4: Your code here. 134 | 135 | // Step 1: install user mode pgfault handler. 136 | set_pgfault_handler(pgfault); 137 | 138 | // Step 2: create child environment. 139 | envid_t envid = sys_exofork(); 140 | if (envid < 0) { 141 | panic("fork: cannot create child env"); 142 | } else if (envid == 0) { 143 | // child environment. 144 | thisenv = &envs[ENVX(sys_getenvid())]; 145 | return 0; 146 | } 147 | 148 | // Step 3: duplicate pages. 149 | int ipd; 150 | for (ipd = 0; ipd != PDX(UTOP); ++ipd) { 151 | // No page table yet. 152 | if (!(vpd[ipd] & PTE_P)) 153 | continue; 154 | 155 | int ipt; 156 | for (ipt = 0; ipt != NPTENTRIES; ++ipt) { 157 | unsigned pn = (ipd << 10) | ipt; 158 | if (pn == PGNUM(UXSTACKTOP - PGSIZE)) { 159 | // allocate a new page for child to hold the exception stack. 160 | if (sys_page_alloc(envid, 161 | (void *)(UXSTACKTOP - PGSIZE), 162 | PTE_W | PTE_U | PTE_P)) 163 | panic("fork: no phys mem for xstk"); 164 | 165 | continue; 166 | } 167 | 168 | if (vpt[pn] & PTE_P) 169 | duppage(envid, pn); 170 | } 171 | } 172 | 173 | // Step 4: set user page fault entry for child. 174 | if (sys_env_set_pgfault_upcall(envid, thisenv->env_pgfault_upcall)) 175 | panic("fork: cannot set pgfault upcall"); 176 | 177 | // Step 5: set child status to ENV_RUNNABLE. 178 | if (sys_env_set_status(envid, ENV_RUNNABLE)) 179 | panic("fork: cannot set env status"); 180 | 181 | return envid; 182 | 183 | // panic("fork not implemented"); 184 | } 185 | 186 | // Challenge! 187 | int 188 | sfork(void) 189 | { 190 | panic("sfork not implemented"); 191 | return -E_INVAL; 192 | } 193 | -------------------------------------------------------------------------------- /lib/fprintf.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Collect up to 256 characters into a buffer 4 | // and perform ONE system call to print all of them, 5 | // in order to make the lines output to the console atomic 6 | // and prevent interrupts from causing context switches 7 | // in the middle of a console output line and such. 8 | struct printbuf { 9 | int fd; // file descriptor 10 | int idx; // current buffer index 11 | ssize_t result; // accumulated results from write 12 | int error; // first error that occurred 13 | char buf[256]; 14 | }; 15 | 16 | 17 | static void 18 | writebuf(struct printbuf *b) 19 | { 20 | if (b->error > 0) { 21 | ssize_t result = write(b->fd, b->buf, b->idx); 22 | if (result > 0) 23 | b->result += result; 24 | if (result != b->idx) // error, or wrote less than supplied 25 | b->error = (result < 0 ? result : 0); 26 | } 27 | } 28 | 29 | static void 30 | putch(int ch, void *thunk) 31 | { 32 | struct printbuf *b = (struct printbuf *) thunk; 33 | b->buf[b->idx++] = ch; 34 | if (b->idx == 256) { 35 | writebuf(b); 36 | b->idx = 0; 37 | } 38 | } 39 | 40 | int 41 | vfprintf(int fd, const char *fmt, va_list ap) 42 | { 43 | struct printbuf b; 44 | 45 | b.fd = fd; 46 | b.idx = 0; 47 | b.result = 0; 48 | b.error = 1; 49 | vprintfmt(putch, &b, fmt, ap); 50 | if (b.idx > 0) 51 | writebuf(&b); 52 | 53 | return (b.result ? b.result : b.error); 54 | } 55 | 56 | int 57 | fprintf(int fd, const char *fmt, ...) 58 | { 59 | va_list ap; 60 | int cnt; 61 | 62 | va_start(ap, fmt); 63 | cnt = vfprintf(fd, fmt, ap); 64 | va_end(ap); 65 | 66 | return cnt; 67 | } 68 | 69 | int 70 | printf(const char *fmt, ...) 71 | { 72 | va_list ap; 73 | int cnt; 74 | 75 | va_start(ap, fmt); 76 | cnt = vcprintf(fmt, ap); 77 | va_end(ap); 78 | 79 | return cnt; 80 | } 81 | 82 | -------------------------------------------------------------------------------- /lib/ipc.c: -------------------------------------------------------------------------------- 1 | // User-level IPC library routines 2 | 3 | #include 4 | 5 | // Receive a value via IPC and return it. 6 | // If 'pg' is nonnull, then any page sent by the sender will be mapped at 7 | // that address. 8 | // If 'from_env_store' is nonnull, then store the IPC sender's envid in 9 | // *from_env_store. 10 | // If 'perm_store' is nonnull, then store the IPC sender's page permission 11 | // in *perm_store (this is nonzero iff a page was successfully 12 | // transferred to 'pg'). 13 | // If the system call fails, then store 0 in *fromenv and *perm (if 14 | // they're nonnull) and return the error. 15 | // Otherwise, return the value sent by the sender 16 | // 17 | // Hint: 18 | // Use 'thisenv' to discover the value and who sent it. 19 | // If 'pg' is null, pass sys_ipc_recv a value that it will understand 20 | // as meaning "no page". (Zero is not the right value, since that's 21 | // a perfectly valid place to map a page.) 22 | int32_t 23 | ipc_recv(envid_t *from_env_store, void *pg, int *perm_store) 24 | { 25 | // LAB 4: Your code here. 26 | if (!pg) 27 | pg = (void *)UTOP; 28 | 29 | int result; 30 | if ((result = sys_ipc_recv(pg))) { 31 | if (from_env_store) 32 | *from_env_store = 0; 33 | if (perm_store) 34 | *perm_store = 0; 35 | 36 | return result; 37 | } 38 | 39 | if (from_env_store) 40 | *from_env_store = thisenv->env_ipc_from; 41 | 42 | if (perm_store) 43 | *perm_store = thisenv->env_ipc_perm; 44 | 45 | // panic("ipc_recv not implemented"); 46 | return thisenv->env_ipc_value; 47 | } 48 | 49 | // Send 'val' (and 'pg' with 'perm', if 'pg' is nonnull) to 'toenv'. 50 | // This function keeps trying until it succeeds. 51 | // It should panic() on any error other than -E_IPC_NOT_RECV. 52 | // 53 | // Hint: 54 | // Use sys_yield() to be CPU-friendly. 55 | // If 'pg' is null, pass sys_ipc_recv a value that it will understand 56 | // as meaning "no page". (Zero is not the right value.) 57 | void 58 | ipc_send(envid_t to_env, uint32_t val, void *pg, int perm) 59 | { 60 | // LAB 4: Your code here. 61 | if (!pg) 62 | pg = (void *)UTOP; 63 | 64 | int result; 65 | while (-E_IPC_NOT_RECV == (result = sys_ipc_try_send(to_env, val, pg, perm))) 66 | sys_yield(); 67 | 68 | if (result) 69 | panic("ipc_send: error"); 70 | 71 | //panic("ipc_send not implemented"); 72 | } 73 | 74 | // Find the first environment of the given type. We'll use this to 75 | // find special environments. 76 | // Returns 0 if no such environment exists. 77 | envid_t 78 | ipc_find_env(enum EnvType type) 79 | { 80 | int i; 81 | for (i = 0; i < NENV; i++) 82 | if (envs[i].env_type == type) 83 | return envs[i].env_id; 84 | return 0; 85 | } 86 | -------------------------------------------------------------------------------- /lib/libmain.c: -------------------------------------------------------------------------------- 1 | // Called from entry.S to get us going. 2 | // entry.S already took care of defining envs, pages, vpd, and vpt. 3 | 4 | #include 5 | 6 | extern void umain(int argc, char **argv); 7 | 8 | const volatile struct Env *thisenv; 9 | const char *binaryname = ""; 10 | 11 | void 12 | libmain(int argc, char **argv) 13 | { 14 | // set thisenv to point at our Env structure in envs[]. 15 | // LAB 3: Your code here. 16 | thisenv = &envs[ENVX(sys_getenvid())]; 17 | 18 | // save the name of the program so that panic() can use it 19 | if (argc > 0) 20 | binaryname = argv[0]; 21 | 22 | // call user main routine 23 | umain(argc, argv); 24 | 25 | // exit gracefully 26 | exit(); 27 | } 28 | 29 | -------------------------------------------------------------------------------- /lib/pageref.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int 4 | pageref(void *v) 5 | { 6 | pte_t pte; 7 | 8 | if (!(vpd[PDX(v)] & PTE_P)) 9 | return 0; 10 | pte = vpt[PGNUM(v)]; 11 | if (!(pte & PTE_P)) 12 | return 0; 13 | return pages[PGNUM(pte)].pp_ref; 14 | } 15 | -------------------------------------------------------------------------------- /lib/panic.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | /* 5 | * Panic is called on unresolvable fatal errors. 6 | * It prints "panic: ", then causes a breakpoint exception, 7 | * which causes JOS to enter the JOS kernel monitor. 8 | */ 9 | void 10 | _panic(const char *file, int line, const char *fmt, ...) 11 | { 12 | va_list ap; 13 | 14 | va_start(ap, fmt); 15 | 16 | // Print the panic message 17 | cprintf("[%08x] user panic in %s at %s:%d: ", 18 | sys_getenvid(), binaryname, file, line); 19 | vcprintf(fmt, ap); 20 | cprintf("\n"); 21 | 22 | // Cause a breakpoint exception 23 | while (1) 24 | asm volatile("int3"); 25 | } 26 | 27 | -------------------------------------------------------------------------------- /lib/pfentry.S: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // Page fault upcall entrypoint. 5 | 6 | // This is where we ask the kernel to redirect us to whenever we cause 7 | // a page fault in user space (see the call to sys_set_pgfault_handler 8 | // in pgfault.c). 9 | // 10 | // When a page fault actually occurs, the kernel switches our ESP to 11 | // point to the user exception stack if we're not already on the user 12 | // exception stack, and then it pushes a UTrapframe onto our user 13 | // exception stack: 14 | // 15 | // trap-time esp 16 | // trap-time eflags 17 | // trap-time eip 18 | // utf_regs.reg_eax 19 | // ... 20 | // utf_regs.reg_esi 21 | // utf_regs.reg_edi 22 | // utf_err (error code) 23 | // utf_fault_va <-- %esp 24 | // 25 | // If this is a recursive fault, the kernel will reserve for us a 26 | // blank word above the trap-time esp for scratch work when we unwind 27 | // the recursive call. 28 | // 29 | // We then have call up to the appropriate page fault handler in C 30 | // code, pointed to by the global variable '_pgfault_handler'. 31 | 32 | .text 33 | .globl _pgfault_upcall 34 | _pgfault_upcall: 35 | // Call the C page fault handler. 36 | pushl %esp // function argument: pointer to UTF 37 | movl _pgfault_handler, %eax 38 | call *%eax 39 | addl $4, %esp // pop function argument 40 | 41 | // Now the C page fault handler has returned and you must return 42 | // to the trap time state. 43 | // Push trap-time %eip onto the trap-time stack. 44 | // 45 | // Explanation: 46 | // We must prepare the trap-time stack for our eventual return to 47 | // re-execute the instruction that faulted. 48 | // Unfortunately, we can't return directly from the exception stack: 49 | // We can't call 'jmp', since that requires that we load the address 50 | // into a register, and all registers must have their trap-time 51 | // values after the return. 52 | // We can't call 'ret' from the exception stack either, since if we 53 | // did, %esp would have the wrong value. 54 | // So instead, we push the trap-time %eip onto the *trap-time* stack! 55 | // Below we'll switch to that stack and call 'ret', which will 56 | // restore %eip to its pre-fault value. 57 | // 58 | // In the case of a recursive fault on the exception stack, 59 | // note that the word we're pushing now will fit in the 60 | // blank word that the kernel reserved for us. 61 | // 62 | // Throughout the remaining code, think carefully about what 63 | // registers are available for intermediate calculations. You 64 | // may find that you have to rearrange your code in non-obvious 65 | // ways as registers become unavailable as scratch space. 66 | // 67 | // LAB 4: Your code here. 68 | movl %esp, %eax /* temporarily save exception stack esp */ 69 | movl 40(%esp), %ebx /* return addr -> ebx */ 70 | movl 48(%esp), %esp /* now trap-time stack */ 71 | pushl %ebx /* push onto trap-time stack */ 72 | movl %esp, 48(%eax) /* esp in frame is no longer its original position, 73 | * we just pushed the return address */ 74 | 75 | // Restore the trap-time registers. After you do this, you 76 | // can no longer modify any general-purpose registers. 77 | // LAB 4: Your code here. 78 | movl %eax, %esp /* now exception stack */ 79 | addl $4, %esp /* skip utf_fault_va */ 80 | addl $4, %esp /* skip utf_err */ 81 | popal /* restore from utf_regs */ 82 | addl $4, %esp /* skip utf_eip (already on trap-time stack) */ 83 | 84 | // Restore eflags from the stack. After you do this, you can 85 | // no longer use arithmetic operations or anything else that 86 | // modifies eflags. 87 | // LAB 4: Your code here. 88 | popfl /* restore from utf_eflags */ 89 | 90 | // Switch back to the adjusted trap-time stack. 91 | // LAB 4: Your code here. 92 | popl %esp /* restore from utf_esp */ 93 | 94 | // Return to re-execute the instruction that faulted. 95 | // LAB 4: Your code here. 96 | ret 97 | -------------------------------------------------------------------------------- /lib/pgfault.c: -------------------------------------------------------------------------------- 1 | // User-level page fault handler support. 2 | // Rather than register the C page fault handler directly with the 3 | // kernel as the page fault handler, we register the assembly language 4 | // wrapper in pfentry.S, which in turns calls the registered C 5 | // function. 6 | 7 | #include 8 | 9 | 10 | // Assembly language pgfault entrypoint defined in lib/pfentry.S. 11 | extern void _pgfault_upcall(void); 12 | 13 | // Pointer to currently installed C-language pgfault handler. 14 | void (*_pgfault_handler)(struct UTrapframe *utf); 15 | 16 | // 17 | // Set the page fault handler function. 18 | // If there isn't one yet, _pgfault_handler will be 0. 19 | // The first time we register a handler, we need to 20 | // allocate an exception stack (one page of memory with its top 21 | // at UXSTACKTOP), and tell the kernel to call the assembly-language 22 | // _pgfault_upcall routine when a page fault occurs. 23 | // 24 | void 25 | set_pgfault_handler(void (*handler)(struct UTrapframe *utf)) 26 | { 27 | int r; 28 | 29 | if (_pgfault_handler == 0) { 30 | // First time through! 31 | // LAB 4: Your code here. 32 | 33 | if (sys_page_alloc(0, 34 | (void *)(UXSTACKTOP - PGSIZE), 35 | PTE_W | PTE_U | PTE_P/* must be present */)) 36 | panic("set_pgfault_handler: no phys mem"); 37 | 38 | sys_env_set_pgfault_upcall(0, _pgfault_upcall); 39 | 40 | // panic("set_pgfault_handler not implemented"); 41 | } 42 | 43 | // Save handler pointer for assembly to call. 44 | _pgfault_handler = handler; 45 | } 46 | -------------------------------------------------------------------------------- /lib/printf.c: -------------------------------------------------------------------------------- 1 | // Implementation of cprintf console output for user environments, 2 | // based on printfmt() and the sys_cputs() system call. 3 | // 4 | // cprintf is a debugging statement, not a generic output statement. 5 | // It is very important that it always go to the console, especially when 6 | // debugging file descriptor code! 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | 14 | // Collect up to 256 characters into a buffer 15 | // and perform ONE system call to print all of them, 16 | // in order to make the lines output to the console atomic 17 | // and prevent interrupts from causing context switches 18 | // in the middle of a console output line and such. 19 | struct printbuf { 20 | int idx; // current buffer index 21 | int cnt; // total bytes printed so far 22 | char buf[256]; 23 | }; 24 | 25 | 26 | static void 27 | putch(int ch, struct printbuf *b) 28 | { 29 | b->buf[b->idx++] = ch; 30 | if (b->idx == 256-1) { 31 | sys_cputs(b->buf, b->idx); 32 | b->idx = 0; 33 | } 34 | b->cnt++; 35 | } 36 | 37 | int 38 | vcprintf(const char *fmt, va_list ap) 39 | { 40 | struct printbuf b; 41 | 42 | b.idx = 0; 43 | b.cnt = 0; 44 | vprintfmt((void*)putch, &b, fmt, ap); 45 | sys_cputs(b.buf, b.idx); 46 | 47 | return b.cnt; 48 | } 49 | 50 | int 51 | cprintf(const char *fmt, ...) 52 | { 53 | va_list ap; 54 | int cnt; 55 | 56 | va_start(ap, fmt); 57 | cnt = vcprintf(fmt, ap); 58 | va_end(ap); 59 | 60 | return cnt; 61 | } 62 | 63 | -------------------------------------------------------------------------------- /lib/printfmt.c: -------------------------------------------------------------------------------- 1 | // Stripped-down primitive printf-style formatting routines, 2 | // used in common by printf, sprintf, fprintf, etc. 3 | // This code is also used by both the kernel and user programs. 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | /* 12 | * Space or zero padding and a field width are supported for the numeric 13 | * formats only. 14 | * 15 | * The special format %e takes an integer error code 16 | * and prints a string describing the error. 17 | * The integer may be positive or negative, 18 | * so that -E_NO_MEM and E_NO_MEM are equivalent. 19 | */ 20 | 21 | static const char * const error_string[MAXERROR] = 22 | { 23 | [E_UNSPECIFIED] = "unspecified error", 24 | [E_BAD_ENV] = "bad environment", 25 | [E_INVAL] = "invalid parameter", 26 | [E_NO_MEM] = "out of memory", 27 | [E_NO_FREE_ENV] = "out of environments", 28 | [E_FAULT] = "segmentation fault", 29 | [E_IPC_NOT_RECV]= "env is not recving", 30 | [E_EOF] = "unexpected end of file", 31 | [E_NO_DISK] = "no free space on disk", 32 | [E_MAX_OPEN] = "too many files are open", 33 | [E_NOT_FOUND] = "file or block not found", 34 | [E_BAD_PATH] = "invalid path", 35 | [E_FILE_EXISTS] = "file already exists", 36 | [E_NOT_EXEC] = "file is not a valid executable", 37 | [E_NOT_SUPP] = "operation not supported", 38 | }; 39 | 40 | /* 41 | * Print a number (base <= 16) in reverse order, 42 | * using specified putch function and associated pointer putdat. 43 | */ 44 | static void 45 | printnum(void (*putch)(int, void*), void *putdat, 46 | unsigned long long num, unsigned base, int width, int padc) 47 | { 48 | // first recursively print all preceding (more significant) digits 49 | if (num >= base) { 50 | printnum(putch, putdat, num / base, base, width - 1, padc); 51 | } else { 52 | // print any needed pad characters before first digit 53 | while (--width > 0) 54 | putch(padc, putdat); 55 | } 56 | 57 | // then print this (the least significant) digit 58 | putch("0123456789abcdef"[num % base], putdat); 59 | } 60 | 61 | // Get an unsigned int of various possible sizes from a varargs list, 62 | // depending on the lflag parameter. 63 | static unsigned long long 64 | getuint(va_list *ap, int lflag) 65 | { 66 | if (lflag >= 2) 67 | return va_arg(*ap, unsigned long long); 68 | else if (lflag) 69 | return va_arg(*ap, unsigned long); 70 | else 71 | return va_arg(*ap, unsigned int); 72 | } 73 | 74 | // Same as getuint but signed - can't use getuint 75 | // because of sign extension 76 | static long long 77 | getint(va_list *ap, int lflag) 78 | { 79 | if (lflag >= 2) 80 | return va_arg(*ap, long long); 81 | else if (lflag) 82 | return va_arg(*ap, long); 83 | else 84 | return va_arg(*ap, int); 85 | } 86 | 87 | 88 | // Main function to format and print a string. 89 | void printfmt(void (*putch)(int, void*), void *putdat, const char *fmt, ...); 90 | 91 | void 92 | vprintfmt(void (*putch)(int, void*), void *putdat, const char *fmt, va_list ap) 93 | { 94 | register const char *p; 95 | register int ch, err; 96 | unsigned long long num; 97 | int base, lflag, width, precision, altflag; 98 | char padc; 99 | 100 | while (1) { 101 | while ((ch = *(unsigned char *) fmt++) != '%') { 102 | if (ch == '\0') 103 | return; 104 | putch(ch, putdat); 105 | } 106 | 107 | // Process a %-escape sequence 108 | padc = ' '; 109 | width = -1; 110 | precision = -1; 111 | lflag = 0; 112 | altflag = 0; 113 | reswitch: 114 | switch (ch = *(unsigned char *) fmt++) { 115 | 116 | // flag to pad on the right 117 | case '-': 118 | padc = '-'; 119 | goto reswitch; 120 | 121 | // flag to pad with 0's instead of spaces 122 | case '0': 123 | padc = '0'; 124 | goto reswitch; 125 | 126 | // width field 127 | case '1': 128 | case '2': 129 | case '3': 130 | case '4': 131 | case '5': 132 | case '6': 133 | case '7': 134 | case '8': 135 | case '9': 136 | for (precision = 0; ; ++fmt) { 137 | precision = precision * 10 + ch - '0'; 138 | ch = *fmt; 139 | if (ch < '0' || ch > '9') 140 | break; 141 | } 142 | goto process_precision; 143 | 144 | case '*': 145 | precision = va_arg(ap, int); 146 | goto process_precision; 147 | 148 | case '.': 149 | if (width < 0) 150 | width = 0; 151 | goto reswitch; 152 | 153 | case '#': 154 | altflag = 1; 155 | goto reswitch; 156 | 157 | process_precision: 158 | if (width < 0) 159 | width = precision, precision = -1; 160 | goto reswitch; 161 | 162 | // long flag (doubled for long long) 163 | case 'l': 164 | lflag++; 165 | goto reswitch; 166 | 167 | // character 168 | case 'c': 169 | putch(va_arg(ap, int), putdat); 170 | break; 171 | 172 | // error message 173 | case 'e': 174 | err = va_arg(ap, int); 175 | if (err < 0) 176 | err = -err; 177 | if (err >= MAXERROR || (p = error_string[err]) == NULL) 178 | printfmt(putch, putdat, "error %d", err); 179 | else 180 | printfmt(putch, putdat, "%s", p); 181 | break; 182 | 183 | // string 184 | case 's': 185 | if ((p = va_arg(ap, char *)) == NULL) 186 | p = "(null)"; 187 | if (width > 0 && padc != '-') 188 | for (width -= strnlen(p, precision); width > 0; width--) 189 | putch(padc, putdat); 190 | for (; (ch = *p++) != '\0' && (precision < 0 || --precision >= 0); width--) 191 | if (altflag && (ch < ' ' || ch > '~')) 192 | putch('?', putdat); 193 | else 194 | putch(ch, putdat); 195 | for (; width > 0; width--) 196 | putch(' ', putdat); 197 | break; 198 | 199 | // (signed) decimal 200 | case 'd': 201 | num = getint(&ap, lflag); 202 | if ((long long) num < 0) { 203 | putch('-', putdat); 204 | num = -(long long) num; 205 | } 206 | base = 10; 207 | goto number; 208 | 209 | // unsigned decimal 210 | case 'u': 211 | num = getuint(&ap, lflag); 212 | base = 10; 213 | goto number; 214 | 215 | // (unsigned) octal 216 | case 'o': 217 | num = getuint(&ap, lflag); 218 | base = 8; 219 | goto number; 220 | /*// Replace this with your code. 221 | putch('X', putdat); 222 | putch('X', putdat); 223 | putch('X', putdat); 224 | break;*/ 225 | 226 | // pointer 227 | case 'p': 228 | putch('0', putdat); 229 | putch('x', putdat); 230 | num = (unsigned long long) 231 | (uintptr_t) va_arg(ap, void *); 232 | base = 16; 233 | goto number; 234 | 235 | // (unsigned) hexadecimal 236 | case 'x': 237 | num = getuint(&ap, lflag); 238 | base = 16; 239 | number: 240 | printnum(putch, putdat, num, base, width, padc); 241 | break; 242 | 243 | // escaped '%' character 244 | case '%': 245 | putch(ch, putdat); 246 | break; 247 | 248 | // unrecognized escape sequence - just print it literally 249 | default: 250 | putch('%', putdat); 251 | for (fmt--; fmt[-1] != '%'; fmt--) 252 | /* do nothing */; 253 | break; 254 | } 255 | } 256 | } 257 | 258 | void 259 | printfmt(void (*putch)(int, void*), void *putdat, const char *fmt, ...) 260 | { 261 | va_list ap; 262 | 263 | va_start(ap, fmt); 264 | vprintfmt(putch, putdat, fmt, ap); 265 | va_end(ap); 266 | } 267 | 268 | struct sprintbuf { 269 | char *buf; 270 | char *ebuf; 271 | int cnt; 272 | }; 273 | 274 | static void 275 | sprintputch(int ch, struct sprintbuf *b) 276 | { 277 | b->cnt++; 278 | if (b->buf < b->ebuf) 279 | *b->buf++ = ch; 280 | } 281 | 282 | int 283 | vsnprintf(char *buf, int n, const char *fmt, va_list ap) 284 | { 285 | struct sprintbuf b = {buf, buf+n-1, 0}; 286 | 287 | if (buf == NULL || n < 1) 288 | return -E_INVAL; 289 | 290 | // print the string to the buffer 291 | vprintfmt((void*)sprintputch, &b, fmt, ap); 292 | 293 | // null terminate the buffer 294 | *b.buf = '\0'; 295 | 296 | return b.cnt; 297 | } 298 | 299 | int 300 | snprintf(char *buf, int n, const char *fmt, ...) 301 | { 302 | va_list ap; 303 | int rc; 304 | 305 | va_start(ap, fmt); 306 | rc = vsnprintf(buf, n, fmt, ap); 307 | va_end(ap); 308 | 309 | return rc; 310 | } 311 | 312 | 313 | -------------------------------------------------------------------------------- /lib/readline.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define BUFLEN 1024 5 | static char buf[BUFLEN]; 6 | 7 | char * 8 | readline(const char *prompt) 9 | { 10 | int i, c, echoing; 11 | 12 | if (prompt != NULL) 13 | cprintf("%s", prompt); 14 | 15 | i = 0; 16 | echoing = iscons(0); 17 | while (1) { 18 | c = getchar(); 19 | if (c < 0) { 20 | cprintf("read error: %e\n", c); 21 | return NULL; 22 | } else if ((c == '\b' || c == '\x7f') && i > 0) { 23 | if (echoing) 24 | cputchar('\b'); 25 | i--; 26 | } else if (c >= ' ' && i < BUFLEN-1) { 27 | if (echoing) 28 | cputchar(c); 29 | buf[i++] = c; 30 | } else if (c == '\n' || c == '\r') { 31 | if (echoing) 32 | cputchar('\n'); 33 | buf[i] = 0; 34 | return buf; 35 | } 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /lib/string.c: -------------------------------------------------------------------------------- 1 | // Basic string routines. Not hardware optimized, but not shabby. 2 | 3 | #include 4 | 5 | // Using assembly for memset/memmove 6 | // makes some difference on real hardware, 7 | // but it makes an even bigger difference on bochs. 8 | // Primespipe runs 3x faster this way. 9 | #define ASM 1 10 | 11 | int 12 | strlen(const char *s) 13 | { 14 | int n; 15 | 16 | for (n = 0; *s != '\0'; s++) 17 | n++; 18 | return n; 19 | } 20 | 21 | int 22 | strnlen(const char *s, size_t size) 23 | { 24 | int n; 25 | 26 | for (n = 0; size > 0 && *s != '\0'; s++, size--) 27 | n++; 28 | return n; 29 | } 30 | 31 | char * 32 | strcpy(char *dst, const char *src) 33 | { 34 | char *ret; 35 | 36 | ret = dst; 37 | while ((*dst++ = *src++) != '\0') 38 | /* do nothing */; 39 | return ret; 40 | } 41 | 42 | char * 43 | strcat(char *dst, const char *src) 44 | { 45 | int len = strlen(dst); 46 | strcpy(dst + len, src); 47 | return dst; 48 | } 49 | 50 | char * 51 | strncpy(char *dst, const char *src, size_t size) { 52 | size_t i; 53 | char *ret; 54 | 55 | ret = dst; 56 | for (i = 0; i < size; i++) { 57 | *dst++ = *src; 58 | // If strlen(src) < size, null-pad 'dst' out to 'size' chars 59 | if (*src != '\0') 60 | src++; 61 | } 62 | return ret; 63 | } 64 | 65 | size_t 66 | strlcpy(char *dst, const char *src, size_t size) 67 | { 68 | char *dst_in; 69 | 70 | dst_in = dst; 71 | if (size > 0) { 72 | while (--size > 0 && *src != '\0') 73 | *dst++ = *src++; 74 | *dst = '\0'; 75 | } 76 | return dst - dst_in; 77 | } 78 | 79 | int 80 | strcmp(const char *p, const char *q) 81 | { 82 | while (*p && *p == *q) 83 | p++, q++; 84 | return (int) ((unsigned char) *p - (unsigned char) *q); 85 | } 86 | 87 | int 88 | strncmp(const char *p, const char *q, size_t n) 89 | { 90 | while (n > 0 && *p && *p == *q) 91 | n--, p++, q++; 92 | if (n == 0) 93 | return 0; 94 | else 95 | return (int) ((unsigned char) *p - (unsigned char) *q); 96 | } 97 | 98 | // Return a pointer to the first occurrence of 'c' in 's', 99 | // or a null pointer if the string has no 'c'. 100 | char * 101 | strchr(const char *s, char c) 102 | { 103 | for (; *s; s++) 104 | if (*s == c) 105 | return (char *) s; 106 | return 0; 107 | } 108 | 109 | // Return a pointer to the first occurrence of 'c' in 's', 110 | // or a pointer to the string-ending null character if the string has no 'c'. 111 | char * 112 | strfind(const char *s, char c) 113 | { 114 | for (; *s; s++) 115 | if (*s == c) 116 | break; 117 | return (char *) s; 118 | } 119 | 120 | #if ASM 121 | void * 122 | memset(void *v, int c, size_t n) 123 | { 124 | char *p; 125 | 126 | if (n == 0) 127 | return v; 128 | if ((int)v%4 == 0 && n%4 == 0) { 129 | c &= 0xFF; 130 | c = (c<<24)|(c<<16)|(c<<8)|c; 131 | asm volatile("cld; rep stosl\n" 132 | :: "D" (v), "a" (c), "c" (n/4) 133 | : "cc", "memory"); 134 | } else 135 | asm volatile("cld; rep stosb\n" 136 | :: "D" (v), "a" (c), "c" (n) 137 | : "cc", "memory"); 138 | return v; 139 | } 140 | 141 | void * 142 | memmove(void *dst, const void *src, size_t n) 143 | { 144 | const char *s; 145 | char *d; 146 | 147 | s = src; 148 | d = dst; 149 | if (s < d && s + n > d) { 150 | s += n; 151 | d += n; 152 | if ((int)s%4 == 0 && (int)d%4 == 0 && n%4 == 0) 153 | asm volatile("std; rep movsl\n" 154 | :: "D" (d-4), "S" (s-4), "c" (n/4) : "cc", "memory"); 155 | else 156 | asm volatile("std; rep movsb\n" 157 | :: "D" (d-1), "S" (s-1), "c" (n) : "cc", "memory"); 158 | // Some versions of GCC rely on DF being clear 159 | asm volatile("cld" ::: "cc"); 160 | } else { 161 | if ((int)s%4 == 0 && (int)d%4 == 0 && n%4 == 0) 162 | asm volatile("cld; rep movsl\n" 163 | :: "D" (d), "S" (s), "c" (n/4) : "cc", "memory"); 164 | else 165 | asm volatile("cld; rep movsb\n" 166 | :: "D" (d), "S" (s), "c" (n) : "cc", "memory"); 167 | } 168 | return dst; 169 | } 170 | 171 | #else 172 | 173 | void * 174 | memset(void *v, int c, size_t n) 175 | { 176 | char *p; 177 | int m; 178 | 179 | p = v; 180 | m = n; 181 | while (--m >= 0) 182 | *p++ = c; 183 | 184 | return v; 185 | } 186 | 187 | /* no memcpy - use memmove instead */ 188 | 189 | void * 190 | memmove(void *dst, const void *src, size_t n) 191 | { 192 | const char *s; 193 | char *d; 194 | 195 | s = src; 196 | d = dst; 197 | if (s < d && s + n > d) { 198 | s += n; 199 | d += n; 200 | while (n-- > 0) 201 | *--d = *--s; 202 | } else 203 | while (n-- > 0) 204 | *d++ = *s++; 205 | 206 | return dst; 207 | } 208 | #endif 209 | 210 | /* sigh - gcc emits references to this for structure assignments! */ 211 | /* it is *not* prototyped in inc/string.h - do not use directly. */ 212 | void * 213 | memcpy(void *dst, void *src, size_t n) 214 | { 215 | return memmove(dst, src, n); 216 | } 217 | 218 | int 219 | memcmp(const void *v1, const void *v2, size_t n) 220 | { 221 | const uint8_t *s1 = (const uint8_t *) v1; 222 | const uint8_t *s2 = (const uint8_t *) v2; 223 | 224 | while (n-- > 0) { 225 | if (*s1 != *s2) 226 | return (int) *s1 - (int) *s2; 227 | s1++, s2++; 228 | } 229 | 230 | return 0; 231 | } 232 | 233 | void * 234 | memfind(const void *s, int c, size_t n) 235 | { 236 | const void *ends = (const char *) s + n; 237 | for (; s < ends; s++) 238 | if (*(const unsigned char *) s == (unsigned char) c) 239 | break; 240 | return (void *) s; 241 | } 242 | 243 | long 244 | strtol(const char *s, char **endptr, int base) 245 | { 246 | int neg = 0; 247 | long val = 0; 248 | 249 | // gobble initial whitespace 250 | while (*s == ' ' || *s == '\t') 251 | s++; 252 | 253 | // plus/minus sign 254 | if (*s == '+') 255 | s++; 256 | else if (*s == '-') 257 | s++, neg = 1; 258 | 259 | // hex or octal base prefix 260 | if ((base == 0 || base == 16) && (s[0] == '0' && s[1] == 'x')) 261 | s += 2, base = 16; 262 | else if (base == 0 && s[0] == '0') 263 | s++, base = 8; 264 | else if (base == 0) 265 | base = 10; 266 | 267 | // digits 268 | while (1) { 269 | int dig; 270 | 271 | if (*s >= '0' && *s <= '9') 272 | dig = *s - '0'; 273 | else if (*s >= 'a' && *s <= 'z') 274 | dig = *s - 'a' + 10; 275 | else if (*s >= 'A' && *s <= 'Z') 276 | dig = *s - 'A' + 10; 277 | else 278 | break; 279 | if (dig >= base) 280 | break; 281 | s++, val = (val * base) + dig; 282 | // we don't properly detect overflow! 283 | } 284 | 285 | if (endptr) 286 | *endptr = (char *) s; 287 | return (neg ? -val : val); 288 | } 289 | 290 | -------------------------------------------------------------------------------- /lib/syscall.c: -------------------------------------------------------------------------------- 1 | // System call stubs. 2 | 3 | #include 4 | #include 5 | 6 | static inline int32_t 7 | syscall(int num, int check, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5) 8 | { 9 | int32_t ret; 10 | 11 | // Generic system call: pass system call number in AX, 12 | // up to five parameters in DX, CX, BX, DI, SI. 13 | // Interrupt kernel with T_SYSCALL. 14 | // 15 | // The "volatile" tells the assembler not to optimize 16 | // this instruction away just because we don't use the 17 | // return value. 18 | // 19 | // The last clause tells the assembler that this can 20 | // potentially change the condition codes and arbitrary 21 | // memory locations. 22 | 23 | asm volatile("int %1\n" 24 | : "=a" (ret) 25 | : "i" (T_SYSCALL), 26 | "a" (num), 27 | "d" (a1), 28 | "c" (a2), 29 | "b" (a3), 30 | "D" (a4), 31 | "S" (a5) 32 | : "cc", "memory"); 33 | 34 | if(check && ret > 0) 35 | panic("syscall %d returned %d (> 0)", num, ret); 36 | 37 | return ret; 38 | } 39 | 40 | void 41 | sys_cputs(const char *s, size_t len) 42 | { 43 | syscall(SYS_cputs, 0, (uint32_t)s, len, 0, 0, 0); 44 | } 45 | 46 | int 47 | sys_cgetc(void) 48 | { 49 | return syscall(SYS_cgetc, 0, 0, 0, 0, 0, 0); 50 | } 51 | 52 | int 53 | sys_env_destroy(envid_t envid) 54 | { 55 | return syscall(SYS_env_destroy, 1, envid, 0, 0, 0, 0); 56 | } 57 | 58 | envid_t 59 | sys_getenvid(void) 60 | { 61 | return syscall(SYS_getenvid, 0, 0, 0, 0, 0, 0); 62 | } 63 | 64 | void 65 | sys_yield(void) 66 | { 67 | syscall(SYS_yield, 0, 0, 0, 0, 0, 0); 68 | } 69 | 70 | int 71 | sys_page_alloc(envid_t envid, void *va, int perm) 72 | { 73 | return syscall(SYS_page_alloc, 1, envid, (uint32_t) va, perm, 0, 0); 74 | } 75 | 76 | int 77 | sys_page_map(envid_t srcenv, void *srcva, envid_t dstenv, void *dstva, int perm) 78 | { 79 | return syscall(SYS_page_map, 1, srcenv, (uint32_t) srcva, dstenv, (uint32_t) dstva, perm); 80 | } 81 | 82 | int 83 | sys_page_unmap(envid_t envid, void *va) 84 | { 85 | return syscall(SYS_page_unmap, 1, envid, (uint32_t) va, 0, 0, 0); 86 | } 87 | 88 | // sys_exofork is inlined in lib.h 89 | 90 | int 91 | sys_env_set_status(envid_t envid, int status) 92 | { 93 | return syscall(SYS_env_set_status, 1, envid, status, 0, 0, 0); 94 | } 95 | 96 | int 97 | sys_env_set_trapframe(envid_t envid, struct Trapframe *tf) 98 | { 99 | return syscall(SYS_env_set_trapframe, 1, envid, (uint32_t) tf, 0, 0, 0); 100 | } 101 | 102 | int 103 | sys_env_set_pgfault_upcall(envid_t envid, void *upcall) 104 | { 105 | return syscall(SYS_env_set_pgfault_upcall, 1, envid, (uint32_t) upcall, 0, 0, 0); 106 | } 107 | 108 | int 109 | sys_ipc_try_send(envid_t envid, uint32_t value, void *srcva, int perm) 110 | { 111 | return syscall(SYS_ipc_try_send, 0, envid, value, (uint32_t) srcva, perm, 0); 112 | } 113 | 114 | int 115 | sys_ipc_recv(void *dstva) 116 | { 117 | return syscall(SYS_ipc_recv, 1, (uint32_t)dstva, 0, 0, 0, 0); 118 | } 119 | 120 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /user/Makefrag: -------------------------------------------------------------------------------- 1 | OBJDIRS += user 2 | 3 | USERLIBS = jos 4 | 5 | $(OBJDIR)/user/%.o: user/%.c 6 | @echo + cc[USER] $< 7 | @mkdir -p $(@D) 8 | $(V)$(CC) -nostdinc $(USER_CFLAGS) -c -o $@ $< 9 | 10 | $(OBJDIR)/user/%: $(OBJDIR)/user/%.o $(OBJDIR)/lib/entry.o $(USERLIBS:%=$(OBJDIR)/lib/lib%.a) user/user.ld 11 | @echo + ld $@ 12 | $(V)$(LD) -o $@.debug $(ULDFLAGS) $(LDFLAGS) -nostdlib $(OBJDIR)/lib/entry.o $@.o -L$(OBJDIR)/lib $(USERLIBS:%=-l%) $(GCC_LIB) 13 | $(V)$(OBJDUMP) -S $@.debug > $@.asm 14 | $(V)$(NM) -n $@.debug > $@.sym 15 | $(V)$(OBJCOPY) -R .stab -R .stabstr --add-gnu-debuglink=$(basename $@.debug) $@.debug $@ 16 | 17 | -------------------------------------------------------------------------------- /user/badsegment.c: -------------------------------------------------------------------------------- 1 | // program to cause a general protection exception 2 | 3 | #include 4 | 5 | void 6 | umain(int argc, char **argv) 7 | { 8 | // Try to load the kernel's TSS selector into the DS register. 9 | asm volatile("movw $0x28,%ax; movw %ax,%ds"); 10 | } 11 | 12 | -------------------------------------------------------------------------------- /user/breakpoint.c: -------------------------------------------------------------------------------- 1 | // program to cause a breakpoint trap 2 | 3 | #include 4 | 5 | void 6 | umain(int argc, char **argv) 7 | { 8 | asm volatile("int $3"); 9 | } 10 | 11 | -------------------------------------------------------------------------------- /user/buggyhello.c: -------------------------------------------------------------------------------- 1 | // buggy hello world -- unmapped pointer passed to kernel 2 | // kernel should destroy user environment in response 3 | 4 | #include 5 | 6 | void 7 | umain(int argc, char **argv) 8 | { 9 | sys_cputs((char*)1, 1); 10 | } 11 | 12 | -------------------------------------------------------------------------------- /user/buggyhello2.c: -------------------------------------------------------------------------------- 1 | // buggy hello world 2 -- pointed-to region extends into unmapped memory 2 | // kernel should destroy user environment in response 3 | 4 | #include 5 | 6 | const char *hello = "hello, world\n"; 7 | 8 | void 9 | umain(int argc, char **argv) 10 | { 11 | sys_cputs(hello, 1024*1024); 12 | } 13 | 14 | -------------------------------------------------------------------------------- /user/divzero.c: -------------------------------------------------------------------------------- 1 | // buggy program - causes a divide by zero exception 2 | 3 | #include 4 | 5 | int zero; 6 | 7 | void 8 | umain(int argc, char **argv) 9 | { 10 | zero = 0; 11 | cprintf("1/0 is %08x!\n", 1/zero); 12 | } 13 | 14 | -------------------------------------------------------------------------------- /user/dumbfork.c: -------------------------------------------------------------------------------- 1 | // Ping-pong a counter between two processes. 2 | // Only need to start one of these -- splits into two, crudely. 3 | 4 | #include 5 | #include 6 | 7 | envid_t dumbfork(void); 8 | 9 | void 10 | umain(int argc, char **argv) 11 | { 12 | envid_t who; 13 | int i; 14 | 15 | // fork a child process 16 | who = dumbfork(); 17 | 18 | // print a message and yield to the other a few times 19 | for (i = 0; i < (who ? 10 : 20); i++) { 20 | cprintf("%d: I am the %s!\n", i, who ? "parent" : "child"); 21 | sys_yield(); 22 | } 23 | } 24 | 25 | void 26 | duppage(envid_t dstenv, void *addr) 27 | { 28 | int r; 29 | 30 | // This is NOT what you should do in your fork. 31 | if ((r = sys_page_alloc(dstenv, addr, PTE_P|PTE_U|PTE_W)) < 0) 32 | panic("sys_page_alloc: %e", r); 33 | if ((r = sys_page_map(dstenv, addr, 0, UTEMP, PTE_P|PTE_U|PTE_W)) < 0) 34 | panic("sys_page_map: %e", r); 35 | memmove(UTEMP, addr, PGSIZE); 36 | if ((r = sys_page_unmap(0, UTEMP)) < 0) 37 | panic("sys_page_unmap: %e", r); 38 | } 39 | 40 | envid_t 41 | dumbfork(void) 42 | { 43 | envid_t envid; 44 | uint8_t *addr; 45 | int r; 46 | extern unsigned char end[]; 47 | 48 | // Allocate a new child environment. 49 | // The kernel will initialize it with a copy of our register state, 50 | // so that the child will appear to have called sys_exofork() too - 51 | // except that in the child, this "fake" call to sys_exofork() 52 | // will return 0 instead of the envid of the child. 53 | envid = sys_exofork(); 54 | if (envid < 0) 55 | panic("sys_exofork: %e", envid); 56 | if (envid == 0) { 57 | // We're the child. 58 | // The copied value of the global variable 'thisenv' 59 | // is no longer valid (it refers to the parent!). 60 | // Fix it and return 0. 61 | thisenv = &envs[ENVX(sys_getenvid())]; 62 | return 0; 63 | } 64 | 65 | // We're the parent. 66 | // Eagerly copy our entire address space into the child. 67 | // This is NOT what you should do in your fork implementation. 68 | for (addr = (uint8_t*) UTEXT; addr < end; addr += PGSIZE) 69 | duppage(envid, addr); 70 | 71 | // Also copy the stack we are currently running on. 72 | duppage(envid, ROUNDDOWN(&addr, PGSIZE)); 73 | 74 | // Start the child environment running 75 | if ((r = sys_env_set_status(envid, ENV_RUNNABLE)) < 0) 76 | panic("sys_env_set_status: %e", r); 77 | 78 | return envid; 79 | } 80 | 81 | -------------------------------------------------------------------------------- /user/evilhello.c: -------------------------------------------------------------------------------- 1 | // evil hello world -- kernel pointer passed to kernel 2 | // kernel should destroy user environment in response 3 | 4 | #include 5 | 6 | void 7 | umain(int argc, char **argv) 8 | { 9 | // try to print the kernel entry point as a string! mua ha ha! 10 | sys_cputs((char*)0xf010000c, 100); 11 | } 12 | 13 | -------------------------------------------------------------------------------- /user/fairness.c: -------------------------------------------------------------------------------- 1 | // Demonstrate lack of fairness in IPC. 2 | // Start three instances of this program as envs 1, 2, and 3. 3 | // (user/idle is env 0). 4 | 5 | #include 6 | 7 | void 8 | umain(int argc, char **argv) 9 | { 10 | envid_t who, id; 11 | 12 | id = sys_getenvid(); 13 | 14 | if (thisenv == &envs[1]) { 15 | while (1) { 16 | ipc_recv(&who, 0, 0); 17 | cprintf("%x recv from %x\n", id, who); 18 | } 19 | } else { 20 | cprintf("%x loop sending to %x\n", id, envs[1].env_id); 21 | while (1) 22 | ipc_send(envs[1].env_id, 0, 0, 0); 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /user/faultalloc.c: -------------------------------------------------------------------------------- 1 | // test user-level fault handler -- alloc pages to fix faults 2 | 3 | #include 4 | 5 | void 6 | handler(struct UTrapframe *utf) 7 | { 8 | int r; 9 | void *addr = (void*)utf->utf_fault_va; 10 | 11 | cprintf("fault %x\n", addr); 12 | if ((r = sys_page_alloc(0, ROUNDDOWN(addr, PGSIZE), 13 | PTE_P|PTE_U|PTE_W)) < 0) 14 | panic("allocating at %x in page fault handler: %e", addr, r); 15 | snprintf((char*) addr, 100, "this string was faulted in at %x", addr); 16 | } 17 | 18 | void 19 | umain(int argc, char **argv) 20 | { 21 | set_pgfault_handler(handler); 22 | cprintf("%s\n", (char*)0xDeadBeef); 23 | cprintf("%s\n", (char*)0xCafeBffe); 24 | } 25 | -------------------------------------------------------------------------------- /user/faultallocbad.c: -------------------------------------------------------------------------------- 1 | // test user-level fault handler -- alloc pages to fix faults 2 | // doesn't work because we sys_cputs instead of cprintf (exercise: why?) 3 | 4 | #include 5 | 6 | void 7 | handler(struct UTrapframe *utf) 8 | { 9 | int r; 10 | void *addr = (void*)utf->utf_fault_va; 11 | 12 | cprintf("fault %x\n", addr); 13 | if ((r = sys_page_alloc(0, ROUNDDOWN(addr, PGSIZE), 14 | PTE_P|PTE_U|PTE_W)) < 0) 15 | panic("allocating at %x in page fault handler: %e", addr, r); 16 | snprintf((char*) addr, 100, "this string was faulted in at %x", addr); 17 | } 18 | 19 | void 20 | umain(int argc, char **argv) 21 | { 22 | set_pgfault_handler(handler); 23 | sys_cputs((char*)0xDEADBEEF, 4); 24 | } 25 | -------------------------------------------------------------------------------- /user/faultbadhandler.c: -------------------------------------------------------------------------------- 1 | // test bad pointer for user-level fault handler 2 | // this is going to fault in the fault handler accessing eip (always!) 3 | // so eventually the kernel kills it (PFM_KILL) because 4 | // we outrun the stack with invocations of the user-level handler 5 | 6 | #include 7 | 8 | void 9 | umain(int argc, char **argv) 10 | { 11 | sys_page_alloc(0, (void*) (UXSTACKTOP - PGSIZE), PTE_P|PTE_U|PTE_W); 12 | sys_env_set_pgfault_upcall(0, (void*) 0xDeadBeef); 13 | *(int*)0 = 0; 14 | } 15 | -------------------------------------------------------------------------------- /user/faultdie.c: -------------------------------------------------------------------------------- 1 | // test user-level fault handler -- just exit when we fault 2 | 3 | #include 4 | 5 | void 6 | handler(struct UTrapframe *utf) 7 | { 8 | void *addr = (void*)utf->utf_fault_va; 9 | uint32_t err = utf->utf_err; 10 | cprintf("i faulted at va %x, err %x\n", addr, err & 7); 11 | sys_env_destroy(sys_getenvid()); 12 | } 13 | 14 | void 15 | umain(int argc, char **argv) 16 | { 17 | set_pgfault_handler(handler); 18 | *(int*)0xDeadBeef = 0; 19 | } 20 | -------------------------------------------------------------------------------- /user/faultevilhandler.c: -------------------------------------------------------------------------------- 1 | // test evil pointer for user-level fault handler 2 | 3 | #include 4 | 5 | void 6 | umain(int argc, char **argv) 7 | { 8 | sys_page_alloc(0, (void*) (UXSTACKTOP - PGSIZE), PTE_P|PTE_U|PTE_W); 9 | sys_env_set_pgfault_upcall(0, (void*) 0xF0100020); 10 | *(int*)0 = 0; 11 | } 12 | -------------------------------------------------------------------------------- /user/faultnostack.c: -------------------------------------------------------------------------------- 1 | // test user fault handler being called with no exception stack mapped 2 | 3 | #include 4 | 5 | void _pgfault_upcall(); 6 | 7 | void 8 | umain(int argc, char **argv) 9 | { 10 | sys_env_set_pgfault_upcall(0, (void*) _pgfault_upcall); 11 | *(int*)0 = 0; 12 | } 13 | -------------------------------------------------------------------------------- /user/faultread.c: -------------------------------------------------------------------------------- 1 | // buggy program - faults with a read from location zero 2 | 3 | #include 4 | 5 | void 6 | umain(int argc, char **argv) 7 | { 8 | cprintf("I read %08x from location 0!\n", *(unsigned*)0); 9 | } 10 | 11 | -------------------------------------------------------------------------------- /user/faultreadkernel.c: -------------------------------------------------------------------------------- 1 | // buggy program - faults with a read from kernel space 2 | 3 | #include 4 | 5 | void 6 | umain(int argc, char **argv) 7 | { 8 | cprintf("I read %08x from location 0xf0100000!\n", *(unsigned*)0xf0100000); 9 | } 10 | 11 | -------------------------------------------------------------------------------- /user/faultregs.c: -------------------------------------------------------------------------------- 1 | // test register restore on user-level page fault return 2 | 3 | #include 4 | 5 | struct regs 6 | { 7 | struct PushRegs regs; 8 | uintptr_t eip; 9 | uint32_t eflags; 10 | uintptr_t esp; 11 | }; 12 | 13 | #define SAVE_REGS(base) \ 14 | "\tmovl %%edi, "base"+0x00\n" \ 15 | "\tmovl %%esi, "base"+0x04\n" \ 16 | "\tmovl %%ebp, "base"+0x08\n" \ 17 | "\tmovl %%ebx, "base"+0x10\n" \ 18 | "\tmovl %%edx, "base"+0x14\n" \ 19 | "\tmovl %%ecx, "base"+0x18\n" \ 20 | "\tmovl %%eax, "base"+0x1c\n" \ 21 | "\tmovl %%esp, "base"+0x28\n" 22 | 23 | #define LOAD_REGS(base) \ 24 | "\tmovl "base"+0x00, %%edi\n" \ 25 | "\tmovl "base"+0x04, %%esi\n" \ 26 | "\tmovl "base"+0x08, %%ebp\n" \ 27 | "\tmovl "base"+0x10, %%ebx\n" \ 28 | "\tmovl "base"+0x14, %%edx\n" \ 29 | "\tmovl "base"+0x18, %%ecx\n" \ 30 | "\tmovl "base"+0x1c, %%eax\n" \ 31 | "\tmovl "base"+0x28, %%esp\n" 32 | 33 | static struct regs before, during, after; 34 | 35 | static void 36 | check_regs(struct regs* a, const char *an, struct regs* b, const char *bn, 37 | const char *testname) 38 | { 39 | int mismatch = 0; 40 | 41 | cprintf("%-6s %-8s %-8s\n", "", an, bn); 42 | 43 | #define CHECK(name, field) \ 44 | do { \ 45 | cprintf("%-6s %08x %08x ", #name, a->field, b->field); \ 46 | if (a->field == b->field) \ 47 | cprintf("OK\n"); \ 48 | else { \ 49 | cprintf("MISMATCH\n"); \ 50 | mismatch = 1; \ 51 | } \ 52 | } while (0) 53 | 54 | CHECK(edi, regs.reg_edi); 55 | CHECK(esi, regs.reg_esi); 56 | CHECK(ebp, regs.reg_ebp); 57 | CHECK(ebx, regs.reg_ebx); 58 | CHECK(edx, regs.reg_edx); 59 | CHECK(ecx, regs.reg_ecx); 60 | CHECK(eax, regs.reg_eax); 61 | CHECK(eip, eip); 62 | CHECK(eflags, eflags); 63 | CHECK(esp, esp); 64 | 65 | #undef CHECK 66 | 67 | cprintf("Registers %s ", testname); 68 | if (!mismatch) 69 | cprintf("OK\n"); 70 | else 71 | cprintf("MISMATCH\n"); 72 | } 73 | 74 | static void 75 | pgfault(struct UTrapframe *utf) 76 | { 77 | int r; 78 | 79 | if (utf->utf_fault_va != (uint32_t)UTEMP) 80 | panic("pgfault expected at UTEMP, got 0x%08x (eip %08x)", 81 | utf->utf_fault_va, utf->utf_eip); 82 | 83 | // Check registers in UTrapframe 84 | during.regs = utf->utf_regs; 85 | during.eip = utf->utf_eip; 86 | during.eflags = utf->utf_eflags; 87 | during.esp = utf->utf_esp; 88 | check_regs(&before, "before", &during, "during", "in UTrapframe"); 89 | 90 | // Map UTEMP so the write succeeds 91 | if ((r = sys_page_alloc(0, UTEMP, PTE_U|PTE_P|PTE_W)) < 0) 92 | panic("sys_page_alloc: %e", r); 93 | } 94 | 95 | void 96 | umain(int argc, char **argv) 97 | { 98 | set_pgfault_handler(pgfault); 99 | 100 | __asm __volatile( 101 | // Light up eflags to catch more errors 102 | "\tpushl %%eax\n" 103 | "\tpushfl\n" 104 | "\tpopl %%eax\n" 105 | "\torl $0x8d5, %%eax\n" 106 | "\tpushl %%eax\n" 107 | "\tpopfl\n" 108 | 109 | // Save before registers 110 | // eflags 111 | "\tmov %%eax, %0+0x24\n" 112 | // eip 113 | "\tleal 0f, %%eax\n" 114 | "\tmovl %%eax, %0+0x20\n" 115 | "\tpopl %%eax\n" 116 | // others 117 | SAVE_REGS("%0") 118 | 119 | // Fault at UTEMP 120 | "\t0: movl $42, 0x400000\n" 121 | 122 | // Save after registers (except eip and eflags) 123 | SAVE_REGS("%1") 124 | // Restore registers (except eip and eflags). This 125 | // way, the test will run even if EIP is the *only* 126 | // thing restored correctly. 127 | LOAD_REGS("%0") 128 | // Save after eflags (now that stack is back); note 129 | // that we were very careful not to modify eflags in 130 | // since we saved it 131 | "\tpushl %%eax\n" 132 | "\tpushfl\n" 133 | "\tpopl %%eax\n" 134 | "\tmov %%eax, %1+0x24\n" 135 | "\tpopl %%eax\n" 136 | : : "m" (before), "m" (after) : "memory", "cc"); 137 | 138 | // Check UTEMP to roughly determine that EIP was restored 139 | // correctly (of course, we probably wouldn't get this far if 140 | // it weren't) 141 | if (*(int*)UTEMP != 42) 142 | cprintf("EIP after page-fault MISMATCH\n"); 143 | after.eip = before.eip; 144 | 145 | check_regs(&before, "before", &after, "after", "after page-fault"); 146 | } 147 | -------------------------------------------------------------------------------- /user/faultwrite.c: -------------------------------------------------------------------------------- 1 | // buggy program - faults with a write to location zero 2 | 3 | #include 4 | 5 | void 6 | umain(int argc, char **argv) 7 | { 8 | *(unsigned*)0 = 0; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /user/faultwritekernel.c: -------------------------------------------------------------------------------- 1 | // buggy program - faults with a write to a kernel location 2 | 3 | #include 4 | 5 | void 6 | umain(int argc, char **argv) 7 | { 8 | *(unsigned*)0xf0100000 = 0; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /user/forktree.c: -------------------------------------------------------------------------------- 1 | // Fork a binary tree of processes and display their structure. 2 | 3 | #include 4 | 5 | #define DEPTH 3 6 | 7 | void forktree(const char *cur); 8 | 9 | void 10 | forkchild(const char *cur, char branch) 11 | { 12 | char nxt[DEPTH+1]; 13 | 14 | if (strlen(cur) >= DEPTH) 15 | return; 16 | 17 | snprintf(nxt, DEPTH+1, "%s%c", cur, branch); 18 | if (fork() == 0) { 19 | forktree(nxt); 20 | exit(); 21 | } 22 | } 23 | 24 | void 25 | forktree(const char *cur) 26 | { 27 | cprintf("%04x: I am '%s'\n", sys_getenvid(), cur); 28 | 29 | forkchild(cur, '0'); 30 | forkchild(cur, '1'); 31 | } 32 | 33 | void 34 | umain(int argc, char **argv) 35 | { 36 | forktree(""); 37 | } 38 | 39 | -------------------------------------------------------------------------------- /user/hello.c: -------------------------------------------------------------------------------- 1 | // hello, world 2 | #include 3 | 4 | void 5 | umain(int argc, char **argv) 6 | { 7 | cprintf("hello, world\n"); 8 | cprintf("i am environment %08x\n", thisenv->env_id); 9 | } 10 | -------------------------------------------------------------------------------- /user/icode.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void 4 | umain(int argc, char **argv) 5 | { 6 | int fd, n, r; 7 | char buf[512+1]; 8 | 9 | binaryname = "icode"; 10 | 11 | cprintf("icode startup\n"); 12 | 13 | cprintf("icode: open /motd\n"); 14 | if ((fd = open("/motd", O_RDONLY)) < 0) 15 | panic("icode: open /motd: %e", fd); 16 | 17 | cprintf("icode: read /motd\n"); 18 | while ((n = read(fd, buf, sizeof buf-1)) > 0) 19 | sys_cputs(buf, n); 20 | 21 | cprintf("icode: close /motd\n"); 22 | close(fd); 23 | 24 | cprintf("icode: spawn /init\n"); 25 | if ((r = spawnl("/init", "init", "initarg1", "initarg2", (char*)0)) < 0) 26 | panic("icode: spawn /init: %e", r); 27 | 28 | cprintf("icode: exiting\n"); 29 | } 30 | -------------------------------------------------------------------------------- /user/idle.c: -------------------------------------------------------------------------------- 1 | // idle loop 2 | 3 | #include 4 | #include 5 | 6 | void 7 | umain(int argc, char **argv) 8 | { 9 | binaryname = "idle"; 10 | 11 | // Loop forever, simply trying to yield to a different environment. 12 | // Instead of busy-waiting like this, 13 | // a better way would be to use the processor's HLT instruction 14 | // to cause the processor to stop executing until the next interrupt - 15 | // doing so allows the processor to conserve power more effectively. 16 | while (1) { 17 | sys_yield(); 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /user/init.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct { 4 | char msg1[5000]; 5 | char msg2[1000]; 6 | } data = { 7 | "this is initialized data", 8 | "so is this" 9 | }; 10 | 11 | char bss[6000]; 12 | 13 | int 14 | sum(const char *s, int n) 15 | { 16 | int i, tot = 0; 17 | for (i = 0; i < n; i++) 18 | tot ^= i * s[i]; 19 | return tot; 20 | } 21 | 22 | void 23 | umain(int argc, char **argv) 24 | { 25 | int i, r, x, want; 26 | char args[256]; 27 | 28 | cprintf("init: running\n"); 29 | 30 | want = 0xf989e; 31 | if ((x = sum((char*)&data, sizeof data)) != want) 32 | cprintf("init: data is not initialized: got sum %08x wanted %08x\n", 33 | x, want); 34 | else 35 | cprintf("init: data seems okay\n"); 36 | if ((x = sum(bss, sizeof bss)) != 0) 37 | cprintf("bss is not initialized: wanted sum 0 got %08x\n", x); 38 | else 39 | cprintf("init: bss seems okay\n"); 40 | 41 | // output in one syscall per line to avoid output interleaving 42 | strcat(args, "init: args:"); 43 | for (i = 0; i < argc; i++) { 44 | strcat(args, " '"); 45 | strcat(args, argv[i]); 46 | strcat(args, "'"); 47 | } 48 | cprintf("%s\n", args); 49 | 50 | cprintf("init: exiting\n"); 51 | } 52 | -------------------------------------------------------------------------------- /user/pingpong.c: -------------------------------------------------------------------------------- 1 | // Ping-pong a counter between two processes. 2 | // Only need to start one of these -- splits into two with fork. 3 | 4 | #include 5 | 6 | void 7 | umain(int argc, char **argv) 8 | { 9 | envid_t who; 10 | 11 | if ((who = fork()) != 0) { 12 | // get the ball rolling 13 | cprintf("send 0 from %x to %x\n", sys_getenvid(), who); 14 | ipc_send(who, 0, 0, 0); 15 | } 16 | 17 | while (1) { 18 | uint32_t i = ipc_recv(&who, 0, 0); 19 | cprintf("%x got %d from %x\n", sys_getenvid(), i, who); 20 | if (i == 10) 21 | return; 22 | i++; 23 | ipc_send(who, i, 0, 0); 24 | if (i == 10) 25 | return; 26 | } 27 | 28 | } 29 | 30 | -------------------------------------------------------------------------------- /user/pingpongs.c: -------------------------------------------------------------------------------- 1 | // Ping-pong a counter between two shared-memory processes. 2 | // Only need to start one of these -- splits into two with sfork. 3 | 4 | #include 5 | 6 | uint32_t val; 7 | 8 | void 9 | umain(int argc, char **argv) 10 | { 11 | envid_t who; 12 | uint32_t i; 13 | 14 | i = 0; 15 | if ((who = sfork()) != 0) { 16 | cprintf("i am %08x; thisenv is %p\n", sys_getenvid(), thisenv); 17 | // get the ball rolling 18 | cprintf("send 0 from %x to %x\n", sys_getenvid(), who); 19 | ipc_send(who, 0, 0, 0); 20 | } 21 | 22 | while (1) { 23 | ipc_recv(&who, 0, 0); 24 | cprintf("%x got %d from %x (thisenv is %p %x)\n", sys_getenvid(), val, who, thisenv, thisenv->env_id); 25 | if (val == 10) 26 | return; 27 | ++val; 28 | ipc_send(who, 0, 0, 0); 29 | if (val == 10) 30 | return; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /user/primes.c: -------------------------------------------------------------------------------- 1 | // Concurrent version of prime sieve of Eratosthenes. 2 | // Invented by Doug McIlroy, inventor of Unix pipes. 3 | // See http://swtch.com/~rsc/thread/. 4 | // The picture halfway down the page and the text surrounding it 5 | // explain what's going on here. 6 | // 7 | // Since NENVS is 1024, we can print 1022 primes before running out. 8 | // The remaining two environments are the integer generator at the bottom 9 | // of main and user/idle. 10 | 11 | #include 12 | 13 | unsigned 14 | primeproc(void) 15 | { 16 | int i, id, p; 17 | envid_t envid; 18 | 19 | // fetch a prime from our left neighbor 20 | top: 21 | p = ipc_recv(&envid, 0, 0); 22 | cprintf("CPU %d: %d ", thisenv->env_cpunum, p); 23 | 24 | // fork a right neighbor to continue the chain 25 | if ((id = fork()) < 0) 26 | panic("fork: %e", id); 27 | if (id == 0) 28 | goto top; 29 | 30 | // filter out multiples of our prime 31 | while (1) { 32 | i = ipc_recv(&envid, 0, 0); 33 | if (i % p) 34 | ipc_send(id, i, 0, 0); 35 | } 36 | } 37 | 38 | void 39 | umain(int argc, char **argv) 40 | { 41 | int i, id; 42 | 43 | // fork the first prime process in the chain 44 | if ((id = fork()) < 0) 45 | panic("fork: %e", id); 46 | if (id == 0) 47 | primeproc(); 48 | 49 | // feed all the integers through 50 | for (i = 2; ; i++) 51 | ipc_send(id, i, 0, 0); 52 | } 53 | 54 | -------------------------------------------------------------------------------- /user/softint.c: -------------------------------------------------------------------------------- 1 | // buggy program - causes an illegal software interrupt 2 | 3 | #include 4 | 5 | void 6 | umain(int argc, char **argv) 7 | { 8 | asm volatile("int $14"); // page fault 9 | } 10 | 11 | -------------------------------------------------------------------------------- /user/spawnhello.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void 4 | umain(int argc, char **argv) 5 | { 6 | int r; 7 | cprintf("i am parent environment %08x\n", thisenv->env_id); 8 | if ((r = spawnl("hello", "hello", 0)) < 0) 9 | panic("spawn(hello) failed: %e", r); 10 | } 11 | -------------------------------------------------------------------------------- /user/spawninit.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void 4 | umain(int argc, char **argv) 5 | { 6 | int r; 7 | cprintf("i am parent environment %08x\n", thisenv->env_id); 8 | if ((r = spawnl("init", "init", "one", "two", 0)) < 0) 9 | panic("spawnl(init) failed: %e", r); 10 | } 11 | -------------------------------------------------------------------------------- /user/spin.c: -------------------------------------------------------------------------------- 1 | // Test preemption by forking off a child process that just spins forever. 2 | // Let it run for a couple time slices, then kill it. 3 | 4 | #include 5 | 6 | void 7 | umain(int argc, char **argv) 8 | { 9 | envid_t env; 10 | 11 | cprintf("I am the parent. Forking the child...\n"); 12 | if ((env = fork()) == 0) { 13 | cprintf("I am the child. Spinning...\n"); 14 | while (1) 15 | /* do nothing */; 16 | } 17 | 18 | cprintf("I am the parent. Running the child...\n"); 19 | sys_yield(); 20 | sys_yield(); 21 | sys_yield(); 22 | sys_yield(); 23 | sys_yield(); 24 | sys_yield(); 25 | sys_yield(); 26 | sys_yield(); 27 | 28 | cprintf("I am the parent. Killing the child...\n"); 29 | sys_env_destroy(env); 30 | } 31 | 32 | -------------------------------------------------------------------------------- /user/stresssched.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | volatile int counter; 4 | 5 | void 6 | umain(int argc, char **argv) 7 | { 8 | int i, j; 9 | int seen; 10 | envid_t parent = sys_getenvid(); 11 | 12 | // Fork several environments 13 | for (i = 0; i < 20; i++) 14 | if (fork() == 0) 15 | break; 16 | if (i == 20) { 17 | sys_yield(); 18 | return; 19 | } 20 | 21 | // Wait for the parent to finish forking 22 | while (envs[ENVX(parent)].env_status != ENV_FREE) 23 | asm volatile("pause"); 24 | 25 | // Check that one environment doesn't run on two CPUs at once 26 | for (i = 0; i < 10; i++) { 27 | sys_yield(); 28 | for (j = 0; j < 10000; j++) 29 | counter++; 30 | } 31 | 32 | if (counter != 10*10000) 33 | panic("ran on two CPUs at once (counter is %d)", counter); 34 | 35 | // Check that we see environments running on different CPUs 36 | cprintf("[%08x] stresssched on CPU %d\n", thisenv->env_id, thisenv->env_cpunum); 37 | 38 | } 39 | 40 | -------------------------------------------------------------------------------- /user/testbss.c: -------------------------------------------------------------------------------- 1 | // test reads and writes to a large bss 2 | 3 | #include 4 | 5 | #define ARRAYSIZE (1024*1024) 6 | 7 | uint32_t bigarray[ARRAYSIZE]; 8 | 9 | void 10 | umain(int argc, char **argv) 11 | { 12 | int i; 13 | 14 | cprintf("Making sure bss works right...\n"); 15 | for (i = 0; i < ARRAYSIZE; i++) 16 | if (bigarray[i] != 0) 17 | panic("bigarray[%d] isn't cleared!\n", i); 18 | for (i = 0; i < ARRAYSIZE; i++) 19 | bigarray[i] = i; 20 | for (i = 0; i < ARRAYSIZE; i++) 21 | if (bigarray[i] != i) 22 | panic("bigarray[%d] didn't hold its value!\n", i); 23 | 24 | cprintf("Yes, good. Now doing a wild write off the end...\n"); 25 | bigarray[ARRAYSIZE+1024] = 0; 26 | panic("SHOULD HAVE TRAPPED!!!"); 27 | } 28 | -------------------------------------------------------------------------------- /user/testfile.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | const char *msg = "This is the NEW message of the day!\n\n"; 4 | 5 | #define FVA ((struct Fd*)0xCCCCC000) 6 | 7 | static int 8 | xopen(const char *path, int mode) 9 | { 10 | extern union Fsipc fsipcbuf; 11 | envid_t fsenv; 12 | 13 | strcpy(fsipcbuf.open.req_path, path); 14 | fsipcbuf.open.req_omode = mode; 15 | 16 | fsenv = ipc_find_env(ENV_TYPE_FS); 17 | ipc_send(fsenv, FSREQ_OPEN, &fsipcbuf, PTE_P | PTE_W | PTE_U); 18 | return ipc_recv(NULL, FVA, NULL); 19 | } 20 | 21 | void 22 | umain(int argc, char **argv) 23 | { 24 | int r, f, i; 25 | struct Fd *fd; 26 | struct Fd fdcopy; 27 | struct Stat st; 28 | char buf[512]; 29 | 30 | // We open files manually first, to avoid the FD layer 31 | if ((r = xopen("/not-found", O_RDONLY)) < 0 && r != -E_NOT_FOUND) 32 | panic("serve_open /not-found: %e", r); 33 | else if (r >= 0) 34 | panic("serve_open /not-found succeeded!"); 35 | 36 | if ((r = xopen("/newmotd", O_RDONLY)) < 0) 37 | panic("serve_open /newmotd: %e", r); 38 | if (FVA->fd_dev_id != 'f' || FVA->fd_offset != 0 || FVA->fd_omode != O_RDONLY) 39 | panic("serve_open did not fill struct Fd correctly\n"); 40 | cprintf("serve_open is good\n"); 41 | 42 | if ((r = devfile.dev_stat(FVA, &st)) < 0) 43 | panic("file_stat: %e", r); 44 | if (strlen(msg) != st.st_size) 45 | panic("file_stat returned size %d wanted %d\n", st.st_size, strlen(msg)); 46 | cprintf("file_stat is good\n"); 47 | 48 | memset(buf, 0, sizeof buf); 49 | if ((r = devfile.dev_read(FVA, buf, sizeof buf)) < 0) 50 | panic("file_read: %e", r); 51 | if (strcmp(buf, msg) != 0) 52 | panic("file_read returned wrong data"); 53 | cprintf("file_read is good\n"); 54 | 55 | if ((r = devfile.dev_close(FVA)) < 0) 56 | panic("file_close: %e", r); 57 | cprintf("file_close is good\n"); 58 | 59 | // We're about to unmap the FD, but still need a way to get 60 | // the stale filenum to serve_read, so we make a local copy. 61 | // The file server won't think it's stale until we unmap the 62 | // FD page. 63 | fdcopy = *FVA; 64 | sys_page_unmap(0, FVA); 65 | 66 | if ((r = devfile.dev_read(&fdcopy, buf, sizeof buf)) != -E_INVAL) 67 | panic("serve_read does not handle stale fileids correctly: %e", r); 68 | cprintf("stale fileid is good\n"); 69 | 70 | // Try writing 71 | if ((r = xopen("/new-file", O_RDWR|O_CREAT)) < 0) 72 | panic("serve_open /new-file: %e", r); 73 | 74 | if ((r = devfile.dev_write(FVA, msg, strlen(msg))) != strlen(msg)) 75 | panic("file_write: %e", r); 76 | cprintf("file_write is good\n"); 77 | 78 | FVA->fd_offset = 0; 79 | memset(buf, 0, sizeof buf); 80 | if ((r = devfile.dev_read(FVA, buf, sizeof buf)) < 0) 81 | panic("file_read after file_write: %e", r); 82 | if (r != strlen(msg)) 83 | panic("file_read after file_write returned wrong length: %d", r); 84 | if (strcmp(buf, msg) != 0) 85 | panic("file_read after file_write returned wrong data"); 86 | cprintf("file_read after file_write is good\n"); 87 | 88 | // Now we'll try out open 89 | if ((r = open("/not-found", O_RDONLY)) < 0 && r != -E_NOT_FOUND) 90 | panic("open /not-found: %e", r); 91 | else if (r >= 0) 92 | panic("open /not-found succeeded!"); 93 | 94 | if ((r = open("/newmotd", O_RDONLY)) < 0) 95 | panic("open /newmotd: %e", r); 96 | fd = (struct Fd*) (0xD0000000 + r*PGSIZE); 97 | if (fd->fd_dev_id != 'f' || fd->fd_offset != 0 || fd->fd_omode != O_RDONLY) 98 | panic("open did not fill struct Fd correctly\n"); 99 | cprintf("open is good\n"); 100 | 101 | // Try files with indirect blocks 102 | if ((f = open("/big", O_WRONLY|O_CREAT)) < 0) 103 | panic("creat /big: %e", f); 104 | memset(buf, 0, sizeof(buf)); 105 | for (i = 0; i < (NDIRECT*3)*BLKSIZE; i += sizeof(buf)) { 106 | *(int*)buf = i; 107 | if ((r = write(f, buf, sizeof(buf))) < 0) 108 | panic("write /big@%d: %e", i, r); 109 | } 110 | close(f); 111 | 112 | if ((f = open("/big", O_RDONLY)) < 0) 113 | panic("open /big: %e", f); 114 | for (i = 0; i < (NDIRECT*3)*BLKSIZE; i += sizeof(buf)) { 115 | *(int*)buf = i; 116 | if ((r = readn(f, buf, sizeof(buf))) < 0) 117 | panic("read /big@%d: %e", i, r); 118 | if (r != sizeof(buf)) 119 | panic("read /big from %d returned %d < %d bytes", 120 | i, r, sizeof(buf)); 121 | if (*(int*)buf != i) 122 | panic("read /big from %d returned bad data %d", 123 | i, *(int*)buf); 124 | } 125 | close(f); 126 | cprintf("large file is good\n"); 127 | } 128 | 129 | -------------------------------------------------------------------------------- /user/user.ld: -------------------------------------------------------------------------------- 1 | /* Simple linker script for JOS user-level programs. 2 | See the GNU ld 'info' manual ("info ld") to learn the syntax. */ 3 | 4 | OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") 5 | OUTPUT_ARCH(i386) 6 | ENTRY(_start) 7 | 8 | SECTIONS 9 | { 10 | /* Load programs at this address: "." means the current address */ 11 | . = 0x800020; 12 | 13 | .text : { 14 | *(.text .stub .text.* .gnu.linkonce.t.*) 15 | } 16 | 17 | PROVIDE(etext = .); /* Define the 'etext' symbol to this value */ 18 | 19 | .rodata : { 20 | *(.rodata .rodata.* .gnu.linkonce.r.*) 21 | } 22 | 23 | /* Adjust the address for the data segment to the next page */ 24 | . = ALIGN(0x1000); 25 | 26 | .data : { 27 | *(.data) 28 | } 29 | 30 | PROVIDE(edata = .); 31 | 32 | .bss : { 33 | *(.bss) 34 | } 35 | 36 | PROVIDE(end = .); 37 | 38 | 39 | /* Place debugging symbols so that they can be found by 40 | * the kernel debugger. 41 | * Specifically, the four words at 0x200000 mark the beginning of 42 | * the stabs, the end of the stabs, the beginning of the stabs 43 | * string table, and the end of the stabs string table, respectively. 44 | */ 45 | 46 | .stab_info 0x200000 : { 47 | LONG(__STAB_BEGIN__); 48 | LONG(__STAB_END__); 49 | LONG(__STABSTR_BEGIN__); 50 | LONG(__STABSTR_END__); 51 | } 52 | 53 | .stab : { 54 | __STAB_BEGIN__ = DEFINED(__STAB_BEGIN__) ? __STAB_BEGIN__ : .; 55 | *(.stab); 56 | __STAB_END__ = DEFINED(__STAB_END__) ? __STAB_END__ : .; 57 | BYTE(0) /* Force the linker to allocate space 58 | for this section */ 59 | } 60 | 61 | .stabstr : { 62 | __STABSTR_BEGIN__ = DEFINED(__STABSTR_BEGIN__) ? __STABSTR_BEGIN__ : .; 63 | *(.stabstr); 64 | __STABSTR_END__ = DEFINED(__STABSTR_END__) ? __STABSTR_END__ : .; 65 | BYTE(0) /* Force the linker to allocate space 66 | for this section */ 67 | } 68 | 69 | /DISCARD/ : { 70 | *(.eh_frame .note.GNU-stack .comment) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /user/writemotd.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void 4 | umain(int argc, char **argv) 5 | { 6 | int rfd, wfd; 7 | char buf[512]; 8 | int n, r; 9 | 10 | if ((rfd = open("/newmotd", O_RDONLY)) < 0) 11 | panic("open /newmotd: %e", rfd); 12 | if ((wfd = open("/motd", O_RDWR)) < 0) 13 | panic("open /motd: %e", wfd); 14 | cprintf("file descriptors %d %d\n", rfd, wfd); 15 | if (rfd == wfd) 16 | panic("open /newmotd and /motd give same file descriptor"); 17 | 18 | cprintf("OLD MOTD\n===\n"); 19 | while ((n = read(wfd, buf, sizeof buf-1)) > 0) 20 | sys_cputs(buf, n); 21 | cprintf("===\n"); 22 | seek(wfd, 0); 23 | 24 | if ((r = ftruncate(wfd, 0)) < 0) 25 | panic("truncate /motd: %e", r); 26 | 27 | cprintf("NEW MOTD\n===\n"); 28 | while ((n = read(rfd, buf, sizeof buf-1)) > 0) { 29 | sys_cputs(buf, n); 30 | if ((r = write(wfd, buf, n)) != n) 31 | panic("write /motd: %e", r); 32 | } 33 | cprintf("===\n"); 34 | 35 | if (n < 0) 36 | panic("read /newmotd: %e", n); 37 | 38 | close(rfd); 39 | close(wfd); 40 | } 41 | -------------------------------------------------------------------------------- /user/yield.c: -------------------------------------------------------------------------------- 1 | // yield the processor to other environments 2 | 3 | #include 4 | 5 | void 6 | umain(int argc, char **argv) 7 | { 8 | int i; 9 | 10 | cprintf("Hello, I am environment %08x.\n", thisenv->env_id); 11 | for (i = 0; i < 5; i++) { 12 | sys_yield(); 13 | cprintf("Back in environment %08x, iteration %d.\n", 14 | thisenv->env_id, i); 15 | } 16 | cprintf("All done in environment %08x.\n", thisenv->env_id); 17 | } 18 | --------------------------------------------------------------------------------