NetBSD の src/sys/arch/x68k/stand/boot_ustar/boot_ustar.S のメモ。

| $Id: boot_ustar.S,v 1.2 2009/11/20 06:37:45 isaki Exp $

| NetBSD/x68k のプライマリブートローダ boot_ustar の解説です。
| boot_ustar はディスクの先頭から 8KB 目以降にある ustarfs という
| ファイルシステム(?)上からセカンダリブートローダ (/boot) を
| 読み込んでそこに実行を移します。
| ustarfs というファイルシステムの構成はというと実は tar フォーマット
| そのものです。つまりこんな感じ?。わけわからんけど。
| 
| ディスクの先頭                          ディスクの終り
|    +------------------------------------+
|    |ブートストラップ | 必要なファイルを |
|    |用領域(8KB)      | tar したもの     |
|    +------------------------------------+
| 
| このディスクに置かれている tar ファイルには次のものが
| (この順で) 含まれています。
| 
|  1. USTAR.volsize.4540
| 	セカンダリブートローダ /usr/mdec/boot をリネームしたもの
|
|  2. netbsd
| 	カーネル
| 
| tar フォーマットは 512 バイトブロックを基本としているらしく、
| ファイルはすべて 512 バイトごとに区切られるようです。
| もちろん端数は切り上げて 0 でパディングされる、んだと思います。
| そして各ファイルの前には1ブロック (つまり 512 バイト) のヘッダがつきます。
| tar アーカイブ自体のヘッダはありません。
| つまりファイルの先頭はいきなり1番目のファイルのヘッダです。

| file: boot_ustar.S
| author: chapuni(webmaster@chapuni.com)
|         Yasha(itohy@netbsd.org)
|	  minoura@netbsd.org
|
| $NetBSD: boot_ustar.S,v 1.4 2002/05/19 08:14:13 isaki Exp $

| supports floppy only

#include <sys/reboot.h>
#include <machine/asm.h>
#include <machine/bootinfo.h>
#include "iocscall.h"

| エラーメッセージを SP に積ませる技。
#define BOOT_ERROR(s)	jbsr boot_error; .asciz s; .even
#define SRAM_MEMSIZE	(0x00ed0008)

	.text
| ここがディスクの先頭。0x2000 になる部分。
ASENTRY_NOPROFILE(start)
ASENTRY_NOPROFILE(top)
		| いきなり entry0 へ相対ジャンプする。
		| IPL-ROM とのお約束により先頭は bra 命令でなければ
		| ならない。"SHARP/X680x0" はマーカなのかしら?
		| その後ろの文字列は謎(笑)。
		bras	_ASM_LABEL(entry0)
		.ascii	"SHARP/"
		.ascii	"X680x0"
		.word	0x8199,0x94e6,0x82ea,0x82bd
		.word	0x8e9e,0x82c9,0x82cd,0x8cbb
		.word	0x8ec0,0x93a6,0x94f0,0x8149
		.word	0
ASENTRY_NOPROFILE(entry0)
		| 起動情報を得る。上位8ビットは不要なので落しておく。
		IOCS(__BOOTINF)
		lsll	#8,%d0		| clear MSByte
		lsrl	#8,%d0		|

		| これが戻り値
		|
		| 0x80...0x8F		SASI
		| 0x90...0x93		Floppy
		| 0xED0000...0xED3FFE	SRAM
		| others		ROM (SCSI?)
		|
		| d0 は保存しておいて、d1 でテストする。
		| d1 の下位8ビットをクリアした結果が 0.l でなければ
		| SRAM か ROM 起動なのでエラー
		movel	%d0,%d1
		clrb	%d1
		tstl	%d1
		jne	boot_dev_unsupported

		| 続きは disklabel の後で。
		bra	_ASM_LABEL(entry)

|	Disklabel= 404bytes
|	Since LABELLOFFSET in <machine/disklabel.h> is 0x40,
|	entry must be after 0x000001d4 (0x000021d4)
		nop
GLOBAL(disklabel)
		.space	404
|
| ここまでをダンプすると以下のようになる。
|
| +0000  6026                 bra entry0
| +0002  5348 4152 502f       .ascii "SHARP/"
| +0008  5836 3830 7830       .ascii "X680x0"
| +000e  8199 94e6 82ea 82bd  .word ...
| +0016  8e9e 82c9 82cd 8cbb  .word ...
| +001e  8ec0 93a6 94f0 8149  .word ...
| +0026  0000                 .word 0
|                            entry0:
| +0028  708e 4e4f            IOCS(__BOOTINF)
| +002c  e188                 lsll  #8,%d0
| +002e  e088                 lsrl  #8,%d0
| +0030  2200                 movel %d0,%d1
| +0032  4201                 clrb  %d1
| +0034  4a81                 tstl  %d1
| +0036  6600 0270            jne   boot_dev_unsupported
| +003a  6000 0198            bra   entry
| +003e  4e71                 nop
|                            disklabel:
| +0040                       .space 404
|                            entry:
| +0444
|

ASENTRY_NOPROFILE(entry)
		|
		| SASI or Floppy
		| d0 を d6 にも保存しておく。下位8ビットが 9x なら
		| FD ブート。そうでなければ SASI ? なのでエラー。
		|
		movel	%d0,%d6
		andib	#0xFC,%d0
		cmpib	#0x90,%d0
		jne	boot_dev_unsupported	| boot from SASI?
		|
		| Floppy
		|   read first 64KB (XXX: CONSTANT!!)
		| フロッピーの最初の64KBを読み込む。
		| XXX 定数埋め込みなのでセカンダリブートローダがもっと大きく
		| なったらここも変更しないといけない。
		|
		| %d0 にドライブ番号を入れて FD フォーマットをチェックする。
		| check_fd_format は chkfmt.s の中。
		| %d1.w の上位バイトはメディア。__BOOTINF のをそのまま使う。
		| %d1.w の下位バイトはモード。0x70 は リトライあり/シークなしらしい。
		| %d2.l は目的読み込み位置。??
		| %d3.l が読み込むバイト数。ここでは 64KB 読み込む。
		| 実際のセカンダリブートローダは 40KB ほどなので
		| ブートストラップ用領域 8KB と合わせて 48KB ほど読み込めば
		| よいことになる。逆にいうとこのままのブートローダでは
		| セカンダリブートローダがあと 16KB 大きくなると読み込みきれない。
		|
		| %a1 は読み込みバッファのアドレス。セカンダリブートローダに
		| 実行を移すためのジャンプ先が BOOT_TEXTADDR (Makefile に書いてある)
		| になるように読み込む。- 8192 - 0x200 - 32 の内訳は次の通り。
		| セクタ 0 から数えると tar フォーマットが始まるまでに 8KB の
		| ブートストラップ領域があるので 8192。その後 tar のヘッダが
		| 512 バイト (= 0x200)。そして /boot の a.out ヘッダが 32 バイト。
		|
		andib	#0x03,%d0	| drive # (head=0)
		jbsr	check_fd_format
		moveb	%d6,%d2
		lslw	#8,%d2
		moveq	#0x70,%d1
		orw	%d2,%d1		| PDA*256 + MODE
		movel	%d0,%d2		| read position (first sector)
		movel	#65536,%d3	| read bytes
		moval	#(BOOT_TEXTADDR-8192-0x200-32),%a1
		moval	%a1,%a4		| save buffer addr
		| 読み込んで boot_read_done へ飛ぶ。
		IOCS(__B_READ)
		jra	boot_read_done

#include "chkfmt.s"

boot_dev_unsupported:
		BOOT_ERROR("unsupported boot device")

booterr_msg:	.ascii	"\r\n\n"
		.ascii	BOOT
		.asciz	": "
reboot_msg:	.asciz	"\r\n[Hit key to reboot]"
		.even

ASENTRY_NOPROFILE(boot_error)
		lea	%pc@(booterr_msg),%a1
		IOCS(__B_PRINT)
		moveal	%sp@+,%a1
		IOCS(__B_PRINT)
		lea	%pc@(reboot_msg),%a1
		IOCS(__B_PRINT)

		| wait for a key press (or release of a modifier)
		IOCS(__B_KEYINP)

		| issue software reset
		trap	#10
		| NOTREACHED

|
| FD の先頭 64KB を読み込んでからの処理。
|
boot_read_done:
		| %a4 から 8192 バイト先の番地を %a1 に入れる。
		| %a4 は FD から読み込んだバッファの先頭番地なので
		| %a1 はデータ領域の開始位置 (tar の開始位置) になる。
		|
		| %a1 からの8バイトが "USTAR.vo" であることを確認する。
		| そうでなければ invalidname。
		lea	%a4@(8192),%a1		| USTAR header
		cmpl	#0x55535441,%a1@	| filename `USTA
		bne	error_invalidname
		cmpl	#0x522e766f,%a1@(4)	|           R.vo...'
		bne	error_invalidname
		| %a1 + 256 からの6バイトが "\0ustar" で
		| あることを確認する。そうでなければ invalidfs。
		cmpl	#0x00757374,%a1@(256)	| magic `\0ust'
		bne	error_invalidfs
		cmpw	#0x6172,%a1@(260)	| magic `ar'
		bne	error_invalidfs

		| %a1 + 512 を %a2 に入れる。
		| %a2 は 1つ目のファイル (/usr/mdec/boot) の先頭。
		| 先頭の32バイト(ロングワード8つ)は a.out のヘッダ。そのうち
		| 最初の6つをレジスタに代入。残りの2つはいらないので単に
		| ポインタを進める。各レジスタには以下の内容が入る。
		| (FreeBSD 4.x の /usr/include/sys/imgact_aout.h より)
		|  %d0 a_midmag	(flags << 26 | mid << 16 | magic)
		|  %d1 a_text	text segment size
		|  %d2 a_data	initialized data size
		|  %d3 a_bss	uninitialized data size
		|  %d4 a_syms	symbol table size
		|  %a5 a_entry	entry point
		|  %a2 text segment の先頭
		|  
		lea	%a1@(0x200),%a2		| a.out header
		movml	%a2@+,%d0-%d4/%a5
		addal	#8,%a2			| start of text

		| entry point (セカンダリブートローダへのジャンプ先) が
		| あらかじめ固定されたアドレスと違うならエラーにする。
		cmpal	#BOOT_TEXTADDR,%a5
		bne	error_invalidboot

		| a.out のマジックが違っていればエラー。
		cmpl	#0x00870107,%d0		| a.out magic
		bne	error_invalidmagic

		| BSS をクリアする。
		| %d1 が text segment 先頭 (%a2) からのオフセットで BSS 開始位置。
		| %d3 が text segment 先頭 (%a2) からのオフセットで BSS 終了位置。
		| %d4 が BSS のサイズ。
		addl	%d2,%d1			| a_text+a_data
		movl	%d3,%d4			| save a_bss
		addl	%d1,%d3			| a_text+a_data+a_bss

	/* clear out bss */
		| dbra 使わないのは 64KB 以上を想定しているから?
		| 実際は数100バイト程度。
		lea	%a2@(%d1),%a3
1:		movb	#0,%a3@+
		subl	#1,%d4
		bne	1b

	/* set args for /boot */
		| /boot への引数。
		| firstpa, lastpa, esym はスタックで渡す。
		| でもこいつら何者?
		pea	%a2@(%d3)		| esym (unused)
		movel	SRAM_MEMSIZE,%sp@-	| lastpa (unused)
		pea	%a2@			| firstpa (unused)

		| 使わないレジスタはクリアしとくってことかしら?
		movq	#0,%d0			| unused
		movq	#0,%d1			| unused
		movq	#0,%d2			| unused
		movq	#0,%d3			| unused
		movq	#0,%d4			| unused
		movq	#0,%d5			| unused
		| /boot への引数のうち、起動デバイス、起動方法は d6, d7 で
		| レジスタ渡しする。
		| まず d6 の起動デバイス情報はこんな感じ。 参照。
		|
		|   (4) (4) (4) (4)  (8)     (8)
		|  --------------------------------
		|  |MA | AD| CT| UN| PART  | TYPE |
		|  --------------------------------
		|
		| この時点までで d6 には IOCS __BOOTINF の戻り値が保存されている。
		| これはこのコードの先頭の entry0 あたり参照。
		| で、それの下位2ビットは FD ドライブ番号、つまりユニット番号を
		| 示しているので swap すると、図でいう UN を用意したことになる。
		| それにマジック(MA)と FD のメジャー番号(TYPE)を追加して完成。
		andil	#3,%d6			| bootdev
		swap	%d6
		addil	#(B_DEVMAGIC+X68K_MAJOR_FD),%d6
		| d7 に起動方法を用意する。 参照。
		| RB_SINGLE はシングルユーザモードで起動するの意だが
		| この値は使われてないらしい。
		movl	#RB_SINGLE,%d7		| boothowto (unused)
		| a2 に入っていたエントリポイントを a0 に移して
		| a1 〜 a6 もクリアする。
		moval	%a2,%a0			| entry
		moval	%d0,%a1			| unused
		moval	%d0,%a2			| unused
		moval	%d0,%a3			| unused
		moval	%d0,%a4			| unused
		moval	%d0,%a5			| unused
		moval	%d0,%a6			| unused

		| /boot へジャンプ
		jmp	%a0@			| here we go

error_invalidboot:
error_invalidname:	BOOT_ERROR("Illegal secondary boot");
error_invalidfs:
error_invalidmagic:	BOOT_ERROR("Invalid magic")

| ここまでが 1KB に収まってないといけないので
| チェック用のグローバルラベル。
ASGLOBAL(first_kbyte)

目次へ戻る
isaki@NetBSD.org