DF-5667 / vk_probe4.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | #!/bin/sh # DF-5667 probe driver v4 (canonical) — vkernel DDB has no symbol table # (init.c kdb_init is #if 0'd) so DDB is driven with raw nm-derived # addresses; the CPU_prvspace base pointer is parsed from an in-vkernel # /dev/kmem od(1) read. DDB ARGS MUST BE 0x-PREFIXED HEX (ddb input radix # is 16 — decimal literals get reinterpreted as hex, which is what crashed # probe v3 into pmap_kextract's KKASSERT). # Proves: # (1) is_globaldata_space/kernacc return TRUE (1) for reserved-but-unmapped # per-CPU slots (dead slots beyond ncpus) # (2) what pmap_kextract() returns for a dead slot (PDE==0 path reads # guest-phys page 0 = bootstrap PML4 through the DMAP) # (3) /dev/kmem composed verdict (kvm_access_check) for a dead slot # (4) /dev/kmem behavior across the whole window (EFAULT boundary, # porous pages, content, or a vkernel panic) set -u VK=/usr/obj/usr/src/sys/VKERNEL64/kernel.debug LOG=/tmp/vk5667.log cd /usr/obj/usr/src/sys/VKERNEL64 pkill -9 -f kernel.debug 2>/dev/null; sleep 1 rm -f /tmp/vkin $LOG mkfifo /tmp/vkin (sleep 1500 > /tmp/vkin 2>/dev/null) </dev/null & HOLDER=$! nsym() { nm $VK | awk -v s="$1" '$3==s{print "0x"$1}' | head -1; } SYM_PRV=$(nsym CPU_prvspace) SYM_NCPUS=$(nsym ncpus) F_KERNACC=$(nsym kernacc) F_PKEX=$(nsym pmap_kextract) F_IGDS=$(nsym is_globaldata_space) F_KVAC=$(nsym kvm_access_check) echo "DF5667 SYMS prv=$SYM_PRV ncpus=$SYM_NCPUS kernacc=$F_KERNACC pkex=$F_PKEX igds=$F_IGDS kvac=$F_KVAC" ( exec 3> /tmp/vkin; echo "ufs:vkd0" >&3 ) & ./kernel.debug -m 512m -r /vkroot.img -n 2 \ -e init_path=/bin/vkinit \ -s < /tmp/vkin 2>&1 | tee $LOG > /dev/console & i=0 while [ $i -lt 150 ]; do grep -q "Mounting root from ufs:vkd0" $LOG 2>/dev/null && grep -q "^# " $LOG 2>/dev/null && break pgrep -f "kernel.debug -m 512m" >/dev/null 2>&1 || break sleep 1; i=$((i+1)) done sleep 4 echo "=== DF5667 vkernel booted (i=$i) ===" send() { ( exec 4> /tmp/vkin; echo "$1" >&4 ); } # ---------- stage 1: read base pointer + ncpus via /dev/kmem ------------- send "echo DF5667_STAGE1" send "dd if=/dev/kmem bs=8 count=1 skip=\$(( $SYM_PRV / 8 )) 2>/dev/null | od -An -tx1" sleep 5 BYTES=$(grep -a -A1 "od -An" $LOG | tail -1 | tr -d '\r') echo "DF5667 raw ptr bytes: [$BYTES]" B10=$(printf '%s\n' "$BYTES" | awk '{ v="0x"; for (i=NF; i>=1; i--) v = v $i; print v }' ) B10=$((B10)) echo "DF5667 BASE16=$(printf 0x%x $B10) BASE10=$B10" if [ "$B10" -lt 1000 ] 2>/dev/null; then echo "DF5667 ERROR: bad base parse, aborting probes" kill $HOLDER 2>/dev/null; pkill -9 -f kernel.debug 2>/dev/null exit 1 fi DEAD=$((B10 + 0x20000 + 0x123)) DEADP=$((B10 + 0x20000)) LIVE=$((B10 + 0x40)) echo "DF5667 targets DEAD=$(printf 0x%x $DEAD) DEADP=$(printf 0x%x $DEADP) LIVE=$(printf 0x%x $LIVE)" # ---------- stage 2: DDB call battery (0x-hex args: ddb radix is 16) ------- send "sysctl -w debug.enter_debugger=ddb" sleep 4 DEAD16=$(printf '0x%x' $DEAD); DEAD816=$(printf '0x%x' $((DEAD+8))) DEADP16=$(printf '0x%x' $DEADP); DEADPE16=$(printf '0x%x' $((DEADP+4096))) LIVE16=$(printf '0x%x' $LIVE); LIVE816=$(printf '0x%x' $((LIVE+8))) ( exec 5> /tmp/vkin printf 'call %s(%s, %s)\n' "$F_IGDS" "$DEAD16" "$DEAD816" >&5; sleep 2 printf 'call %s(%s, 8, 1)\n' "$F_KERNACC" "$DEAD16" >&5; sleep 2 printf 'call %s(%s)\n' "$F_PKEX" "$DEAD16" >&5; sleep 2 printf 'call %s(%s, %s, 1)\n' "$F_KVAC" "$DEADP16" "$DEADPE16" >&5; sleep 2 printf 'call %s(%s, 8, 1)\n' "$F_KERNACC" "$LIVE16" >&5; sleep 2 printf 'call %s(%s)\n' "$F_PKEX" "$LIVE16" >&5; sleep 2 printf 'call %s(%s, %s)\n' "$F_IGDS" "$LIVE16" "$LIVE816" >&5; sleep 2 ) & sleep 18 send "c" sleep 3 # ---------- stage 3: /dev/kmem page scan (console transitions only) ------- send "echo DF5667_STAGE3 BASE=$B10" send "n=0; prev=X; while [ \$n -lt 1700 ]; do off=\$(( $B10/4096 + n )); if dd if=/dev/kmem of=/dev/null bs=4096 count=1 skip=\$off 2>/dev/null; then st=OK; else st=EF; fi; if [ \"\$st\" != \"\$prev\" ]; then echo \"PG \$n \$st addr=\$((off*4096))\"; prev=\$st; fi; n=\$((n+1)); done; echo DF5667_SCAN_DONE" sleep 170 # ---------- outcome -------------------------------------------------------- if pgrep -f "kernel.debug -m 512m" >/dev/null 2>&1; then ALIVE=yes; else ALIVE=no; fi echo "DF5667 VKERNEL_ALIVE=$ALIVE" echo "=== DDB call battery ===" grep -an "^call\|= 0x\|0x8000" $LOG | head -40 echo "=== scan transitions ===" grep -an "PG \|SCAN_DONE" $LOG | head -40 echo "=== tail ===" tail -8 $LOG if [ "$ALIVE" = yes ]; then send "echo DF5667_BYE; reboot" sleep 6 fi kill $HOLDER 2>/dev/null pkill -9 -f kernel.debug 2>/dev/null echo "DF5667_DRIVER_DONE" |