Initial Upload
This commit is contained in:
16
.cvsignore
Normal file
16
.cvsignore
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
*.asm
|
||||||
|
*.d
|
||||||
|
*.sym
|
||||||
|
_*
|
||||||
|
kernel
|
||||||
|
user1
|
||||||
|
userfs
|
||||||
|
usertests
|
||||||
|
xv6.img
|
||||||
|
vectors.S
|
||||||
|
bochsout.txt
|
||||||
|
bootblock
|
||||||
|
bootother
|
||||||
|
bootother.out
|
||||||
|
parport.out
|
||||||
|
fmt
|
4
.dir-locals.el
Normal file
4
.dir-locals.el
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
((c-mode
|
||||||
|
(indent-tabs-mode . nil)
|
||||||
|
(c-file-style . "bsd")
|
||||||
|
(c-basic-offset . 2)))
|
27
.gdbinit.tmpl
Normal file
27
.gdbinit.tmpl
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
set $lastcs = -1
|
||||||
|
|
||||||
|
define hook-stop
|
||||||
|
# There doesn't seem to be a good way to detect if we're in 16- or
|
||||||
|
# 32-bit mode, but in 32-bit mode we always run with CS == 8 in the
|
||||||
|
# kernel and CS == 35 in user space
|
||||||
|
if $cs == 8 || $cs == 35
|
||||||
|
if $lastcs != 8 && $lastcs != 35
|
||||||
|
set architecture i386
|
||||||
|
end
|
||||||
|
x/i $pc
|
||||||
|
else
|
||||||
|
if $lastcs == -1 || $lastcs == 8 || $lastcs == 35
|
||||||
|
set architecture i8086
|
||||||
|
end
|
||||||
|
# Translate the segment:offset into a physical address
|
||||||
|
printf "[%4x:%4x] ", $cs, $eip
|
||||||
|
x/i $cs*16+$eip
|
||||||
|
end
|
||||||
|
set $lastcs = $cs
|
||||||
|
end
|
||||||
|
|
||||||
|
echo + target remote localhost:1234\n
|
||||||
|
target remote localhost:1234
|
||||||
|
|
||||||
|
echo + symbol-file kernel\n
|
||||||
|
symbol-file kernel
|
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.img filter=lfs diff=lfs merge=lfs -text
|
28
.vscode/launch.json
vendored
Normal file
28
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "(gdb) Launch",
|
||||||
|
"type": "cppdbg",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceFolder}/kernel",
|
||||||
|
"args": [],
|
||||||
|
"stopAtEntry": true,
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"environment": [],
|
||||||
|
"externalConsole": false,
|
||||||
|
"MIMode": "gdb",
|
||||||
|
"setupCommands": [
|
||||||
|
{
|
||||||
|
"description": "Enable pretty-printing for gdb",
|
||||||
|
"text": "-enable-pretty-printing",
|
||||||
|
"ignoreFailures": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"miDebuggerPath": "/usr/bin/gdb"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
35
.vscode/tasks.json
vendored
Normal file
35
.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||||
|
// for the documentation about the tasks.json format
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "Build Xv6 operating system",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "make",
|
||||||
|
"problemMatcher": [],
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Run Xv6 under QEMU",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "make qemu",
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Run Xv6 under QEMU for debugging",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "make qemu-gdb",
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Clean non-source files",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "make clean",
|
||||||
|
"problemMatcher": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
24
LICENSE
Normal file
24
LICENSE
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
The xv6 software is:
|
||||||
|
|
||||||
|
Copyright (c) 2006-2018 Frans Kaashoek, Robert Morris, Russ Cox,
|
||||||
|
Massachusetts Institute of Technology
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
250
Makefile
Normal file
250
Makefile
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
OBJS = \
|
||||||
|
bio.o\
|
||||||
|
console.o\
|
||||||
|
exec.o\
|
||||||
|
file.o\
|
||||||
|
fs.o\
|
||||||
|
ide.o\
|
||||||
|
ioapic.o\
|
||||||
|
kalloc.o\
|
||||||
|
kbd.o\
|
||||||
|
lapic.o\
|
||||||
|
log.o\
|
||||||
|
main.o\
|
||||||
|
mp.o\
|
||||||
|
picirq.o\
|
||||||
|
pipe.o\
|
||||||
|
proc.o\
|
||||||
|
sleeplock.o\
|
||||||
|
spinlock.o\
|
||||||
|
string.o\
|
||||||
|
swtch.o\
|
||||||
|
syscall.o\
|
||||||
|
sysfile.o\
|
||||||
|
sysproc.o\
|
||||||
|
trapasm.o\
|
||||||
|
trap.o\
|
||||||
|
uart.o\
|
||||||
|
vectors.o\
|
||||||
|
vm.o\
|
||||||
|
|
||||||
|
# Cross-compiling (e.g., on Mac OS X)
|
||||||
|
# TOOLPREFIX = i386-jos-elf
|
||||||
|
|
||||||
|
# Using native tools (e.g., on X86 Linux)
|
||||||
|
#TOOLPREFIX =
|
||||||
|
|
||||||
|
# Try to infer the correct TOOLPREFIX if not set
|
||||||
|
ifndef TOOLPREFIX
|
||||||
|
TOOLPREFIX := $(shell if i386-jos-elf-objdump -i 2>&1 | grep '^elf32-i386$$' >/dev/null 2>&1; \
|
||||||
|
then echo 'i386-jos-elf-'; \
|
||||||
|
elif objdump -i 2>&1 | grep 'elf32-i386' >/dev/null 2>&1; \
|
||||||
|
then echo ''; \
|
||||||
|
else echo "***" 1>&2; \
|
||||||
|
echo "*** Error: Couldn't find an i386-*-elf version of GCC/binutils." 1>&2; \
|
||||||
|
echo "*** Is the directory with i386-jos-elf-gcc in your PATH?" 1>&2; \
|
||||||
|
echo "*** If your i386-*-elf toolchain is installed with a command" 1>&2; \
|
||||||
|
echo "*** prefix other than 'i386-jos-elf-', set your TOOLPREFIX" 1>&2; \
|
||||||
|
echo "*** environment variable to that prefix and run 'make' again." 1>&2; \
|
||||||
|
echo "*** To turn off this error, run 'gmake TOOLPREFIX= ...'." 1>&2; \
|
||||||
|
echo "***" 1>&2; exit 1; fi)
|
||||||
|
endif
|
||||||
|
|
||||||
|
# If the makefile can't find QEMU, specify its path here
|
||||||
|
# QEMU = qemu-system-i386
|
||||||
|
|
||||||
|
# Try to infer the correct QEMU
|
||||||
|
ifndef QEMU
|
||||||
|
QEMU = $(shell if which qemu > /dev/null; \
|
||||||
|
then echo qemu; exit; \
|
||||||
|
elif which qemu-system-i386 > /dev/null; \
|
||||||
|
then echo qemu-system-i386; exit; \
|
||||||
|
elif which qemu-system-x86_64 > /dev/null; \
|
||||||
|
then echo qemu-system-x86_64; exit; \
|
||||||
|
else \
|
||||||
|
qemu=/Applications/Q.app/Contents/MacOS/i386-softmmu.app/Contents/MacOS/i386-softmmu; \
|
||||||
|
if test -x $$qemu; then echo $$qemu; exit; fi; fi; \
|
||||||
|
echo "***" 1>&2; \
|
||||||
|
echo "*** Error: Couldn't find a working QEMU executable." 1>&2; \
|
||||||
|
echo "*** Is the directory containing the qemu binary in your PATH" 1>&2; \
|
||||||
|
echo "*** or have you tried setting the QEMU variable in Makefile?" 1>&2; \
|
||||||
|
echo "***" 1>&2; exit 1)
|
||||||
|
endif
|
||||||
|
|
||||||
|
CC = $(TOOLPREFIX)gcc
|
||||||
|
AS = $(TOOLPREFIX)gas
|
||||||
|
LD = $(TOOLPREFIX)ld
|
||||||
|
OBJCOPY = $(TOOLPREFIX)objcopy
|
||||||
|
OBJDUMP = $(TOOLPREFIX)objdump
|
||||||
|
CFLAGS = -fno-pic -static -fno-builtin -fno-strict-aliasing -O2 -Wall -MD -ggdb -m32 -Werror -fno-omit-frame-pointer
|
||||||
|
CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector)
|
||||||
|
ASFLAGS = -m32 -gdwarf-2 -Wa,-divide
|
||||||
|
# FreeBSD ld wants ``elf_i386_fbsd''
|
||||||
|
LDFLAGS += -m $(shell $(LD) -V | grep elf_i386 2>/dev/null | head -n 1)
|
||||||
|
|
||||||
|
# Disable PIE when possible (for Ubuntu 16.10 toolchain)
|
||||||
|
ifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]no-pie'),)
|
||||||
|
CFLAGS += -fno-pie -no-pie
|
||||||
|
endif
|
||||||
|
ifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]nopie'),)
|
||||||
|
CFLAGS += -fno-pie -nopie
|
||||||
|
endif
|
||||||
|
|
||||||
|
xv6.img: bootblock kernel
|
||||||
|
dd if=/dev/zero of=xv6.img count=10000
|
||||||
|
dd if=bootblock of=xv6.img conv=notrunc
|
||||||
|
dd if=kernel of=xv6.img seek=1 conv=notrunc
|
||||||
|
|
||||||
|
xv6memfs.img: bootblock kernelmemfs
|
||||||
|
dd if=/dev/zero of=xv6memfs.img count=10000
|
||||||
|
dd if=bootblock of=xv6memfs.img conv=notrunc
|
||||||
|
dd if=kernelmemfs of=xv6memfs.img seek=1 conv=notrunc
|
||||||
|
|
||||||
|
bootblock: bootasm.S bootmain.c
|
||||||
|
$(CC) $(CFLAGS) -fno-pic -O -nostdinc -I. -c bootmain.c
|
||||||
|
$(CC) $(CFLAGS) -fno-pic -nostdinc -I. -c bootasm.S
|
||||||
|
$(LD) $(LDFLAGS) -N -e start -Ttext 0x7C00 -o bootblock.o bootasm.o bootmain.o
|
||||||
|
$(OBJDUMP) -S bootblock.o > bootblock.asm
|
||||||
|
$(OBJCOPY) -S -O binary -j .text bootblock.o bootblock
|
||||||
|
# The following line is here since it has been noticed that if you use Explorer to
|
||||||
|
# copy folders on wsl, sometimees the execute permissions can be removed from perl scripts.
|
||||||
|
# Uncomment if needed, but it will flag as a change for git.
|
||||||
|
# chmod +x sign.pl
|
||||||
|
./sign.pl bootblock
|
||||||
|
|
||||||
|
syscall.h: gensyscalls.pl
|
||||||
|
./gensyscalls.pl -h > syscall.h
|
||||||
|
|
||||||
|
syscalltable.h: gensyscalls.pl
|
||||||
|
./gensyscalls.pl -c > syscalltable.h
|
||||||
|
|
||||||
|
usys.S: gensyscalls.pl
|
||||||
|
./gensyscalls.pl -a > usys.S
|
||||||
|
|
||||||
|
entryother: entryother.S
|
||||||
|
$(CC) $(CFLAGS) -fno-pic -nostdinc -I. -c entryother.S
|
||||||
|
$(LD) $(LDFLAGS) -N -e start -Ttext 0x7000 -o bootblockother.o entryother.o
|
||||||
|
$(OBJCOPY) -S -O binary -j .text bootblockother.o entryother
|
||||||
|
$(OBJDUMP) -S bootblockother.o > entryother.asm
|
||||||
|
|
||||||
|
initcode: initcode.S
|
||||||
|
$(CC) $(CFLAGS) -nostdinc -I. -c initcode.S
|
||||||
|
$(LD) $(LDFLAGS) -N -e start -Ttext 0 -o initcode.out initcode.o
|
||||||
|
$(OBJCOPY) -S -O binary initcode.out initcode
|
||||||
|
$(OBJDUMP) -S initcode.o > initcode.asm
|
||||||
|
|
||||||
|
kernel: syscall.h syscalltable.h $(OBJS) entry.o entryother initcode kernel.ld
|
||||||
|
$(LD) $(LDFLAGS) -T kernel.ld -o kernel entry.o $(OBJS) -b binary initcode entryother
|
||||||
|
$(OBJDUMP) -S kernel > kernel.asm
|
||||||
|
$(OBJDUMP) -t kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernel.sym
|
||||||
|
|
||||||
|
# kernelmemfs is a copy of kernel that maintains the
|
||||||
|
# disk image in memory instead of writing to a disk.
|
||||||
|
# This is not so useful for testing persistent storage or
|
||||||
|
# exploring disk buffering implementations, but it is
|
||||||
|
# great for testing the kernel on real hardware without
|
||||||
|
# needing a scratch disk.
|
||||||
|
MEMFSOBJS = $(filter-out ide.o,$(OBJS)) memide.o
|
||||||
|
kernelmemfs: $(MEMFSOBJS) entry.o entryother initcode kernel.ld fs.img
|
||||||
|
$(LD) $(LDFLAGS) -T kernel.ld -o kernelmemfs entry.o $(MEMFSOBJS) -b binary initcode entryother fs.img
|
||||||
|
$(OBJDUMP) -S kernelmemfs > kernelmemfs.asm
|
||||||
|
$(OBJDUMP) -t kernelmemfs | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernelmemfs.sym
|
||||||
|
|
||||||
|
tags: $(OBJS) entryother.S _init
|
||||||
|
etags *.S *.c
|
||||||
|
|
||||||
|
vectors.S: vectors.pl
|
||||||
|
# chmod +x vectors.pl
|
||||||
|
./vectors.pl > vectors.S
|
||||||
|
|
||||||
|
ULIB = ulib.o usys.o printf.o umalloc.o
|
||||||
|
|
||||||
|
_%: %.o $(ULIB)
|
||||||
|
$(LD) $(LDFLAGS) -N -e main -Ttext 0 -o $@ $^
|
||||||
|
$(OBJDUMP) -S $@ > $*.asm
|
||||||
|
$(OBJDUMP) -t $@ | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $*.sym
|
||||||
|
|
||||||
|
_forktest: forktest.o $(ULIB)
|
||||||
|
# forktest has less library code linked in - needs to be small
|
||||||
|
# in order to be able to max out the proc table.
|
||||||
|
$(LD) $(LDFLAGS) -N -e main -Ttext 0 -o _forktest forktest.o ulib.o usys.o
|
||||||
|
$(OBJDUMP) -S _forktest > forktest.asm
|
||||||
|
|
||||||
|
mkfs: mkfs.c fs.h
|
||||||
|
gcc -Werror -Wall -o mkfs mkfs.c
|
||||||
|
|
||||||
|
# Prevent deletion of intermediate files, e.g. cat.o, after first build, so
|
||||||
|
# that disk image changes after first build are persistent until clean. More
|
||||||
|
# details:
|
||||||
|
# http://www.gnu.org/software/make/manual/html_node/Chained-Rules.html
|
||||||
|
.PRECIOUS: %.o
|
||||||
|
|
||||||
|
UPROGS=\
|
||||||
|
_cat\
|
||||||
|
_echo\
|
||||||
|
_forktest\
|
||||||
|
_grep\
|
||||||
|
_init\
|
||||||
|
_kill\
|
||||||
|
_ln\
|
||||||
|
_ls\
|
||||||
|
_mkdir\
|
||||||
|
_rm\
|
||||||
|
_sh\
|
||||||
|
_stressfs\
|
||||||
|
_usertests\
|
||||||
|
_wc\
|
||||||
|
_zombie\
|
||||||
|
_hello\
|
||||||
|
_shutdown\
|
||||||
|
_argtest\
|
||||||
|
_screen\
|
||||||
|
|
||||||
|
fs.img: mkfs $(UPROGS)
|
||||||
|
./mkfs fs.img $(UPROGS)
|
||||||
|
|
||||||
|
-include *.d
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg \
|
||||||
|
*.o *.d *.asm *.sym vectors.S bootblock entryother \
|
||||||
|
initcode initcode.out kernel xv6.img fs.img kernelmemfs \
|
||||||
|
xv6memfs.img mkfs .gdbinit \
|
||||||
|
syscall.h syscalltable.h usys.S \
|
||||||
|
$(UPROGS)
|
||||||
|
|
||||||
|
# run in emulators
|
||||||
|
|
||||||
|
# try to generate a unique GDB port
|
||||||
|
GDBPORT = $(shell expr `id -u` % 5000 + 25000)
|
||||||
|
# QEMU's gdb stub command line changed in 0.11
|
||||||
|
QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \
|
||||||
|
then echo "-gdb tcp::$(GDBPORT)"; \
|
||||||
|
else echo "-s -p $(GDBPORT)"; fi)
|
||||||
|
ifndef CPUS
|
||||||
|
CPUS := 2
|
||||||
|
endif
|
||||||
|
QEMUOPTS = -drive file=fs.img,index=1,media=disk,format=raw -drive file=xv6.img,index=0,media=disk,format=raw -smp $(CPUS) -m 512 $(QEMUEXTRA)
|
||||||
|
|
||||||
|
qemu: fs.img xv6.img
|
||||||
|
$(QEMU) -vga std -serial mon:stdio $(QEMUOPTS)
|
||||||
|
|
||||||
|
qemu-memfs: xv6memfs.img
|
||||||
|
$(QEMU) -vga std -drive file=xv6memfs.img,index=0,media=disk,format=raw -smp $(CPUS) -m 256
|
||||||
|
|
||||||
|
qemu-nox: fs.img xv6.img
|
||||||
|
$(QEMU) -nographic $(QEMUOPTS)
|
||||||
|
|
||||||
|
qemu-curses: fs.img xv6.img
|
||||||
|
$(QEMU) -curses $(QEMUOPTS)
|
||||||
|
|
||||||
|
.gdbinit: .gdbinit.tmpl
|
||||||
|
sed "s/localhost:1234/localhost:$(GDBPORT)/" < $^ > $@
|
||||||
|
|
||||||
|
qemu-gdb: fs.img xv6.img .gdbinit
|
||||||
|
@echo "*** Now run 'gdb'." 1>&2
|
||||||
|
$(QEMU) -vga std -serial mon:stdio $(QEMUOPTS) -S $(QEMUGDB)
|
||||||
|
|
||||||
|
qemu-nox-gdb: fs.img xv6.img .gdbinit
|
||||||
|
@echo "*** Now run 'gdb'." 1>&2
|
||||||
|
$(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB)
|
BIN
_usertests
Normal file
BIN
_usertests
Normal file
Binary file not shown.
1162
argtest.asm
Normal file
1162
argtest.asm
Normal file
File diff suppressed because it is too large
Load Diff
10
argtest.c
Normal file
10
argtest.c
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#include "types.h"
|
||||||
|
#include "user.h"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
printf(1, "Arg %d: %s\n", i, argv[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit();
|
||||||
|
}
|
1
argtest.d
Normal file
1
argtest.d
Normal file
@ -0,0 +1 @@
|
|||||||
|
argtest.o: argtest.c /usr/include/stdc-predef.h types.h user.h
|
48
argtest.sym
Normal file
48
argtest.sym
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
00000000 argtest.c
|
||||||
|
00000000 ulib.c
|
||||||
|
00000000 printf.c
|
||||||
|
00000360 printint
|
||||||
|
000007a4 digits.0
|
||||||
|
00000000 umalloc.c
|
||||||
|
00000a58 freep
|
||||||
|
00000a5c base
|
||||||
|
00000050 strcpy
|
||||||
|
00000410 printf
|
||||||
|
0000034b greeting
|
||||||
|
00000270 memmove
|
||||||
|
0000031b mknod
|
||||||
|
00000170 gets
|
||||||
|
000002eb getpid
|
||||||
|
00000640 malloc
|
||||||
|
000002fb sleep
|
||||||
|
000002b3 pipe
|
||||||
|
00000343 getch
|
||||||
|
00000313 write
|
||||||
|
000002d3 fstat
|
||||||
|
000002c3 kill
|
||||||
|
000002db chdir
|
||||||
|
000002cb exec
|
||||||
|
000002ab wait
|
||||||
|
000002bb read
|
||||||
|
00000323 unlink
|
||||||
|
0000029b fork
|
||||||
|
000002f3 sbrk
|
||||||
|
00000303 uptime
|
||||||
|
00000a58 __bss_start
|
||||||
|
00000110 memset
|
||||||
|
00000000 main
|
||||||
|
00000080 strcmp
|
||||||
|
00000353 shutdown
|
||||||
|
000002e3 dup
|
||||||
|
000001e0 stat
|
||||||
|
00000a58 _edata
|
||||||
|
00000a64 _end
|
||||||
|
0000032b link
|
||||||
|
000002a3 exit
|
||||||
|
00000230 atoi
|
||||||
|
000000e0 strlen
|
||||||
|
0000030b open
|
||||||
|
00000130 strchr
|
||||||
|
00000333 mkdir
|
||||||
|
0000033b close
|
||||||
|
000005b0 free
|
18
asm.h
Normal file
18
asm.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
//
|
||||||
|
// assembler macros to create x86 segments
|
||||||
|
//
|
||||||
|
|
||||||
|
#define SEG_NULLASM \
|
||||||
|
.word 0, 0; \
|
||||||
|
.byte 0, 0, 0, 0
|
||||||
|
|
||||||
|
// The 0xC0 means the limit is in 4096-byte units
|
||||||
|
// and (for executable segments) 32-bit mode.
|
||||||
|
#define SEG_ASM(type, base, lim) \
|
||||||
|
.word(((lim) >> 12) & 0xffff), ((base) & 0xffff); \
|
||||||
|
.byte(((base) >> 16) & 0xff), (0x90 | (type)), \
|
||||||
|
(0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff)
|
||||||
|
|
||||||
|
#define STA_X 0x8 // Executable segment
|
||||||
|
#define STA_W 0x2 // Writeable (non-executable segments)
|
||||||
|
#define STA_R 0x2 // Readable (executable segments)
|
140
bio.c
Normal file
140
bio.c
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
// Buffer cache.
|
||||||
|
//
|
||||||
|
// The buffer cache is a linked list of buf structures holding
|
||||||
|
// cached copies of disk block contents. Caching disk blocks
|
||||||
|
// in memory reduces the number of disk reads and also provides
|
||||||
|
// a synchronization point for disk blocks used by multiple processes.
|
||||||
|
//
|
||||||
|
// Interface:
|
||||||
|
// * To get a buffer for a particular disk block, call bread.
|
||||||
|
// * After changing buffer data, call bwrite to write it to disk.
|
||||||
|
// * When done with the buffer, call brelse.
|
||||||
|
// * Do not use the buffer after calling brelse.
|
||||||
|
// * Only one process at a time can use a buffer,
|
||||||
|
// so do not keep them longer than necessary.
|
||||||
|
//
|
||||||
|
// The implementation uses two state flags internally:
|
||||||
|
// * B_VALID: the buffer data has been read from the disk.
|
||||||
|
// * B_DIRTY: the buffer data has been modified
|
||||||
|
// and needs to be written to disk.
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "defs.h"
|
||||||
|
#include "param.h"
|
||||||
|
#include "spinlock.h"
|
||||||
|
#include "sleeplock.h"
|
||||||
|
#include "fs.h"
|
||||||
|
#include "buf.h"
|
||||||
|
|
||||||
|
struct {
|
||||||
|
struct spinlock lock;
|
||||||
|
struct buf buf[NBUF];
|
||||||
|
|
||||||
|
// Linked list of all buffers, through prev/next.
|
||||||
|
// head.next is most recently used.
|
||||||
|
struct buf head;
|
||||||
|
} bcache;
|
||||||
|
|
||||||
|
void binit(void) {
|
||||||
|
struct buf *b;
|
||||||
|
|
||||||
|
initlock(&bcache.lock, "bcache");
|
||||||
|
|
||||||
|
// Create linked list of buffers
|
||||||
|
bcache.head.prev = &bcache.head;
|
||||||
|
bcache.head.next = &bcache.head;
|
||||||
|
for (b = bcache.buf; b < bcache.buf + NBUF; b++) {
|
||||||
|
b->next = bcache.head.next;
|
||||||
|
b->prev = &bcache.head;
|
||||||
|
initsleeplock(&b->lock, "buffer");
|
||||||
|
bcache.head.next->prev = b;
|
||||||
|
bcache.head.next = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look through buffer cache for block on device dev.
|
||||||
|
// If not found, allocate a buffer.
|
||||||
|
// In either case, return locked buffer.
|
||||||
|
|
||||||
|
static struct buf* bget(uint dev, uint blockno) {
|
||||||
|
struct buf *b;
|
||||||
|
|
||||||
|
acquire(&bcache.lock);
|
||||||
|
|
||||||
|
// Is the block already cached?
|
||||||
|
for (b = bcache.head.next; b != &bcache.head; b = b->next) {
|
||||||
|
if (b->dev == dev && b->blockno == blockno) {
|
||||||
|
b->refcnt++;
|
||||||
|
release(&bcache.lock);
|
||||||
|
acquiresleep(&b->lock);
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not cached; recycle an unused buffer.
|
||||||
|
// Even if refcnt==0, B_DIRTY indicates a buffer is in use
|
||||||
|
// because log.c has modified it but not yet committed it.
|
||||||
|
|
||||||
|
for (b = bcache.head.prev; b != &bcache.head; b = b->prev) {
|
||||||
|
if (b->refcnt == 0 && (b->flags & B_DIRTY) == 0) {
|
||||||
|
b->dev = dev;
|
||||||
|
b->blockno = blockno;
|
||||||
|
b->flags = 0;
|
||||||
|
b->refcnt = 1;
|
||||||
|
release(&bcache.lock);
|
||||||
|
acquiresleep(&b->lock);
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic("bget: no buffers");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a locked buf with the contents of the indicated block.
|
||||||
|
|
||||||
|
struct buf*bread(uint dev, uint blockno) {
|
||||||
|
struct buf *b;
|
||||||
|
|
||||||
|
b = bget(dev, blockno);
|
||||||
|
if ((b->flags & B_VALID) == 0) {
|
||||||
|
iderw(b);
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write b's contents to disk. Must be locked.
|
||||||
|
|
||||||
|
void bwrite(struct buf *b) {
|
||||||
|
if (!holdingsleep(&b->lock)) {
|
||||||
|
panic("bwrite");
|
||||||
|
}
|
||||||
|
b->flags |= B_DIRTY;
|
||||||
|
iderw(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release a locked buffer.
|
||||||
|
// Move to the head of the MRU list.
|
||||||
|
|
||||||
|
void brelse(struct buf *b) {
|
||||||
|
if (!holdingsleep(&b->lock)) {
|
||||||
|
panic("brelse");
|
||||||
|
}
|
||||||
|
|
||||||
|
releasesleep(&b->lock);
|
||||||
|
|
||||||
|
acquire(&bcache.lock);
|
||||||
|
b->refcnt--;
|
||||||
|
if (b->refcnt == 0) {
|
||||||
|
// no one is waiting for it.
|
||||||
|
b->next->prev = b->prev;
|
||||||
|
b->prev->next = b->next;
|
||||||
|
b->next = bcache.head.next;
|
||||||
|
b->prev = &bcache.head;
|
||||||
|
bcache.head.next->prev = b;
|
||||||
|
bcache.head.next = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
release(&bcache.lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
2
bio.d
Normal file
2
bio.d
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
bio.o: bio.c /usr/include/stdc-predef.h types.h defs.h param.h spinlock.h \
|
||||||
|
sleeplock.h fs.h buf.h
|
81
bootasm.S
Normal file
81
bootasm.S
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
#include "asm.h"
|
||||||
|
#include "memlayout.h"
|
||||||
|
#include "mmu.h"
|
||||||
|
|
||||||
|
# Start the first CPU: switch to 32-bit protected mode, jump into C.
|
||||||
|
# The BIOS loads this code from the first sector of the hard disk into
|
||||||
|
# memory at physical address 0x7c00 and starts executing in real mode
|
||||||
|
# with %cs=0 %ip=7c00.
|
||||||
|
|
||||||
|
.code16 # Assemble for 16-bit mode
|
||||||
|
.globl start
|
||||||
|
start:
|
||||||
|
cli # BIOS enabled interrupts; disable
|
||||||
|
|
||||||
|
# Zero data segment registers DS, ES, and SS.
|
||||||
|
xorw %ax,%ax # Set %ax to zero
|
||||||
|
movw %ax,%ds # -> Data Segment
|
||||||
|
movw %ax,%es # -> Extra Segment
|
||||||
|
movw %ax,%ss # -> Stack Segment
|
||||||
|
|
||||||
|
# Physical address line A20 is tied to zero so that the first PCs
|
||||||
|
# with 2 MB would run software that assumed 1 MB. Undo that.
|
||||||
|
seta20.1:
|
||||||
|
inb $0x64,%al # Wait for not busy
|
||||||
|
testb $0x2,%al
|
||||||
|
jnz seta20.1
|
||||||
|
|
||||||
|
movb $0xd1,%al # 0xd1 -> port 0x64
|
||||||
|
outb %al,$0x64
|
||||||
|
|
||||||
|
seta20.2:
|
||||||
|
inb $0x64,%al # Wait for not busy
|
||||||
|
testb $0x2,%al
|
||||||
|
jnz seta20.2
|
||||||
|
|
||||||
|
movb $0xdf,%al # 0xdf -> port 0x60
|
||||||
|
outb %al,$0x60
|
||||||
|
|
||||||
|
# Switch from real to protected mode. Use a bootstrap GDT that makes
|
||||||
|
# virtual addresses map directly to physical addresses so that the
|
||||||
|
# effective memory map doesn't change during the transition.
|
||||||
|
lgdt gdtdesc
|
||||||
|
movl %cr0, %eax
|
||||||
|
orl $CR0_PE, %eax
|
||||||
|
movl %eax, %cr0
|
||||||
|
|
||||||
|
# Complete the transition to 32-bit protected mode by using a long jmp
|
||||||
|
# to reload %cs and %eip. The segment descriptors are set up with no
|
||||||
|
# translation, so that the mapping is still the identity mapping.
|
||||||
|
ljmp $(SEG_KCODE<<3), $start32
|
||||||
|
|
||||||
|
.code32 # Tell assembler to generate 32-bit code now.
|
||||||
|
start32:
|
||||||
|
# Set up the protected-mode data segment registers
|
||||||
|
movw $(SEG_KDATA<<3), %ax # Our data segment selector
|
||||||
|
movw %ax, %ds # -> DS: Data Segment
|
||||||
|
movw %ax, %es # -> ES: Extra Segment
|
||||||
|
movw %ax, %ss # -> SS: Stack Segment
|
||||||
|
movw $0, %ax # Zero segments not ready for use
|
||||||
|
movw %ax, %fs # -> FS
|
||||||
|
movw %ax, %gs # -> GS
|
||||||
|
|
||||||
|
# Set up the stack pointer and call into C.
|
||||||
|
movl $start, %esp
|
||||||
|
call bootmain
|
||||||
|
|
||||||
|
# If bootmain returns (it shouldn't), loop.
|
||||||
|
spin:
|
||||||
|
jmp spin
|
||||||
|
|
||||||
|
# Bootstrap GDT
|
||||||
|
.p2align 2 # force 4 byte alignment
|
||||||
|
gdt:
|
||||||
|
SEG_NULLASM # null seg
|
||||||
|
SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg
|
||||||
|
SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg
|
||||||
|
|
||||||
|
gdtdesc:
|
||||||
|
.word (gdtdesc - gdt - 1) # sizeof(gdt) - 1
|
||||||
|
.long gdt # address gdt
|
||||||
|
|
341
bootblock.asm
Normal file
341
bootblock.asm
Normal file
@ -0,0 +1,341 @@
|
|||||||
|
|
||||||
|
bootblock.o: file format elf32-i386
|
||||||
|
|
||||||
|
|
||||||
|
Disassembly of section .text:
|
||||||
|
|
||||||
|
00007c00 <start>:
|
||||||
|
# with %cs=0 %ip=7c00.
|
||||||
|
|
||||||
|
.code16 # Assemble for 16-bit mode
|
||||||
|
.globl start
|
||||||
|
start:
|
||||||
|
cli # BIOS enabled interrupts; disable
|
||||||
|
7c00: fa cli
|
||||||
|
|
||||||
|
# Zero data segment registers DS, ES, and SS.
|
||||||
|
xorw %ax,%ax # Set %ax to zero
|
||||||
|
7c01: 31 c0 xor %eax,%eax
|
||||||
|
movw %ax,%ds # -> Data Segment
|
||||||
|
7c03: 8e d8 mov %eax,%ds
|
||||||
|
movw %ax,%es # -> Extra Segment
|
||||||
|
7c05: 8e c0 mov %eax,%es
|
||||||
|
movw %ax,%ss # -> Stack Segment
|
||||||
|
7c07: 8e d0 mov %eax,%ss
|
||||||
|
|
||||||
|
00007c09 <seta20.1>:
|
||||||
|
|
||||||
|
# Physical address line A20 is tied to zero so that the first PCs
|
||||||
|
# with 2 MB would run software that assumed 1 MB. Undo that.
|
||||||
|
seta20.1:
|
||||||
|
inb $0x64,%al # Wait for not busy
|
||||||
|
7c09: e4 64 in $0x64,%al
|
||||||
|
testb $0x2,%al
|
||||||
|
7c0b: a8 02 test $0x2,%al
|
||||||
|
jnz seta20.1
|
||||||
|
7c0d: 75 fa jne 7c09 <seta20.1>
|
||||||
|
|
||||||
|
movb $0xd1,%al # 0xd1 -> port 0x64
|
||||||
|
7c0f: b0 d1 mov $0xd1,%al
|
||||||
|
outb %al,$0x64
|
||||||
|
7c11: e6 64 out %al,$0x64
|
||||||
|
|
||||||
|
00007c13 <seta20.2>:
|
||||||
|
|
||||||
|
seta20.2:
|
||||||
|
inb $0x64,%al # Wait for not busy
|
||||||
|
7c13: e4 64 in $0x64,%al
|
||||||
|
testb $0x2,%al
|
||||||
|
7c15: a8 02 test $0x2,%al
|
||||||
|
jnz seta20.2
|
||||||
|
7c17: 75 fa jne 7c13 <seta20.2>
|
||||||
|
|
||||||
|
movb $0xdf,%al # 0xdf -> port 0x60
|
||||||
|
7c19: b0 df mov $0xdf,%al
|
||||||
|
outb %al,$0x60
|
||||||
|
7c1b: e6 60 out %al,$0x60
|
||||||
|
|
||||||
|
# Switch from real to protected mode. Use a bootstrap GDT that makes
|
||||||
|
# virtual addresses map directly to physical addresses so that the
|
||||||
|
# effective memory map doesn't change during the transition.
|
||||||
|
lgdt gdtdesc
|
||||||
|
7c1d: 0f 01 16 lgdtl (%esi)
|
||||||
|
7c20: 68 7c 0f 20 c0 push $0xc0200f7c
|
||||||
|
movl %cr0, %eax
|
||||||
|
orl $CR0_PE, %eax
|
||||||
|
7c25: 66 83 c8 01 or $0x1,%ax
|
||||||
|
movl %eax, %cr0
|
||||||
|
7c29: 0f 22 c0 mov %eax,%cr0
|
||||||
|
|
||||||
|
# Complete the transition to 32-bit protected mode by using a long jmp
|
||||||
|
# to reload %cs and %eip. The segment descriptors are set up with no
|
||||||
|
# translation, so that the mapping is still the identity mapping.
|
||||||
|
ljmp $(SEG_KCODE<<3), $start32
|
||||||
|
7c2c: ea .byte 0xea
|
||||||
|
7c2d: 31 7c 08 00 xor %edi,0x0(%eax,%ecx,1)
|
||||||
|
|
||||||
|
00007c31 <start32>:
|
||||||
|
|
||||||
|
.code32 # Tell assembler to generate 32-bit code now.
|
||||||
|
start32:
|
||||||
|
# Set up the protected-mode data segment registers
|
||||||
|
movw $(SEG_KDATA<<3), %ax # Our data segment selector
|
||||||
|
7c31: 66 b8 10 00 mov $0x10,%ax
|
||||||
|
movw %ax, %ds # -> DS: Data Segment
|
||||||
|
7c35: 8e d8 mov %eax,%ds
|
||||||
|
movw %ax, %es # -> ES: Extra Segment
|
||||||
|
7c37: 8e c0 mov %eax,%es
|
||||||
|
movw %ax, %ss # -> SS: Stack Segment
|
||||||
|
7c39: 8e d0 mov %eax,%ss
|
||||||
|
movw $0, %ax # Zero segments not ready for use
|
||||||
|
7c3b: 66 b8 00 00 mov $0x0,%ax
|
||||||
|
movw %ax, %fs # -> FS
|
||||||
|
7c3f: 8e e0 mov %eax,%fs
|
||||||
|
movw %ax, %gs # -> GS
|
||||||
|
7c41: 8e e8 mov %eax,%gs
|
||||||
|
|
||||||
|
# Set up the stack pointer and call into C.
|
||||||
|
movl $start, %esp
|
||||||
|
7c43: bc 00 7c 00 00 mov $0x7c00,%esp
|
||||||
|
call bootmain
|
||||||
|
7c48: e8 e0 00 00 00 call 7d2d <bootmain>
|
||||||
|
|
||||||
|
00007c4d <spin>:
|
||||||
|
|
||||||
|
# If bootmain returns (it shouldn't), loop.
|
||||||
|
spin:
|
||||||
|
jmp spin
|
||||||
|
7c4d: eb fe jmp 7c4d <spin>
|
||||||
|
7c4f: 90 nop
|
||||||
|
|
||||||
|
00007c50 <gdt>:
|
||||||
|
...
|
||||||
|
7c58: ff (bad)
|
||||||
|
7c59: ff 00 incl (%eax)
|
||||||
|
7c5b: 00 00 add %al,(%eax)
|
||||||
|
7c5d: 9a cf 00 ff ff 00 00 lcall $0x0,$0xffff00cf
|
||||||
|
7c64: 00 .byte 0x0
|
||||||
|
7c65: 92 xchg %eax,%edx
|
||||||
|
7c66: cf iret
|
||||||
|
...
|
||||||
|
|
||||||
|
00007c68 <gdtdesc>:
|
||||||
|
7c68: 17 pop %ss
|
||||||
|
7c69: 00 50 7c add %dl,0x7c(%eax)
|
||||||
|
...
|
||||||
|
|
||||||
|
00007c6e <waitdisk>:
|
||||||
|
// Routines to let C code use special x86 instructions.
|
||||||
|
|
||||||
|
static inline uchar inb(ushort port) {
|
||||||
|
uchar data;
|
||||||
|
|
||||||
|
asm volatile ("in %1,%0" : "=a" (data) : "d" (port));
|
||||||
|
7c6e: ba f7 01 00 00 mov $0x1f7,%edx
|
||||||
|
7c73: ec in (%dx),%al
|
||||||
|
entry();
|
||||||
|
}
|
||||||
|
|
||||||
|
void waitdisk(void) {
|
||||||
|
// Wait for disk ready.
|
||||||
|
while ((inb(0x1F7) & 0xC0) != 0x40) {
|
||||||
|
7c74: 83 e0 c0 and $0xffffffc0,%eax
|
||||||
|
7c77: 3c 40 cmp $0x40,%al
|
||||||
|
7c79: 75 f8 jne 7c73 <waitdisk+0x5>
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
7c7b: c3 ret
|
||||||
|
|
||||||
|
00007c7c <readsect>:
|
||||||
|
|
||||||
|
// Read a single sector at offset into dst.
|
||||||
|
void readsect(void *dst, uint offset) {
|
||||||
|
7c7c: 55 push %ebp
|
||||||
|
7c7d: 89 e5 mov %esp,%ebp
|
||||||
|
7c7f: 57 push %edi
|
||||||
|
7c80: 53 push %ebx
|
||||||
|
7c81: 8b 5d 0c mov 0xc(%ebp),%ebx
|
||||||
|
// Issue command.
|
||||||
|
waitdisk();
|
||||||
|
7c84: e8 e5 ff ff ff call 7c6e <waitdisk>
|
||||||
|
"d" (port), "0" (addr), "1" (cnt) :
|
||||||
|
"memory", "cc");
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void outb(ushort port, uchar data) {
|
||||||
|
asm volatile ("out %0,%1" : : "a" (data), "d" (port));
|
||||||
|
7c89: b8 01 00 00 00 mov $0x1,%eax
|
||||||
|
7c8e: ba f2 01 00 00 mov $0x1f2,%edx
|
||||||
|
7c93: ee out %al,(%dx)
|
||||||
|
7c94: ba f3 01 00 00 mov $0x1f3,%edx
|
||||||
|
7c99: 89 d8 mov %ebx,%eax
|
||||||
|
7c9b: ee out %al,(%dx)
|
||||||
|
outb(0x1F2, 1); // count = 1
|
||||||
|
outb(0x1F3, offset);
|
||||||
|
outb(0x1F4, offset >> 8);
|
||||||
|
7c9c: 89 d8 mov %ebx,%eax
|
||||||
|
7c9e: c1 e8 08 shr $0x8,%eax
|
||||||
|
7ca1: ba f4 01 00 00 mov $0x1f4,%edx
|
||||||
|
7ca6: ee out %al,(%dx)
|
||||||
|
outb(0x1F5, offset >> 16);
|
||||||
|
7ca7: 89 d8 mov %ebx,%eax
|
||||||
|
7ca9: c1 e8 10 shr $0x10,%eax
|
||||||
|
7cac: ba f5 01 00 00 mov $0x1f5,%edx
|
||||||
|
7cb1: ee out %al,(%dx)
|
||||||
|
outb(0x1F6, (offset >> 24) | 0xE0);
|
||||||
|
7cb2: 89 d8 mov %ebx,%eax
|
||||||
|
7cb4: c1 e8 18 shr $0x18,%eax
|
||||||
|
7cb7: 83 c8 e0 or $0xffffffe0,%eax
|
||||||
|
7cba: ba f6 01 00 00 mov $0x1f6,%edx
|
||||||
|
7cbf: ee out %al,(%dx)
|
||||||
|
7cc0: b8 20 00 00 00 mov $0x20,%eax
|
||||||
|
7cc5: ba f7 01 00 00 mov $0x1f7,%edx
|
||||||
|
7cca: ee out %al,(%dx)
|
||||||
|
outb(0x1F7, 0x20); // cmd 0x20 - read sectors
|
||||||
|
|
||||||
|
// Read data.
|
||||||
|
waitdisk();
|
||||||
|
7ccb: e8 9e ff ff ff call 7c6e <waitdisk>
|
||||||
|
asm volatile ("cld; rep insl" :
|
||||||
|
7cd0: 8b 7d 08 mov 0x8(%ebp),%edi
|
||||||
|
7cd3: b9 80 00 00 00 mov $0x80,%ecx
|
||||||
|
7cd8: ba f0 01 00 00 mov $0x1f0,%edx
|
||||||
|
7cdd: fc cld
|
||||||
|
7cde: f3 6d rep insl (%dx),%es:(%edi)
|
||||||
|
insl(0x1F0, dst, SECTSIZE / 4);
|
||||||
|
}
|
||||||
|
7ce0: 5b pop %ebx
|
||||||
|
7ce1: 5f pop %edi
|
||||||
|
7ce2: 5d pop %ebp
|
||||||
|
7ce3: c3 ret
|
||||||
|
|
||||||
|
00007ce4 <readseg>:
|
||||||
|
|
||||||
|
// Read 'count' bytes at 'offset' from kernel into physical address 'pa'.
|
||||||
|
// Might copy more than asked.
|
||||||
|
|
||||||
|
void readseg(uchar* pa, uint count, uint offset) {
|
||||||
|
7ce4: 55 push %ebp
|
||||||
|
7ce5: 89 e5 mov %esp,%ebp
|
||||||
|
7ce7: 57 push %edi
|
||||||
|
7ce8: 56 push %esi
|
||||||
|
7ce9: 53 push %ebx
|
||||||
|
7cea: 83 ec 0c sub $0xc,%esp
|
||||||
|
7ced: 8b 5d 08 mov 0x8(%ebp),%ebx
|
||||||
|
7cf0: 8b 75 10 mov 0x10(%ebp),%esi
|
||||||
|
uchar* epa;
|
||||||
|
|
||||||
|
epa = pa + count;
|
||||||
|
7cf3: 89 df mov %ebx,%edi
|
||||||
|
7cf5: 03 7d 0c add 0xc(%ebp),%edi
|
||||||
|
|
||||||
|
// Round down to sector boundary.
|
||||||
|
pa -= offset % SECTSIZE;
|
||||||
|
7cf8: 89 f0 mov %esi,%eax
|
||||||
|
7cfa: 25 ff 01 00 00 and $0x1ff,%eax
|
||||||
|
7cff: 29 c3 sub %eax,%ebx
|
||||||
|
|
||||||
|
// Translate from bytes to sectors; kernel starts at sector 1.
|
||||||
|
offset = (offset / SECTSIZE) + 1;
|
||||||
|
7d01: c1 ee 09 shr $0x9,%esi
|
||||||
|
7d04: 83 c6 01 add $0x1,%esi
|
||||||
|
|
||||||
|
// If this is too slow, we could read lots of sectors at a time.
|
||||||
|
// We'd write more to memory than asked, but it doesn't matter --
|
||||||
|
// we load in increasing order.
|
||||||
|
for (; pa < epa; pa += SECTSIZE, offset++) {
|
||||||
|
7d07: 39 df cmp %ebx,%edi
|
||||||
|
7d09: 76 1a jbe 7d25 <readseg+0x41>
|
||||||
|
readsect(pa, offset);
|
||||||
|
7d0b: 83 ec 08 sub $0x8,%esp
|
||||||
|
7d0e: 56 push %esi
|
||||||
|
7d0f: 53 push %ebx
|
||||||
|
7d10: e8 67 ff ff ff call 7c7c <readsect>
|
||||||
|
for (; pa < epa; pa += SECTSIZE, offset++) {
|
||||||
|
7d15: 81 c3 00 02 00 00 add $0x200,%ebx
|
||||||
|
7d1b: 83 c6 01 add $0x1,%esi
|
||||||
|
7d1e: 83 c4 10 add $0x10,%esp
|
||||||
|
7d21: 39 df cmp %ebx,%edi
|
||||||
|
7d23: 77 e6 ja 7d0b <readseg+0x27>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
7d25: 8d 65 f4 lea -0xc(%ebp),%esp
|
||||||
|
7d28: 5b pop %ebx
|
||||||
|
7d29: 5e pop %esi
|
||||||
|
7d2a: 5f pop %edi
|
||||||
|
7d2b: 5d pop %ebp
|
||||||
|
7d2c: c3 ret
|
||||||
|
|
||||||
|
00007d2d <bootmain>:
|
||||||
|
void bootmain(void) {
|
||||||
|
7d2d: 55 push %ebp
|
||||||
|
7d2e: 89 e5 mov %esp,%ebp
|
||||||
|
7d30: 57 push %edi
|
||||||
|
7d31: 56 push %esi
|
||||||
|
7d32: 53 push %ebx
|
||||||
|
7d33: 83 ec 10 sub $0x10,%esp
|
||||||
|
readseg((uchar*)elf, 4096, 0);
|
||||||
|
7d36: 6a 00 push $0x0
|
||||||
|
7d38: 68 00 10 00 00 push $0x1000
|
||||||
|
7d3d: 68 00 00 01 00 push $0x10000
|
||||||
|
7d42: e8 9d ff ff ff call 7ce4 <readseg>
|
||||||
|
if (elf->magic != ELF_MAGIC) {
|
||||||
|
7d47: 83 c4 10 add $0x10,%esp
|
||||||
|
7d4a: 81 3d 00 00 01 00 7f cmpl $0x464c457f,0x10000
|
||||||
|
7d51: 45 4c 46
|
||||||
|
7d54: 75 21 jne 7d77 <bootmain+0x4a>
|
||||||
|
ph = (struct proghdr*)((uchar*)elf + elf->phoff);
|
||||||
|
7d56: a1 1c 00 01 00 mov 0x1001c,%eax
|
||||||
|
7d5b: 8d 98 00 00 01 00 lea 0x10000(%eax),%ebx
|
||||||
|
eph = ph + elf->phnum;
|
||||||
|
7d61: 0f b7 35 2c 00 01 00 movzwl 0x1002c,%esi
|
||||||
|
7d68: c1 e6 05 shl $0x5,%esi
|
||||||
|
7d6b: 01 de add %ebx,%esi
|
||||||
|
for (; ph < eph; ph++) {
|
||||||
|
7d6d: 39 f3 cmp %esi,%ebx
|
||||||
|
7d6f: 72 15 jb 7d86 <bootmain+0x59>
|
||||||
|
entry();
|
||||||
|
7d71: ff 15 18 00 01 00 call *0x10018
|
||||||
|
}
|
||||||
|
7d77: 8d 65 f4 lea -0xc(%ebp),%esp
|
||||||
|
7d7a: 5b pop %ebx
|
||||||
|
7d7b: 5e pop %esi
|
||||||
|
7d7c: 5f pop %edi
|
||||||
|
7d7d: 5d pop %ebp
|
||||||
|
7d7e: c3 ret
|
||||||
|
for (; ph < eph; ph++) {
|
||||||
|
7d7f: 83 c3 20 add $0x20,%ebx
|
||||||
|
7d82: 39 de cmp %ebx,%esi
|
||||||
|
7d84: 76 eb jbe 7d71 <bootmain+0x44>
|
||||||
|
pa = (uchar*)ph->paddr;
|
||||||
|
7d86: 8b 7b 0c mov 0xc(%ebx),%edi
|
||||||
|
readseg(pa, ph->filesz, ph->off);
|
||||||
|
7d89: 83 ec 04 sub $0x4,%esp
|
||||||
|
7d8c: ff 73 04 push 0x4(%ebx)
|
||||||
|
7d8f: ff 73 10 push 0x10(%ebx)
|
||||||
|
7d92: 57 push %edi
|
||||||
|
7d93: e8 4c ff ff ff call 7ce4 <readseg>
|
||||||
|
if (ph->memsz > ph->filesz) {
|
||||||
|
7d98: 8b 4b 14 mov 0x14(%ebx),%ecx
|
||||||
|
7d9b: 8b 43 10 mov 0x10(%ebx),%eax
|
||||||
|
7d9e: 83 c4 10 add $0x10,%esp
|
||||||
|
7da1: 39 c1 cmp %eax,%ecx
|
||||||
|
7da3: 76 da jbe 7d7f <bootmain+0x52>
|
||||||
|
stosb(pa + ph->filesz, 0, ph->memsz - ph->filesz);
|
||||||
|
7da5: 01 c7 add %eax,%edi
|
||||||
|
7da7: 29 c1 sub %eax,%ecx
|
||||||
|
"d" (port), "0" (addr), "1" (cnt) :
|
||||||
|
"cc");
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void stosb(void *addr, int data, int cnt) {
|
||||||
|
asm volatile ("cld; rep stosb" :
|
||||||
|
7da9: b8 00 00 00 00 mov $0x0,%eax
|
||||||
|
7dae: fc cld
|
||||||
|
7daf: f3 aa rep stos %al,%es:(%edi)
|
||||||
|
"=D" (addr), "=c" (cnt) :
|
||||||
|
"0" (addr), "1" (cnt), "a" (data) :
|
||||||
|
"memory", "cc");
|
||||||
|
}
|
||||||
|
7db1: eb cc jmp 7d7f <bootmain+0x52>
|
BIN
bootblock.o
Normal file
BIN
bootblock.o
Normal file
Binary file not shown.
BIN
bootblockother.o
Normal file
BIN
bootblockother.o
Normal file
Binary file not shown.
93
bootmain.c
Normal file
93
bootmain.c
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
// Boot loader.
|
||||||
|
//
|
||||||
|
// Part of the boot block, along with bootasm.S, which calls bootmain().
|
||||||
|
// bootasm.S has put the processor into protected 32-bit mode.
|
||||||
|
// bootmain() loads an ELF kernel image from the disk starting at
|
||||||
|
// sector 1 and then jumps to the kernel entry routine.
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "elf.h"
|
||||||
|
#include "x86.h"
|
||||||
|
#include "memlayout.h"
|
||||||
|
|
||||||
|
#define SECTSIZE 512
|
||||||
|
|
||||||
|
void readseg(uchar*, uint, uint);
|
||||||
|
|
||||||
|
void bootmain(void) {
|
||||||
|
struct elfhdr *elf;
|
||||||
|
struct proghdr *ph, *eph;
|
||||||
|
void (*entry)(void);
|
||||||
|
uchar* pa;
|
||||||
|
|
||||||
|
elf = (struct elfhdr*)0x10000; // scratch space
|
||||||
|
|
||||||
|
// Read 1st page off disk
|
||||||
|
readseg((uchar*)elf, 4096, 0);
|
||||||
|
|
||||||
|
// Is this an ELF executable?
|
||||||
|
if (elf->magic != ELF_MAGIC) {
|
||||||
|
return; // let bootasm.S handle error
|
||||||
|
|
||||||
|
}
|
||||||
|
// Load each program segment (ignores ph flags).
|
||||||
|
ph = (struct proghdr*)((uchar*)elf + elf->phoff);
|
||||||
|
eph = ph + elf->phnum;
|
||||||
|
for (; ph < eph; ph++) {
|
||||||
|
pa = (uchar*)ph->paddr;
|
||||||
|
readseg(pa, ph->filesz, ph->off);
|
||||||
|
if (ph->memsz > ph->filesz) {
|
||||||
|
stosb(pa + ph->filesz, 0, ph->memsz - ph->filesz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the entry point from the ELF header.
|
||||||
|
// Does not return!
|
||||||
|
entry = (void (*)(void))(elf->entry);
|
||||||
|
entry();
|
||||||
|
}
|
||||||
|
|
||||||
|
void waitdisk(void) {
|
||||||
|
// Wait for disk ready.
|
||||||
|
while ((inb(0x1F7) & 0xC0) != 0x40) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read a single sector at offset into dst.
|
||||||
|
void readsect(void *dst, uint offset) {
|
||||||
|
// Issue command.
|
||||||
|
waitdisk();
|
||||||
|
outb(0x1F2, 1); // count = 1
|
||||||
|
outb(0x1F3, offset);
|
||||||
|
outb(0x1F4, offset >> 8);
|
||||||
|
outb(0x1F5, offset >> 16);
|
||||||
|
outb(0x1F6, (offset >> 24) | 0xE0);
|
||||||
|
outb(0x1F7, 0x20); // cmd 0x20 - read sectors
|
||||||
|
|
||||||
|
// Read data.
|
||||||
|
waitdisk();
|
||||||
|
insl(0x1F0, dst, SECTSIZE / 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read 'count' bytes at 'offset' from kernel into physical address 'pa'.
|
||||||
|
// Might copy more than asked.
|
||||||
|
|
||||||
|
void readseg(uchar* pa, uint count, uint offset) {
|
||||||
|
uchar* epa;
|
||||||
|
|
||||||
|
epa = pa + count;
|
||||||
|
|
||||||
|
// Round down to sector boundary.
|
||||||
|
pa -= offset % SECTSIZE;
|
||||||
|
|
||||||
|
// Translate from bytes to sectors; kernel starts at sector 1.
|
||||||
|
offset = (offset / SECTSIZE) + 1;
|
||||||
|
|
||||||
|
// If this is too slow, we could read lots of sectors at a time.
|
||||||
|
// We'd write more to memory than asked, but it doesn't matter --
|
||||||
|
// we load in increasing order.
|
||||||
|
for (; pa < epa; pa += SECTSIZE, offset++) {
|
||||||
|
readsect(pa, offset);
|
||||||
|
}
|
||||||
|
}
|
1
bootmain.d
Normal file
1
bootmain.d
Normal file
@ -0,0 +1 @@
|
|||||||
|
bootmain.o: bootmain.c types.h elf.h x86.h memlayout.h
|
BIN
bootmain.o
Normal file
BIN
bootmain.o
Normal file
Binary file not shown.
14
buf.h
Normal file
14
buf.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
struct buf {
|
||||||
|
int flags;
|
||||||
|
uint dev;
|
||||||
|
uint blockno;
|
||||||
|
struct sleeplock lock;
|
||||||
|
uint refcnt;
|
||||||
|
struct buf *prev; // LRU cache list
|
||||||
|
struct buf *next;
|
||||||
|
struct buf *qnext; // disk queue
|
||||||
|
uchar data[BSIZE];
|
||||||
|
};
|
||||||
|
#define B_VALID 0x2 // buffer has been read from disk
|
||||||
|
#define B_DIRTY 0x4 // buffer needs to be written to disk
|
||||||
|
|
39
cat.c
Normal file
39
cat.c
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#include "types.h"
|
||||||
|
#include "stat.h"
|
||||||
|
#include "user.h"
|
||||||
|
|
||||||
|
char buf[512];
|
||||||
|
|
||||||
|
void cat(int fd) {
|
||||||
|
int n;
|
||||||
|
|
||||||
|
while ((n = read(fd, buf, sizeof(buf))) > 0) {
|
||||||
|
if (write(1, buf, n) != n) {
|
||||||
|
printf(1, "cat: write error\n");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (n < 0) {
|
||||||
|
printf(1, "cat: read error\n");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
int fd, i;
|
||||||
|
|
||||||
|
if (argc <= 1) {
|
||||||
|
cat(0);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 1; i < argc; i++) {
|
||||||
|
if ((fd = open(argv[i], 0)) < 0) {
|
||||||
|
printf(1, "cat: cannot open %s\n", argv[i]);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
cat(fd);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
exit();
|
||||||
|
}
|
1
cat.d
Normal file
1
cat.d
Normal file
@ -0,0 +1 @@
|
|||||||
|
cat.o: cat.c /usr/include/stdc-predef.h types.h stat.h user.h
|
50
cat.sym
Normal file
50
cat.sym
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
00000000 cat.c
|
||||||
|
00000000 ulib.c
|
||||||
|
00000000 printf.c
|
||||||
|
00000420 printint
|
||||||
|
00000890 digits.0
|
||||||
|
00000000 umalloc.c
|
||||||
|
00000d80 freep
|
||||||
|
00000d84 base
|
||||||
|
00000110 strcpy
|
||||||
|
000004d0 printf
|
||||||
|
0000040b greeting
|
||||||
|
00000330 memmove
|
||||||
|
000003db mknod
|
||||||
|
00000230 gets
|
||||||
|
000003ab getpid
|
||||||
|
00000090 cat
|
||||||
|
00000700 malloc
|
||||||
|
000003bb sleep
|
||||||
|
00000373 pipe
|
||||||
|
00000403 getch
|
||||||
|
000003d3 write
|
||||||
|
00000393 fstat
|
||||||
|
00000383 kill
|
||||||
|
0000039b chdir
|
||||||
|
0000038b exec
|
||||||
|
0000036b wait
|
||||||
|
0000037b read
|
||||||
|
000003e3 unlink
|
||||||
|
0000035b fork
|
||||||
|
000003b3 sbrk
|
||||||
|
000003c3 uptime
|
||||||
|
00000b74 __bss_start
|
||||||
|
000001d0 memset
|
||||||
|
00000000 main
|
||||||
|
00000140 strcmp
|
||||||
|
00000413 shutdown
|
||||||
|
000003a3 dup
|
||||||
|
00000b80 buf
|
||||||
|
000002a0 stat
|
||||||
|
00000b74 _edata
|
||||||
|
00000d8c _end
|
||||||
|
000003eb link
|
||||||
|
00000363 exit
|
||||||
|
000002f0 atoi
|
||||||
|
000001a0 strlen
|
||||||
|
000003cb open
|
||||||
|
000001f0 strchr
|
||||||
|
000003f3 mkdir
|
||||||
|
000003fb close
|
||||||
|
00000670 free
|
2
config.xlaunch
Normal file
2
config.xlaunch
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<XLaunch WindowMode="MultiWindow" ClientMode="NoClient" LocalClient="False" Display="-1" LocalProgram="xcalc" RemoteProgram="xterm" RemotePassword="" PrivateKey="" RemoteHost="" RemoteUser="" XDMCPHost="" XDMCPBroadcast="False" XDMCPIndirect="False" Clipboard="True" ClipboardPrimary="True" ExtraParams="" Wgl="True" DisableAC="True" XDMCPTerminate="False"/>
|
332
console.c
Normal file
332
console.c
Normal file
@ -0,0 +1,332 @@
|
|||||||
|
// Console input and output.
|
||||||
|
// Input is from the keyboard or serial port.
|
||||||
|
// Output is written to the screen and serial port.
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "defs.h"
|
||||||
|
#include "param.h"
|
||||||
|
#include "traps.h"
|
||||||
|
#include "spinlock.h"
|
||||||
|
#include "sleeplock.h"
|
||||||
|
#include "fs.h"
|
||||||
|
#include "file.h"
|
||||||
|
#include "memlayout.h"
|
||||||
|
#include "mmu.h"
|
||||||
|
#include "proc.h"
|
||||||
|
#include "x86.h"
|
||||||
|
|
||||||
|
#define INPUT_BUF 128
|
||||||
|
|
||||||
|
struct kbdbuffer {
|
||||||
|
char buf[INPUT_BUF];
|
||||||
|
uint r; // Read index
|
||||||
|
uint w; // Write index
|
||||||
|
uint e; // Edit index
|
||||||
|
};
|
||||||
|
|
||||||
|
struct kbdbuffer inputBuffer;
|
||||||
|
|
||||||
|
struct kbdbuffer * input = 0;
|
||||||
|
|
||||||
|
#define C(x) ((x) - '@') // Control-x
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void consputc(int);
|
||||||
|
|
||||||
|
static int panicked = 0;
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
struct spinlock lock;
|
||||||
|
int locking;
|
||||||
|
} cons;
|
||||||
|
|
||||||
|
static void printint(int xx, int base, int sign) {
|
||||||
|
static char digits[] = "0123456789abcdef";
|
||||||
|
char buf[16];
|
||||||
|
int i;
|
||||||
|
uint x;
|
||||||
|
|
||||||
|
if (sign && (sign = xx < 0)) {
|
||||||
|
x = -xx;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
x = xx;
|
||||||
|
}
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
do {
|
||||||
|
buf[i++] = digits[x % base];
|
||||||
|
}
|
||||||
|
while ((x /= base) != 0);
|
||||||
|
|
||||||
|
if (sign) {
|
||||||
|
buf[i++] = '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
while (--i >= 0) {
|
||||||
|
consputc(buf[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print to the console. only understands %d, %x, %p, %s.
|
||||||
|
void cprintf(char *fmt, ...) {
|
||||||
|
int i, c, locking;
|
||||||
|
uint *argp;
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
locking = cons.locking;
|
||||||
|
if (locking) {
|
||||||
|
acquire(&cons.lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fmt == 0) {
|
||||||
|
panic("null fmt");
|
||||||
|
}
|
||||||
|
|
||||||
|
argp = (uint*)(void*)(&fmt + 1);
|
||||||
|
for (i = 0; (c = fmt[i] & 0xff) != 0; i++) {
|
||||||
|
if (c != '%') {
|
||||||
|
consputc(c);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
c = fmt[++i] & 0xff;
|
||||||
|
if (c == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch (c) {
|
||||||
|
case 'd':
|
||||||
|
printint(*argp++, 10, 1);
|
||||||
|
break;
|
||||||
|
case 'x':
|
||||||
|
case 'p':
|
||||||
|
printint(*argp++, 16, 0);
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
if ((s = (char*)*argp++) == 0) {
|
||||||
|
s = "(null)";
|
||||||
|
}
|
||||||
|
for (; *s; s++) {
|
||||||
|
consputc(*s);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '%':
|
||||||
|
consputc('%');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Print unknown % sequence to draw attention.
|
||||||
|
consputc('%');
|
||||||
|
consputc(c);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (locking) {
|
||||||
|
release(&cons.lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void panic(char *s) {
|
||||||
|
int i;
|
||||||
|
uint pcs[10];
|
||||||
|
|
||||||
|
cli();
|
||||||
|
cons.locking = 0;
|
||||||
|
// use lapiccpunum so that we can call panic from mycpu()
|
||||||
|
cprintf("lapicid %d: panic: ", lapicid());
|
||||||
|
cprintf(s);
|
||||||
|
cprintf("\n");
|
||||||
|
getcallerpcs(&s, pcs);
|
||||||
|
for (i = 0; i < 10; i++) {
|
||||||
|
cprintf(" %p", pcs[i]);
|
||||||
|
}
|
||||||
|
panicked = 1; // freeze other CPU
|
||||||
|
for (;;) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define BACKSPACE 0x100
|
||||||
|
#define CRTPORT 0x3d4
|
||||||
|
static ushort *crt = (ushort*)P2V(0xb8000); // CGA memory
|
||||||
|
|
||||||
|
static void cgaputc(int c) {
|
||||||
|
int pos;
|
||||||
|
|
||||||
|
// Cursor position: col + 80*row.
|
||||||
|
outb(CRTPORT, 14);
|
||||||
|
pos = inb(CRTPORT + 1) << 8;
|
||||||
|
outb(CRTPORT, 15);
|
||||||
|
pos |= inb(CRTPORT + 1);
|
||||||
|
|
||||||
|
if (c == '\n') {
|
||||||
|
pos += 80 - pos % 80;
|
||||||
|
}
|
||||||
|
else if (c == BACKSPACE) {
|
||||||
|
if (pos > 0) {
|
||||||
|
--pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
crt[pos++] = (c & 0xff) | 0x0700; // black on white
|
||||||
|
|
||||||
|
}
|
||||||
|
if (pos < 0 || pos > 25 * 80) {
|
||||||
|
panic("pos under/overflow");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((pos / 80) >= 24) { // Scroll up.
|
||||||
|
memmove(crt, crt + 80, sizeof(crt[0]) * 23 * 80);
|
||||||
|
pos -= 80;
|
||||||
|
memset(crt + pos, 0, sizeof(crt[0]) * (24 * 80 - pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
outb(CRTPORT, 14);
|
||||||
|
outb(CRTPORT + 1, pos >> 8);
|
||||||
|
outb(CRTPORT, 15);
|
||||||
|
outb(CRTPORT + 1, pos);
|
||||||
|
crt[pos] = ' ' | 0x0700;
|
||||||
|
}
|
||||||
|
|
||||||
|
void consputc(int c) {
|
||||||
|
if (panicked) {
|
||||||
|
cli();
|
||||||
|
for (;;) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == BACKSPACE) {
|
||||||
|
uartputc('\b');
|
||||||
|
uartputc(' ');
|
||||||
|
uartputc('\b');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
uartputc(c);
|
||||||
|
}
|
||||||
|
cgaputc(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
int consoleget(void) {
|
||||||
|
int c;
|
||||||
|
|
||||||
|
acquire(&cons.lock);
|
||||||
|
|
||||||
|
while ((c = kbdgetc()) <= 0) {
|
||||||
|
if (c == 0) {
|
||||||
|
c = kbdgetc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
release(&cons.lock);
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
void consoleintr(int (*getc)(void)) {
|
||||||
|
int c, doprocdump = 0;
|
||||||
|
|
||||||
|
acquire(&cons.lock);
|
||||||
|
while ((c = getc()) >= 0) {
|
||||||
|
switch (c) {
|
||||||
|
case C('P'): // Process listing.
|
||||||
|
// procdump() locks cons.lock indirectly; invoke later
|
||||||
|
doprocdump = 1;
|
||||||
|
break;
|
||||||
|
case C('U'): // Kill line.
|
||||||
|
while (input->e != input->w &&
|
||||||
|
input->buf[(input->e - 1) % INPUT_BUF] != '\n') {
|
||||||
|
input->e--;
|
||||||
|
consputc(BACKSPACE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case C('H'):
|
||||||
|
case '\x7f': // Backspace
|
||||||
|
if (input->e != input->w) {
|
||||||
|
input->e--;
|
||||||
|
consputc(BACKSPACE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (c != 0 && input->e - input->r < INPUT_BUF) {
|
||||||
|
c = (c == '\r') ? '\n' : c;
|
||||||
|
input->buf[input->e++ % INPUT_BUF] = c;
|
||||||
|
consputc(c);
|
||||||
|
if (c == '\n' || c == C('D') || input->e == input->r + INPUT_BUF) {
|
||||||
|
input->w = input->e;
|
||||||
|
wakeup(&(input->r));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
release(&cons.lock);
|
||||||
|
if (doprocdump) {
|
||||||
|
procdump(); // now call procdump() wo. cons.lock held
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int consoleread(struct inode *ip, char *dst, int n) {
|
||||||
|
uint target;
|
||||||
|
int c;
|
||||||
|
|
||||||
|
iunlock(ip);
|
||||||
|
target = n;
|
||||||
|
acquire(&cons.lock);
|
||||||
|
while (n > 0) {
|
||||||
|
while (input->r == input->w) {
|
||||||
|
if (myproc()->killed) {
|
||||||
|
release(&cons.lock);
|
||||||
|
ilock(ip);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
sleep(&(input->r), &cons.lock);
|
||||||
|
}
|
||||||
|
c = input->buf[input->r++ % INPUT_BUF];
|
||||||
|
if (c == C('D')) { // EOF
|
||||||
|
if (n < target) {
|
||||||
|
// Save ^D for next time, to make sure
|
||||||
|
// caller gets a 0-byte result.
|
||||||
|
input->r--;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*dst++ = c;
|
||||||
|
--n;
|
||||||
|
if (c == '\n') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
release(&cons.lock);
|
||||||
|
ilock(ip);
|
||||||
|
|
||||||
|
return target - n;
|
||||||
|
}
|
||||||
|
|
||||||
|
int consolewrite(struct inode *ip, char *buf, int n) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
iunlock(ip);
|
||||||
|
acquire(&cons.lock);
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
consputc(buf[i] & 0xff);
|
||||||
|
}
|
||||||
|
release(&cons.lock);
|
||||||
|
ilock(ip);
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void consoleinit(void) {
|
||||||
|
initlock(&cons.lock, "console");
|
||||||
|
|
||||||
|
// Initialise pointer to point to our console input buffer
|
||||||
|
input = &inputBuffer;
|
||||||
|
|
||||||
|
devsw[CONSOLE].write = consolewrite;
|
||||||
|
devsw[CONSOLE].read = consoleread;
|
||||||
|
cons.locking = 1;
|
||||||
|
|
||||||
|
ioapicenable(IRQ_KBD, 0);
|
||||||
|
}
|
||||||
|
|
3
console.d
Normal file
3
console.d
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
console.o: console.c /usr/include/stdc-predef.h types.h defs.h param.h \
|
||||||
|
traps.h spinlock.h sleeplock.h fs.h file.h memlayout.h mmu.h proc.h \
|
||||||
|
x86.h
|
48
cuth
Normal file
48
cuth
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#!/usr/bin/perl
|
||||||
|
|
||||||
|
$| = 1;
|
||||||
|
|
||||||
|
sub writefile($@){
|
||||||
|
my ($file, @lines) = @_;
|
||||||
|
|
||||||
|
sleep(1);
|
||||||
|
open(F, ">$file") || die "open >$file: $!";
|
||||||
|
print F @lines;
|
||||||
|
close(F);
|
||||||
|
}
|
||||||
|
|
||||||
|
# Cut out #include lines that don't contribute anything.
|
||||||
|
for($i=0; $i<@ARGV; $i++){
|
||||||
|
$file = $ARGV[$i];
|
||||||
|
if(!open(F, $file)){
|
||||||
|
print STDERR "open $file: $!\n";
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
@lines = <F>;
|
||||||
|
close(F);
|
||||||
|
|
||||||
|
$obj = "$file.o";
|
||||||
|
$obj =~ s/\.c\.o$/.o/;
|
||||||
|
system("touch $file");
|
||||||
|
|
||||||
|
if(system("make CC='gcc -Werror' $obj >/dev/null 2>\&1") != 0){
|
||||||
|
print STDERR "make $obj failed: $rv\n";
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
system("cp $file =$file");
|
||||||
|
for($j=@lines-1; $j>=0; $j--){
|
||||||
|
if($lines[$j] =~ /^#include/){
|
||||||
|
$old = $lines[$j];
|
||||||
|
$lines[$j] = "/* CUT-H */\n";
|
||||||
|
writefile($file, @lines);
|
||||||
|
if(system("make CC='gcc -Werror' $obj >/dev/null 2>\&1") != 0){
|
||||||
|
$lines[$j] = $old;
|
||||||
|
}else{
|
||||||
|
print STDERR "$file $old";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writefile($file, grep {!/CUT-H/} @lines);
|
||||||
|
system("rm =$file");
|
||||||
|
}
|
8
date.h
Normal file
8
date.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
struct rtcdate {
|
||||||
|
uint second;
|
||||||
|
uint minute;
|
||||||
|
uint hour;
|
||||||
|
uint day;
|
||||||
|
uint month;
|
||||||
|
uint year;
|
||||||
|
};
|
191
defs.h
Normal file
191
defs.h
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
struct buf;
|
||||||
|
struct context;
|
||||||
|
struct file;
|
||||||
|
struct inode;
|
||||||
|
struct pipe;
|
||||||
|
struct proc;
|
||||||
|
struct rtcdate;
|
||||||
|
struct spinlock;
|
||||||
|
struct sleeplock;
|
||||||
|
struct stat;
|
||||||
|
struct superblock;
|
||||||
|
|
||||||
|
// bio.c
|
||||||
|
void binit(void);
|
||||||
|
struct buf* bread(uint, uint);
|
||||||
|
void brelse(struct buf*);
|
||||||
|
void bwrite(struct buf*);
|
||||||
|
|
||||||
|
// console.c
|
||||||
|
void consoleinit(void);
|
||||||
|
void cprintf(char*, ...);
|
||||||
|
void consoleintr(int (*)(void));
|
||||||
|
int consoleget(void);
|
||||||
|
void panic(char*) __attribute__((noreturn));
|
||||||
|
|
||||||
|
// exec.c
|
||||||
|
int exec(char*, char**);
|
||||||
|
|
||||||
|
// file.c
|
||||||
|
struct file* filealloc(void);
|
||||||
|
void fileclose(struct file*);
|
||||||
|
struct file* filedup(struct file*);
|
||||||
|
void fileinit(void);
|
||||||
|
int fileread(struct file*, char*, int n);
|
||||||
|
int filestat(struct file*, struct stat*);
|
||||||
|
int filewrite(struct file*, char*, int n);
|
||||||
|
|
||||||
|
// fs.c
|
||||||
|
void readsb(int dev, struct superblock *sb);
|
||||||
|
int dirlink(struct inode*, char*, uint);
|
||||||
|
struct inode* dirlookup(struct inode*, char*, uint*);
|
||||||
|
struct inode* ialloc(uint, short);
|
||||||
|
struct inode* idup(struct inode*);
|
||||||
|
void iinit(int dev);
|
||||||
|
void ilock(struct inode*);
|
||||||
|
void iput(struct inode*);
|
||||||
|
void iunlock(struct inode*);
|
||||||
|
void iunlockput(struct inode*);
|
||||||
|
void iupdate(struct inode*);
|
||||||
|
int namecmp(const char*, const char*);
|
||||||
|
struct inode* namei(char*);
|
||||||
|
struct inode* nameiparent(char*, char*);
|
||||||
|
int readi(struct inode*, char*, uint, uint);
|
||||||
|
void stati(struct inode*, struct stat*);
|
||||||
|
int writei(struct inode*, char*, uint, uint);
|
||||||
|
|
||||||
|
// ide.c
|
||||||
|
void ideinit(void);
|
||||||
|
void ideintr(void);
|
||||||
|
void iderw(struct buf*);
|
||||||
|
|
||||||
|
// ioapic.c
|
||||||
|
void ioapicenable(int irq, int cpu);
|
||||||
|
extern uchar ioapicid;
|
||||||
|
void ioapicinit(void);
|
||||||
|
|
||||||
|
// kalloc.c
|
||||||
|
char* kalloc(void);
|
||||||
|
void kfree(char*);
|
||||||
|
void kinit1(void*, void*);
|
||||||
|
void kinit2(void*, void*);
|
||||||
|
|
||||||
|
// kbd.c
|
||||||
|
void kbdintr(void);
|
||||||
|
int kbdgetc(void);
|
||||||
|
|
||||||
|
// lapic.c
|
||||||
|
void cmostime(struct rtcdate *r);
|
||||||
|
int lapicid(void);
|
||||||
|
extern volatile uint* lapic;
|
||||||
|
void lapiceoi(void);
|
||||||
|
void lapicinit(void);
|
||||||
|
void lapicstartap(uchar, uint);
|
||||||
|
void microdelay(int);
|
||||||
|
|
||||||
|
// log.c
|
||||||
|
void initlog(int dev);
|
||||||
|
void log_write(struct buf*);
|
||||||
|
void begin_op();
|
||||||
|
void end_op();
|
||||||
|
|
||||||
|
// mp.c
|
||||||
|
extern int ismp;
|
||||||
|
void mpinit(void);
|
||||||
|
|
||||||
|
// picirq.c
|
||||||
|
void picenable(int);
|
||||||
|
void picinit(void);
|
||||||
|
|
||||||
|
// pipe.c
|
||||||
|
int pipealloc(struct file**, struct file**);
|
||||||
|
void pipeclose(struct pipe*, int);
|
||||||
|
int piperead(struct pipe*, char*, int);
|
||||||
|
int pipewrite(struct pipe*, char*, int);
|
||||||
|
|
||||||
|
// proc.c
|
||||||
|
int cpuid(void);
|
||||||
|
void exit(void);
|
||||||
|
int fork(void);
|
||||||
|
int growproc(int);
|
||||||
|
int kill(int);
|
||||||
|
struct cpu* mycpu(void);
|
||||||
|
struct proc* myproc();
|
||||||
|
void pinit(void);
|
||||||
|
void procdump(void);
|
||||||
|
void scheduler(void) __attribute__((noreturn));
|
||||||
|
void sched(void);
|
||||||
|
void setproc(struct proc*);
|
||||||
|
void sleep(void*, struct spinlock*);
|
||||||
|
void userinit(void);
|
||||||
|
int wait(void);
|
||||||
|
void wakeup(void*);
|
||||||
|
void yield(void);
|
||||||
|
|
||||||
|
// swtch.S
|
||||||
|
void swtch(struct context**, struct context*);
|
||||||
|
|
||||||
|
// spinlock.c
|
||||||
|
void acquire(struct spinlock*);
|
||||||
|
void getcallerpcs(void*, uint*);
|
||||||
|
int holding(struct spinlock*);
|
||||||
|
void initlock(struct spinlock*, char*);
|
||||||
|
void release(struct spinlock*);
|
||||||
|
void pushcli(void);
|
||||||
|
void popcli(void);
|
||||||
|
|
||||||
|
// sleeplock.c
|
||||||
|
void acquiresleep(struct sleeplock*);
|
||||||
|
void releasesleep(struct sleeplock*);
|
||||||
|
int holdingsleep(struct sleeplock*);
|
||||||
|
void initsleeplock(struct sleeplock*, char*);
|
||||||
|
|
||||||
|
// string.c
|
||||||
|
int memcmp(const void*, const void*, uint);
|
||||||
|
void* memmove(void*, const void*, uint);
|
||||||
|
void* memset(void*, int, uint);
|
||||||
|
char* safestrcpy(char*, const char*, int);
|
||||||
|
int strlen(const char*);
|
||||||
|
int strncmp(const char*, const char*, uint);
|
||||||
|
char* strncpy(char*, const char*, int);
|
||||||
|
|
||||||
|
// syscall.c
|
||||||
|
int argint(int, int*);
|
||||||
|
int argptr(int, char**, int);
|
||||||
|
int argstr(int, char**);
|
||||||
|
int fetchint(uint, int*);
|
||||||
|
int fetchstr(uint, char**);
|
||||||
|
void syscall(void);
|
||||||
|
|
||||||
|
// timer.c
|
||||||
|
void timerinit(void);
|
||||||
|
|
||||||
|
// trap.c
|
||||||
|
void idtinit(void);
|
||||||
|
extern uint ticks;
|
||||||
|
void tvinit(void);
|
||||||
|
extern struct spinlock tickslock;
|
||||||
|
|
||||||
|
// uart.c
|
||||||
|
void uartinit(void);
|
||||||
|
void uartintr(void);
|
||||||
|
void uartputc(int);
|
||||||
|
|
||||||
|
// vm.c
|
||||||
|
void seginit(void);
|
||||||
|
void kvmalloc(void);
|
||||||
|
pde_t* setupkvm(void);
|
||||||
|
char* uva2ka(pde_t*, char*);
|
||||||
|
int allocuvm(pde_t*, uint, uint);
|
||||||
|
int deallocuvm(pde_t*, uint, uint);
|
||||||
|
void freevm(pde_t*);
|
||||||
|
void inituvm(pde_t*, char*, uint);
|
||||||
|
int loaduvm(pde_t*, char*, struct inode*, uint, uint);
|
||||||
|
pde_t* copyuvm(pde_t*, uint);
|
||||||
|
void switchuvm(struct proc*);
|
||||||
|
void switchkvm(void);
|
||||||
|
int copyout(pde_t*, uint, void*, uint);
|
||||||
|
void clearpteu(pde_t *pgdir, char *uva);
|
||||||
|
|
||||||
|
// number of elements in fixed-size array
|
||||||
|
#define NELEM(x) (sizeof(x) / sizeof((x)[0]))
|
12
echo.c
Normal file
12
echo.c
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#include "types.h"
|
||||||
|
#include "stat.h"
|
||||||
|
#include "user.h"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 1; i < argc; i++) {
|
||||||
|
printf(1, "%s%s", argv[i], i + 1 < argc ? " " : "\n");
|
||||||
|
}
|
||||||
|
exit();
|
||||||
|
}
|
1
echo.d
Normal file
1
echo.d
Normal file
@ -0,0 +1 @@
|
|||||||
|
echo.o: echo.c /usr/include/stdc-predef.h types.h stat.h user.h
|
48
echo.sym
Normal file
48
echo.sym
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
00000000 echo.c
|
||||||
|
00000000 ulib.c
|
||||||
|
00000000 printf.c
|
||||||
|
00000380 printint
|
||||||
|
000007c0 digits.0
|
||||||
|
00000000 umalloc.c
|
||||||
|
00000a74 freep
|
||||||
|
00000a78 base
|
||||||
|
00000070 strcpy
|
||||||
|
00000430 printf
|
||||||
|
0000036b greeting
|
||||||
|
00000290 memmove
|
||||||
|
0000033b mknod
|
||||||
|
00000190 gets
|
||||||
|
0000030b getpid
|
||||||
|
00000660 malloc
|
||||||
|
0000031b sleep
|
||||||
|
000002d3 pipe
|
||||||
|
00000363 getch
|
||||||
|
00000333 write
|
||||||
|
000002f3 fstat
|
||||||
|
000002e3 kill
|
||||||
|
000002fb chdir
|
||||||
|
000002eb exec
|
||||||
|
000002cb wait
|
||||||
|
000002db read
|
||||||
|
00000343 unlink
|
||||||
|
000002bb fork
|
||||||
|
00000313 sbrk
|
||||||
|
00000323 uptime
|
||||||
|
00000a74 __bss_start
|
||||||
|
00000130 memset
|
||||||
|
00000000 main
|
||||||
|
000000a0 strcmp
|
||||||
|
00000373 shutdown
|
||||||
|
00000303 dup
|
||||||
|
00000200 stat
|
||||||
|
00000a74 _edata
|
||||||
|
00000a80 _end
|
||||||
|
0000034b link
|
||||||
|
000002c3 exit
|
||||||
|
00000250 atoi
|
||||||
|
00000100 strlen
|
||||||
|
0000032b open
|
||||||
|
00000150 strchr
|
||||||
|
00000353 mkdir
|
||||||
|
0000035b close
|
||||||
|
000005d0 free
|
42
elf.h
Normal file
42
elf.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// Format of an ELF executable file
|
||||||
|
|
||||||
|
#define ELF_MAGIC 0x464C457FU // "\x7FELF" in little endian
|
||||||
|
|
||||||
|
// File header
|
||||||
|
struct elfhdr {
|
||||||
|
uint magic; // must equal ELF_MAGIC
|
||||||
|
uchar elf[12];
|
||||||
|
ushort type;
|
||||||
|
ushort machine;
|
||||||
|
uint version;
|
||||||
|
uint entry;
|
||||||
|
uint phoff;
|
||||||
|
uint shoff;
|
||||||
|
uint flags;
|
||||||
|
ushort ehsize;
|
||||||
|
ushort phentsize;
|
||||||
|
ushort phnum;
|
||||||
|
ushort shentsize;
|
||||||
|
ushort shnum;
|
||||||
|
ushort shstrndx;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Program section header
|
||||||
|
struct proghdr {
|
||||||
|
uint type;
|
||||||
|
uint off;
|
||||||
|
uint vaddr;
|
||||||
|
uint paddr;
|
||||||
|
uint filesz;
|
||||||
|
uint memsz;
|
||||||
|
uint flags;
|
||||||
|
uint align;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Values for Proghdr type
|
||||||
|
#define ELF_PROG_LOAD 1
|
||||||
|
|
||||||
|
// Flag bits for Proghdr flags
|
||||||
|
#define ELF_PROG_FLAG_EXEC 1
|
||||||
|
#define ELF_PROG_FLAG_WRITE 2
|
||||||
|
#define ELF_PROG_FLAG_READ 4
|
68
entry.S
Normal file
68
entry.S
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
# The xv6 kernel starts executing in this file. This file is linked with
|
||||||
|
# the kernel C code, so it can refer to kernel symbols such as main().
|
||||||
|
# The boot block (bootasm.S and bootmain.c) jumps to entry below.
|
||||||
|
|
||||||
|
# Multiboot header, for multiboot boot loaders like GNU Grub.
|
||||||
|
# http://www.gnu.org/software/grub/manual/multiboot/multiboot.html
|
||||||
|
#
|
||||||
|
# Using GRUB 2, you can boot xv6 from a file stored in a
|
||||||
|
# Linux file system by copying kernel or kernelmemfs to /boot
|
||||||
|
# and then adding this menu entry:
|
||||||
|
#
|
||||||
|
# menuentry "xv6" {
|
||||||
|
# insmod ext2
|
||||||
|
# set root='(hd0,msdos1)'
|
||||||
|
# set kernel='/boot/kernel'
|
||||||
|
# echo "Loading ${kernel}..."
|
||||||
|
# multiboot ${kernel} ${kernel}
|
||||||
|
# boot
|
||||||
|
# }
|
||||||
|
|
||||||
|
#include "asm.h"
|
||||||
|
#include "memlayout.h"
|
||||||
|
#include "mmu.h"
|
||||||
|
#include "param.h"
|
||||||
|
|
||||||
|
# Multiboot header. Data to direct multiboot loader.
|
||||||
|
.p2align 2
|
||||||
|
.text
|
||||||
|
.globl multiboot_header
|
||||||
|
multiboot_header:
|
||||||
|
#define magic 0x1badb002
|
||||||
|
#define flags 0
|
||||||
|
.long magic
|
||||||
|
.long flags
|
||||||
|
.long (-magic-flags)
|
||||||
|
|
||||||
|
# By convention, the _start symbol specifies the ELF entry point.
|
||||||
|
# Since we haven't set up virtual memory yet, our entry point is
|
||||||
|
# the physical address of 'entry'.
|
||||||
|
.globl _start
|
||||||
|
_start = V2P_WO(entry)
|
||||||
|
|
||||||
|
# Entering xv6 on boot processor, with paging off.
|
||||||
|
.globl entry
|
||||||
|
entry:
|
||||||
|
# Turn on page size extension for 4Mbyte pages
|
||||||
|
movl %cr4, %eax
|
||||||
|
orl $(CR4_PSE), %eax
|
||||||
|
movl %eax, %cr4
|
||||||
|
# Set page directory
|
||||||
|
movl $(V2P_WO(entrypgdir)), %eax
|
||||||
|
movl %eax, %cr3
|
||||||
|
# Turn on paging.
|
||||||
|
movl %cr0, %eax
|
||||||
|
orl $(CR0_PG|CR0_WP), %eax
|
||||||
|
movl %eax, %cr0
|
||||||
|
|
||||||
|
# Set up the stack pointer.
|
||||||
|
movl $(stack + KSTACKSIZE), %esp
|
||||||
|
|
||||||
|
# Jump to main(), and switch to executing at
|
||||||
|
# high addresses. The indirect call is needed because
|
||||||
|
# the assembler produces a PC-relative instruction
|
||||||
|
# for a direct jump.
|
||||||
|
mov $main, %eax
|
||||||
|
jmp *%eax
|
||||||
|
|
||||||
|
.comm stack, KSTACKSIZE
|
BIN
entryother
Normal file
BIN
entryother
Normal file
Binary file not shown.
89
entryother.S
Normal file
89
entryother.S
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
#include "asm.h"
|
||||||
|
#include "memlayout.h"
|
||||||
|
#include "mmu.h"
|
||||||
|
|
||||||
|
# Each non-boot CPU ("AP") is started up in response to a STARTUP
|
||||||
|
# IPI from the boot CPU. Section B.4.2 of the Multi-Processor
|
||||||
|
# Specification says that the AP will start in real mode with CS:IP
|
||||||
|
# set to XY00:0000, where XY is an 8-bit value sent with the
|
||||||
|
# STARTUP. Thus this code must start at a 4096-byte boundary.
|
||||||
|
#
|
||||||
|
# Because this code sets DS to zero, it must sit
|
||||||
|
# at an address in the low 2^16 bytes.
|
||||||
|
#
|
||||||
|
# Startothers (in main.c) sends the STARTUPs one at a time.
|
||||||
|
# It copies this code (start) at 0x7000. It puts the address of
|
||||||
|
# a newly allocated per-core stack in start-4,the address of the
|
||||||
|
# place to jump to (mpenter) in start-8, and the physical address
|
||||||
|
# of entrypgdir in start-12.
|
||||||
|
#
|
||||||
|
# This code combines elements of bootasm.S and entry.S.
|
||||||
|
|
||||||
|
.code16
|
||||||
|
.globl start
|
||||||
|
start:
|
||||||
|
cli
|
||||||
|
|
||||||
|
# Zero data segment registers DS, ES, and SS.
|
||||||
|
xorw %ax,%ax
|
||||||
|
movw %ax,%ds
|
||||||
|
movw %ax,%es
|
||||||
|
movw %ax,%ss
|
||||||
|
|
||||||
|
# Switch from real to protected mode. Use a bootstrap GDT that makes
|
||||||
|
# virtual addresses map directly to physical addresses so that the
|
||||||
|
# effective memory map doesn't change during the transition.
|
||||||
|
lgdt gdtdesc
|
||||||
|
movl %cr0, %eax
|
||||||
|
orl $CR0_PE, %eax
|
||||||
|
movl %eax, %cr0
|
||||||
|
|
||||||
|
# Complete the transition to 32-bit protected mode by using a long jmp
|
||||||
|
# to reload %cs and %eip. The segment descriptors are set up with no
|
||||||
|
# translation, so that the mapping is still the identity mapping.
|
||||||
|
ljmpl $(SEG_KCODE<<3), $(start32)
|
||||||
|
|
||||||
|
|
||||||
|
.code32 # Tell assembler to generate 32-bit code now.
|
||||||
|
start32:
|
||||||
|
# Set up the protected-mode data segment registers
|
||||||
|
movw $(SEG_KDATA<<3), %ax # Our data segment selector
|
||||||
|
movw %ax, %ds # -> DS: Data Segment
|
||||||
|
movw %ax, %es # -> ES: Extra Segment
|
||||||
|
movw %ax, %ss # -> SS: Stack Segment
|
||||||
|
movw $0, %ax # Zero segments not ready for use
|
||||||
|
movw %ax, %fs # -> FS
|
||||||
|
movw %ax, %gs # -> GS
|
||||||
|
|
||||||
|
# Turn on page size extension for 4Mbyte pages
|
||||||
|
movl %cr4, %eax
|
||||||
|
orl $(CR4_PSE), %eax
|
||||||
|
movl %eax, %cr4
|
||||||
|
# Use entrypgdir as our initial page table
|
||||||
|
movl (start-12), %eax
|
||||||
|
movl %eax, %cr3
|
||||||
|
# Turn on paging.
|
||||||
|
movl %cr0, %eax
|
||||||
|
orl $(CR0_PE|CR0_PG|CR0_WP), %eax
|
||||||
|
movl %eax, %cr0
|
||||||
|
|
||||||
|
# Switch to the stack allocated by startothers()
|
||||||
|
movl (start-4), %esp
|
||||||
|
# Call mpenter()
|
||||||
|
call *(start-8)
|
||||||
|
|
||||||
|
# We should never return.
|
||||||
|
spin:
|
||||||
|
jmp spin
|
||||||
|
|
||||||
|
.p2align 2
|
||||||
|
gdt:
|
||||||
|
SEG_NULLASM
|
||||||
|
SEG_ASM(STA_X|STA_R, 0, 0xffffffff)
|
||||||
|
SEG_ASM(STA_W, 0, 0xffffffff)
|
||||||
|
|
||||||
|
|
||||||
|
gdtdesc:
|
||||||
|
.word (gdtdesc - gdt - 1)
|
||||||
|
.long gdt
|
||||||
|
|
115
entryother.asm
Normal file
115
entryother.asm
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
|
||||||
|
bootblockother.o: file format elf32-i386
|
||||||
|
|
||||||
|
|
||||||
|
Disassembly of section .text:
|
||||||
|
|
||||||
|
00007000 <start>:
|
||||||
|
# This code combines elements of bootasm.S and entry.S.
|
||||||
|
|
||||||
|
.code16
|
||||||
|
.globl start
|
||||||
|
start:
|
||||||
|
cli
|
||||||
|
7000: fa cli
|
||||||
|
|
||||||
|
# Zero data segment registers DS, ES, and SS.
|
||||||
|
xorw %ax,%ax
|
||||||
|
7001: 31 c0 xor %eax,%eax
|
||||||
|
movw %ax,%ds
|
||||||
|
7003: 8e d8 mov %eax,%ds
|
||||||
|
movw %ax,%es
|
||||||
|
7005: 8e c0 mov %eax,%es
|
||||||
|
movw %ax,%ss
|
||||||
|
7007: 8e d0 mov %eax,%ss
|
||||||
|
|
||||||
|
# Switch from real to protected mode. Use a bootstrap GDT that makes
|
||||||
|
# virtual addresses map directly to physical addresses so that the
|
||||||
|
# effective memory map doesn't change during the transition.
|
||||||
|
lgdt gdtdesc
|
||||||
|
7009: 0f 01 16 lgdtl (%esi)
|
||||||
|
700c: 74 70 je 707e <_end+0x2>
|
||||||
|
movl %cr0, %eax
|
||||||
|
700e: 0f 20 c0 mov %cr0,%eax
|
||||||
|
orl $CR0_PE, %eax
|
||||||
|
7011: 66 83 c8 01 or $0x1,%ax
|
||||||
|
movl %eax, %cr0
|
||||||
|
7015: 0f 22 c0 mov %eax,%cr0
|
||||||
|
|
||||||
|
# Complete the transition to 32-bit protected mode by using a long jmp
|
||||||
|
# to reload %cs and %eip. The segment descriptors are set up with no
|
||||||
|
# translation, so that the mapping is still the identity mapping.
|
||||||
|
ljmpl $(SEG_KCODE<<3), $(start32)
|
||||||
|
7018: 66 ea 20 70 00 00 ljmpw $0x0,$0x7020
|
||||||
|
701e: 08 00 or %al,(%eax)
|
||||||
|
|
||||||
|
00007020 <start32>:
|
||||||
|
|
||||||
|
|
||||||
|
.code32 # Tell assembler to generate 32-bit code now.
|
||||||
|
start32:
|
||||||
|
# Set up the protected-mode data segment registers
|
||||||
|
movw $(SEG_KDATA<<3), %ax # Our data segment selector
|
||||||
|
7020: 66 b8 10 00 mov $0x10,%ax
|
||||||
|
movw %ax, %ds # -> DS: Data Segment
|
||||||
|
7024: 8e d8 mov %eax,%ds
|
||||||
|
movw %ax, %es # -> ES: Extra Segment
|
||||||
|
7026: 8e c0 mov %eax,%es
|
||||||
|
movw %ax, %ss # -> SS: Stack Segment
|
||||||
|
7028: 8e d0 mov %eax,%ss
|
||||||
|
movw $0, %ax # Zero segments not ready for use
|
||||||
|
702a: 66 b8 00 00 mov $0x0,%ax
|
||||||
|
movw %ax, %fs # -> FS
|
||||||
|
702e: 8e e0 mov %eax,%fs
|
||||||
|
movw %ax, %gs # -> GS
|
||||||
|
7030: 8e e8 mov %eax,%gs
|
||||||
|
|
||||||
|
# Turn on page size extension for 4Mbyte pages
|
||||||
|
movl %cr4, %eax
|
||||||
|
7032: 0f 20 e0 mov %cr4,%eax
|
||||||
|
orl $(CR4_PSE), %eax
|
||||||
|
7035: 83 c8 10 or $0x10,%eax
|
||||||
|
movl %eax, %cr4
|
||||||
|
7038: 0f 22 e0 mov %eax,%cr4
|
||||||
|
# Use entrypgdir as our initial page table
|
||||||
|
movl (start-12), %eax
|
||||||
|
703b: a1 f4 6f 00 00 mov 0x6ff4,%eax
|
||||||
|
movl %eax, %cr3
|
||||||
|
7040: 0f 22 d8 mov %eax,%cr3
|
||||||
|
# Turn on paging.
|
||||||
|
movl %cr0, %eax
|
||||||
|
7043: 0f 20 c0 mov %cr0,%eax
|
||||||
|
orl $(CR0_PE|CR0_PG|CR0_WP), %eax
|
||||||
|
7046: 0d 01 00 01 80 or $0x80010001,%eax
|
||||||
|
movl %eax, %cr0
|
||||||
|
704b: 0f 22 c0 mov %eax,%cr0
|
||||||
|
|
||||||
|
# Switch to the stack allocated by startothers()
|
||||||
|
movl (start-4), %esp
|
||||||
|
704e: 8b 25 fc 6f 00 00 mov 0x6ffc,%esp
|
||||||
|
# Call mpenter()
|
||||||
|
call *(start-8)
|
||||||
|
7054: ff 15 f8 6f 00 00 call *0x6ff8
|
||||||
|
|
||||||
|
0000705a <spin>:
|
||||||
|
|
||||||
|
# We should never return.
|
||||||
|
spin:
|
||||||
|
jmp spin
|
||||||
|
705a: eb fe jmp 705a <spin>
|
||||||
|
|
||||||
|
0000705c <gdt>:
|
||||||
|
...
|
||||||
|
7064: ff (bad)
|
||||||
|
7065: ff 00 incl (%eax)
|
||||||
|
7067: 00 00 add %al,(%eax)
|
||||||
|
7069: 9a cf 00 ff ff 00 00 lcall $0x0,$0xffff00cf
|
||||||
|
7070: 00 .byte 0x0
|
||||||
|
7071: 92 xchg %eax,%edx
|
||||||
|
7072: cf iret
|
||||||
|
...
|
||||||
|
|
||||||
|
00007074 <gdtdesc>:
|
||||||
|
7074: 17 pop %ss
|
||||||
|
7075: 00 5c 70 00 add %bl,0x0(%eax,%esi,2)
|
||||||
|
...
|
1
entryother.d
Normal file
1
entryother.d
Normal file
@ -0,0 +1 @@
|
|||||||
|
entryother.o: entryother.S asm.h memlayout.h mmu.h
|
BIN
entryother.o
Normal file
BIN
entryother.o
Normal file
Binary file not shown.
142
exec.c
Normal file
142
exec.c
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
#include "types.h"
|
||||||
|
#include "param.h"
|
||||||
|
#include "memlayout.h"
|
||||||
|
#include "mmu.h"
|
||||||
|
#include "proc.h"
|
||||||
|
#include "defs.h"
|
||||||
|
#include "x86.h"
|
||||||
|
#include "elf.h"
|
||||||
|
|
||||||
|
void cleanupexec(pde_t * pgdir, struct inode *ip) {
|
||||||
|
if (pgdir) {
|
||||||
|
freevm(pgdir);
|
||||||
|
}
|
||||||
|
if (ip) {
|
||||||
|
iunlockput(ip);
|
||||||
|
end_op();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int exec(char *path, char **argv) {
|
||||||
|
char *s, *last;
|
||||||
|
int i, off;
|
||||||
|
uint argc, sz, sp, ustack[3 + MAXARG + 1];
|
||||||
|
struct elfhdr elf;
|
||||||
|
struct inode *ip;
|
||||||
|
struct proghdr ph;
|
||||||
|
pde_t *pgdir, *oldpgdir;
|
||||||
|
struct proc *curproc = myproc();
|
||||||
|
|
||||||
|
begin_op();
|
||||||
|
|
||||||
|
if ((ip = namei(path)) == 0) {
|
||||||
|
end_op();
|
||||||
|
cprintf("exec: fail\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ilock(ip);
|
||||||
|
pgdir = 0;
|
||||||
|
|
||||||
|
// Check ELF header
|
||||||
|
if (readi(ip, (char*)&elf, 0, sizeof(elf)) != sizeof(elf)) {
|
||||||
|
cleanupexec(pgdir, ip);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (elf.magic != ELF_MAGIC) {
|
||||||
|
cleanupexec(pgdir, ip);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((pgdir = setupkvm()) == 0) {
|
||||||
|
cleanupexec(pgdir, ip);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load program into memory.
|
||||||
|
sz = 0;
|
||||||
|
for (i = 0, off = elf.phoff; i < elf.phnum; i++, off += sizeof(ph)) {
|
||||||
|
if (readi(ip, (char*)&ph, off, sizeof(ph)) != sizeof(ph)) {
|
||||||
|
cleanupexec(pgdir, ip);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (ph.type != ELF_PROG_LOAD) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ph.memsz < ph.filesz) {
|
||||||
|
cleanupexec(pgdir, ip);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (ph.vaddr + ph.memsz < ph.vaddr) {
|
||||||
|
cleanupexec(pgdir, ip);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if ((sz = allocuvm(pgdir, sz, ph.vaddr + ph.memsz)) == 0) {
|
||||||
|
cleanupexec(pgdir, ip);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (ph.vaddr % PGSIZE != 0) {
|
||||||
|
cleanupexec(pgdir, ip);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (loaduvm(pgdir, (char*)ph.vaddr, ip, ph.off, ph.filesz) < 0) {
|
||||||
|
cleanupexec(pgdir, ip);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iunlockput(ip);
|
||||||
|
end_op();
|
||||||
|
ip = 0;
|
||||||
|
|
||||||
|
// Allocate two pages at the next page boundary.
|
||||||
|
// Make the first inaccessible. Use the second as the user stack.
|
||||||
|
sz = PGROUNDUP(sz);
|
||||||
|
if ((sz = allocuvm(pgdir, sz, sz + 2 * PGSIZE)) == 0) {
|
||||||
|
cleanupexec(pgdir, ip);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
clearpteu(pgdir, (char*)(sz - 2 * PGSIZE));
|
||||||
|
sp = sz;
|
||||||
|
|
||||||
|
// Push argument strings, prepare rest of stack in ustack.
|
||||||
|
for (argc = 0; argv[argc]; argc++) {
|
||||||
|
if (argc >= MAXARG) {
|
||||||
|
cleanupexec(pgdir, ip);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
sp = (sp - (strlen(argv[argc]) + 1)) & ~3;
|
||||||
|
if (copyout(pgdir, sp, argv[argc], strlen(argv[argc]) + 1) < 0) {
|
||||||
|
cleanupexec(pgdir, ip);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ustack[3 + argc] = sp;
|
||||||
|
}
|
||||||
|
ustack[3 + argc] = 0;
|
||||||
|
|
||||||
|
ustack[0] = 0xffffffff; // fake return PC
|
||||||
|
ustack[1] = argc;
|
||||||
|
ustack[2] = sp - (argc + 1) * 4; // argv pointer
|
||||||
|
|
||||||
|
sp -= (3 + argc + 1) * 4;
|
||||||
|
if (copyout(pgdir, sp, ustack, (3 + argc + 1) * 4) < 0) {
|
||||||
|
cleanupexec(pgdir, ip);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save program name for debugging.
|
||||||
|
for (last = s = path; *s; s++) {
|
||||||
|
if (*s == '/') {
|
||||||
|
last = s + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
safestrcpy(curproc->name, last, sizeof(curproc->name));
|
||||||
|
|
||||||
|
// Commit to the user image.
|
||||||
|
oldpgdir = curproc->pgdir;
|
||||||
|
curproc->pgdir = pgdir;
|
||||||
|
curproc->sz = sz;
|
||||||
|
curproc->tf->eip = elf.entry; // main
|
||||||
|
curproc->tf->esp = sp;
|
||||||
|
switchuvm(curproc);
|
||||||
|
freevm(oldpgdir);
|
||||||
|
return 0;
|
||||||
|
}
|
2
exec.d
Normal file
2
exec.d
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
exec.o: exec.c /usr/include/stdc-predef.h types.h param.h memlayout.h \
|
||||||
|
mmu.h proc.h defs.h x86.h elf.h
|
4
fcntl.h
Normal file
4
fcntl.h
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#define O_RDONLY 0x000
|
||||||
|
#define O_WRONLY 0x001
|
||||||
|
#define O_RDWR 0x002
|
||||||
|
#define O_CREATE 0x200
|
155
file.c
Normal file
155
file.c
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
//
|
||||||
|
// File descriptors
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "defs.h"
|
||||||
|
#include "param.h"
|
||||||
|
#include "fs.h"
|
||||||
|
#include "spinlock.h"
|
||||||
|
#include "sleeplock.h"
|
||||||
|
#include "file.h"
|
||||||
|
|
||||||
|
struct devsw devsw[NDEV];
|
||||||
|
struct {
|
||||||
|
struct spinlock lock;
|
||||||
|
struct file file[NFILE];
|
||||||
|
} ftable;
|
||||||
|
|
||||||
|
void fileinit(void) {
|
||||||
|
initlock(&ftable.lock, "ftable");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate a file structure.
|
||||||
|
struct file* filealloc(void) {
|
||||||
|
struct file *f;
|
||||||
|
|
||||||
|
acquire(&ftable.lock);
|
||||||
|
for (f = ftable.file; f < ftable.file + NFILE; f++) {
|
||||||
|
if (f->ref == 0) {
|
||||||
|
f->ref = 1;
|
||||||
|
release(&ftable.lock);
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
release(&ftable.lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment ref count for file f.
|
||||||
|
struct file* filedup(struct file *f) {
|
||||||
|
acquire(&ftable.lock);
|
||||||
|
if (f->ref < 1) {
|
||||||
|
panic("filedup");
|
||||||
|
}
|
||||||
|
f->ref++;
|
||||||
|
release(&ftable.lock);
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close file f. (Decrement ref count, close when reaches 0.)
|
||||||
|
void fileclose(struct file *f) {
|
||||||
|
struct file ff;
|
||||||
|
|
||||||
|
acquire(&ftable.lock);
|
||||||
|
if (f->ref < 1) {
|
||||||
|
panic("fileclose");
|
||||||
|
}
|
||||||
|
if (--f->ref > 0) {
|
||||||
|
release(&ftable.lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ff = *f;
|
||||||
|
f->ref = 0;
|
||||||
|
f->type = FD_NONE;
|
||||||
|
release(&ftable.lock);
|
||||||
|
|
||||||
|
if (ff.type == FD_PIPE) {
|
||||||
|
pipeclose(ff.pipe, ff.writable);
|
||||||
|
}
|
||||||
|
else if (ff.type == FD_INODE) {
|
||||||
|
begin_op();
|
||||||
|
iput(ff.ip);
|
||||||
|
end_op();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get metadata about file f.
|
||||||
|
int filestat(struct file *f, struct stat *st) {
|
||||||
|
if (f->type == FD_INODE) {
|
||||||
|
ilock(f->ip);
|
||||||
|
stati(f->ip, st);
|
||||||
|
iunlock(f->ip);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read from file f.
|
||||||
|
int fileread(struct file *f, char *addr, int n) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (f->readable == 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (f->type == FD_PIPE) {
|
||||||
|
return piperead(f->pipe, addr, n);
|
||||||
|
}
|
||||||
|
if (f->type == FD_INODE) {
|
||||||
|
ilock(f->ip);
|
||||||
|
if ((r = readi(f->ip, addr, f->off, n)) > 0) {
|
||||||
|
f->off += r;
|
||||||
|
}
|
||||||
|
iunlock(f->ip);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
panic("fileread");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Write to file f.
|
||||||
|
int filewrite(struct file *f, char *addr, int n) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (f->writable == 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (f->type == FD_PIPE) {
|
||||||
|
return pipewrite(f->pipe, addr, n);
|
||||||
|
}
|
||||||
|
if (f->type == FD_INODE) {
|
||||||
|
// write a few blocks at a time to avoid exceeding
|
||||||
|
// the maximum log transaction size, including
|
||||||
|
// i-node, indirect block, allocation blocks,
|
||||||
|
// and 2 blocks of slop for non-aligned writes.
|
||||||
|
// this really belongs lower down, since writei()
|
||||||
|
// might be writing a device like the console.
|
||||||
|
int max = ((MAXOPBLOCKS - 1 - 1 - 2) / 2) * 512;
|
||||||
|
int i = 0;
|
||||||
|
while (i < n) {
|
||||||
|
int n1 = n - i;
|
||||||
|
if (n1 > max) {
|
||||||
|
n1 = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
begin_op();
|
||||||
|
ilock(f->ip);
|
||||||
|
if ((r = writei(f->ip, addr + i, f->off, n1)) > 0) {
|
||||||
|
f->off += r;
|
||||||
|
}
|
||||||
|
iunlock(f->ip);
|
||||||
|
end_op();
|
||||||
|
|
||||||
|
if (r < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (r != n1) {
|
||||||
|
panic("short filewrite");
|
||||||
|
}
|
||||||
|
i += r;
|
||||||
|
}
|
||||||
|
return i == n ? n : -1;
|
||||||
|
}
|
||||||
|
panic("filewrite");
|
||||||
|
}
|
||||||
|
|
2
file.d
Normal file
2
file.d
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
file.o: file.c /usr/include/stdc-predef.h types.h defs.h param.h fs.h \
|
||||||
|
spinlock.h sleeplock.h file.h
|
37
file.h
Normal file
37
file.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
struct file {
|
||||||
|
enum { FD_NONE, FD_PIPE, FD_INODE } type;
|
||||||
|
int ref; // reference count
|
||||||
|
char readable;
|
||||||
|
char writable;
|
||||||
|
struct pipe *pipe;
|
||||||
|
struct inode *ip;
|
||||||
|
uint off;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// in-memory copy of an inode
|
||||||
|
struct inode {
|
||||||
|
uint dev; // Device number
|
||||||
|
uint inum; // Inode number
|
||||||
|
int ref; // Reference count
|
||||||
|
struct sleeplock lock; // protects everything below here
|
||||||
|
int valid; // inode has been read from disk?
|
||||||
|
|
||||||
|
short type; // copy of disk inode
|
||||||
|
short major;
|
||||||
|
short minor;
|
||||||
|
short nlink;
|
||||||
|
uint size;
|
||||||
|
uint addrs[NDIRECT + 1];
|
||||||
|
};
|
||||||
|
|
||||||
|
// table mapping major device number to
|
||||||
|
// device functions
|
||||||
|
struct devsw {
|
||||||
|
int (*read)(struct inode*, char*, int);
|
||||||
|
int (*write)(struct inode*, char*, int);
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct devsw devsw[];
|
||||||
|
|
||||||
|
#define CONSOLE 1
|
673
forktest.asm
Normal file
673
forktest.asm
Normal file
@ -0,0 +1,673 @@
|
|||||||
|
|
||||||
|
_forktest: file format elf32-i386
|
||||||
|
|
||||||
|
|
||||||
|
Disassembly of section .text:
|
||||||
|
|
||||||
|
00000000 <main>:
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(1, "fork test OK\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
0: 55 push %ebp
|
||||||
|
1: 89 e5 mov %esp,%ebp
|
||||||
|
3: 83 e4 f0 and $0xfffffff0,%esp
|
||||||
|
forktest();
|
||||||
|
6: e8 35 00 00 00 call 40 <forktest>
|
||||||
|
exit();
|
||||||
|
b: e8 63 03 00 00 call 373 <exit>
|
||||||
|
|
||||||
|
00000010 <printf>:
|
||||||
|
void printf(int fd, const char *s, ...) {
|
||||||
|
10: 55 push %ebp
|
||||||
|
11: 89 e5 mov %esp,%ebp
|
||||||
|
13: 53 push %ebx
|
||||||
|
14: 83 ec 10 sub $0x10,%esp
|
||||||
|
17: 8b 5d 0c mov 0xc(%ebp),%ebx
|
||||||
|
write(fd, s, strlen(s));
|
||||||
|
1a: 53 push %ebx
|
||||||
|
1b: e8 90 01 00 00 call 1b0 <strlen>
|
||||||
|
20: 83 c4 0c add $0xc,%esp
|
||||||
|
23: 50 push %eax
|
||||||
|
24: 53 push %ebx
|
||||||
|
25: ff 75 08 push 0x8(%ebp)
|
||||||
|
28: e8 b6 03 00 00 call 3e3 <write>
|
||||||
|
}
|
||||||
|
2d: 8b 5d fc mov -0x4(%ebp),%ebx
|
||||||
|
30: 83 c4 10 add $0x10,%esp
|
||||||
|
33: c9 leave
|
||||||
|
34: c3 ret
|
||||||
|
35: 8d b4 26 00 00 00 00 lea 0x0(%esi,%eiz,1),%esi
|
||||||
|
3c: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
|
||||||
|
|
||||||
|
00000040 <forktest>:
|
||||||
|
void forktest(void) {
|
||||||
|
40: 55 push %ebp
|
||||||
|
41: 89 e5 mov %esp,%ebp
|
||||||
|
43: 53 push %ebx
|
||||||
|
for (n = 0; n < N; n++) {
|
||||||
|
44: 31 db xor %ebx,%ebx
|
||||||
|
void forktest(void) {
|
||||||
|
46: 83 ec 10 sub $0x10,%esp
|
||||||
|
write(fd, s, strlen(s));
|
||||||
|
49: 68 2c 04 00 00 push $0x42c
|
||||||
|
4e: e8 5d 01 00 00 call 1b0 <strlen>
|
||||||
|
53: 83 c4 0c add $0xc,%esp
|
||||||
|
56: 50 push %eax
|
||||||
|
57: 68 2c 04 00 00 push $0x42c
|
||||||
|
5c: 6a 01 push $0x1
|
||||||
|
5e: e8 80 03 00 00 call 3e3 <write>
|
||||||
|
63: 83 c4 10 add $0x10,%esp
|
||||||
|
66: eb 19 jmp 81 <forktest+0x41>
|
||||||
|
68: 8d b4 26 00 00 00 00 lea 0x0(%esi,%eiz,1),%esi
|
||||||
|
6f: 90 nop
|
||||||
|
if (pid == 0) {
|
||||||
|
70: 74 58 je ca <forktest+0x8a>
|
||||||
|
for (n = 0; n < N; n++) {
|
||||||
|
72: 83 c3 01 add $0x1,%ebx
|
||||||
|
75: 81 fb e8 03 00 00 cmp $0x3e8,%ebx
|
||||||
|
7b: 0f 84 83 00 00 00 je 104 <forktest+0xc4>
|
||||||
|
pid = fork();
|
||||||
|
81: e8 e5 02 00 00 call 36b <fork>
|
||||||
|
if (pid < 0) {
|
||||||
|
86: 85 c0 test %eax,%eax
|
||||||
|
88: 79 e6 jns 70 <forktest+0x30>
|
||||||
|
for (; n > 0; n--) {
|
||||||
|
8a: 85 db test %ebx,%ebx
|
||||||
|
8c: 74 10 je 9e <forktest+0x5e>
|
||||||
|
8e: 66 90 xchg %ax,%ax
|
||||||
|
if (wait() < 0) {
|
||||||
|
90: e8 e6 02 00 00 call 37b <wait>
|
||||||
|
95: 85 c0 test %eax,%eax
|
||||||
|
97: 78 36 js cf <forktest+0x8f>
|
||||||
|
for (; n > 0; n--) {
|
||||||
|
99: 83 eb 01 sub $0x1,%ebx
|
||||||
|
9c: 75 f2 jne 90 <forktest+0x50>
|
||||||
|
if (wait() != -1) {
|
||||||
|
9e: e8 d8 02 00 00 call 37b <wait>
|
||||||
|
a3: 83 f8 ff cmp $0xffffffff,%eax
|
||||||
|
a6: 75 49 jne f1 <forktest+0xb1>
|
||||||
|
write(fd, s, strlen(s));
|
||||||
|
a8: 83 ec 0c sub $0xc,%esp
|
||||||
|
ab: 68 5e 04 00 00 push $0x45e
|
||||||
|
b0: e8 fb 00 00 00 call 1b0 <strlen>
|
||||||
|
b5: 83 c4 0c add $0xc,%esp
|
||||||
|
b8: 50 push %eax
|
||||||
|
b9: 68 5e 04 00 00 push $0x45e
|
||||||
|
be: 6a 01 push $0x1
|
||||||
|
c0: e8 1e 03 00 00 call 3e3 <write>
|
||||||
|
}
|
||||||
|
c5: 8b 5d fc mov -0x4(%ebp),%ebx
|
||||||
|
c8: c9 leave
|
||||||
|
c9: c3 ret
|
||||||
|
exit();
|
||||||
|
ca: e8 a4 02 00 00 call 373 <exit>
|
||||||
|
write(fd, s, strlen(s));
|
||||||
|
cf: 83 ec 0c sub $0xc,%esp
|
||||||
|
d2: 68 37 04 00 00 push $0x437
|
||||||
|
d7: e8 d4 00 00 00 call 1b0 <strlen>
|
||||||
|
dc: 83 c4 0c add $0xc,%esp
|
||||||
|
df: 50 push %eax
|
||||||
|
e0: 68 37 04 00 00 push $0x437
|
||||||
|
e5: 6a 01 push $0x1
|
||||||
|
e7: e8 f7 02 00 00 call 3e3 <write>
|
||||||
|
exit();
|
||||||
|
ec: e8 82 02 00 00 call 373 <exit>
|
||||||
|
printf(1, "wait got too many\n");
|
||||||
|
f1: 52 push %edx
|
||||||
|
f2: 52 push %edx
|
||||||
|
f3: 68 4b 04 00 00 push $0x44b
|
||||||
|
f8: 6a 01 push $0x1
|
||||||
|
fa: e8 11 ff ff ff call 10 <printf>
|
||||||
|
exit();
|
||||||
|
ff: e8 6f 02 00 00 call 373 <exit>
|
||||||
|
printf(1, "fork claimed to work N times!\n", N);
|
||||||
|
104: 50 push %eax
|
||||||
|
105: 68 e8 03 00 00 push $0x3e8
|
||||||
|
10a: 68 6c 04 00 00 push $0x46c
|
||||||
|
10f: 6a 01 push $0x1
|
||||||
|
111: e8 fa fe ff ff call 10 <printf>
|
||||||
|
exit();
|
||||||
|
116: e8 58 02 00 00 call 373 <exit>
|
||||||
|
11b: 66 90 xchg %ax,%ax
|
||||||
|
11d: 66 90 xchg %ax,%ax
|
||||||
|
11f: 90 nop
|
||||||
|
|
||||||
|
00000120 <strcpy>:
|
||||||
|
#include "stat.h"
|
||||||
|
#include "fcntl.h"
|
||||||
|
#include "user.h"
|
||||||
|
#include "x86.h"
|
||||||
|
|
||||||
|
char*strcpy(char *s, const char *t) {
|
||||||
|
120: 55 push %ebp
|
||||||
|
char *os;
|
||||||
|
|
||||||
|
os = s;
|
||||||
|
while ((*s++ = *t++) != 0) {
|
||||||
|
121: 31 c0 xor %eax,%eax
|
||||||
|
char*strcpy(char *s, const char *t) {
|
||||||
|
123: 89 e5 mov %esp,%ebp
|
||||||
|
125: 53 push %ebx
|
||||||
|
126: 8b 4d 08 mov 0x8(%ebp),%ecx
|
||||||
|
129: 8b 5d 0c mov 0xc(%ebp),%ebx
|
||||||
|
12c: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
|
||||||
|
while ((*s++ = *t++) != 0) {
|
||||||
|
130: 0f b6 14 03 movzbl (%ebx,%eax,1),%edx
|
||||||
|
134: 88 14 01 mov %dl,(%ecx,%eax,1)
|
||||||
|
137: 83 c0 01 add $0x1,%eax
|
||||||
|
13a: 84 d2 test %dl,%dl
|
||||||
|
13c: 75 f2 jne 130 <strcpy+0x10>
|
||||||
|
;
|
||||||
|
}
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
13e: 8b 5d fc mov -0x4(%ebp),%ebx
|
||||||
|
141: 89 c8 mov %ecx,%eax
|
||||||
|
143: c9 leave
|
||||||
|
144: c3 ret
|
||||||
|
145: 8d b4 26 00 00 00 00 lea 0x0(%esi,%eiz,1),%esi
|
||||||
|
14c: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
|
||||||
|
|
||||||
|
00000150 <strcmp>:
|
||||||
|
|
||||||
|
int strcmp(const char *p, const char *q) {
|
||||||
|
150: 55 push %ebp
|
||||||
|
151: 89 e5 mov %esp,%ebp
|
||||||
|
153: 53 push %ebx
|
||||||
|
154: 8b 55 08 mov 0x8(%ebp),%edx
|
||||||
|
157: 8b 4d 0c mov 0xc(%ebp),%ecx
|
||||||
|
while (*p && *p == *q) {
|
||||||
|
15a: 0f b6 02 movzbl (%edx),%eax
|
||||||
|
15d: 84 c0 test %al,%al
|
||||||
|
15f: 75 17 jne 178 <strcmp+0x28>
|
||||||
|
161: eb 3a jmp 19d <strcmp+0x4d>
|
||||||
|
163: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
|
||||||
|
167: 90 nop
|
||||||
|
168: 0f b6 42 01 movzbl 0x1(%edx),%eax
|
||||||
|
p++, q++;
|
||||||
|
16c: 83 c2 01 add $0x1,%edx
|
||||||
|
16f: 8d 59 01 lea 0x1(%ecx),%ebx
|
||||||
|
while (*p && *p == *q) {
|
||||||
|
172: 84 c0 test %al,%al
|
||||||
|
174: 74 1a je 190 <strcmp+0x40>
|
||||||
|
p++, q++;
|
||||||
|
176: 89 d9 mov %ebx,%ecx
|
||||||
|
while (*p && *p == *q) {
|
||||||
|
178: 0f b6 19 movzbl (%ecx),%ebx
|
||||||
|
17b: 38 c3 cmp %al,%bl
|
||||||
|
17d: 74 e9 je 168 <strcmp+0x18>
|
||||||
|
}
|
||||||
|
return (uchar) * p - (uchar) * q;
|
||||||
|
17f: 29 d8 sub %ebx,%eax
|
||||||
|
}
|
||||||
|
181: 8b 5d fc mov -0x4(%ebp),%ebx
|
||||||
|
184: c9 leave
|
||||||
|
185: c3 ret
|
||||||
|
186: 8d b4 26 00 00 00 00 lea 0x0(%esi,%eiz,1),%esi
|
||||||
|
18d: 8d 76 00 lea 0x0(%esi),%esi
|
||||||
|
return (uchar) * p - (uchar) * q;
|
||||||
|
190: 0f b6 59 01 movzbl 0x1(%ecx),%ebx
|
||||||
|
194: 31 c0 xor %eax,%eax
|
||||||
|
196: 29 d8 sub %ebx,%eax
|
||||||
|
}
|
||||||
|
198: 8b 5d fc mov -0x4(%ebp),%ebx
|
||||||
|
19b: c9 leave
|
||||||
|
19c: c3 ret
|
||||||
|
return (uchar) * p - (uchar) * q;
|
||||||
|
19d: 0f b6 19 movzbl (%ecx),%ebx
|
||||||
|
1a0: 31 c0 xor %eax,%eax
|
||||||
|
1a2: eb db jmp 17f <strcmp+0x2f>
|
||||||
|
1a4: 8d b4 26 00 00 00 00 lea 0x0(%esi,%eiz,1),%esi
|
||||||
|
1ab: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
|
||||||
|
1af: 90 nop
|
||||||
|
|
||||||
|
000001b0 <strlen>:
|
||||||
|
|
||||||
|
uint strlen(const char *s) {
|
||||||
|
1b0: 55 push %ebp
|
||||||
|
1b1: 89 e5 mov %esp,%ebp
|
||||||
|
1b3: 8b 55 08 mov 0x8(%ebp),%edx
|
||||||
|
int n;
|
||||||
|
|
||||||
|
for (n = 0; s[n]; n++) {
|
||||||
|
1b6: 80 3a 00 cmpb $0x0,(%edx)
|
||||||
|
1b9: 74 15 je 1d0 <strlen+0x20>
|
||||||
|
1bb: 31 c0 xor %eax,%eax
|
||||||
|
1bd: 8d 76 00 lea 0x0(%esi),%esi
|
||||||
|
1c0: 83 c0 01 add $0x1,%eax
|
||||||
|
1c3: 80 3c 02 00 cmpb $0x0,(%edx,%eax,1)
|
||||||
|
1c7: 89 c1 mov %eax,%ecx
|
||||||
|
1c9: 75 f5 jne 1c0 <strlen+0x10>
|
||||||
|
;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
1cb: 89 c8 mov %ecx,%eax
|
||||||
|
1cd: 5d pop %ebp
|
||||||
|
1ce: c3 ret
|
||||||
|
1cf: 90 nop
|
||||||
|
for (n = 0; s[n]; n++) {
|
||||||
|
1d0: 31 c9 xor %ecx,%ecx
|
||||||
|
}
|
||||||
|
1d2: 5d pop %ebp
|
||||||
|
1d3: 89 c8 mov %ecx,%eax
|
||||||
|
1d5: c3 ret
|
||||||
|
1d6: 8d b4 26 00 00 00 00 lea 0x0(%esi,%eiz,1),%esi
|
||||||
|
1dd: 8d 76 00 lea 0x0(%esi),%esi
|
||||||
|
|
||||||
|
000001e0 <memset>:
|
||||||
|
|
||||||
|
void* memset(void *dst, int c, uint n) {
|
||||||
|
1e0: 55 push %ebp
|
||||||
|
1e1: 89 e5 mov %esp,%ebp
|
||||||
|
1e3: 57 push %edi
|
||||||
|
1e4: 8b 55 08 mov 0x8(%ebp),%edx
|
||||||
|
"d" (port), "0" (addr), "1" (cnt) :
|
||||||
|
"cc");
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void stosb(void *addr, int data, int cnt) {
|
||||||
|
asm volatile ("cld; rep stosb" :
|
||||||
|
1e7: 8b 4d 10 mov 0x10(%ebp),%ecx
|
||||||
|
1ea: 8b 45 0c mov 0xc(%ebp),%eax
|
||||||
|
1ed: 89 d7 mov %edx,%edi
|
||||||
|
1ef: fc cld
|
||||||
|
1f0: f3 aa rep stos %al,%es:(%edi)
|
||||||
|
stosb(dst, c, n);
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
1f2: 8b 7d fc mov -0x4(%ebp),%edi
|
||||||
|
1f5: 89 d0 mov %edx,%eax
|
||||||
|
1f7: c9 leave
|
||||||
|
1f8: c3 ret
|
||||||
|
1f9: 8d b4 26 00 00 00 00 lea 0x0(%esi,%eiz,1),%esi
|
||||||
|
|
||||||
|
00000200 <strchr>:
|
||||||
|
|
||||||
|
char* strchr(const char *s, char c) {
|
||||||
|
200: 55 push %ebp
|
||||||
|
201: 89 e5 mov %esp,%ebp
|
||||||
|
203: 8b 45 08 mov 0x8(%ebp),%eax
|
||||||
|
206: 0f b6 4d 0c movzbl 0xc(%ebp),%ecx
|
||||||
|
for (; *s; s++) {
|
||||||
|
20a: 0f b6 10 movzbl (%eax),%edx
|
||||||
|
20d: 84 d2 test %dl,%dl
|
||||||
|
20f: 75 12 jne 223 <strchr+0x23>
|
||||||
|
211: eb 1d jmp 230 <strchr+0x30>
|
||||||
|
213: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
|
||||||
|
217: 90 nop
|
||||||
|
218: 0f b6 50 01 movzbl 0x1(%eax),%edx
|
||||||
|
21c: 83 c0 01 add $0x1,%eax
|
||||||
|
21f: 84 d2 test %dl,%dl
|
||||||
|
221: 74 0d je 230 <strchr+0x30>
|
||||||
|
if (*s == c) {
|
||||||
|
223: 38 d1 cmp %dl,%cl
|
||||||
|
225: 75 f1 jne 218 <strchr+0x18>
|
||||||
|
return (char*)s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
227: 5d pop %ebp
|
||||||
|
228: c3 ret
|
||||||
|
229: 8d b4 26 00 00 00 00 lea 0x0(%esi,%eiz,1),%esi
|
||||||
|
return 0;
|
||||||
|
230: 31 c0 xor %eax,%eax
|
||||||
|
}
|
||||||
|
232: 5d pop %ebp
|
||||||
|
233: c3 ret
|
||||||
|
234: 8d b4 26 00 00 00 00 lea 0x0(%esi,%eiz,1),%esi
|
||||||
|
23b: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
|
||||||
|
23f: 90 nop
|
||||||
|
|
||||||
|
00000240 <gets>:
|
||||||
|
|
||||||
|
char* gets(char *buf, int max) {
|
||||||
|
240: 55 push %ebp
|
||||||
|
241: 89 e5 mov %esp,%ebp
|
||||||
|
243: 57 push %edi
|
||||||
|
244: 56 push %esi
|
||||||
|
int i, cc;
|
||||||
|
char c;
|
||||||
|
|
||||||
|
for (i = 0; i + 1 < max;) {
|
||||||
|
cc = read(0, &c, 1);
|
||||||
|
245: 8d 7d e7 lea -0x19(%ebp),%edi
|
||||||
|
char* gets(char *buf, int max) {
|
||||||
|
248: 53 push %ebx
|
||||||
|
for (i = 0; i + 1 < max;) {
|
||||||
|
249: 31 db xor %ebx,%ebx
|
||||||
|
char* gets(char *buf, int max) {
|
||||||
|
24b: 83 ec 1c sub $0x1c,%esp
|
||||||
|
for (i = 0; i + 1 < max;) {
|
||||||
|
24e: eb 27 jmp 277 <gets+0x37>
|
||||||
|
cc = read(0, &c, 1);
|
||||||
|
250: 83 ec 04 sub $0x4,%esp
|
||||||
|
253: 6a 01 push $0x1
|
||||||
|
255: 57 push %edi
|
||||||
|
256: 6a 00 push $0x0
|
||||||
|
258: e8 2e 01 00 00 call 38b <read>
|
||||||
|
if (cc < 1) {
|
||||||
|
25d: 83 c4 10 add $0x10,%esp
|
||||||
|
260: 85 c0 test %eax,%eax
|
||||||
|
262: 7e 1d jle 281 <gets+0x41>
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buf[i++] = c;
|
||||||
|
264: 0f b6 45 e7 movzbl -0x19(%ebp),%eax
|
||||||
|
268: 8b 55 08 mov 0x8(%ebp),%edx
|
||||||
|
26b: 88 44 1a ff mov %al,-0x1(%edx,%ebx,1)
|
||||||
|
if (c == '\n' || c == '\r') {
|
||||||
|
26f: 3c 0a cmp $0xa,%al
|
||||||
|
271: 74 1d je 290 <gets+0x50>
|
||||||
|
273: 3c 0d cmp $0xd,%al
|
||||||
|
275: 74 19 je 290 <gets+0x50>
|
||||||
|
for (i = 0; i + 1 < max;) {
|
||||||
|
277: 89 de mov %ebx,%esi
|
||||||
|
279: 83 c3 01 add $0x1,%ebx
|
||||||
|
27c: 3b 5d 0c cmp 0xc(%ebp),%ebx
|
||||||
|
27f: 7c cf jl 250 <gets+0x10>
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf[i] = '\0';
|
||||||
|
281: 8b 45 08 mov 0x8(%ebp),%eax
|
||||||
|
284: c6 04 30 00 movb $0x0,(%eax,%esi,1)
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
288: 8d 65 f4 lea -0xc(%ebp),%esp
|
||||||
|
28b: 5b pop %ebx
|
||||||
|
28c: 5e pop %esi
|
||||||
|
28d: 5f pop %edi
|
||||||
|
28e: 5d pop %ebp
|
||||||
|
28f: c3 ret
|
||||||
|
buf[i] = '\0';
|
||||||
|
290: 8b 45 08 mov 0x8(%ebp),%eax
|
||||||
|
293: 89 de mov %ebx,%esi
|
||||||
|
295: c6 04 30 00 movb $0x0,(%eax,%esi,1)
|
||||||
|
}
|
||||||
|
299: 8d 65 f4 lea -0xc(%ebp),%esp
|
||||||
|
29c: 5b pop %ebx
|
||||||
|
29d: 5e pop %esi
|
||||||
|
29e: 5f pop %edi
|
||||||
|
29f: 5d pop %ebp
|
||||||
|
2a0: c3 ret
|
||||||
|
2a1: 8d b4 26 00 00 00 00 lea 0x0(%esi,%eiz,1),%esi
|
||||||
|
2a8: 8d b4 26 00 00 00 00 lea 0x0(%esi,%eiz,1),%esi
|
||||||
|
2af: 90 nop
|
||||||
|
|
||||||
|
000002b0 <stat>:
|
||||||
|
|
||||||
|
int stat(const char *n, struct stat *st) {
|
||||||
|
2b0: 55 push %ebp
|
||||||
|
2b1: 89 e5 mov %esp,%ebp
|
||||||
|
2b3: 56 push %esi
|
||||||
|
2b4: 53 push %ebx
|
||||||
|
int fd;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
fd = open(n, O_RDONLY);
|
||||||
|
2b5: 83 ec 08 sub $0x8,%esp
|
||||||
|
2b8: 6a 00 push $0x0
|
||||||
|
2ba: ff 75 08 push 0x8(%ebp)
|
||||||
|
2bd: e8 19 01 00 00 call 3db <open>
|
||||||
|
if (fd < 0) {
|
||||||
|
2c2: 83 c4 10 add $0x10,%esp
|
||||||
|
2c5: 85 c0 test %eax,%eax
|
||||||
|
2c7: 78 27 js 2f0 <stat+0x40>
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
r = fstat(fd, st);
|
||||||
|
2c9: 83 ec 08 sub $0x8,%esp
|
||||||
|
2cc: ff 75 0c push 0xc(%ebp)
|
||||||
|
2cf: 89 c3 mov %eax,%ebx
|
||||||
|
2d1: 50 push %eax
|
||||||
|
2d2: e8 cc 00 00 00 call 3a3 <fstat>
|
||||||
|
close(fd);
|
||||||
|
2d7: 89 1c 24 mov %ebx,(%esp)
|
||||||
|
r = fstat(fd, st);
|
||||||
|
2da: 89 c6 mov %eax,%esi
|
||||||
|
close(fd);
|
||||||
|
2dc: e8 2a 01 00 00 call 40b <close>
|
||||||
|
return r;
|
||||||
|
2e1: 83 c4 10 add $0x10,%esp
|
||||||
|
}
|
||||||
|
2e4: 8d 65 f8 lea -0x8(%ebp),%esp
|
||||||
|
2e7: 89 f0 mov %esi,%eax
|
||||||
|
2e9: 5b pop %ebx
|
||||||
|
2ea: 5e pop %esi
|
||||||
|
2eb: 5d pop %ebp
|
||||||
|
2ec: c3 ret
|
||||||
|
2ed: 8d 76 00 lea 0x0(%esi),%esi
|
||||||
|
return -1;
|
||||||
|
2f0: be ff ff ff ff mov $0xffffffff,%esi
|
||||||
|
2f5: eb ed jmp 2e4 <stat+0x34>
|
||||||
|
2f7: 8d b4 26 00 00 00 00 lea 0x0(%esi,%eiz,1),%esi
|
||||||
|
2fe: 66 90 xchg %ax,%ax
|
||||||
|
|
||||||
|
00000300 <atoi>:
|
||||||
|
|
||||||
|
int atoi(const char *s) {
|
||||||
|
300: 55 push %ebp
|
||||||
|
301: 89 e5 mov %esp,%ebp
|
||||||
|
303: 53 push %ebx
|
||||||
|
304: 8b 55 08 mov 0x8(%ebp),%edx
|
||||||
|
int n;
|
||||||
|
|
||||||
|
n = 0;
|
||||||
|
while ('0' <= *s && *s <= '9') {
|
||||||
|
307: 0f be 02 movsbl (%edx),%eax
|
||||||
|
30a: 8d 48 d0 lea -0x30(%eax),%ecx
|
||||||
|
30d: 80 f9 09 cmp $0x9,%cl
|
||||||
|
n = 0;
|
||||||
|
310: b9 00 00 00 00 mov $0x0,%ecx
|
||||||
|
while ('0' <= *s && *s <= '9') {
|
||||||
|
315: 77 1e ja 335 <atoi+0x35>
|
||||||
|
317: 8d b4 26 00 00 00 00 lea 0x0(%esi,%eiz,1),%esi
|
||||||
|
31e: 66 90 xchg %ax,%ax
|
||||||
|
n = n * 10 + *s++ - '0';
|
||||||
|
320: 83 c2 01 add $0x1,%edx
|
||||||
|
323: 8d 0c 89 lea (%ecx,%ecx,4),%ecx
|
||||||
|
326: 8d 4c 48 d0 lea -0x30(%eax,%ecx,2),%ecx
|
||||||
|
while ('0' <= *s && *s <= '9') {
|
||||||
|
32a: 0f be 02 movsbl (%edx),%eax
|
||||||
|
32d: 8d 58 d0 lea -0x30(%eax),%ebx
|
||||||
|
330: 80 fb 09 cmp $0x9,%bl
|
||||||
|
333: 76 eb jbe 320 <atoi+0x20>
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
335: 8b 5d fc mov -0x4(%ebp),%ebx
|
||||||
|
338: 89 c8 mov %ecx,%eax
|
||||||
|
33a: c9 leave
|
||||||
|
33b: c3 ret
|
||||||
|
33c: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
|
||||||
|
|
||||||
|
00000340 <memmove>:
|
||||||
|
|
||||||
|
void* memmove(void *vdst, const void *vsrc, int n) {
|
||||||
|
340: 55 push %ebp
|
||||||
|
341: 89 e5 mov %esp,%ebp
|
||||||
|
343: 57 push %edi
|
||||||
|
344: 8b 45 10 mov 0x10(%ebp),%eax
|
||||||
|
347: 8b 55 08 mov 0x8(%ebp),%edx
|
||||||
|
34a: 56 push %esi
|
||||||
|
34b: 8b 75 0c mov 0xc(%ebp),%esi
|
||||||
|
char *dst;
|
||||||
|
const char *src;
|
||||||
|
|
||||||
|
dst = vdst;
|
||||||
|
src = vsrc;
|
||||||
|
while (n-- > 0) {
|
||||||
|
34e: 85 c0 test %eax,%eax
|
||||||
|
350: 7e 13 jle 365 <memmove+0x25>
|
||||||
|
352: 01 d0 add %edx,%eax
|
||||||
|
dst = vdst;
|
||||||
|
354: 89 d7 mov %edx,%edi
|
||||||
|
356: 8d b4 26 00 00 00 00 lea 0x0(%esi,%eiz,1),%esi
|
||||||
|
35d: 8d 76 00 lea 0x0(%esi),%esi
|
||||||
|
*dst++ = *src++;
|
||||||
|
360: a4 movsb %ds:(%esi),%es:(%edi)
|
||||||
|
while (n-- > 0) {
|
||||||
|
361: 39 f8 cmp %edi,%eax
|
||||||
|
363: 75 fb jne 360 <memmove+0x20>
|
||||||
|
}
|
||||||
|
return vdst;
|
||||||
|
}
|
||||||
|
365: 5e pop %esi
|
||||||
|
366: 89 d0 mov %edx,%eax
|
||||||
|
368: 5f pop %edi
|
||||||
|
369: 5d pop %ebp
|
||||||
|
36a: c3 ret
|
||||||
|
|
||||||
|
0000036b <fork>:
|
||||||
|
name: \
|
||||||
|
movl $SYS_ ## name, %eax; \
|
||||||
|
int $T_SYSCALL; \
|
||||||
|
ret
|
||||||
|
|
||||||
|
SYSCALL(fork)
|
||||||
|
36b: b8 01 00 00 00 mov $0x1,%eax
|
||||||
|
370: cd 40 int $0x40
|
||||||
|
372: c3 ret
|
||||||
|
|
||||||
|
00000373 <exit>:
|
||||||
|
SYSCALL(exit)
|
||||||
|
373: b8 02 00 00 00 mov $0x2,%eax
|
||||||
|
378: cd 40 int $0x40
|
||||||
|
37a: c3 ret
|
||||||
|
|
||||||
|
0000037b <wait>:
|
||||||
|
SYSCALL(wait)
|
||||||
|
37b: b8 03 00 00 00 mov $0x3,%eax
|
||||||
|
380: cd 40 int $0x40
|
||||||
|
382: c3 ret
|
||||||
|
|
||||||
|
00000383 <pipe>:
|
||||||
|
SYSCALL(pipe)
|
||||||
|
383: b8 04 00 00 00 mov $0x4,%eax
|
||||||
|
388: cd 40 int $0x40
|
||||||
|
38a: c3 ret
|
||||||
|
|
||||||
|
0000038b <read>:
|
||||||
|
SYSCALL(read)
|
||||||
|
38b: b8 05 00 00 00 mov $0x5,%eax
|
||||||
|
390: cd 40 int $0x40
|
||||||
|
392: c3 ret
|
||||||
|
|
||||||
|
00000393 <kill>:
|
||||||
|
SYSCALL(kill)
|
||||||
|
393: b8 06 00 00 00 mov $0x6,%eax
|
||||||
|
398: cd 40 int $0x40
|
||||||
|
39a: c3 ret
|
||||||
|
|
||||||
|
0000039b <exec>:
|
||||||
|
SYSCALL(exec)
|
||||||
|
39b: b8 07 00 00 00 mov $0x7,%eax
|
||||||
|
3a0: cd 40 int $0x40
|
||||||
|
3a2: c3 ret
|
||||||
|
|
||||||
|
000003a3 <fstat>:
|
||||||
|
SYSCALL(fstat)
|
||||||
|
3a3: b8 08 00 00 00 mov $0x8,%eax
|
||||||
|
3a8: cd 40 int $0x40
|
||||||
|
3aa: c3 ret
|
||||||
|
|
||||||
|
000003ab <chdir>:
|
||||||
|
SYSCALL(chdir)
|
||||||
|
3ab: b8 09 00 00 00 mov $0x9,%eax
|
||||||
|
3b0: cd 40 int $0x40
|
||||||
|
3b2: c3 ret
|
||||||
|
|
||||||
|
000003b3 <dup>:
|
||||||
|
SYSCALL(dup)
|
||||||
|
3b3: b8 0a 00 00 00 mov $0xa,%eax
|
||||||
|
3b8: cd 40 int $0x40
|
||||||
|
3ba: c3 ret
|
||||||
|
|
||||||
|
000003bb <getpid>:
|
||||||
|
SYSCALL(getpid)
|
||||||
|
3bb: b8 0b 00 00 00 mov $0xb,%eax
|
||||||
|
3c0: cd 40 int $0x40
|
||||||
|
3c2: c3 ret
|
||||||
|
|
||||||
|
000003c3 <sbrk>:
|
||||||
|
SYSCALL(sbrk)
|
||||||
|
3c3: b8 0c 00 00 00 mov $0xc,%eax
|
||||||
|
3c8: cd 40 int $0x40
|
||||||
|
3ca: c3 ret
|
||||||
|
|
||||||
|
000003cb <sleep>:
|
||||||
|
SYSCALL(sleep)
|
||||||
|
3cb: b8 0d 00 00 00 mov $0xd,%eax
|
||||||
|
3d0: cd 40 int $0x40
|
||||||
|
3d2: c3 ret
|
||||||
|
|
||||||
|
000003d3 <uptime>:
|
||||||
|
SYSCALL(uptime)
|
||||||
|
3d3: b8 0e 00 00 00 mov $0xe,%eax
|
||||||
|
3d8: cd 40 int $0x40
|
||||||
|
3da: c3 ret
|
||||||
|
|
||||||
|
000003db <open>:
|
||||||
|
SYSCALL(open)
|
||||||
|
3db: b8 0f 00 00 00 mov $0xf,%eax
|
||||||
|
3e0: cd 40 int $0x40
|
||||||
|
3e2: c3 ret
|
||||||
|
|
||||||
|
000003e3 <write>:
|
||||||
|
SYSCALL(write)
|
||||||
|
3e3: b8 10 00 00 00 mov $0x10,%eax
|
||||||
|
3e8: cd 40 int $0x40
|
||||||
|
3ea: c3 ret
|
||||||
|
|
||||||
|
000003eb <mknod>:
|
||||||
|
SYSCALL(mknod)
|
||||||
|
3eb: b8 11 00 00 00 mov $0x11,%eax
|
||||||
|
3f0: cd 40 int $0x40
|
||||||
|
3f2: c3 ret
|
||||||
|
|
||||||
|
000003f3 <unlink>:
|
||||||
|
SYSCALL(unlink)
|
||||||
|
3f3: b8 12 00 00 00 mov $0x12,%eax
|
||||||
|
3f8: cd 40 int $0x40
|
||||||
|
3fa: c3 ret
|
||||||
|
|
||||||
|
000003fb <link>:
|
||||||
|
SYSCALL(link)
|
||||||
|
3fb: b8 13 00 00 00 mov $0x13,%eax
|
||||||
|
400: cd 40 int $0x40
|
||||||
|
402: c3 ret
|
||||||
|
|
||||||
|
00000403 <mkdir>:
|
||||||
|
SYSCALL(mkdir)
|
||||||
|
403: b8 14 00 00 00 mov $0x14,%eax
|
||||||
|
408: cd 40 int $0x40
|
||||||
|
40a: c3 ret
|
||||||
|
|
||||||
|
0000040b <close>:
|
||||||
|
SYSCALL(close)
|
||||||
|
40b: b8 15 00 00 00 mov $0x15,%eax
|
||||||
|
410: cd 40 int $0x40
|
||||||
|
412: c3 ret
|
||||||
|
|
||||||
|
00000413 <getch>:
|
||||||
|
SYSCALL(getch)
|
||||||
|
413: b8 16 00 00 00 mov $0x16,%eax
|
||||||
|
418: cd 40 int $0x40
|
||||||
|
41a: c3 ret
|
||||||
|
|
||||||
|
0000041b <greeting>:
|
||||||
|
SYSCALL(greeting)
|
||||||
|
41b: b8 17 00 00 00 mov $0x17,%eax
|
||||||
|
420: cd 40 int $0x40
|
||||||
|
422: c3 ret
|
||||||
|
|
||||||
|
00000423 <shutdown>:
|
||||||
|
SYSCALL(shutdown)
|
||||||
|
423: b8 18 00 00 00 mov $0x18,%eax
|
||||||
|
428: cd 40 int $0x40
|
||||||
|
42a: c3 ret
|
52
forktest.c
Normal file
52
forktest.c
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// Test that fork fails gracefully.
|
||||||
|
// Tiny executable so that the limit can be filling the proc table.
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "stat.h"
|
||||||
|
#include "user.h"
|
||||||
|
|
||||||
|
#define N 1000
|
||||||
|
|
||||||
|
void printf(int fd, const char *s, ...) {
|
||||||
|
write(fd, s, strlen(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
void forktest(void) {
|
||||||
|
int n, pid;
|
||||||
|
|
||||||
|
printf(1, "fork test\n");
|
||||||
|
|
||||||
|
for (n = 0; n < N; n++) {
|
||||||
|
pid = fork();
|
||||||
|
if (pid < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (pid == 0) {
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n == N) {
|
||||||
|
printf(1, "fork claimed to work N times!\n", N);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; n > 0; n--) {
|
||||||
|
if (wait() < 0) {
|
||||||
|
printf(1, "wait stopped early\n");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wait() != -1) {
|
||||||
|
printf(1, "wait got too many\n");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(1, "fork test OK\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
forktest();
|
||||||
|
exit();
|
||||||
|
}
|
1
forktest.d
Normal file
1
forktest.d
Normal file
@ -0,0 +1 @@
|
|||||||
|
forktest.o: forktest.c /usr/include/stdc-predef.h types.h stat.h user.h
|
BIN
forktest.o
Normal file
BIN
forktest.o
Normal file
Binary file not shown.
649
fs.c
Normal file
649
fs.c
Normal file
@ -0,0 +1,649 @@
|
|||||||
|
// File system implementation. Five layers:
|
||||||
|
// + Blocks: allocator for raw disk blocks.
|
||||||
|
// + Log: crash recovery for multi-step updates.
|
||||||
|
// + Files: inode allocator, reading, writing, metadata.
|
||||||
|
// + Directories: inode with special contents (list of other inodes!)
|
||||||
|
// + Names: paths like /usr/rtm/xv6/fs.c for convenient naming.
|
||||||
|
//
|
||||||
|
// This file contains the low-level file system manipulation
|
||||||
|
// routines. The (higher-level) system call implementations
|
||||||
|
// are in sysfile.c.
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "defs.h"
|
||||||
|
#include "param.h"
|
||||||
|
#include "stat.h"
|
||||||
|
#include "mmu.h"
|
||||||
|
#include "proc.h"
|
||||||
|
#include "spinlock.h"
|
||||||
|
#include "sleeplock.h"
|
||||||
|
#include "fs.h"
|
||||||
|
#include "buf.h"
|
||||||
|
#include "file.h"
|
||||||
|
|
||||||
|
#define min(a, b) ((a) < (b) ? (a) : (b))
|
||||||
|
|
||||||
|
static void itrunc(struct inode*);
|
||||||
|
// there should be one superblock per disk device, but we run with
|
||||||
|
// only one device
|
||||||
|
struct superblock sb;
|
||||||
|
|
||||||
|
// Read the super block.
|
||||||
|
void readsb(int dev, struct superblock *sb) {
|
||||||
|
struct buf *bp;
|
||||||
|
|
||||||
|
bp = bread(dev, 1);
|
||||||
|
memmove(sb, bp->data, sizeof(*sb));
|
||||||
|
brelse(bp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zero a block.
|
||||||
|
static void bzero(int dev, int bno) {
|
||||||
|
struct buf *bp;
|
||||||
|
|
||||||
|
bp = bread(dev, bno);
|
||||||
|
memset(bp->data, 0, BSIZE);
|
||||||
|
log_write(bp);
|
||||||
|
brelse(bp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blocks.
|
||||||
|
|
||||||
|
// Allocate a zeroed disk block.
|
||||||
|
static uint balloc(uint dev) {
|
||||||
|
int b, bi, m;
|
||||||
|
struct buf *bp;
|
||||||
|
|
||||||
|
bp = 0;
|
||||||
|
for (b = 0; b < sb.size; b += BPB) {
|
||||||
|
bp = bread(dev, BBLOCK(b, sb));
|
||||||
|
for (bi = 0; bi < BPB && b + bi < sb.size; bi++) {
|
||||||
|
m = 1 << (bi % 8);
|
||||||
|
if ((bp->data[bi / 8] & m) == 0) { // Is block free?
|
||||||
|
bp->data[bi / 8] |= m; // Mark block in use.
|
||||||
|
log_write(bp);
|
||||||
|
brelse(bp);
|
||||||
|
bzero(dev, b + bi);
|
||||||
|
return b + bi;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
brelse(bp);
|
||||||
|
}
|
||||||
|
panic("balloc: out of blocks");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free a disk block.
|
||||||
|
static void bfree(int dev, uint b) {
|
||||||
|
struct buf *bp;
|
||||||
|
int bi, m;
|
||||||
|
|
||||||
|
bp = bread(dev, BBLOCK(b, sb));
|
||||||
|
bi = b % BPB;
|
||||||
|
m = 1 << (bi % 8);
|
||||||
|
if ((bp->data[bi / 8] & m) == 0) {
|
||||||
|
panic("freeing free block");
|
||||||
|
}
|
||||||
|
bp->data[bi / 8] &= ~m;
|
||||||
|
log_write(bp);
|
||||||
|
brelse(bp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inodes.
|
||||||
|
//
|
||||||
|
// An inode describes a single unnamed file.
|
||||||
|
// The inode disk structure holds metadata: the file's type,
|
||||||
|
// its size, the number of links referring to it, and the
|
||||||
|
// list of blocks holding the file's content.
|
||||||
|
//
|
||||||
|
// The inodes are laid out sequentially on disk at
|
||||||
|
// sb.startinode. Each inode has a number, indicating its
|
||||||
|
// position on the disk.
|
||||||
|
//
|
||||||
|
// The kernel keeps a cache of in-use inodes in memory
|
||||||
|
// to provide a place for synchronizing access
|
||||||
|
// to inodes used by multiple processes. The cached
|
||||||
|
// inodes include book-keeping information that is
|
||||||
|
// not stored on disk: ip->ref and ip->valid.
|
||||||
|
//
|
||||||
|
// An inode and its in-memory representation go through a
|
||||||
|
// sequence of states before they can be used by the
|
||||||
|
// rest of the file system code.
|
||||||
|
//
|
||||||
|
// * Allocation: an inode is allocated if its type (on disk)
|
||||||
|
// is non-zero. ialloc() allocates, and iput() frees if
|
||||||
|
// the reference and link counts have fallen to zero.
|
||||||
|
//
|
||||||
|
// * Referencing in cache: an entry in the inode cache
|
||||||
|
// is free if ip->ref is zero. Otherwise ip->ref tracks
|
||||||
|
// the number of in-memory pointers to the entry (open
|
||||||
|
// files and current directories). iget() finds or
|
||||||
|
// creates a cache entry and increments its ref; iput()
|
||||||
|
// decrements ref.
|
||||||
|
//
|
||||||
|
// * Valid: the information (type, size, &c) in an inode
|
||||||
|
// cache entry is only correct when ip->valid is 1.
|
||||||
|
// ilock() reads the inode from
|
||||||
|
// the disk and sets ip->valid, while iput() clears
|
||||||
|
// ip->valid if ip->ref has fallen to zero.
|
||||||
|
//
|
||||||
|
// * Locked: file system code may only examine and modify
|
||||||
|
// the information in an inode and its content if it
|
||||||
|
// has first locked the inode.
|
||||||
|
//
|
||||||
|
// Thus a typical sequence is:
|
||||||
|
// ip = iget(dev, inum)
|
||||||
|
// ilock(ip)
|
||||||
|
// ... examine and modify ip->xxx ...
|
||||||
|
// iunlock(ip)
|
||||||
|
// iput(ip)
|
||||||
|
//
|
||||||
|
// ilock() is separate from iget() so that system calls can
|
||||||
|
// get a long-term reference to an inode (as for an open file)
|
||||||
|
// and only lock it for short periods (e.g., in read()).
|
||||||
|
// The separation also helps avoid deadlock and races during
|
||||||
|
// pathname lookup. iget() increments ip->ref so that the inode
|
||||||
|
// stays cached and pointers to it remain valid.
|
||||||
|
//
|
||||||
|
// Many internal file system functions expect the caller to
|
||||||
|
// have locked the inodes involved; this lets callers create
|
||||||
|
// multi-step atomic operations.
|
||||||
|
//
|
||||||
|
// The icache.lock spin-lock protects the allocation of icache
|
||||||
|
// entries. Since ip->ref indicates whether an entry is free,
|
||||||
|
// and ip->dev and ip->inum indicate which i-node an entry
|
||||||
|
// holds, one must hold icache.lock while using any of those fields.
|
||||||
|
//
|
||||||
|
// An ip->lock sleep-lock protects all ip-> fields other than ref,
|
||||||
|
// dev, and inum. One must hold ip->lock in order to
|
||||||
|
// read or write that inode's ip->valid, ip->size, ip->type, &c.
|
||||||
|
|
||||||
|
struct {
|
||||||
|
struct spinlock lock;
|
||||||
|
struct inode inode[NINODE];
|
||||||
|
} icache;
|
||||||
|
|
||||||
|
void iinit(int dev) {
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
initlock(&icache.lock, "icache");
|
||||||
|
for (i = 0; i < NINODE; i++) {
|
||||||
|
initsleeplock(&icache.inode[i].lock, "inode");
|
||||||
|
}
|
||||||
|
|
||||||
|
readsb(dev, &sb);
|
||||||
|
cprintf("sb: size %d nblocks %d ninodes %d nlog %d logstart %d\
|
||||||
|
inodestart %d bmap start %d\n", sb.size, sb.nblocks,
|
||||||
|
sb.ninodes, sb.nlog, sb.logstart, sb.inodestart,
|
||||||
|
sb.bmapstart);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct inode* iget(uint dev, uint inum);
|
||||||
|
|
||||||
|
|
||||||
|
// Allocate an inode on device dev.
|
||||||
|
// Mark it as allocated by giving it type type.
|
||||||
|
// Returns an unlocked but allocated and referenced inode.
|
||||||
|
struct inode* ialloc(uint dev, short type) {
|
||||||
|
int inum;
|
||||||
|
struct buf *bp;
|
||||||
|
struct dinode *dip;
|
||||||
|
|
||||||
|
for (inum = 1; inum < sb.ninodes; inum++) {
|
||||||
|
bp = bread(dev, IBLOCK(inum, sb));
|
||||||
|
dip = (struct dinode*)bp->data + inum % IPB;
|
||||||
|
if (dip->type == 0) { // a free inode
|
||||||
|
memset(dip, 0, sizeof(*dip));
|
||||||
|
dip->type = type;
|
||||||
|
log_write(bp); // mark it allocated on the disk
|
||||||
|
brelse(bp);
|
||||||
|
return iget(dev, inum);
|
||||||
|
}
|
||||||
|
brelse(bp);
|
||||||
|
}
|
||||||
|
panic("ialloc: no inodes");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy a modified in-memory inode to disk.
|
||||||
|
// Must be called after every change to an ip->xxx field
|
||||||
|
// that lives on disk, since i-node cache is write-through.
|
||||||
|
// Caller must hold ip->lock.
|
||||||
|
void iupdate(struct inode *ip) {
|
||||||
|
struct buf *bp;
|
||||||
|
struct dinode *dip;
|
||||||
|
|
||||||
|
bp = bread(ip->dev, IBLOCK(ip->inum, sb));
|
||||||
|
dip = (struct dinode*)bp->data + ip->inum % IPB;
|
||||||
|
dip->type = ip->type;
|
||||||
|
dip->major = ip->major;
|
||||||
|
dip->minor = ip->minor;
|
||||||
|
dip->nlink = ip->nlink;
|
||||||
|
dip->size = ip->size;
|
||||||
|
memmove(dip->addrs, ip->addrs, sizeof(ip->addrs));
|
||||||
|
log_write(bp);
|
||||||
|
brelse(bp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the inode with number inum on device dev
|
||||||
|
// and return the in-memory copy. Does not lock
|
||||||
|
// the inode and does not read it from disk.
|
||||||
|
static struct inode* iget(uint dev, uint inum) {
|
||||||
|
struct inode *ip, *empty;
|
||||||
|
|
||||||
|
acquire(&icache.lock);
|
||||||
|
|
||||||
|
// Is the inode already cached?
|
||||||
|
empty = 0;
|
||||||
|
for (ip = &icache.inode[0]; ip < &icache.inode[NINODE]; ip++) {
|
||||||
|
if (ip->ref > 0 && ip->dev == dev && ip->inum == inum) {
|
||||||
|
ip->ref++;
|
||||||
|
release(&icache.lock);
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
if (empty == 0 && ip->ref == 0) { // Remember empty slot.
|
||||||
|
empty = ip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recycle an inode cache entry.
|
||||||
|
if (empty == 0) {
|
||||||
|
panic("iget: no inodes");
|
||||||
|
}
|
||||||
|
|
||||||
|
ip = empty;
|
||||||
|
ip->dev = dev;
|
||||||
|
ip->inum = inum;
|
||||||
|
ip->ref = 1;
|
||||||
|
ip->valid = 0;
|
||||||
|
release(&icache.lock);
|
||||||
|
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment reference count for ip.
|
||||||
|
// Returns ip to enable ip = idup(ip1) idiom.
|
||||||
|
struct inode* idup(struct inode *ip) {
|
||||||
|
acquire(&icache.lock);
|
||||||
|
ip->ref++;
|
||||||
|
release(&icache.lock);
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock the given inode.
|
||||||
|
// Reads the inode from disk if necessary.
|
||||||
|
void ilock(struct inode *ip) {
|
||||||
|
struct buf *bp;
|
||||||
|
struct dinode *dip;
|
||||||
|
|
||||||
|
if (ip == 0 || ip->ref < 1) {
|
||||||
|
panic("ilock");
|
||||||
|
}
|
||||||
|
|
||||||
|
acquiresleep(&ip->lock);
|
||||||
|
|
||||||
|
if (ip->valid == 0) {
|
||||||
|
bp = bread(ip->dev, IBLOCK(ip->inum, sb));
|
||||||
|
dip = (struct dinode*)bp->data + ip->inum % IPB;
|
||||||
|
ip->type = dip->type;
|
||||||
|
ip->major = dip->major;
|
||||||
|
ip->minor = dip->minor;
|
||||||
|
ip->nlink = dip->nlink;
|
||||||
|
ip->size = dip->size;
|
||||||
|
memmove(ip->addrs, dip->addrs, sizeof(ip->addrs));
|
||||||
|
brelse(bp);
|
||||||
|
ip->valid = 1;
|
||||||
|
if (ip->type == 0) {
|
||||||
|
panic("ilock: no type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlock the given inode.
|
||||||
|
void iunlock(struct inode *ip) {
|
||||||
|
if (ip == 0 || !holdingsleep(&ip->lock) || ip->ref < 1) {
|
||||||
|
panic("iunlock");
|
||||||
|
}
|
||||||
|
|
||||||
|
releasesleep(&ip->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop a reference to an in-memory inode.
|
||||||
|
// If that was the last reference, the inode cache entry can
|
||||||
|
// be recycled.
|
||||||
|
// If that was the last reference and the inode has no links
|
||||||
|
// to it, free the inode (and its content) on disk.
|
||||||
|
// All calls to iput() must be inside a transaction in
|
||||||
|
// case it has to free the inode.
|
||||||
|
void iput(struct inode *ip) {
|
||||||
|
acquiresleep(&ip->lock);
|
||||||
|
if (ip->valid && ip->nlink == 0) {
|
||||||
|
acquire(&icache.lock);
|
||||||
|
int r = ip->ref;
|
||||||
|
release(&icache.lock);
|
||||||
|
if (r == 1) {
|
||||||
|
// inode has no links and no other references: truncate and free.
|
||||||
|
itrunc(ip);
|
||||||
|
ip->type = 0;
|
||||||
|
iupdate(ip);
|
||||||
|
ip->valid = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
releasesleep(&ip->lock);
|
||||||
|
|
||||||
|
acquire(&icache.lock);
|
||||||
|
ip->ref--;
|
||||||
|
release(&icache.lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Common idiom: unlock, then put.
|
||||||
|
void iunlockput(struct inode *ip) {
|
||||||
|
iunlock(ip);
|
||||||
|
iput(ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Inode content
|
||||||
|
//
|
||||||
|
// The content (data) associated with each inode is stored
|
||||||
|
// in blocks on the disk. The first NDIRECT block numbers
|
||||||
|
// are listed in ip->addrs[]. The next NINDIRECT blocks are
|
||||||
|
// listed in block ip->addrs[NDIRECT].
|
||||||
|
|
||||||
|
// Return the disk block address of the nth block in inode ip.
|
||||||
|
// If there is no such block, bmap allocates one.
|
||||||
|
static uint bmap(struct inode *ip, uint bn) {
|
||||||
|
uint addr, *a;
|
||||||
|
struct buf *bp;
|
||||||
|
|
||||||
|
if (bn < NDIRECT) {
|
||||||
|
if ((addr = ip->addrs[bn]) == 0) {
|
||||||
|
ip->addrs[bn] = addr = balloc(ip->dev);
|
||||||
|
}
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
bn -= NDIRECT;
|
||||||
|
|
||||||
|
if (bn < NINDIRECT) {
|
||||||
|
// Load indirect block, allocating if necessary.
|
||||||
|
if ((addr = ip->addrs[NDIRECT]) == 0) {
|
||||||
|
ip->addrs[NDIRECT] = addr = balloc(ip->dev);
|
||||||
|
}
|
||||||
|
bp = bread(ip->dev, addr);
|
||||||
|
a = (uint*)bp->data;
|
||||||
|
if ((addr = a[bn]) == 0) {
|
||||||
|
a[bn] = addr = balloc(ip->dev);
|
||||||
|
log_write(bp);
|
||||||
|
}
|
||||||
|
brelse(bp);
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("bmap: out of range");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Truncate inode (discard contents).
|
||||||
|
// Only called when the inode has no links
|
||||||
|
// to it (no directory entries referring to it)
|
||||||
|
// and has no in-memory reference to it (is
|
||||||
|
// not an open file or current directory).
|
||||||
|
static void itrunc(struct inode *ip) {
|
||||||
|
int i, j;
|
||||||
|
struct buf *bp;
|
||||||
|
uint *a;
|
||||||
|
|
||||||
|
for (i = 0; i < NDIRECT; i++) {
|
||||||
|
if (ip->addrs[i]) {
|
||||||
|
bfree(ip->dev, ip->addrs[i]);
|
||||||
|
ip->addrs[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ip->addrs[NDIRECT]) {
|
||||||
|
bp = bread(ip->dev, ip->addrs[NDIRECT]);
|
||||||
|
a = (uint*)bp->data;
|
||||||
|
for (j = 0; j < NINDIRECT; j++) {
|
||||||
|
if (a[j]) {
|
||||||
|
bfree(ip->dev, a[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
brelse(bp);
|
||||||
|
bfree(ip->dev, ip->addrs[NDIRECT]);
|
||||||
|
ip->addrs[NDIRECT] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ip->size = 0;
|
||||||
|
iupdate(ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy stat information from inode.
|
||||||
|
// Caller must hold ip->lock.
|
||||||
|
void stati(struct inode *ip, struct stat *st) {
|
||||||
|
st->dev = ip->dev;
|
||||||
|
st->ino = ip->inum;
|
||||||
|
st->type = ip->type;
|
||||||
|
st->nlink = ip->nlink;
|
||||||
|
st->size = ip->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Read data from inode.
|
||||||
|
// Caller must hold ip->lock.
|
||||||
|
int readi(struct inode *ip, char *dst, uint off, uint n) {
|
||||||
|
uint tot, m;
|
||||||
|
struct buf *bp;
|
||||||
|
|
||||||
|
if (ip->type == T_DEV) {
|
||||||
|
if (ip->major < 0 || ip->major >= NDEV || !devsw[ip->major].read) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return devsw[ip->major].read(ip, dst, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (off > ip->size || off + n < off) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (off + n > ip->size) {
|
||||||
|
n = ip->size - off;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (tot = 0; tot < n; tot += m, off += m, dst += m) {
|
||||||
|
bp = bread(ip->dev, bmap(ip, off / BSIZE));
|
||||||
|
m = min(n - tot, BSIZE - off % BSIZE);
|
||||||
|
memmove(dst, bp->data + off % BSIZE, m);
|
||||||
|
brelse(bp);
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write data to inode.
|
||||||
|
// Caller must hold ip->lock.
|
||||||
|
int writei(struct inode *ip, char *src, uint off, uint n) {
|
||||||
|
uint tot, m;
|
||||||
|
struct buf *bp;
|
||||||
|
|
||||||
|
if (ip->type == T_DEV) {
|
||||||
|
if (ip->major < 0 || ip->major >= NDEV || !devsw[ip->major].write) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return devsw[ip->major].write(ip, src, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (off > ip->size || off + n < off) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (off + n > MAXFILE * BSIZE) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (tot = 0; tot < n; tot += m, off += m, src += m) {
|
||||||
|
bp = bread(ip->dev, bmap(ip, off / BSIZE));
|
||||||
|
m = min(n - tot, BSIZE - off % BSIZE);
|
||||||
|
memmove(bp->data + off % BSIZE, src, m);
|
||||||
|
log_write(bp);
|
||||||
|
brelse(bp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n > 0 && off > ip->size) {
|
||||||
|
ip->size = off;
|
||||||
|
iupdate(ip);
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Directories
|
||||||
|
|
||||||
|
int namecmp(const char *s, const char *t) {
|
||||||
|
return strncmp(s, t, DIRSIZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for a directory entry in a directory.
|
||||||
|
// If found, set *poff to byte offset of entry.
|
||||||
|
struct inode* dirlookup(struct inode *dp, char *name, uint *poff) {
|
||||||
|
uint off, inum;
|
||||||
|
struct dirent de;
|
||||||
|
|
||||||
|
if (dp->type != T_DIR) {
|
||||||
|
panic("dirlookup not DIR");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (off = 0; off < dp->size; off += sizeof(de)) {
|
||||||
|
if (readi(dp, (char*)&de, off, sizeof(de)) != sizeof(de)) {
|
||||||
|
panic("dirlookup read");
|
||||||
|
}
|
||||||
|
if (de.inum == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (namecmp(name, de.name) == 0) {
|
||||||
|
// entry matches path element
|
||||||
|
if (poff) {
|
||||||
|
*poff = off;
|
||||||
|
}
|
||||||
|
inum = de.inum;
|
||||||
|
return iget(dp->dev, inum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write a new directory entry (name, inum) into the directory dp.
|
||||||
|
int dirlink(struct inode *dp, char *name, uint inum) {
|
||||||
|
int off;
|
||||||
|
struct dirent de;
|
||||||
|
struct inode *ip;
|
||||||
|
|
||||||
|
// Check that name is not present.
|
||||||
|
if ((ip = dirlookup(dp, name, 0)) != 0) {
|
||||||
|
iput(ip);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for an empty dirent.
|
||||||
|
for (off = 0; off < dp->size; off += sizeof(de)) {
|
||||||
|
if (readi(dp, (char*)&de, off, sizeof(de)) != sizeof(de)) {
|
||||||
|
panic("dirlink read");
|
||||||
|
}
|
||||||
|
if (de.inum == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
strncpy(de.name, name, DIRSIZ);
|
||||||
|
de.inum = inum;
|
||||||
|
if (writei(dp, (char*)&de, off, sizeof(de)) != sizeof(de)) {
|
||||||
|
panic("dirlink");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Paths
|
||||||
|
|
||||||
|
// Copy the next path element from path into name.
|
||||||
|
// Return a pointer to the element following the copied one.
|
||||||
|
// The returned path has no leading slashes,
|
||||||
|
// so the caller can check *path=='\0' to see if the name is the last one.
|
||||||
|
// If no name to remove, return 0.
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
// skipelem("a/bb/c", name) = "bb/c", setting name = "a"
|
||||||
|
// skipelem("///a//bb", name) = "bb", setting name = "a"
|
||||||
|
// skipelem("a", name) = "", setting name = "a"
|
||||||
|
// skipelem("", name) = skipelem("////", name) = 0
|
||||||
|
//
|
||||||
|
static char* skipelem(char *path, char *name) {
|
||||||
|
char *s;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
while (*path == '/') {
|
||||||
|
path++;
|
||||||
|
}
|
||||||
|
if (*path == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
s = path;
|
||||||
|
while (*path != '/' && *path != 0) {
|
||||||
|
path++;
|
||||||
|
}
|
||||||
|
len = path - s;
|
||||||
|
if (len >= DIRSIZ) {
|
||||||
|
memmove(name, s, DIRSIZ);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
memmove(name, s, len);
|
||||||
|
name[len] = 0;
|
||||||
|
}
|
||||||
|
while (*path == '/') {
|
||||||
|
path++;
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look up and return the inode for a path name.
|
||||||
|
// If parent != 0, return the inode for the parent and copy the final
|
||||||
|
// path element into name, which must have room for DIRSIZ bytes.
|
||||||
|
// Must be called inside a transaction since it calls iput().
|
||||||
|
static struct inode* namex(char *path, int nameiparent, char *name) {
|
||||||
|
struct inode *ip, *next;
|
||||||
|
|
||||||
|
if (*path == '/') {
|
||||||
|
ip = iget(ROOTDEV, ROOTINO);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ip = idup(myproc()->cwd);
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((path = skipelem(path, name)) != 0) {
|
||||||
|
ilock(ip);
|
||||||
|
if (ip->type != T_DIR) {
|
||||||
|
iunlockput(ip);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (nameiparent && *path == '\0') {
|
||||||
|
// Stop one level early.
|
||||||
|
iunlock(ip);
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
if ((next = dirlookup(ip, name, 0)) == 0) {
|
||||||
|
iunlockput(ip);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
iunlockput(ip);
|
||||||
|
ip = next;
|
||||||
|
}
|
||||||
|
if (nameiparent) {
|
||||||
|
iput(ip);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct inode* namei(char *path) {
|
||||||
|
char name[DIRSIZ];
|
||||||
|
return namex(path, 0, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct inode*nameiparent(char *path, char *name) {
|
||||||
|
return namex(path, 1, name);
|
||||||
|
}
|
2
fs.d
Normal file
2
fs.d
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
fs.o: fs.c /usr/include/stdc-predef.h types.h defs.h param.h stat.h mmu.h \
|
||||||
|
proc.h spinlock.h sleeplock.h fs.h buf.h file.h
|
57
fs.h
Normal file
57
fs.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// On-disk file system format.
|
||||||
|
// Both the kernel and user programs use this header file.
|
||||||
|
|
||||||
|
|
||||||
|
#define ROOTINO 1 // root i-number
|
||||||
|
#define BSIZE 512 // block size
|
||||||
|
|
||||||
|
// Disk layout:
|
||||||
|
// [ boot block | super block | log | inode blocks |
|
||||||
|
// free bit map | data blocks]
|
||||||
|
//
|
||||||
|
// mkfs computes the super block and builds an initial file system. The
|
||||||
|
// super block describes the disk layout:
|
||||||
|
struct superblock {
|
||||||
|
uint size; // Size of file system image (blocks)
|
||||||
|
uint nblocks; // Number of data blocks
|
||||||
|
uint ninodes; // Number of inodes.
|
||||||
|
uint nlog; // Number of log blocks
|
||||||
|
uint logstart; // Block number of first log block
|
||||||
|
uint inodestart; // Block number of first inode block
|
||||||
|
uint bmapstart; // Block number of first free map block
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NDIRECT 12
|
||||||
|
#define NINDIRECT (BSIZE / sizeof(uint))
|
||||||
|
#define MAXFILE (NDIRECT + NINDIRECT)
|
||||||
|
|
||||||
|
// On-disk inode structure
|
||||||
|
struct dinode {
|
||||||
|
short type; // File type
|
||||||
|
short major; // Major device number (T_DEV only)
|
||||||
|
short minor; // Minor device number (T_DEV only)
|
||||||
|
short nlink; // Number of links to inode in file system
|
||||||
|
uint size; // Size of file (bytes)
|
||||||
|
uint addrs[NDIRECT + 1]; // Data block addresses
|
||||||
|
};
|
||||||
|
|
||||||
|
// Inodes per block.
|
||||||
|
#define IPB (BSIZE / sizeof(struct dinode))
|
||||||
|
|
||||||
|
// Block containing inode i
|
||||||
|
#define IBLOCK(i, sb) ((i) / IPB + sb.inodestart)
|
||||||
|
|
||||||
|
// Bitmap bits per block
|
||||||
|
#define BPB (BSIZE * 8)
|
||||||
|
|
||||||
|
// Block of free map containing bit for block b
|
||||||
|
#define BBLOCK(b, sb) (b / BPB + sb.bmapstart)
|
||||||
|
|
||||||
|
// Directory is a file containing a sequence of dirent structures.
|
||||||
|
#define DIRSIZ 14
|
||||||
|
|
||||||
|
struct dirent {
|
||||||
|
ushort inum;
|
||||||
|
char name[DIRSIZ];
|
||||||
|
};
|
||||||
|
|
291
gdbutil
Normal file
291
gdbutil
Normal file
@ -0,0 +1,291 @@
|
|||||||
|
# -*- gdb-script -*-
|
||||||
|
|
||||||
|
# Utility functions to pretty-print x86 segment/interrupt descriptors.
|
||||||
|
# To load this file, run "source gdbutil" in gdb.
|
||||||
|
# printdesc and printdescs are the main entry points.
|
||||||
|
|
||||||
|
# IA32 2007, Volume 3A, Table 3-2
|
||||||
|
set $STS_T16A = 0x1
|
||||||
|
set $STS_LDT = 0x2
|
||||||
|
set $STS_T16B = 0x3
|
||||||
|
set $STS_CG16 = 0x4
|
||||||
|
set $STS_TG = 0x5
|
||||||
|
set $STS_IG16 = 0x6
|
||||||
|
set $STS_TG16 = 0x7
|
||||||
|
set $STS_T32A = 0x9
|
||||||
|
set $STS_T32B = 0xB
|
||||||
|
set $STS_CG32 = 0xC
|
||||||
|
set $STS_IG32 = 0xE
|
||||||
|
set $STS_TG32 = 0xF
|
||||||
|
|
||||||
|
define outputsts
|
||||||
|
while 1
|
||||||
|
if $arg0 == $STS_T16A
|
||||||
|
echo STS_T16A
|
||||||
|
loop_break
|
||||||
|
end
|
||||||
|
if $arg0 == $STS_LDT
|
||||||
|
echo STS_LDT\
|
||||||
|
loop_break
|
||||||
|
end
|
||||||
|
if $arg0 == $STS_T16B
|
||||||
|
echo STS_T16B
|
||||||
|
loop_break
|
||||||
|
end
|
||||||
|
if $arg0 == $STS_CG16
|
||||||
|
echo STS_CG16
|
||||||
|
loop_break
|
||||||
|
end
|
||||||
|
if $arg0 == $STS_TG
|
||||||
|
echo STS_TG\ \
|
||||||
|
loop_break
|
||||||
|
end
|
||||||
|
if $arg0 == $STS_IG16
|
||||||
|
echo STS_IG16
|
||||||
|
loop_break
|
||||||
|
end
|
||||||
|
if $arg0 == $STS_TG16
|
||||||
|
echo STS_TG16
|
||||||
|
loop_break
|
||||||
|
end
|
||||||
|
if $arg0 == $STS_T32A
|
||||||
|
echo STS_T32A
|
||||||
|
loop_break
|
||||||
|
end
|
||||||
|
if $arg0 == $STS_T32B
|
||||||
|
echo STS_T32B
|
||||||
|
loop_break
|
||||||
|
end
|
||||||
|
if $arg0 == $STS_CG32
|
||||||
|
echo STS_CG32
|
||||||
|
loop_break
|
||||||
|
end
|
||||||
|
if $arg0 == $STS_IG32
|
||||||
|
echo STS_IG32
|
||||||
|
loop_break
|
||||||
|
end
|
||||||
|
if $arg0 == $STS_TG32
|
||||||
|
echo STS_TG32
|
||||||
|
loop_break
|
||||||
|
end
|
||||||
|
echo Reserved
|
||||||
|
loop_break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# IA32 2007, Volume 3A, Table 3-1
|
||||||
|
set $STA_X = 0x8
|
||||||
|
set $STA_E = 0x4
|
||||||
|
set $STA_C = 0x4
|
||||||
|
set $STA_W = 0x2
|
||||||
|
set $STA_R = 0x2
|
||||||
|
set $STA_A = 0x1
|
||||||
|
|
||||||
|
define outputsta
|
||||||
|
if $arg0 & $STA_X
|
||||||
|
# Code segment
|
||||||
|
echo code
|
||||||
|
if $arg0 & $STA_C
|
||||||
|
echo |STA_C
|
||||||
|
end
|
||||||
|
if $arg0 & $STA_R
|
||||||
|
echo |STA_R
|
||||||
|
end
|
||||||
|
else
|
||||||
|
# Data segment
|
||||||
|
echo data
|
||||||
|
if $arg0 & $STA_E
|
||||||
|
echo |STA_E
|
||||||
|
end
|
||||||
|
if $arg0 & $STA_W
|
||||||
|
echo |STA_W
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if $arg0 & $STA_A
|
||||||
|
echo |STA_A
|
||||||
|
else
|
||||||
|
printf " "
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# xv6-specific
|
||||||
|
set $SEG_KCODE = 1
|
||||||
|
set $SEG_KDATA = 2
|
||||||
|
set $SEG_KCPU = 3
|
||||||
|
set $SEG_UCODE = 4
|
||||||
|
set $SEG_UDATA = 5
|
||||||
|
set $SEG_TSS = 6
|
||||||
|
|
||||||
|
define outputcs
|
||||||
|
if ($arg0 & 4) == 0
|
||||||
|
if $arg0 >> 3 == $SEG_KCODE
|
||||||
|
printf "SEG_KCODE<<3"
|
||||||
|
end
|
||||||
|
if $arg0 >> 3 == $SEG_KDATA
|
||||||
|
printf "SEG_KDATA<<3"
|
||||||
|
end
|
||||||
|
if $arg0 >> 3 == $SEG_KCPU
|
||||||
|
printf "SEG_KCPU<<3"
|
||||||
|
end
|
||||||
|
if $arg0 >> 3 == $SEG_UCODE
|
||||||
|
printf "SEG_UCODE<<3"
|
||||||
|
end
|
||||||
|
if $arg0 >> 3 == $SEG_UDATA
|
||||||
|
printf "SEG_UDATA<<3"
|
||||||
|
end
|
||||||
|
if $arg0 >> 3 == $SEG_TSS
|
||||||
|
printf "SEG_TSS<<3"
|
||||||
|
end
|
||||||
|
if ($arg0 >> 3 < 1) + ($arg0 >> 3 > 6)
|
||||||
|
printf "GDT[%d]", $arg0 >> 3
|
||||||
|
end
|
||||||
|
else
|
||||||
|
printf "LDT[%d]", $arg0 >> 3
|
||||||
|
end
|
||||||
|
if ($arg0 & 3) > 0
|
||||||
|
printf "|"
|
||||||
|
outputdpl ($arg0&3)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
define outputdpl
|
||||||
|
if $arg0 == 0
|
||||||
|
printf "DPL_KERN"
|
||||||
|
else
|
||||||
|
if $arg0 == 3
|
||||||
|
printf "DPL_USER"
|
||||||
|
else
|
||||||
|
printf "DPL%d", $arg0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
define printdesc
|
||||||
|
if $argc != 1
|
||||||
|
echo Usage: printdesc expr
|
||||||
|
else
|
||||||
|
_printdesc ((uint*)&($arg0))[0] ((uint*)&($arg0))[1]
|
||||||
|
printf "\n"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
document printdesc
|
||||||
|
Print an x86 segment or gate descriptor.
|
||||||
|
printdesc EXPR
|
||||||
|
EXPR must evaluate to a descriptor value. It can be of any C type.
|
||||||
|
end
|
||||||
|
|
||||||
|
define _printdesc
|
||||||
|
_printdesc1 $arg0 $arg1 ($arg1>>15&1) ($arg1>>13&3) ($arg1>>12&1) ($arg1>>8&15)
|
||||||
|
end
|
||||||
|
|
||||||
|
define _printdesc1
|
||||||
|
# 2:P 3:DPL 4:S 5:Type
|
||||||
|
if $arg2 == 0
|
||||||
|
printf "P = 0 (Not present)"
|
||||||
|
else
|
||||||
|
printf "type = "
|
||||||
|
if $arg4 == 0
|
||||||
|
# System segment
|
||||||
|
outputsts $arg5
|
||||||
|
printf " (0x%x) ", $arg5
|
||||||
|
_printsysdesc $arg0 $arg1 $arg5
|
||||||
|
else
|
||||||
|
# Code/data segment
|
||||||
|
outputsta $arg5
|
||||||
|
printf " "
|
||||||
|
_printsegdesc $arg0 $arg1
|
||||||
|
end
|
||||||
|
|
||||||
|
printf " DPL = "
|
||||||
|
outputdpl $arg3
|
||||||
|
printf " (%d)", $arg3
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
define _printsysdesc
|
||||||
|
# 2:Type
|
||||||
|
# GDB's || is buggy
|
||||||
|
if ($arg2 == $STS_TG) + (($arg2&7) == $STS_IG16) + (($arg2&7) == $STS_TG16)
|
||||||
|
# Gate descriptor
|
||||||
|
_printgate $arg2 ($arg0>>16) ($arg0&0xFFFF) ($arg1>>16)
|
||||||
|
else
|
||||||
|
# System segment descriptor
|
||||||
|
_printsegdesc $arg0 $arg1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
define _printgate
|
||||||
|
# IA32 2007, Voume 3A, Figure 5-2
|
||||||
|
# 0:Type 1:CS 2:Offset 15..0 3:Offset 31..16
|
||||||
|
printf "CS = "
|
||||||
|
outputcs $arg1
|
||||||
|
printf " (%d)", $arg1
|
||||||
|
|
||||||
|
if (($arg0&7) == $STS_IG16) + (($arg0&7) == $STS_TG16)
|
||||||
|
printf " Offset = "
|
||||||
|
output/a $arg3 << 16 | $arg2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
define _printsegdesc
|
||||||
|
# IA32 20007, Volume 3A, Figure 3-8 and Figure 4-1
|
||||||
|
_printsegdesc1 ($arg0>>16) ($arg1&0xFF) ($arg1>>24) ($arg0&0xFFFF) ($arg1>>16&15) ($arg1>>23&1)
|
||||||
|
if ($arg1>>12&1) == 1
|
||||||
|
printf " AVL = %d", $arg1>>20&1
|
||||||
|
if ($arg1>>11&1) == 0
|
||||||
|
# Data segment
|
||||||
|
if ($arg1>>22&1) == 0
|
||||||
|
printf " B = small (0) "
|
||||||
|
else
|
||||||
|
printf " B = big (1) "
|
||||||
|
end
|
||||||
|
else
|
||||||
|
# Code segment
|
||||||
|
printf " D = "
|
||||||
|
if ($arg1>>22&1) == 0
|
||||||
|
printf "16-bit (0)"
|
||||||
|
else
|
||||||
|
printf "32-bit (1)"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
define _printsegdesc1
|
||||||
|
# 0:Base 0..15 1:Base 16..23 2:Base 24..32 3:Limit 0..15 4:Limit 16..19 5:G
|
||||||
|
printf "base = 0x%08x", $arg0 | ($arg1<<16) | ($arg2<<24)
|
||||||
|
printf " limit = 0x"
|
||||||
|
if $arg5 == 0
|
||||||
|
printf "%08x", $arg3 | ($arg4<<16)
|
||||||
|
else
|
||||||
|
printf "%08x", (($arg3 | ($arg4<<16)) << 12) | 0xFFF
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
define printdescs
|
||||||
|
if $argc < 1 || $argc > 2
|
||||||
|
echo Usage: printdescs expr [count]
|
||||||
|
else
|
||||||
|
if $argc == 1
|
||||||
|
_printdescs ($arg0) (sizeof($arg0)/sizeof(($arg0)[0]))
|
||||||
|
else
|
||||||
|
_printdescs ($arg0) ($arg1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
document printdescs
|
||||||
|
Print an array of x86 segment or gate descriptors.
|
||||||
|
printdescs EXPR [COUNT]
|
||||||
|
EXPR must evaluate to an array of descriptors.
|
||||||
|
end
|
||||||
|
|
||||||
|
define _printdescs
|
||||||
|
set $i = 0
|
||||||
|
while $i < $arg1
|
||||||
|
printf "[%d] ", $i
|
||||||
|
printdesc $arg0[$i]
|
||||||
|
set $i = $i + 1
|
||||||
|
end
|
||||||
|
end
|
104
gensyscalls.pl
Normal file
104
gensyscalls.pl
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
#!/usr/bin/perl -w
|
||||||
|
|
||||||
|
# Generate syscall.h, syscalltable.h or usys.S. These are the header and assembly
|
||||||
|
# files for system calls.
|
||||||
|
#
|
||||||
|
# Generating these files from one script avoids them getting out of sync.
|
||||||
|
#
|
||||||
|
# Specify an argument of -h to generate syscall.h
|
||||||
|
# Specify an argument of -c to generate syscalltable.h
|
||||||
|
# Specify an argument of -a to generate usys.S
|
||||||
|
#
|
||||||
|
# Note that you also need to update user.h with the declarations for these functions that
|
||||||
|
# user programs will use. This ensures that the C compiler generates the correct code to
|
||||||
|
# push the parameters on to the stack.
|
||||||
|
|
||||||
|
my @syscalls = (
|
||||||
|
"fork",
|
||||||
|
"exit",
|
||||||
|
"wait",
|
||||||
|
"pipe",
|
||||||
|
"read",
|
||||||
|
"kill",
|
||||||
|
"exec",
|
||||||
|
"fstat",
|
||||||
|
"chdir",
|
||||||
|
"dup",
|
||||||
|
"getpid",
|
||||||
|
"sbrk",
|
||||||
|
"sleep",
|
||||||
|
"uptime",
|
||||||
|
"open",
|
||||||
|
"write",
|
||||||
|
"mknod",
|
||||||
|
"unlink",
|
||||||
|
"link",
|
||||||
|
"mkdir",
|
||||||
|
"close",
|
||||||
|
"getch",
|
||||||
|
"greeting",
|
||||||
|
"shutdown"
|
||||||
|
);
|
||||||
|
|
||||||
|
my $i;
|
||||||
|
if ($#ARGV == -1)
|
||||||
|
{
|
||||||
|
print 'Error: No argument supplied to gensyscalls.pl';
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (($ARGV[0] ne '-h') && ($ARGV[0] ne '-a') && ($ARGV[0] ne '-c'))
|
||||||
|
{
|
||||||
|
print 'Error: Invalid argument to gensyscalls.pl';
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if ($ARGV[0] eq '-h'|| $ARGV[0] eq '-c')
|
||||||
|
{
|
||||||
|
print "// Generated by gensyscalls.pl. Do not edit.\n";
|
||||||
|
print "// To change syscall numbers or add new syscalls, edit gensyscalls.pl\n";
|
||||||
|
print "\n";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
print "# Generated by gensyscalls.pl. Do not edit.\n";
|
||||||
|
print "# To change syscall numbers or add new syscalls, edit gensyscalls.pl\n";
|
||||||
|
print "\n";
|
||||||
|
}
|
||||||
|
for ($i = 0; $i < scalar(@syscalls); $i++)
|
||||||
|
{
|
||||||
|
my $index = $i + 1;
|
||||||
|
if ($ARGV[0] eq '-h')
|
||||||
|
{
|
||||||
|
print "#define SYS_$syscalls[$i]\t\t$index\n";
|
||||||
|
}
|
||||||
|
elsif ($ARGV[0] eq '-c')
|
||||||
|
{
|
||||||
|
print "extern int sys_$syscalls[$i](void);\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($ARGV[0] eq '-a')
|
||||||
|
{
|
||||||
|
print "#include \"syscall.h\"\n";
|
||||||
|
print "#include \"traps.h\"\n";
|
||||||
|
print "\n";
|
||||||
|
print "#define SYSCALL(name) \\\n";
|
||||||
|
print ".globl name; \\\n";
|
||||||
|
print "name: \\\n";
|
||||||
|
print "\tmovl\t\$SYS_ ## name, \%eax; \\\n";
|
||||||
|
print "\tint\t\$T_SYSCALL; \\\n";
|
||||||
|
print "\tret\n";
|
||||||
|
print "\n";
|
||||||
|
for ($i = 0; $i < scalar(@syscalls); $i++)
|
||||||
|
{
|
||||||
|
print "SYSCALL($syscalls[$i])\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elsif ($ARGV[0] eq '-c')
|
||||||
|
{
|
||||||
|
print "\n";
|
||||||
|
print "static int(*syscalls[])(void) = {\n";
|
||||||
|
for ($i = 0; $i < scalar(@syscalls); $i++)
|
||||||
|
{
|
||||||
|
print "[SYS_$syscalls[$i]]\tsys_$syscalls[$i],\n";
|
||||||
|
}
|
||||||
|
print "};\n"
|
||||||
|
}
|
109
grep.c
Normal file
109
grep.c
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
// Simple grep. Only supports ^ . * $ operators.
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "stat.h"
|
||||||
|
#include "user.h"
|
||||||
|
|
||||||
|
char buf[1024];
|
||||||
|
int match(char*, char*);
|
||||||
|
|
||||||
|
void grep(char *pattern, int fd) {
|
||||||
|
int n, m;
|
||||||
|
char *p, *q;
|
||||||
|
|
||||||
|
m = 0;
|
||||||
|
while ((n = read(fd, buf + m, sizeof(buf) - m - 1)) > 0) {
|
||||||
|
m += n;
|
||||||
|
buf[m] = '\0';
|
||||||
|
p = buf;
|
||||||
|
while ((q = strchr(p, '\n')) != 0) {
|
||||||
|
*q = 0;
|
||||||
|
if (match(pattern, p)) {
|
||||||
|
*q = '\n';
|
||||||
|
write(1, p, q + 1 - p);
|
||||||
|
}
|
||||||
|
p = q + 1;
|
||||||
|
}
|
||||||
|
if (p == buf) {
|
||||||
|
m = 0;
|
||||||
|
}
|
||||||
|
if (m > 0) {
|
||||||
|
m -= p - buf;
|
||||||
|
memmove(buf, p, m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
int fd, i;
|
||||||
|
char *pattern;
|
||||||
|
|
||||||
|
if (argc <= 1) {
|
||||||
|
printf(2, "usage: grep pattern [file ...]\n");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
pattern = argv[1];
|
||||||
|
|
||||||
|
if (argc <= 2) {
|
||||||
|
grep(pattern, 0);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 2; i < argc; i++) {
|
||||||
|
if ((fd = open(argv[i], 0)) < 0) {
|
||||||
|
printf(1, "grep: cannot open %s\n", argv[i]);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
grep(pattern, fd);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regexp matcher from Kernighan & Pike,
|
||||||
|
// The Practice of Programming, Chapter 9.
|
||||||
|
|
||||||
|
int matchhere(char*, char*);
|
||||||
|
int matchstar(int, char*, char*);
|
||||||
|
|
||||||
|
int match(char *re, char *text) {
|
||||||
|
if (re[0] == '^') {
|
||||||
|
return matchhere(re + 1, text);
|
||||||
|
}
|
||||||
|
do { // must look at empty string
|
||||||
|
if (matchhere(re, text)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (*text++ != '\0');
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// matchhere: search for re at beginning of text
|
||||||
|
int matchhere(char *re, char *text){
|
||||||
|
if (re[0] == '\0') {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (re[1] == '*') {
|
||||||
|
return matchstar(re[0], re + 2, text);
|
||||||
|
}
|
||||||
|
if (re[0] == '$' && re[1] == '\0') {
|
||||||
|
return *text == '\0';
|
||||||
|
}
|
||||||
|
if (*text != '\0' && (re[0] == '.' || re[0] == *text)) {
|
||||||
|
return matchhere(re + 1, text + 1);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// matchstar: search for c*re at beginning of text
|
||||||
|
int matchstar(int c, char *re, char *text) {
|
||||||
|
do { // a * matches zero or more instances
|
||||||
|
if (matchhere(re, text)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (*text != '\0' && (*text++ == c || c == '.'));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
1
grep.d
Normal file
1
grep.d
Normal file
@ -0,0 +1 @@
|
|||||||
|
grep.o: grep.c /usr/include/stdc-predef.h types.h stat.h user.h
|
53
grep.sym
Normal file
53
grep.sym
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
00000000 grep.c
|
||||||
|
00000000 ulib.c
|
||||||
|
00000000 printf.c
|
||||||
|
00000680 printint
|
||||||
|
00000af0 digits.0
|
||||||
|
00000000 umalloc.c
|
||||||
|
000012a0 freep
|
||||||
|
000012a4 base
|
||||||
|
00000370 strcpy
|
||||||
|
00000730 printf
|
||||||
|
0000066b greeting
|
||||||
|
00000590 memmove
|
||||||
|
000000c0 matchhere
|
||||||
|
0000063b mknod
|
||||||
|
00000490 gets
|
||||||
|
0000060b getpid
|
||||||
|
000001d0 grep
|
||||||
|
00000960 malloc
|
||||||
|
0000061b sleep
|
||||||
|
000005d3 pipe
|
||||||
|
00000663 getch
|
||||||
|
00000633 write
|
||||||
|
000005f3 fstat
|
||||||
|
000005e3 kill
|
||||||
|
000005fb chdir
|
||||||
|
000005eb exec
|
||||||
|
000005cb wait
|
||||||
|
000005db read
|
||||||
|
00000643 unlink
|
||||||
|
000005bb fork
|
||||||
|
00000613 sbrk
|
||||||
|
00000623 uptime
|
||||||
|
00000e90 __bss_start
|
||||||
|
00000430 memset
|
||||||
|
00000000 main
|
||||||
|
00000310 matchstar
|
||||||
|
000003a0 strcmp
|
||||||
|
00000673 shutdown
|
||||||
|
00000603 dup
|
||||||
|
00000ea0 buf
|
||||||
|
00000500 stat
|
||||||
|
00000e90 _edata
|
||||||
|
000012ac _end
|
||||||
|
00000170 match
|
||||||
|
0000064b link
|
||||||
|
000005c3 exit
|
||||||
|
00000550 atoi
|
||||||
|
00000400 strlen
|
||||||
|
0000062b open
|
||||||
|
00000450 strchr
|
||||||
|
00000653 mkdir
|
||||||
|
0000065b close
|
||||||
|
000008d0 free
|
7
hello.c
Normal file
7
hello.c
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#include "types.h"
|
||||||
|
#include "user.h"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
greeting();
|
||||||
|
exit();
|
||||||
|
}
|
1
hello.d
Normal file
1
hello.d
Normal file
@ -0,0 +1 @@
|
|||||||
|
hello.o: hello.c /usr/include/stdc-predef.h types.h user.h
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user