add lab answers

This commit is contained in:
chyyuu
2014-08-20 15:42:20 +08:00
parent d9ec12887b
commit f9773095fe
731 changed files with 92876 additions and 0 deletions

View File

@@ -0,0 +1,15 @@
OUTPUT_FORMAT("elf32-i386")
OUTPUT_ARCH(i386)
SECTIONS {
. = 0x7C00;
.startup : {
*bootasm.o(.text)
}
.text : { *(.text) }
.data : { *(.data .rodata) }
/DISCARD/ : { *(.eh_*) }
}

View File

@@ -0,0 +1,95 @@
OBJPREFIX := __objs_
.SECONDEXPANSION:
# -------------------- function begin --------------------
# list all files in some directories: (#directories, #types)
listf = $(filter $(if $(2),$(addprefix %.,$(2)),%),\
$(wildcard $(addsuffix $(SLASH)*,$(1))))
# get .o obj files: (#files[, packet])
toobj = $(addprefix $(OBJDIR)$(SLASH)$(if $(2),$(2)$(SLASH)),\
$(addsuffix .o,$(basename $(1))))
# get .d dependency files: (#files[, packet])
todep = $(patsubst %.o,%.d,$(call toobj,$(1),$(2)))
totarget = $(addprefix $(BINDIR)$(SLASH),$(1))
# change $(name) to $(OBJPREFIX)$(name): (#names)
packetname = $(if $(1),$(addprefix $(OBJPREFIX),$(1)),$(OBJPREFIX))
# cc compile template, generate rule for dep, obj: (file, cc[, flags, dir])
define cc_template
$$(call todep,$(1),$(4)): $(1) | $$$$(dir $$$$@)
@$(2) -I$$(dir $(1)) $(3) -MM $$< -MT "$$(patsubst %.d,%.o,$$@) $$@"> $$@
$$(call toobj,$(1),$(4)): $(1) | $$$$(dir $$$$@)
@echo + cc $$<
$(V)$(2) -I$$(dir $(1)) $(3) -c $$< -o $$@
ALLOBJS += $$(call toobj,$(1),$(4))
endef
# compile file: (#files, cc[, flags, dir])
define do_cc_compile
$$(foreach f,$(1),$$(eval $$(call cc_template,$$(f),$(2),$(3),$(4))))
endef
# add files to packet: (#files, cc[, flags, packet, dir])
define do_add_files_to_packet
__temp_packet__ := $(call packetname,$(4))
ifeq ($$(origin $$(__temp_packet__)),undefined)
$$(__temp_packet__) :=
endif
__temp_objs__ := $(call toobj,$(1),$(5))
$$(foreach f,$(1),$$(eval $$(call cc_template,$$(f),$(2),$(3),$(5))))
$$(__temp_packet__) += $$(__temp_objs__)
endef
# add objs to packet: (#objs, packet)
define do_add_objs_to_packet
__temp_packet__ := $(call packetname,$(2))
ifeq ($$(origin $$(__temp_packet__)),undefined)
$$(__temp_packet__) :=
endif
$$(__temp_packet__) += $(1)
endef
# add packets and objs to target (target, #packes, #objs[, cc, flags])
define do_create_target
__temp_target__ = $(call totarget,$(1))
__temp_objs__ = $$(foreach p,$(call packetname,$(2)),$$($$(p))) $(3)
TARGETS += $$(__temp_target__)
ifneq ($(4),)
$$(__temp_target__): $$(__temp_objs__) | $$$$(dir $$$$@)
$(V)$(4) $(5) $$^ -o $$@
else
$$(__temp_target__): $$(__temp_objs__) | $$$$(dir $$$$@)
endif
endef
# finish all
define do_finish_all
ALLDEPS = $$(ALLOBJS:.o=.d)
$$(sort $$(dir $$(ALLOBJS)) $(BINDIR)$(SLASH) $(OBJDIR)$(SLASH)):
@$(MKDIR) $$@
endef
# -------------------- function end --------------------
# compile file: (#files, cc[, flags, dir])
cc_compile = $(eval $(call do_cc_compile,$(1),$(2),$(3),$(4)))
# add files to packet: (#files, cc[, flags, packet, dir])
add_files = $(eval $(call do_add_files_to_packet,$(1),$(2),$(3),$(4),$(5)))
# add objs to packet: (#objs, packet)
add_objs = $(eval $(call do_add_objs_to_packet,$(1),$(2)))
# add packets and objs to target (target, #packes, #objs, cc, [, flags])
create_target = $(eval $(call do_create_target,$(1),$(2),$(3),$(4),$(5)))
read_packet = $(foreach p,$(call packetname,$(1)),$($(p)))
add_dependency = $(eval $(1): $(2))
finish_all = $(eval $(call do_finish_all))

View File

@@ -0,0 +1,3 @@
file bin/kernel
target remote :1234
break kern_init

View File

@@ -0,0 +1,636 @@
#!/bin/sh
verbose=false
if [ "x$1" = "x-v" ]; then
verbose=true
out=/dev/stdout
err=/dev/stderr
else
out=/dev/null
err=/dev/null
fi
## make & makeopts
if gmake --version > /dev/null 2>&1; then
make=gmake;
else
make=make;
fi
makeopts="--quiet --no-print-directory -j"
make_print() {
echo `$make $makeopts print-$1`
}
## command tools
awk='awk'
bc='bc'
date='date'
grep='grep'
rm='rm -f'
sed='sed'
## symbol table
sym_table='obj/kernel.sym'
## gdb & gdbopts
gdb="$(make_print GDB)"
gdbport='1234'
gdb_in="$(make_print GRADE_GDB_IN)"
## qemu & qemuopts
qemu="$(make_print qemu)"
qemu_out="$(make_print GRADE_QEMU_OUT)"
if $qemu -nographic -help | grep -q '^-gdb'; then
qemugdb="-gdb tcp::$gdbport"
else
qemugdb="-s -p $gdbport"
fi
## default variables
default_timeout=30
default_pts=5
pts=5
part=0
part_pos=0
total=0
total_pos=0
## default functions
update_score() {
total=`expr $total + $part`
total_pos=`expr $total_pos + $part_pos`
part=0
part_pos=0
}
get_time() {
echo `$date +%s.%N 2> /dev/null`
}
show_part() {
echo "Part $1 Score: $part/$part_pos"
echo
update_score
}
show_final() {
update_score
echo "Total Score: $total/$total_pos"
if [ $total -lt $total_pos ]; then
exit 1
fi
}
show_time() {
t1=$(get_time)
time=`echo "scale=1; ($t1-$t0)/1" | $sed 's/.N/.0/g' | $bc 2> /dev/null`
echo "(${time}s)"
}
show_build_tag() {
echo "$1:" | $awk '{printf "%-24s ", $0}'
}
show_check_tag() {
echo "$1:" | $awk '{printf " -%-40s ", $0}'
}
show_msg() {
echo $1
shift
if [ $# -gt 0 ]; then
echo "$@" | awk '{printf " %s\n", $0}'
echo
fi
}
pass() {
show_msg OK "$@"
part=`expr $part + $pts`
part_pos=`expr $part_pos + $pts`
}
fail() {
show_msg WRONG "$@"
part_pos=`expr $part_pos + $pts`
}
run_qemu() {
# Run qemu with serial output redirected to $qemu_out. If $brkfun is non-empty,
# wait until $brkfun is reached or $timeout expires, then kill QEMU
qemuextra=
if [ "$brkfun" ]; then
qemuextra="-S $qemugdb"
fi
if [ -z "$timeout" ] || [ $timeout -le 0 ]; then
timeout=$default_timeout;
fi
t0=$(get_time)
(
ulimit -t $timeout
exec $qemu -nographic $qemuopts -serial file:$qemu_out -monitor null -no-reboot $qemuextra
) > $out 2> $err &
pid=$!
# wait for QEMU to start
sleep 1
if [ -n "$brkfun" ]; then
# find the address of the kernel $brkfun function
brkaddr=`$grep " $brkfun\$" $sym_table | $sed -e's/ .*$//g'`
(
echo "target remote localhost:$gdbport"
echo "break *0x$brkaddr"
echo "continue"
) > $gdb_in
$gdb -batch -nx -x $gdb_in > /dev/null 2>&1
# make sure that QEMU is dead
# on OS X, exiting gdb doesn't always exit qemu
kill $pid > /dev/null 2>&1
fi
}
build_run() {
# usage: build_run <tag> <args>
show_build_tag "$1"
shift
if $verbose; then
echo "$make $@ ..."
fi
$make $makeopts $@ 'DEFS+=-DDEBUG_GRADE' > $out 2> $err
if [ $? -ne 0 ]; then
echo $make $@ failed
exit 1
fi
# now run qemu and save the output
run_qemu
show_time
}
check_result() {
# usage: check_result <tag> <check> <check args...>
show_check_tag "$1"
shift
# give qemu some time to run (for asynchronous mode)
if [ ! -s $qemu_out ]; then
sleep 4
fi
if [ ! -s $qemu_out ]; then
fail > /dev/null
echo 'no $qemu_out'
else
check=$1
shift
$check "$@"
fi
}
check_regexps() {
okay=yes
not=0
reg=0
error=
for i do
if [ "x$i" = "x!" ]; then
not=1
elif [ "x$i" = "x-" ]; then
reg=1
else
if [ $reg -ne 0 ]; then
$grep '-E' "^$i\$" $qemu_out > /dev/null
else
$grep '-F' "$i" $qemu_out > /dev/null
fi
found=$(($? == 0))
if [ $found -eq $not ]; then
if [ $found -eq 0 ]; then
msg="!! error: missing '$i'"
else
msg="!! error: got unexpected line '$i'"
fi
okay=no
if [ -z "$error" ]; then
error="$msg"
else
error="$error\n$msg"
fi
fi
not=0
reg=0
fi
done
if [ "$okay" = "yes" ]; then
pass
else
fail "$error"
if $verbose; then
exit 1
fi
fi
}
run_test() {
# usage: run_test [-tag <tag>] [-prog <prog>] [-Ddef...] [-check <check>] checkargs ...
tag=
prog=
check=check_regexps
while true; do
select=
case $1 in
-tag|-prog)
select=`expr substr $1 2 ${#1}`
eval $select='$2'
;;
esac
if [ -z "$select" ]; then
break
fi
shift
shift
done
defs=
while expr "x$1" : "x-D.*" > /dev/null; do
defs="DEFS+='$1' $defs"
shift
done
if [ "x$1" = "x-check" ]; then
check=$2
shift
shift
fi
if [ -z "$prog" ]; then
$make $makeopts touch > /dev/null 2>&1
args="$defs"
else
if [ -z "$tag" ]; then
tag="$prog"
fi
args="build-$prog $defs"
fi
build_run "$tag" "$args"
check_result 'check result' "$check" "$@"
}
quick_run() {
# usage: quick_run <tag> [-Ddef...]
tag="$1"
shift
defs=
while expr "x$1" : "x-D.*" > /dev/null; do
defs="DEFS+='$1' $defs"
shift
done
$make $makeopts touch > /dev/null 2>&1
build_run "$tag" "$defs"
}
quick_check() {
# usage: quick_check <tag> checkargs ...
tag="$1"
shift
check_result "$tag" check_regexps "$@"
}
## kernel image
osimg=$(make_print ucoreimg)
## swap image
swapimg=$(make_print swapimg)
## set default qemu-options
qemuopts="-hda $osimg -drive file=$swapimg,media=disk,cache=writeback"
## set break-function, default is readline
brkfun=readline
default_check() {
pts=7
check_regexps "$@"
pts=3
quick_check 'check output' \
'memory management: default_pmm_manager' \
'check_alloc_page() succeeded!' \
'check_pgdir() succeeded!' \
'check_boot_pgdir() succeeded!' \
'PDE(0e0) c0000000-f8000000 38000000 urw' \
' |-- PTE(38000) c0000000-f8000000 38000000 -rw' \
'PDE(001) fac00000-fb000000 00400000 -rw' \
' |-- PTE(000e0) faf00000-fafe0000 000e0000 urw' \
' |-- PTE(00001) fafeb000-fafec000 00001000 -rw' \
'check_slab() succeeded!' \
'check_vma_struct() succeeded!' \
'page fault at 0x00000100: K/W [no page found].' \
'check_pgfault() succeeded!' \
'check_vmm() succeeded.' \
'page fault at 0x00001000: K/W [no page found].' \
'page fault at 0x00002000: K/W [no page found].' \
'page fault at 0x00003000: K/W [no page found].' \
'page fault at 0x00004000: K/W [no page found].' \
'write Virt Page e in fifo_check_swap' \
'page fault at 0x00005000: K/W [no page found].' \
'page fault at 0x00001000: K/W [no page found]' \
'page fault at 0x00002000: K/W [no page found].' \
'page fault at 0x00003000: K/W [no page found].' \
'page fault at 0x00004000: K/W [no page found].' \
'check_swap() succeeded!' \
'++ setup timer interrupts'
}
## check now!!
run_test -prog 'badsegment' -check default_check \
- 'kernel_execve: pid = ., name = "badsegment".*' \
- 'trapframe at 0xc.......' \
'trap 0x0000000d General Protection' \
' err 0x00000028' \
- ' eip 0x008.....' \
- ' esp 0xaff.....' \
' cs 0x----001b' \
' ss 0x----0023' \
! - 'user panic at .*'
run_test -prog 'divzero' -check default_check \
- 'kernel_execve: pid = ., name = "divzero".*' \
- 'trapframe at 0xc.......' \
'trap 0x00000000 Divide error' \
- ' eip 0x008.....' \
- ' esp 0xaff.....' \
' cs 0x----001b' \
' ss 0x----0023' \
! - 'user panic at .*'
run_test -prog 'softint' -check default_check \
- 'kernel_execve: pid = ., name = "softint".*' \
- 'trapframe at 0xc.......' \
'trap 0x0000000d General Protection' \
' err 0x00000072' \
- ' eip 0x008.....' \
- ' esp 0xaff.....' \
' cs 0x----001b' \
' ss 0x----0023' \
! - 'user panic at .*'
pts=10
run_test -prog 'faultread' -check default_check \
- 'kernel_execve: pid = ., name = "faultread".*' \
- 'trapframe at 0xc.......' \
'trap 0x0000000e Page Fault' \
' err 0x00000004' \
- ' eip 0x008.....' \
! - 'user panic at .*'
run_test -prog 'faultreadkernel' -check default_check \
- 'kernel_execve: pid = ., name = "faultreadkernel".*' \
- 'trapframe at 0xc.......' \
'trap 0x0000000e Page Fault' \
' err 0x00000005' \
- ' eip 0x008.....' \
! - 'user panic at .*'
run_test -prog 'hello' -check default_check \
- 'kernel_execve: pid = ., name = "hello".*' \
'Hello world!!.' \
- 'I am process .*' \
'hello pass.'
run_test -prog 'testbss' -check default_check \
- 'kernel_execve: pid = ., name = "testbss".*' \
'Making sure bss works right...' \
'Yes, good. Now doing a wild write off the end...' \
'testbss may pass.' \
- 'trapframe at 0xc.......' \
'trap 0x0000000e Page Fault' \
' err 0x00000006' \
- ' eip 0x008.....' \
'killed by kernel.' \
! - 'user panic at .*'
run_test -prog 'pgdir' -check default_check \
- 'kernel_execve: pid = ., name = "pgdir".*' \
- 'I am .*' \
'PDE(001) 00800000-00c00000 00400000 urw' \
' |-- PTE(00002) 00800000-00802000 00002000 ur-' \
' |-- PTE(00001) 00802000-00803000 00001000 urw' \
'PDE(001) afc00000-b0000000 00400000 urw' \
' |-- PTE(00004) afffc000-b0000000 00004000 urw' \
'PDE(0e0) c0000000-f8000000 38000000 urw' \
' |-- PTE(38000) c0000000-f8000000 38000000 -rw' \
'pgdir pass.'
run_test -prog 'yield' -check default_check \
- 'kernel_execve: pid = ., name = "yield".*' \
'Hello, I am process 2.' \
- 'Back in process ., iteration 0.' \
- 'Back in process ., iteration 1.' \
- 'Back in process ., iteration 2.' \
- 'Back in process ., iteration 3.' \
- 'Back in process ., iteration 4.' \
- 'All done in process .*' \
'yield pass.'
run_test -prog 'badarg' -check default_check \
- 'kernel_execve: pid = ., name = "badarg".*' \
'fork ok.' \
'badarg pass.' \
'all user-mode processes have quit.' \
'init check memory pass.' \
! - 'user panic at .*'
pts=10
run_test -prog 'exit' -check default_check \
- 'kernel_execve: pid = ., name = "exit".*' \
'I am the parent. Forking the child...' \
'I am the parent, waiting now..' \
'I am the child.' \
- 'waitpid [0-9]+ ok\.' \
'exit pass.' \
'all user-mode processes have quit.' \
'init check memory pass.' \
! - 'user panic at .*'
run_test -prog 'spin' -check default_check \
- 'kernel_execve: pid = ., name = "spin".*' \
'I am the parent. Forking the child...' \
'I am the parent. Running the child...' \
'I am the child. spinning ...' \
'I am the parent. Killing the child...' \
'kill returns 0' \
'wait returns 0' \
'spin may pass.' \
'all user-mode processes have quit.' \
'init check memory pass.' \
! - 'user panic at .*'
run_test -prog 'waitkill' -check default_check \
- 'kernel_execve: pid = ., name = "waitkill".*' \
'wait child 1.' \
'child 2.' \
'child 1.' \
'kill parent ok.' \
'kill child1 ok.' \
'all user-mode processes have quit.' \
'init check memory pass.' \
! - 'user panic at .*'
pts=15
run_test -prog 'forktest' -check default_check \
- 'kernel_execve: pid = ., name = "forktest".*' \
'I am child 31' \
'I am child 19' \
'I am child 13' \
'I am child 0' \
'forktest pass.' \
'all user-mode processes have quit.' \
'init check memory pass.' \
! - 'fork claimed to work [0-9]+ times!' \
! 'wait stopped early' \
! 'wait got too many' \
! - 'user panic at .*'
pts=10
run_test -prog 'forktree' -check default_check \
- 'kernel_execve: pid = ., name = "forktree".*' \
- '....: I am '\'''\' \
- '....: I am '\''0'\' \
- '....: I am '\'''\' \
- '....: I am '\''1'\' \
- '....: I am '\''0'\' \
- '....: I am '\''01'\' \
- '....: I am '\''00'\' \
- '....: I am '\''11'\' \
- '....: I am '\''10'\' \
- '....: I am '\''101'\' \
- '....: I am '\''100'\' \
- '....: I am '\''111'\' \
- '....: I am '\''110'\' \
- '....: I am '\''001'\' \
- '....: I am '\''000'\' \
- '....: I am '\''011'\' \
- '....: I am '\''010'\' \
- '....: I am '\''0101'\' \
- '....: I am '\''0100'\' \
- '....: I am '\''0111'\' \
- '....: I am '\''0110'\' \
- '....: I am '\''0001'\' \
- '....: I am '\''0000'\' \
- '....: I am '\''0011'\' \
- '....: I am '\''0010'\' \
- '....: I am '\''1101'\' \
- '....: I am '\''1100'\' \
- '....: I am '\''1111'\' \
- '....: I am '\''1110'\' \
- '....: I am '\''1001'\' \
- '....: I am '\''1000'\' \
- '....: I am '\''1011'\' \
- '....: I am '\''1010'\' \
'all user-mode processes have quit.' \
'init check memory pass.'
pts=20
timeout=150
run_test -prog 'priority' -check default_check \
'sched class: stride_scheduler' \
- 'kernel_execve: pid = ., name = "priority".*' \
'main: fork ok,now need to wait pids.' \
'stride sched correct result: 1 2 3 4 5' \
'all user-mode processes have quit.' \
'init check memory pass.' \
! - 'user panic at .*'
pts=20
timeout=240
run_test -prog 'sleep' -check default_check \
- 'kernel_execve: pid = ., name = "sleep".*' \
'sleep 1 x 100 slices.' \
'sleep 3 x 100 slices.' \
'sleep 7 x 100 slices.' \
'sleep 10 x 100 slices.' \
- 'use 1... msecs.' \
'sleep pass.' \
'all user-mode processes have quit.' \
'init check memory pass.' \
! ' trap 0x0000000e Page Fault' \
! 'killed by kernel.' \
! - 'user panic at .*'
pts=20
timeout=240
run_test -prog 'sleepkill' -check default_check \
- 'kernel_execve: pid = ., name = "sleepkill".*' \
'sleepkill pass.' \
'all user-mode processes have quit.' \
'init check memory pass.' \
! - 'user panic at .*'
pts=40
timeout=500
run_test -prog 'matrix' -check default_check \
'Iter 1, No.0 philosopher_sema is thinking' \
'Iter 1, No.1 philosopher_sema is thinking' \
'Iter 1, No.2 philosopher_sema is thinking' \
'Iter 1, No.3 philosopher_sema is thinking' \
'Iter 1, No.4 philosopher_sema is thinking' \
'Iter 1, No.0 philosopher_sema is eating' \
'Iter 1, No.1 philosopher_sema is eating' \
'Iter 1, No.2 philosopher_sema is eating' \
'Iter 1, No.3 philosopher_sema is eating' \
'Iter 1, No.4 philosopher_sema is eating' \
'No.0 philosopher_sema quit' \
'No.1 philosopher_sema quit' \
'No.2 philosopher_sema quit' \
'No.3 philosopher_sema quit' \
'No.4 philosopher_sema quit' \
'Iter 1, No.0 philosopher_condvar is thinking' \
'Iter 1, No.1 philosopher_condvar is thinking' \
'Iter 1, No.2 philosopher_condvar is thinking' \
'Iter 1, No.3 philosopher_condvar is thinking' \
'Iter 1, No.4 philosopher_condvar is thinking' \
'Iter 1, No.0 philosopher_condvar is eating' \
'Iter 1, No.1 philosopher_condvar is eating' \
'Iter 1, No.2 philosopher_condvar is eating' \
'Iter 1, No.3 philosopher_condvar is eating' \
'Iter 1, No.4 philosopher_condvar is eating' \
'No.0 philosopher_condvar quit' \
'No.1 philosopher_condvar quit' \
'No.2 philosopher_condvar quit' \
'No.3 philosopher_condvar quit' \
'No.4 philosopher_condvar quit' \
- 'kernel_execve: pid = ., name = "matrix".*' \
'fork ok.' \
'pid 13 done!.' \
'pid 17 done!.' \
'pid 23 done!.' \
'matrix pass.' \
'all user-mode processes have quit.' \
'init check memory pass.' \
! - 'user panic at .*'
## print final-score
show_final

View File

@@ -0,0 +1,58 @@
/* Simple linker script for the ucore kernel.
See the GNU ld 'info' manual ("info ld") to learn the syntax. */
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
OUTPUT_ARCH(i386)
ENTRY(kern_entry)
SECTIONS {
/* Load the kernel at this address: "." means the current address */
. = 0xC0100000;
.text : {
*(.text .stub .text.* .gnu.linkonce.t.*)
}
PROVIDE(etext = .); /* Define the 'etext' symbol to this value */
.rodata : {
*(.rodata .rodata.* .gnu.linkonce.r.*)
}
/* Include debugging information in kernel memory */
.stab : {
PROVIDE(__STAB_BEGIN__ = .);
*(.stab);
PROVIDE(__STAB_END__ = .);
BYTE(0) /* Force the linker to allocate space
for this section */
}
.stabstr : {
PROVIDE(__STABSTR_BEGIN__ = .);
*(.stabstr);
PROVIDE(__STABSTR_END__ = .);
BYTE(0) /* Force the linker to allocate space
for this section */
}
/* Adjust the address for the data segment to the next page */
. = ALIGN(0x1000);
/* The data segment */
.data : {
*(.data)
}
PROVIDE(edata = .);
.bss : {
*(.bss)
}
PROVIDE(end = .);
/DISCARD/ : {
*(.eh_frame .note.GNU-stack)
}
}

View File

@@ -0,0 +1,582 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <limits.h>
#include <dirent.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <assert.h>
typedef int bool;
#define __error(msg, quit, ...) \
do { \
fprintf(stderr, #msg ": function %s - line %d: ", __FUNCTION__, __LINE__); \
if (errno != 0) { \
fprintf(stderr, "[error] %s: ", strerror(errno)); \
} \
fprintf(stderr, "\n\t"), fprintf(stderr, __VA_ARGS__); \
errno = 0; \
if (quit) { \
exit(-1); \
} \
} while (0)
#define warn(...) __error(warn, 0, __VA_ARGS__)
#define bug(...) __error(bug, 1, __VA_ARGS__)
#define static_assert(x) \
switch (x) {case 0: case (x): ; }
/* 2^31 + 2^29 - 2^25 + 2^22 - 2^19 - 2^16 + 1 */
#define GOLDEN_RATIO_PRIME_32 0x9e370001UL
#define HASH_SHIFT 10
#define HASH_LIST_SIZE (1 << HASH_SHIFT)
static inline uint32_t
__hash32(uint32_t val, unsigned int bits) {
uint32_t hash = val * GOLDEN_RATIO_PRIME_32;
return (hash >> (32 - bits));
}
static uint32_t
hash32(uint32_t val) {
return __hash32(val, HASH_SHIFT);
}
static uint32_t
hash64(uint64_t val) {
return __hash32((uint32_t)val, HASH_SHIFT);
}
void *
safe_malloc(size_t size) {
void *ret;
if ((ret = malloc(size)) == NULL) {
bug("malloc %lu bytes failed.\n", (long unsigned)size);
}
return ret;
}
char *
safe_strdup(const char *str) {
char *ret;
if ((ret = strdup(str)) == NULL) {
bug("strdup failed: %s\n", str);
}
return ret;
}
struct stat *
safe_stat(const char *filename) {
static struct stat __stat;
if (stat(filename, &__stat) != 0) {
bug("stat %s failed.\n", filename);
}
return &__stat;
}
struct stat *
safe_fstat(int fd) {
static struct stat __stat;
if (fstat(fd, &__stat) != 0) {
bug("fstat %d failed.\n", fd);
}
return &__stat;
}
struct stat *
safe_lstat(const char *name) {
static struct stat __stat;
if (lstat(name, &__stat) != 0) {
bug("lstat '%s' failed.\n", name);
}
return &__stat;
}
void
safe_fchdir(int fd) {
if (fchdir(fd) != 0) {
bug("fchdir failed %d.\n", fd);
}
}
#define SFS_MAGIC 0x2f8dbe2a
#define SFS_NDIRECT 12
#define SFS_BLKSIZE 4096 // 4K
#define SFS_MAX_NBLKS (1024UL * 512) // 4K * 512K
#define SFS_MAX_INFO_LEN 31
#define SFS_MAX_FNAME_LEN 255
#define SFS_MAX_FILE_SIZE (1024UL * 1024 * 128) // 128M
#define SFS_BLKBITS (SFS_BLKSIZE * CHAR_BIT)
#define SFS_TYPE_FILE 1
#define SFS_TYPE_DIR 2
#define SFS_TYPE_LINK 3
#define SFS_BLKN_SUPER 0
#define SFS_BLKN_ROOT 1
#define SFS_BLKN_FREEMAP 2
struct cache_block {
uint32_t ino;
struct cache_block *hash_next;
void *cache;
};
struct cache_inode {
struct inode {
uint32_t size;
uint16_t type;
uint16_t nlinks;
uint32_t blocks;
uint32_t direct[SFS_NDIRECT];
uint32_t indirect;
uint32_t db_indirect;
} inode;
ino_t real;
uint32_t ino;
uint32_t nblks;
struct cache_block *l1, *l2;
struct cache_inode *hash_next;
};
struct sfs_fs {
struct {
uint32_t magic;
uint32_t blocks;
uint32_t unused_blocks;
char info[SFS_MAX_INFO_LEN + 1];
} super;
struct subpath {
struct subpath *next, *prev;
char *subname;
} __sp_nil, *sp_root, *sp_end;
int imgfd;
uint32_t ninos, next_ino;
struct cache_inode *root;
struct cache_inode *inodes[HASH_LIST_SIZE];
struct cache_block *blocks[HASH_LIST_SIZE];
};
struct sfs_entry {
uint32_t ino;
char name[SFS_MAX_FNAME_LEN + 1];
};
static uint32_t
sfs_alloc_ino(struct sfs_fs *sfs) {
if (sfs->next_ino < sfs->ninos) {
sfs->super.unused_blocks --;
return sfs->next_ino ++;
}
bug("out of disk space.\n");
}
static struct cache_block *
alloc_cache_block(struct sfs_fs *sfs, uint32_t ino) {
struct cache_block *cb = safe_malloc(sizeof(struct cache_block));
cb->ino = (ino != 0) ? ino : sfs_alloc_ino(sfs);
cb->cache = memset(safe_malloc(SFS_BLKSIZE), 0, SFS_BLKSIZE);
struct cache_block **head = sfs->blocks + hash32(ino);
cb->hash_next = *head, *head = cb;
return cb;
}
struct cache_block *
search_cache_block(struct sfs_fs *sfs, uint32_t ino) {
struct cache_block *cb = sfs->blocks[hash32(ino)];
while (cb != NULL && cb->ino != ino) {
cb = cb->hash_next;
}
return cb;
}
static struct cache_inode *
alloc_cache_inode(struct sfs_fs *sfs, ino_t real, uint32_t ino, uint16_t type) {
struct cache_inode *ci = safe_malloc(sizeof(struct cache_inode));
ci->ino = (ino != 0) ? ino : sfs_alloc_ino(sfs);
ci->real = real, ci->nblks = 0, ci->l1 = ci->l2 = NULL;
struct inode *inode = &(ci->inode);
memset(inode, 0, sizeof(struct inode));
inode->type = type;
struct cache_inode **head = sfs->inodes + hash64(real);
ci->hash_next = *head, *head = ci;
return ci;
}
struct cache_inode *
search_cache_inode(struct sfs_fs *sfs, ino_t real) {
struct cache_inode *ci = sfs->inodes[hash64(real)];
while (ci != NULL && ci->real != real) {
ci = ci->hash_next;
}
return ci;
}
struct sfs_fs *
create_sfs(int imgfd) {
uint32_t ninos, next_ino;
struct stat *stat = safe_fstat(imgfd);
if ((ninos = stat->st_size / SFS_BLKSIZE) > SFS_MAX_NBLKS) {
ninos = SFS_MAX_NBLKS;
warn("img file is too big (%llu bytes, only use %u blocks).\n",
(unsigned long long)stat->st_size, ninos);
}
if ((next_ino = SFS_BLKN_FREEMAP + (ninos + SFS_BLKBITS - 1) / SFS_BLKBITS) >= ninos) {
bug("img file is too small (%llu bytes, %u blocks, bitmap use at least %u blocks).\n",
(unsigned long long)stat->st_size, ninos, next_ino - 2);
}
struct sfs_fs *sfs = safe_malloc(sizeof(struct sfs_fs));
sfs->super.magic = SFS_MAGIC;
sfs->super.blocks = ninos, sfs->super.unused_blocks = ninos - next_ino;
snprintf(sfs->super.info, SFS_MAX_INFO_LEN, "simple file system");
sfs->ninos = ninos, sfs->next_ino = next_ino, sfs->imgfd = imgfd;
sfs->sp_root = sfs->sp_end = &(sfs->__sp_nil);
sfs->sp_end->prev = sfs->sp_end->next = NULL;
int i;
for (i = 0; i < HASH_LIST_SIZE; i ++) {
sfs->inodes[i] = NULL;
sfs->blocks[i] = NULL;
}
sfs->root = alloc_cache_inode(sfs, 0, SFS_BLKN_ROOT, SFS_TYPE_DIR);
return sfs;
}
static void
subpath_push(struct sfs_fs *sfs, const char *subname) {
struct subpath *subpath = safe_malloc(sizeof(struct subpath));
subpath->subname = safe_strdup(subname);
sfs->sp_end->next = subpath;
subpath->prev = sfs->sp_end;
subpath->next = NULL;
sfs->sp_end = subpath;
}
static void
subpath_pop(struct sfs_fs *sfs) {
assert(sfs->sp_root != sfs->sp_end);
struct subpath *subpath = sfs->sp_end;
sfs->sp_end = sfs->sp_end->prev, sfs->sp_end->next = NULL;
free(subpath->subname), free(subpath);
}
static void
subpath_show(FILE *fout, struct sfs_fs *sfs, const char *name) {
struct subpath *subpath = sfs->sp_root;
fprintf(fout, "current is: /");
while ((subpath = subpath->next) != NULL) {
fprintf(fout, "%s/", subpath->subname);
}
if (name != NULL) {
fprintf(fout, "%s", name);
}
fprintf(fout, "\n");
}
static void
write_block(struct sfs_fs *sfs, void *data, size_t len, uint32_t ino) {
assert(len <= SFS_BLKSIZE && ino < sfs->ninos);
static char buffer[SFS_BLKSIZE];
if (len != SFS_BLKSIZE) {
memset(buffer, 0, sizeof(buffer));
data = memcpy(buffer, data, len);
}
off_t offset = (off_t)ino * SFS_BLKSIZE;
ssize_t ret;
if ((ret = pwrite(sfs->imgfd, data, SFS_BLKSIZE, offset)) != SFS_BLKSIZE) {
bug("write %u block failed: (%d/%d).\n", ino, (int)ret, SFS_BLKSIZE);
}
}
static void
flush_cache_block(struct sfs_fs *sfs, struct cache_block *cb) {
write_block(sfs, cb->cache, SFS_BLKSIZE, cb->ino);
}
static void
flush_cache_inode(struct sfs_fs *sfs, struct cache_inode *ci) {
write_block(sfs, &(ci->inode), sizeof(ci->inode), ci->ino);
}
void
close_sfs(struct sfs_fs *sfs) {
static char buffer[SFS_BLKSIZE];
uint32_t i, j, ino = SFS_BLKN_FREEMAP;
uint32_t ninos = sfs->ninos, next_ino = sfs->next_ino;
for (i = 0; i < ninos; ino ++, i += SFS_BLKBITS) {
memset(buffer, 0, sizeof(buffer));
if (i + SFS_BLKBITS > next_ino) {
uint32_t start = 0, end = SFS_BLKBITS;
if (i < next_ino) {
start = next_ino - i;
}
if (i + SFS_BLKBITS > ninos) {
end = ninos - i;
}
uint32_t *data = (uint32_t *)buffer;
const uint32_t bits = sizeof(bits) * CHAR_BIT;
for (j = start; j < end; j ++) {
data[j / bits] |= (1 << (j % bits));
}
}
write_block(sfs, buffer, sizeof(buffer), ino);
}
write_block(sfs, &(sfs->super), sizeof(sfs->super), SFS_BLKN_SUPER);
for (i = 0; i < HASH_LIST_SIZE; i ++) {
struct cache_block *cb = sfs->blocks[i];
while (cb != NULL) {
flush_cache_block(sfs, cb);
cb = cb->hash_next;
}
struct cache_inode *ci = sfs->inodes[i];
while (ci != NULL) {
flush_cache_inode(sfs, ci);
ci = ci->hash_next;
}
}
}
struct sfs_fs *
open_img(const char *imgname) {
const char *expect = ".img", *ext = imgname + strlen(imgname) - strlen(expect);
if (ext <= imgname || strcmp(ext, expect) != 0) {
bug("invalid .img file name '%s'.\n", imgname);
}
int imgfd;
if ((imgfd = open(imgname, O_WRONLY)) < 0) {
bug("open '%s' failed.\n", imgname);
}
return create_sfs(imgfd);
}
#define open_bug(sfs, name, ...) \
do { \
subpath_show(stderr, sfs, name); \
bug(__VA_ARGS__); \
} while (0)
#define show_fullpath(sfs, name) subpath_show(stderr, sfs, name)
void open_dir(struct sfs_fs *sfs, struct cache_inode *current, struct cache_inode *parent);
void open_file(struct sfs_fs *sfs, struct cache_inode *file, const char *filename, int fd);
void open_link(struct sfs_fs *sfs, struct cache_inode *file, const char *filename);
#define SFS_BLK_NENTRY (SFS_BLKSIZE / sizeof(uint32_t))
#define SFS_L0_NBLKS SFS_NDIRECT
#define SFS_L1_NBLKS (SFS_BLK_NENTRY + SFS_L0_NBLKS)
#define SFS_L2_NBLKS (SFS_BLK_NENTRY * SFS_BLK_NENTRY + SFS_L1_NBLKS)
#define SFS_LN_NBLKS (SFS_MAX_FILE_SIZE / SFS_BLKSIZE)
static void
update_cache(struct sfs_fs *sfs, struct cache_block **cbp, uint32_t *inop) {
uint32_t ino = *inop;
struct cache_block *cb = *cbp;
if (ino == 0) {
cb = alloc_cache_block(sfs, 0);
ino = cb->ino;
}
else if (cb == NULL || cb->ino != ino) {
cb = search_cache_block(sfs, ino);
assert(cb != NULL && cb->ino == ino);
}
*cbp = cb, *inop = ino;
}
static void
append_block(struct sfs_fs *sfs, struct cache_inode *file, size_t size, uint32_t ino, const char *filename) {
static_assert(SFS_LN_NBLKS <= SFS_L2_NBLKS);
assert(size <= SFS_BLKSIZE);
uint32_t nblks = file->nblks;
struct inode *inode = &(file->inode);
if (nblks >= SFS_LN_NBLKS) {
open_bug(sfs, filename, "file is too big.\n");
}
if (nblks < SFS_L0_NBLKS) {
inode->direct[nblks] = ino;
}
else if (nblks < SFS_L1_NBLKS) {
nblks -= SFS_L0_NBLKS;
update_cache(sfs, &(file->l1), &(inode->indirect));
uint32_t *data = file->l1->cache;
data[nblks] = ino;
}
else if (nblks < SFS_L2_NBLKS) {
nblks -= SFS_L1_NBLKS;
update_cache(sfs, &(file->l2), &(inode->db_indirect));
uint32_t *data2 = file->l2->cache;
update_cache(sfs, &(file->l1), &data2[nblks / SFS_BLK_NENTRY]);
uint32_t *data1 = file->l1->cache;
data1[nblks % SFS_BLK_NENTRY] = ino;
}
file->nblks ++;
inode->size += size;
inode->blocks ++;
}
static void
add_entry(struct sfs_fs *sfs, struct cache_inode *current, struct cache_inode *file, const char *name) {
static struct sfs_entry __entry, *entry = &__entry;
assert(current->inode.type == SFS_TYPE_DIR && strlen(name) <= SFS_MAX_FNAME_LEN);
entry->ino = file->ino, strcpy(entry->name, name);
uint32_t entry_ino = sfs_alloc_ino(sfs);
write_block(sfs, entry, sizeof(entry->name), entry_ino);
append_block(sfs, current, sizeof(entry->name), entry_ino, name);
file->inode.nlinks ++;
}
static void
add_dir(struct sfs_fs *sfs, struct cache_inode *parent, const char *dirname, int curfd, int fd, ino_t real) {
assert(search_cache_inode(sfs, real) == NULL);
struct cache_inode *current = alloc_cache_inode(sfs, real, 0, SFS_TYPE_DIR);
safe_fchdir(fd), subpath_push(sfs, dirname);
open_dir(sfs, current, parent);
safe_fchdir(curfd), subpath_pop(sfs);
add_entry(sfs, parent, current, dirname);
}
static void
add_file(struct sfs_fs *sfs, struct cache_inode *current, const char *filename, int fd, ino_t real) {
struct cache_inode *file;
if ((file = search_cache_inode(sfs, real)) == NULL) {
file = alloc_cache_inode(sfs, real, 0, SFS_TYPE_FILE);
open_file(sfs, file, filename, fd);
}
add_entry(sfs, current, file, filename);
}
static void
add_link(struct sfs_fs *sfs, struct cache_inode *current, const char *filename, ino_t real) {
struct cache_inode *file = alloc_cache_inode(sfs, real, 0, SFS_TYPE_LINK);
open_link(sfs, file, filename);
add_entry(sfs, current, file, filename);
}
void
open_dir(struct sfs_fs *sfs, struct cache_inode *current, struct cache_inode *parent) {
DIR *dir;
if ((dir = opendir(".")) == NULL) {
open_bug(sfs, NULL, "opendir failed.\n");
}
add_entry(sfs, current, current, ".");
add_entry(sfs, current, parent, "..");
struct dirent *direntp;
while ((direntp = readdir(dir)) != NULL) {
const char *name = direntp->d_name;
if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
continue ;
}
if (name[0] == '.') {
continue ;
}
if (strlen(name) > SFS_MAX_FNAME_LEN) {
open_bug(sfs, NULL, "file name is too long: %s\n", name);
}
struct stat *stat = safe_lstat(name);
if (S_ISLNK(stat->st_mode)) {
add_link(sfs, current, name, stat->st_ino);
}
else {
int fd;
if ((fd = open(name, O_RDONLY)) < 0) {
open_bug(sfs, NULL, "open failed: %s\n", name);
}
if (S_ISDIR(stat->st_mode)) {
add_dir(sfs, current, name, dirfd(dir), fd, stat->st_ino);
}
else if (S_ISREG(stat->st_mode)) {
add_file(sfs, current, name, fd, stat->st_ino);
}
else {
char mode = '?';
if (S_ISFIFO(stat->st_mode)) mode = 'f';
if (S_ISSOCK(stat->st_mode)) mode = 's';
if (S_ISCHR(stat->st_mode)) mode = 'c';
if (S_ISBLK(stat->st_mode)) mode = 'b';
show_fullpath(sfs, NULL);
warn("unsupported mode %07x (%c): file %s\n", stat->st_mode, mode, name);
}
close(fd);
}
}
closedir(dir);
}
void
open_file(struct sfs_fs *sfs, struct cache_inode *file, const char *filename, int fd) {
static char buffer[SFS_BLKSIZE];
ssize_t ret, last = SFS_BLKSIZE;
while ((ret = read(fd, buffer, sizeof(buffer))) != 0) {
assert(last == SFS_BLKSIZE);
uint32_t ino = sfs_alloc_ino(sfs);
write_block(sfs, buffer, ret, ino);
append_block(sfs, file, ret, ino, filename);
last = ret;
}
if (ret < 0) {
open_bug(sfs, filename, "read file failed.\n");
}
}
void
open_link(struct sfs_fs *sfs, struct cache_inode *file, const char *filename) {
static char buffer[SFS_BLKSIZE];
uint32_t ino = sfs_alloc_ino(sfs);
ssize_t ret = readlink(filename, buffer, sizeof(buffer));
if (ret < 0 || ret == SFS_BLKSIZE) {
open_bug(sfs, filename, "read link failed, %d", (int)ret);
}
write_block(sfs, buffer, ret, ino);
append_block(sfs, file, ret, ino, filename);
}
int
create_img(struct sfs_fs *sfs, const char *home) {
int curfd, homefd;
if ((curfd = open(".", O_RDONLY)) < 0) {
bug("get current fd failed.\n");
}
if ((homefd = open(home, O_RDONLY | O_NOFOLLOW)) < 0) {
bug("open home directory '%s' failed.\n", home);
}
safe_fchdir(homefd);
open_dir(sfs, sfs->root, sfs->root);
safe_fchdir(curfd);
close(curfd), close(homefd);
close_sfs(sfs);
return 0;
}
static void
static_check(void) {
static_assert(sizeof(off_t) == 8);
static_assert(sizeof(ino_t) == 8);
static_assert(SFS_MAX_NBLKS <= 0x80000000UL);
static_assert(SFS_MAX_FILE_SIZE <= 0x80000000UL);
}
int
main(int argc, char **argv) {
static_check();
if (argc != 3) {
bug("usage: <input *.img> <input dirname>\n");
}
const char *imgname = argv[1], *home = argv[2];
if (create_img(open_img(imgname), home) != 0) {
bug("create img failed.\n");
}
printf("create %s (%s) successfully.\n", imgname, home);
return 0;
}

View File

@@ -0,0 +1,43 @@
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
int
main(int argc, char *argv[]) {
struct stat st;
if (argc != 3) {
fprintf(stderr, "Usage: <input filename> <output filename>\n");
return -1;
}
if (stat(argv[1], &st) != 0) {
fprintf(stderr, "Error opening file '%s': %s\n", argv[1], strerror(errno));
return -1;
}
printf("'%s' size: %lld bytes\n", argv[1], (long long)st.st_size);
if (st.st_size > 510) {
fprintf(stderr, "%lld >> 510!!\n", (long long)st.st_size);
return -1;
}
char buf[512];
memset(buf, 0, sizeof(buf));
FILE *ifp = fopen(argv[1], "rb");
int size = fread(buf, 1, st.st_size, ifp);
if (size != st.st_size) {
fprintf(stderr, "read '%s' error, size is %d.\n", argv[1], size);
return -1;
}
fclose(ifp);
buf[510] = 0x55;
buf[511] = 0xAA;
FILE *ofp = fopen(argv[2], "wb+");
size = fwrite(buf, 1, 512, ofp);
if (size != 512) {
fprintf(stderr, "write '%s' error, size is %d.\n", argv[2], size);
return -1;
}
fclose(ofp);
printf("build 512 bytes boot sector: '%s' success!\n", argv[2]);
return 0;
}

View File

@@ -0,0 +1,71 @@
/* Simple linker script for ucore user-level programs.
See the GNU ld 'info' manual ("info ld") to learn the syntax. */
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
OUTPUT_ARCH(i386)
ENTRY(_start)
SECTIONS {
/* Load programs at this address: "." means the current address */
. = 0x800020;
.text : {
*(.text .stub .text.* .gnu.linkonce.t.*)
}
PROVIDE(etext = .); /* Define the 'etext' symbol to this value */
.rodata : {
*(.rodata .rodata.* .gnu.linkonce.r.*)
}
/* Adjust the address for the data segment to the next page */
. = ALIGN(0x1000);
.data : {
*(.data)
}
PROVIDE(edata = .);
.bss : {
*(.bss)
}
PROVIDE(end = .);
/* Place debugging symbols so that they can be found by
* the kernel debugger.
* Specifically, the four words at 0x200000 mark the beginning of
* the stabs, the end of the stabs, the beginning of the stabs
* string table, and the end of the stabs string table, respectively.
*/
.stab_info 0x200000 : {
LONG(__STAB_BEGIN__);
LONG(__STAB_END__);
LONG(__STABSTR_BEGIN__);
LONG(__STABSTR_END__);
}
.stab : {
__STAB_BEGIN__ = DEFINED(__STAB_BEGIN__) ? __STAB_BEGIN__ : .;
*(.stab);
__STAB_END__ = DEFINED(__STAB_END__) ? __STAB_END__ : .;
BYTE(0) /* Force the linker to allocate space
for this section */
}
.stabstr : {
__STABSTR_BEGIN__ = DEFINED(__STABSTR_BEGIN__) ? __STABSTR_BEGIN__ : .;
*(.stabstr);
__STABSTR_END__ = DEFINED(__STABSTR_END__) ? __STABSTR_END__ : .;
BYTE(0) /* Force the linker to allocate space
for this section */
}
/DISCARD/ : {
*(.eh_frame .note.GNU-stack .comment)
}
}

View File

@@ -0,0 +1,29 @@
#include <stdio.h>
int
main(void) {
printf("# handler\n");
printf(".text\n");
printf(".globl __alltraps\n");
int i;
for (i = 0; i < 256; i ++) {
printf(".globl vector%d\n", i);
printf("vector%d:\n", i);
if ((i < 8 || i > 14) && i != 17) {
printf(" pushl $0\n");
}
printf(" pushl $%d\n", i);
printf(" jmp __alltraps\n");
}
printf("\n");
printf("# vector table\n");
printf(".data\n");
printf(".globl __vectors\n");
printf("__vectors:\n");
for (i = 0; i < 256; i ++) {
printf(" .long vector%d\n", i);
}
return 0;
}