Boot Manager (boot0)

OpenBSDでも、freebsd-update相当のシステムパッチを当てられるそうなので、やってみた。 まずは、下記のようにパッチの取得元を登録する。(最初から有ればいいのにね)

# cat /etc/installurl
#https://ftp.openbsd.org/pub/OpenBSD
#https://mirrors.sonic.net/pub/OpenBSD
https://cloudflare.cdn.openbsd.org/pub/OpenBSD

次は、どんなパッチを当てられるか(提供されてるか)-cオプションで確認する。有名なメルトダウンも対象になってたぞ。

# syspatch -c
001_tcb_invalid
002_fktrace
003_mpls
004_libssl
005_ahopts
006_prevhdr
007_etherip
008_unbound
009_meltdown
# syspatch
Get/Verify syspatch62-001_tcb_inv... 100% |*************|   465 KB    00:00
Installing patch 001_tcb_invalid
Get/Verify syspatch62-002_fktrace... 100% |*************|   785 KB    00:00
Installing patch 002_fktrace
Get/Verify syspatch62-003_mpls.tgz 100% |***************|   837 KB    00:00
Installing patch 003_mpls
Get/Verify syspatch62-004_libssl.tgz 100% |*************|  2515 KB    00:00
Installing patch 004_libssl
Get/Verify syspatch62-005_ahopts.tgz 100% |*************|   703 KB    00:00
Installing patch 005_ahopts
Get/Verify syspatch62-006_prevhdr... 100% |*************|   783 KB    00:00
Installing patch 006_prevhdr
Get/Verify syspatch62-007_etherip... 100% |*************|  1030 KB    00:00
Installing patch 007_etherip
Get/Verify syspatch62-008_unbound... 100% |*************|  1294 KB    00:00
Installing patch 008_unbound
Get/Verify syspatch62-009_meltdow... 100% |*************| 40344 KB    00:07
Installing patch 009_meltdown
Relinking to create unique kernel... done.

引数無しでコマンドを実行すると、有無を言わさず全部適用。危険な穴を塞ぐのが目的なので、当然の処置と思うぞ。

# ls -l /var/syspatch/
total 36
drwxr-xr-x  2 root  bin  512 Mar 12 06:55 62-001_tcb_invalid
drwxr-xr-x  2 root  bin  512 Mar 12 06:55 62-002_fktrace
drwxr-xr-x  2 root  bin  512 Mar 12 06:55 62-003_mpls
drwxr-xr-x  2 root  bin  512 Mar 12 06:55 62-004_libssl
drwxr-xr-x  2 root  bin  512 Mar 12 06:55 62-005_ahopts
drwxr-xr-x  2 root  bin  512 Mar 12 06:55 62-006_prevhdr
drwxr-xr-x  2 root  bin  512 Mar 12 06:55 62-007_etherip
drwxr-xr-x  2 root  bin  512 Mar 12 06:55 62-008_unbound
drwxr-xr-x  2 root  bin  512 Mar 12 06:56 62-009_meltdown
s -l /var/syspatch/62-001_tcb_invalid/                                        <
total 612
-r--r--r--  1 root  bin      1292 Oct 12 16:37 001_tcb_invalid.patch.sig
-rw-r--r--  1 root  wheel  294544 Mar 12 06:55 rollback.tgz

ログと言うか資料が残ってる。これが有るので、不具合が有ったら元に戻す事が出来るとな。

i386なマシンに入れてるOpenBSDで試したら、メルトダウンは対象外だった。投機に走るような不純な石じゃないわけね。いや、セレロンと言う貧乏人専用な石だから、投機も何も。。。(以下略)

too small kernel

前回、カーネルを適当にダイエットさせた。もう少し頑張れば更に痩せられると思って、チャレンジしてみた。で、その結果。

f51# nm /boot/kernel/kernel | wc
   11441   34323  352222

Genericの時は、21728だったから、1万個の名前(変数名や関数名)が削減された事になる。

deb9:tmp$ ls -lh kernel*
-rw-r--r-- 1 sakae sakae 2.4M Mar 13 06:51 kernel
-rw-r--r-- 1 sakae sakae  16M Mar 13 06:51 kernel.debug

そして、5.3Mだったのが、完全に半分以下になった。もっと古いOSを使うと、フロッピー1枚に OSを押し込む事が出来るかな。フロッピー1枚でルーターを作るとかいうのを見た覚えがあるぞ。

どこをどうしたら、こうなったかは、巻末にconfigファイルを残しておくよ。

boot0

例のムック本を頭から読み始めた。最初はMBRの話である。OSをインストールする時に、ブート・マネージャをMBRに入れるとしたやつだ。リナで言うと、LILOとかgurbに相当する。

アセンブラしかも16Bit版しか出てこない。出鼻をくじく仕組みになってる。今回は頑張って、アセンブラを乗り越えてみる積り。(辛抱出来るかな?)

説明の中で、やたらと boot0cfgが出てくるので、マニュアルを参照してみた。 gasらしいので、資料もね。

use gdb

実際にboot0のコードをgdbで追ってみる。 初っ端からステップ実行してくとBIOSの中のコードを見せられてしまうので、有名な0x7c00に BPを置いてから継続するのが吉。

(gdb) target remote localhost:1234
Remote debugging using localhost:1234
0x0000fff0 in ?? ()
(gdb) b *0x7c00
Breakpoint 1 at 0x7c00
(gdb) c
Continuing.

Breakpoint 1, 0x00007c00 in ?? ()
(gdb) si
0x00007c01 in ?? ()
(gdb) display/i $pc
1: x/i $pc
=> 0x7c01:      xor    %eax,%eax
(gdb) si
1: x/i $pc
=> 0x7c03:      mov    %eax,%es
0x00007c03 in ?? ()
1: x/i $pc
=> 0x7c03:      mov    %eax,%es

そして、闇夜のカラスにならないように、次に実行する命令を逆アセンブルさせるのが吉。

=> 0x7c14:      rep movsl %ds:(%esi),%es:(%edi)
(gdb) p/x $cx
$1 = 0xfe
(gdb) si 256
1: x/i $pc
=> 0x7c1a:      rep stos %eax,%es:(%edi)
0x00007c1a in ?? ()
1: x/i $pc
=> 0x7c1a:      rep stos %eax,%es:(%edi)
(gdb) info registers
eax            0x0      0
ecx            0x8      8
edx            0x80     128
ebx            0x0      0
esp            0x7c00   0x7c00
ebp            0x800    0x800
esi            0x7e00   32256
edi            0x800    2048
eip            0x7c1a   0x7c1a
eflags         0x246    [ PF ZF IF ]
cs             0x0      0
ss             0x0      0
ds             0x0      0
es             0x0      0
fs             0x0      0
gs             0x0      0

途中で、rep付の命令に出会うと、ecxがZEROになるまで、足止めを喰らう事になる。そういう時は、ステップ実行を何回か勝手にgdbが行うように指示しよう。

途中でBIOSの中に潜り込んでしまい、本流が見えなくなる事がある。こういう時は、手元に アセンブルリストを用意して、止めたい所にBPを貼ってから継続すれば良い。

アセンブラーだと技巧的な事が出来て、読み込んだ512バイトを冒頭で0x600へ引っ越ししている。(次にboot1を読み込むため)そしてそこへ移動して実行してる。そんな事もあって、是非リストを手に入れておきたい。(CISCの糞石だと、命令長が可変だからね。行数数えて番地を割り出すなんて、無理な相談です。)

アセンブルリストを手に入れる常套手段は、objdump -S する事だけど、こやつは、codeとdataの区別がつかないという根源的な問題がある。

$ objdump -S boot0.out
  :
000007a7 <os_freebsd>:
 7a7:   46                      inc    %esi
 7a8:   72 65                   jb     80f <__bss_start+0xf>
 7aa:   65                      gs

あたかも、そこに命令が有るように(適当に)表示されてしまった。まあ、甘いも辛いも分かってる人が使う分には手ごろでいいんだけどね。

じゃ、その解決方法は有るのか? アセンブルする時に、アセンブルリストを出すのが良い。-aがそれだ。何もオプションを与えないと結果が標準出力に出て来る。下記のようにすると、hogeってファイルに出力される。

$ as boot0.s -a=hoge
  56 0000 FC            start:          cld                             # String ops inc
  57 0001 31C0                          xorw %ax,%ax                    # Zero
GAS LISTING boot0.s                   page 2
  :
 394 01a7 46726565      os_freebsd:     .ascii "Free"

上記は、hogeの出力。一番左の数値がソースの行番号。その右がアドレス。更に右側が命令コード若しくはデータ。その右側が、ソースだ。

このリスティングには、シンボルテーブルも最後についてくる。

DEFINED SYMBOLS
                            *ABS*:000000b6 TICKS
                            *ABS*:0000000f FLAGS
             boot0.s:20     *ABS*:00000475 NHRDRV
             boot0.s:21     *ABS*:00000600 ORIGIN
               :
             boot0.s:56     .text:00000000 start
             boot0.s:89     .text:00000022 main
             boot0.s:93     .text:0000002c main.1
               :
             boot0.s:394    .text:000001a7 os_freebsd

詳細に確認したい時は便利だ。

(gdb) si
1: x/i $pc
=> 0x7c1f:      jmp    0x46f70624
0x00007c1f in ?? ()
1: x/i $pc
=> 0x7c1f:      jmp    0x46f70624
(gdb)
1: x/i $pc
=> 0x622:       testb  $0x20,-0x45(%esi)
0x00000622 in ?? ()
1: x/i $pc
=> 0x622:       testb  $0x20,-0x45(%esi)
(gdb) b *0x68b
Breakpoint 2 at 0x68b
(gdb) c
Continuing.
1: x/i $pc
=> 0x68b:       xor    %ah,%ah

Breakpoint 2, 0x0000068b in ?? ()
1: x/i $pc
=> 0x68b:       xor    %ah,%ah

上記、移動先へ遷移した所と、途中にあるBIOS呼び出しを飛ばして、少し先へBPを置いて、実行した例。0x600が移動先で、そこからの変位がアセンブラーリストに記されているので、足し算したアドレスにBPを置けばよい。

mbrの書き換え

いきなりだけど、説明を読むとMBRが読まれて、書き換えられているそうな。どんな風になってるか、調べてみる。

vboxに入れておいたFreeBSD5.1の冒頭セクターを取り出す

f51# dd if=/dev/ad0 of=org count=1
1+0 records in
1+0 records out
512 bytes transferred in 0.000308 secs (1662139 bytes/sec)

待ち時間を変更して、同じ事をする。

f51# boot0cfg -t 2 ad0
f51# dd if=/dev/ad0 of=two count=1
1+0 records in
1+0 records out
512 bytes transferred in 0.000188 secs (2725233 bytes/sec)

差異を比べてみる。

f51# cmp -x org two
000001bc b6 02

初期値のb6ってのは、10進数で182。一秒が18.2カウントに相当するので、 10秒も待たされる設定という事が判明。

書き換えるエリアが有るそうなので、ダンプしてみた。

f51# hd org
  :
000001a0  49 d8 4c 69 6e 75 f8 46  72 65 65 42 53 c4 90 90  |I.Linu.FreeBS...|
000001b0  66 bb 44 72 69 76 65 20  00 00 80 0f b6 00 80 01  |f.Drive ........|

How to make boot0

所で、boot0ってどうやって作ってるの? /sys/boot/i386/boot0にファイルとMakefileが置いてあったので、/tmpの下でコンパイルしてみた。

$ ls
Makefile        boot0.s
$ make
Warning: Object directory not changed from original /tmp
as  --defsym FLAGS=0xf  --defsym TICKS=0xb6 boot0.s -o boot0.o
ld -N -e start -Ttext 0x600 -o boot0.out boot0.o
objcopy -S -O binary boot0.out boot0
$ ls
Makefile        boot0           boot0.o         boot0.out       boot0.s

FLAGSとTICKSは、コンパイル時に決定してるんだ。アセンブラにコマンドラインから変数を渡して、埋め込む方法が分かったよ。

#
# These values are sometimes changed before writing back to the drive
# Be especially careful that nxtdrv: must come after drive:, as it
# is part of the same string.
#
drive:          .ascii "Drive "
nxtdrv:         .byte 0x0                       # Next drive number
opt:            .byte 0x0                       # Option
setdrv:         .byte 0x80                      # Drive to force
flags:          .byte FLAGS                     # Flags
ticks:          .word TICKS                     # Delay

install boot

所で、boot0は、インストール時にどんな風に使われているのかな? FreeBSD11.1では、bsdinstallがインストーラーだったんだけど、昔のインストーラーは、確か/stand/sysinstall だったはず。ソースを眺めてみたいな。入れてないから、ISOから追加しよう。

vboxだと、コンソール画面のウィンドウメニューから、デバイス->光学ドライブで、ISOを指定するんだな。

f51# df
Filesystem  1K-blocks   Used   Avail Capacity  Mounted on
/dev/ad0s1a   1775686 415072 1218560    25%    /
devfs               1      1       0   100%    /dev

あれれ、cdromが見えないぞ。こういう時は、/etc/fstabにヒントがきっとあると思うので見る。

f51# mount /dev/acd0
f51# df
Filesystem  1K-blocks   Used   Avail Capacity  Mounted on
/dev/ad0s1a   1775686 415072 1218560    25%    /
devfs               1      1       0   100%    /dev
/dev/acd0      630048 630048       0   100%    /cdrom
f51# ls /cdrom
5.1-RELEASE     INSTALL.TXT     catpages        crypto          packages
EARLY.HTM       README.HTM      cdrom.inf       dict            ports
EARLY.TXT       README.TXT      compat1x        doc             proflibs
ERRATA.HTM      RELNOTES.HTM    compat20        docbook.css     src
ERRATA.TXT      RELNOTES.TXT    compat21        floppies        tools
HARDWARE.HTM    base            compat22        games
HARDWARE.TXT    boot            compat3x        info
INSTALL.HTM     boot.catalog    compat4x        manpages

srcの下あたりを見ると、1.44Mにsplitされたファイルが多数入っていたぞ。OSをフロッピーで 回覧@日経MIXとかが懐かしいな。

sysinstallを起動して、configureからdistoributionを選んで、必要そうなソースを追加したよ。もう10年ぐらいは、このオペレーションをやっていないはずなんだけど、意外に覚えているものだな。

pkgも少ないけど同梱されてた。editorsの項を見ると、emacs 21.3 なんてのが入っていた。懐かしいバージョンだな。

release

CDのイメージをどうやって作っているんだろう? そういう時は出荷方法を見ればいいのかな。 てな事で、 /usr/src/release/scripts/doS.shを覗いてみた。

if [ -f "${RD}/trees/base/boot/boot" ]; then
        BOOT="-B -b ${RD}/trees/base/boot/boot"
elif [ -f "${RD}/trees/base/boot/boot1" ]; then
        BOOT="-B -b ${RD}/trees/base/boot/boot1"
        if [ -f "${RD}/trees/base/boot/boot2" ]; then
                BOOT="${BOOT} -s ${RD}/trees/base/boot/boot2"
        fi
else
        BOOT="-r"
fi
         :
        dd of=${FSIMG} if=/dev/zero count=${FSSIZE} bs=1k 2>/dev/null

        vnconfig -s labels -c /dev/r${VNDEVICE} ${FSIMG}

        disklabel -w ${BOOT} ${VNDEVICE} ${FSLABEL}
        newfs -i ${FSINODE} -o space -m 0 /dev/r${VNDEVICE}c

zeroで埋めたファイルを作ってから、boot関係者を、disklabelコマンドで登録してるんだな。 それからファイルシステムを作ってる。あれ、fdisk相当はやっておかなくてもいいのかな?

     Completely wipe any prior information on the disk, creating a new
     bootable disk with a DOS partition table containing one slice, covering
     the whole disk.  Initialize the label on this slice, then edit it.  The
     dd(1) commands are optional, but may be necessary for some BIOSes to
     properly recognize the disk:

           dd if=/dev/zero of=/dev/da0 bs=512 count=32
           gpart create -s MBR da0
           gpart add -t freebsd da0
           gpart set -a active -i 1 da0
           gpart bootcode -b /boot/mbr da0
           dd if=/dev/zero of=/dev/da0s1 bs=512 count=32
           bsdlabel -w -B da0s1
           bsdlabel -e da0s1

man bsdlabelに上記のような例が出てた。fdiskなんて古いからgpartを使えとな。

# $FreeBSD: releng/11.1/release/picobsd/qemu/PICOBSD 274331 2014-11-09 21:33:01Z melifaro $
# A configuration file to run tests on qemu.
# We disable SMP because it does not work well with qemu, and set HZ=1000
# to avoid it being overridden.

面白い物を発見した。FreeBSDもqemuで楽しんで下さいとな。HZは普通のやつだと100なんだけど、1000にする。これはオイラーが前回やった時に、GUIだと入力応答が悪いと嘆いていたのの対策であろうか。

確かにプロセスの切り替え頻度を頻繁(当社比10倍)にすれば、応答は良くなるな。但し、ホストのCPU負荷は上がると思われる。カーネルを直接にパッチして実験してみるかな。

(gdb) p hz
$4 = 100
(gdb) p hz=1000
$5 = 1000

返って、応答が悪くなったような気がするよ。どういう意図で1000にしてるんだろう? hz=50ぐらいにした方が、つっかかりが無いぞ。

そして、この値はDATAセクションに置いてあるかと思ったら、BSSエリアに有った。ブートの途中で、初期化されてるんだな。どこでやってるのだろうか?

そんな事より、kernelをnmして、hzの場所を特定した。今の値は50にしてるんで、そうなってるか確認してみる。

(gdb) x/w 0xc030defc
0xc030defc <hz>:        50

gdbが有れば、kernelもただの大きなアプリじゃんと思うぞ。

kernel ダイエット

下記、kernelを作る時のconfigファイルから、コメントと空行を削除した結果。

deb9:conf$ egrep -v '^#' SAKAE | sed '/^$/d'
machine         i386
cpu             I486_CPU
cpu             I586_CPU
cpu             I686_CPU
ident           SAKAE
hints           "GENERIC.hints"         #Default places to look for devices.
makeoptions     DEBUG=-g                #Build kernel with gdb(1) debug symbols
makeoptions     NO_MODULES=yes
options         SCHED_4BSD              #4BSD scheduler
options         INET                    #InterNETworking
options         FFS                     #Berkeley Fast Filesystem
options         SOFTUPDATES             #Enable FFS soft updates support
options         UFS_ACL                 #Support for access control lists
options         UFS_DIRHASH             #Improve performance on big directories
options         MD_ROOT                 #MD is a potential root device
options         CD9660                  #ISO 9660 Filesystem
options         PROCFS                  #Process filesystem (requires PSEUDOFS)
options         PSEUDOFS                #Pseudo-filesystem framework
options         COMPAT_43               #Compatible with BSD 4.3 [KEEP THIS!]
options         COMPAT_FREEBSD4         #Compatible with FreeBSD4
options         SCSI_DELAY=15000        #Delay (in ms) before probing SCSI
options         SYSVSHM                 #SYSV-style shared memory
options         SYSVMSG                 #SYSV-style message queues
options         SYSVSEM                 #SYSV-style semaphores
options         _KPOSIX_PRIORITY_SCHEDULING #Posix P1003_1B real-time extensions
options         KBD_INSTALL_CDEV        # install a CDEV entry in /dev
options         AHC_REG_PRETTY_PRINT    # Print register bitfields in debug
                                        # output.  Adds ~128k to driver.
options         AHD_REG_PRETTY_PRINT    # Print register bitfields in debug
                                        # output.  Adds ~215k to driver.
options         DDB                     #Enable the kernel debugger
options         DDB_NOKLDSYM
options         INVARIANT_SUPPORT       #Extra sanity checks of internal structu
res, required by INVARIANTS
device          isa
device          eisa
device          pci
device          fdc
device          ata
device          atadisk                 # ATA disk drives
device          atapicd                 # ATAPI CDROM drives
device          atapifd                 # ATAPI floppy drives
device          atapist                 # ATAPI tape drives
options         ATA_STATIC_ID           #Static device numbering
device          atkbdc          # AT keyboard controller
device          atkbd           # AT keyboard
device          psm             # PS/2 mouse
device          vga             # VGA video card driver
device          sc
device          npx
device          pmtimer
device          sio             # 8250, 16[45]50 based serial ports
device          em              # Intel PRO/1000 adapter Gigabit Ethernet Card
device          miibus          # MII bus support
device          random          # Entropy device
device          loop            # Network loopback
device          ether           # Ethernet support
device          sl              # Kernel SLIP
device          ppp             # Kernel PPP
device          tun             # Packet tunnel.
device          pty             # Pseudo-ttys (telnet etc)
device          md              # Memory "disks"
device          gif             # IPv6 and IPv4 tunneling
device          faith           # IPv6-to-IPv4 relaying (translation)
device          bpf             # Berkeley packet filter

最後の方にある仮想デバイスslだとかpppだとかgifなんてのを削れるね。世の中ではそろそろ、IPv6 Readyな気配もあるようなんでね。

etc

Google 翻訳の利用:PDFやWordのファイルを翻訳する方法

booklet これ、調布にある大学のパソコン部だかが出してる部報のアーカイブ。暇に任せて読むのには丁度よいな。