2023年7月9日日曜日

UNIX V7のブート 4

PDP-11が起動し最初にディスクから読み出されるコードは、ディスクの先頭に書かれているブートプログラムです。そのソースコードは、/usr/mdec/rpuboot.s です。UNIX V6だと、このプログラムがUNIXカーネルをディスクから読み出して実行するのですが、V7では、/bootを読み込んで実行します。そしてbootがUNIXカーネルを読み込んで実行します。bootのソースコードは、/usr/src/cmd/standaloneにあります。

ブートプログラムはアセンブリ言語で書かれています。コードのメインの部分を見ていきます。

/ now start reading the inodes
/ starting at the root and
/ going through directories
1:
	mov	$names,r1
	mov	$2,r0			/ ルートディレクトリのinode番号
1:
	clr	bno
	jsr	pc,iget			
	tst	(r1)			/ namesの終端か
	beq	1f
2:
	jsr	pc,rmblk		/ 
		br start		/ rmblkが正常の終了の場合はスキップ
	mov	$buf,r2			/ r2 = buf[]
3:
	mov	r1,r3			/ r3 = names[]
	mov	r2,r4			/ r4 = buf[]
	add	$16.,r2			/ r2 = 次のディレクトリエントリ
	tst	(r4)+			/ inodeは空か
	beq	5f
4:
	cmpb	(r3)+,(r4)+	/ ファイル名の比較
	bne	5f
	cmp	r4,r2			/ 
	blo	4b
	mov	-16.(r2),r0		/ r0 = inode番号
	add	$14.,r1			/ 
	br	1b
5:
	cmp	r2,$buf+512.	/ 
	blo	3b				/ buf[]の最後でない
	br	2b				/ buf[]の最後

/ read file into core until
/ a mapping error, (no disk address)
1:
	clr	r1				/ r1 = 0
1:
	jsr	pc,rmblk
		br 1f
	mov	$buf,r2
2:
	mov	(r2)+,(r1)+
	cmp	r2,$buf+512.
	blo	2b
	br	1b
1:
	clr	r1

6−9行で、ルートディレクトリのinodeを読み込んでいます。igetは、r0で指定した番号のinodeをinode[]にコピーします。

13行は、rmblkでルートディレクトリをbuf[]に読み込みます。15行目からは、1エントリごとにファイル名とnames[]を比較します。

一致した場合は、27行でinode番号をr0に読み込みます。r1に14を足して8行目に飛び、igetでファイル名が一致したファイルのinodeを読み込みます。

11行でファイル名が終わっていることを確認し、38行目からinodeのdi_addr[]に沿ってファイルをメモリーに読み込んでいきます。


次のリストは igetです。

/ get the inode specified in r0
iget:
	add	$15.,r0
	mov	r0,r5
	ash	$-3.,r0			/ r0 = (r0 + 15) / 8
	bic	$!17777,r0		/ r0 &= 0x01fff
	mov	r0,dno
	clr	r0
	jsr	pc,rblk
	bic	$!7,r5			/ r5 &= 0x0007
	ash	$6,r5			/ r5 *= 64
	add	$buf,r5			/ r5 += buf
	mov	$inod,r4
1:
	mov	(r5)+,(r4)+
	cmp	r4,$inod+64.
	blo	1b
	rts	pc

3-6行でinode番号をinodeが存在しているブロック番号に変換し、9行でそのブロックをbuf[]に読み込んでいます。

10-12行で目的のinodeのbuf[]内でのオフセットを求め、13ー17行でinode[]にコピーしています。


次のrmblkは、bnoで指定されたブロックを、inode内のdi_addr[]に従ってbuff[]に読み込みます。

/ read a mapped block
/ offset in file is in bno.
/ skip if success, no skip if fail
/ the algorithm only handles a single
/ indirect block. that means that
/ files longer than 10+128 blocks cannot
/ be loaded.
rmblk:
	add	$2,(sp)			/ リターンアドレスを2増やす
	mov	bno,r0
	cmp	r0,$10.
	blt	1f
	mov	$10.,r0			/ 間接参照
1:
	mov	r0,-(sp)		/ 
	asl	r0				/ 
	add	(sp)+,r0		/ r0 = bno * 3 
	add	$addr+1,r0		/ r0 += di_addr + 1
	movb	(r0)+,dno	
	movb	(r0)+,dno+1	/ dno = ブロック番号の下位2バイト
	movb	-3(r0),r0	/ ブロック番号の上位1バイト
	bne	1f
	tst	dno
	beq	2f
1:
	jsr	pc,rblk			/ ブロック読み込み
	mov	bno,r0
	inc	bno
	sub	$10.,r0
	blt	1f				/ 直接参照ならリターン
	ash	$2,r0			/ r0 *= 4
	mov	buf+2(r0),dno	/ dno = ディスク番号の下位2バイト
	mov	buf(r0),r0		/ r0 = ディスク番号の上位2バイト
	bne	rblk
	tst	dno
	bne	rblk
2:
	sub	$2,(sp)
1:
	rts	pc

ブロック番号が0から9までは直接参照で10は間接参照になりますが、コメントにあるとおり、2重間接参照には対応していません。

13行でブロック番号10以上は、間接参照に対応するため10に固定しています。

15-18行で、ブロック番号からdi_addr[]内でのディスク番号下位2バイトのオフセットを求めています。

22-24行は、ブロック番号が0だった場合、rmblkを呼び出した直後のアドレスにリターンします。

26-30行で、ブロックを読み込んだ後、直接参照の場合はリターンします。

31行目からは間接参照の処理で、29行で10引かれたディスク番号を4倍しオフセットを求め、先に読み込んだブロックからブロック番号を取り出します。

34-36行で、番号が0でなければブロックを読み出します。

ブロックが正常に読み込まれた場合は、9行でリターンアドレスが+2されているので、rmblkを呼び出した次の命令をスキップします。


これで、UNIXが起動する第一段階を追いかけられました。次は、2段階目の/bootになります。

0 件のコメント:

コメントを投稿