67f05c0ce0
The kernel may panic early when paging is not yet configured. Breaking only on the virtual address of brkfunc does not work in this case and leads to timeouts during 'make grade'. Signed-off-by: Junjie Mao <eternal.n08@gmail.com>
559 lines
18 KiB
Bash
559 lines
18 KiB
Bash
#!/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 -e "$@" | 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'`
|
|
brkaddr_phys=`echo $brkaddr | sed "s/^c0/00/g"`
|
|
(
|
|
echo "target remote localhost:$gdbport"
|
|
echo "break *0x$brkaddr"
|
|
if [ "$brkaddr" != "$brkaddr_phys" ]; then
|
|
echo "break *0x$brkaddr_phys"
|
|
fi
|
|
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_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 = 2, 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 = 2, 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 = 2, 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 = 2, 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 = 2, 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 = 2, name = "hello".' \
|
|
'Hello world!!.' \
|
|
'I am process 2.' \
|
|
'hello pass.'
|
|
|
|
run_test -prog 'testbss' -check default_check \
|
|
'kernel_execve: pid = 2, 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 = 2, name = "pgdir".' \
|
|
'I am 2, print pgdir.' \
|
|
'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 = 2, name = "yield".' \
|
|
'Hello, I am process 2.' \
|
|
'Back in process 2, iteration 0.' \
|
|
'Back in process 2, iteration 1.' \
|
|
'Back in process 2, iteration 2.' \
|
|
'Back in process 2, iteration 3.' \
|
|
'Back in process 2, iteration 4.' \
|
|
'All done in process 2.' \
|
|
'yield pass.'
|
|
|
|
|
|
run_test -prog 'badarg' -check default_check \
|
|
'kernel_execve: pid = 2, 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 = 2, 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 = 2, 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 = 2, 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 = 2, 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 = 2, 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.'
|
|
|
|
## print final-score
|
|
show_final
|