Home | History | Annotate | Line # | Download | only in xxboot
      1 | $NetBSD: boot.S,v 1.13 2020/08/16 06:43:43 isaki Exp $
      2 
      3 |
      4 | (1) IPL (or previous stage loader) loads first 1KB of this primary
      5 |     bootloader to (*).  (*) is 0x2000 (from FD) or 0x2400 (from SASI/SCSI).
      6 |
      7 | (2) The first 1KB loads full primary bootloader (including first 1KB) from
      8 |     the boot partition to 0x3000.  And jump to there.
      9 |
     10 | (3) The full primary bootloader loads the secondary bootloader known as
     11 |     /boot from its filesystem to 0x6000.  And jump to there.
     12 |
     13 |       (1)         ->        (2)         ->        (3)
     14 |  +------------+        +------------+        +------------+    0x000000
     15 |  :            :        :            :        :            :
     16 |  +------------+        +------------+        +------------+    (*)
     17 |  | first 1KB  |        | first 1KB  |        | first 1KB  |
     18 |  +------------+        +------------+        +------------+    (*)+0x400
     19 |  :            :        :            :        :            :
     20 |  :            :        +------------+        +------------+    0x003000
     21 |  :            :        |full primary|        |full primary|
     22 |  :            :        |boot loader |        |boot loader |
     23 |  :            :        |(text+data) |        |(text+data) |
     24 |  :            :        +------------+        +------------+    0x005000
     25 |  :            :        |(startregs) |        |(startregs) |
     26 |  :            :        |(bss)       |        |(bss)       |
     27 |  :            :        +------------+        +------------+    0x006000
     28 |  :            :        :            :        | /boot      |
     29 |  :            :        :            :        +------------+
     30 |  :            :        :            :        :            :
     31 |  ~            ~        ~            ~        ~            ~
     32 |  :            :        :            :<-SP    :            :<-SP
     33 |  + - - - - - -+        + - - - - - -+        + - - - - - -+    0x100000
     34 |  :            :        :(heap)      :        :(heap)      :
     35 |  :            :        :            :        :            :
     36 |
     37 | The program code before first_kbyte
     38 | - must not access any text/data labels after first_kbyte
     39 |   (because it may not be loaded yet).
     40 | - must access any labels before first_kbyte by PC relative addressing
     41 |   (because this loader is assembled as starting from 0x3000 but is loaded
     42 |   at 0x2000 or 0x2400).
     43 | - must use RELOC() macro to access bss section (and first_kbyte as a jump
     44 |   destination address).
     45 |
     46 | The program code after first_kbyte can access any labels in all sections
     47 | directly.
     48 
     49 #include <machine/asm.h>
     50 #include "iocscall.h"
     51 
     52 #define RELOC(var)	%a5@(((var)-top):W)
     53 
     54 #define BOOT_ERROR(s)	jbsr boot_error; .asciz s; .even
     55 
     56 #define minN	(0)
     57 #define minC	(1)
     58 #define minH	(2)
     59 #define minR	(3)
     60 #define maxN	(4)
     61 #define maxC	(5)
     62 #define maxH	(6)
     63 #define maxR	(7)
     64 
     65 		.globl	_C_LABEL(bootmain)
     66 		.globl	_C_LABEL(startregs)
     67 		.text
     68 
     69 ASENTRY_NOPROFILE(start)
     70 ASENTRY_NOPROFILE(top)
     71 		bras	entry
     72 		.ascii	"SHARP/"
     73 		.ascii	"X680x0"
     74 		.word	0x8199,0x94e6,0x82ea,0x82bd
     75 		.word	0x8e9e,0x82c9,0x82cd,0x8cbb
     76 		.word	0x8ec0,0x93a6,0x94f0,0x8149
     77 msg_progname:
     78 		| This will be printed on boot_error.  And it also roles
     79 		| a signature in binary dump.
     80 		| Max length of PROG(without \0) is 14 ("fdboot_ustarfs").
     81 		.ascii	"\r\n\n"		| 3
     82 		.ascii	PROG			| 14
     83 		.asciz	": "			| 2+1
     84 		.org	msg_progname + 20
     85 entry:
     86 		jbra	disklabel_end
     87 
     88 		| Disklabel must be placed at 0x40 and the size is 404 bytes.
     89 		| (See LABELOFFSET in <machine/disklabel.h>)
     90 		.org	0x40
     91 disklabel:
     92 		.space	404
     93 disklabel_end:
     94 		| At first save all initial registers for observing traces
     95 		| of the IPL (or the previous bootloader).  At this point
     96 		| we cannot use RELOC() yet so that use absolute addressing.
     97 		| To prevent startregs from being cleared by subsequent bss
     98 		| initialization, we place it out of bss area.
     99 		moveml	%d0-%d7/%a0-%a7,startregs:W
    100 
    101 		| Initialize the screen.  Some IPL (060turbo ROM or genuine
    102 		| boot selector) don't initialize the screen.  It should be
    103 		| done as early as possible.
    104 		moveql	#0x10,%d1
    105 		IOCS(__CRTMOD)
    106 
    107 		| Set system stack
    108 		swap	%d1			| %d1 = 0x0010_0000
    109 		moveal	%d1,%sp
    110 
    111 		| Set base pointer.  Now we can use RELOC() macro.
    112 		leal	TEXTADDR:W,%a5
    113 
    114 		| Initialize bss.
    115 		| This code limits bss less than 64KB but it's no matter.
    116 		| The bss cannot grow more than 4KB.  See xxboot.ldscript.
    117 		leal	RELOC(__bss_start),%a1
    118 		movew	#_end - 1,%d0		| bss end
    119 
    120 		subw	%a1,%d0			| don't change this op!!
    121 clrbss:						|  see chkmpu below
    122 		clrb	%a1@+
    123 		dbra	%d0,clrbss
    124 
    125 		| If it boots from SCSI, %d4 has SCSI ID.
    126 		movel	%d4,RELOC(SCSI_ID)
    127 
    128 chkmpu:
    129 		| Check MPU beforehand since we want to use 68020 instructions.
    130 		| Here the above "subw %a1,%d0" = 0x9049 and %d0.w = -1 at this
    131 		| point, so that subsequent moveb loads
    132 		|   0x49 if MPU <= 010 (clrbss + %d0.w)
    133 		|   0x90 if MPU >= 020 (clrbss + %d0.w*2).
    134 		| This is a MOVE op, not a TST op because TST with pc-relative
    135 		| is not available on 000/010.
    136 		moveb	%pc@(clrbss-chkmpu-2:B,%d0:W:2),%d0
    137 		jmi	mpuok
    138 		BOOT_ERROR("MPU 68000?");
    139 mpuok:
    140 		|
    141 		| Check where did I boot from.
    142 		|
    143 		IOCS(__BOOTINF)
    144 		movel	%d0,RELOC(BOOT_INFO)	| save whole result
    145 
    146 		| %d0 = 0xHHWWWWWW
    147 		|
    148 		| HH:     how did I boot (powersw or alarm etc)
    149 		| WWWWWW: where did I boot from
    150 		|  0x80...0x8f		SASI
    151 		|  0x90...0x93		Floppy
    152 		|  0xed0000...0xed3ffe	SRAM
    153 		|  others		ROM (maybe SCSI)
    154 
    155 		bfextu	%d0{#8:#8},%d1
    156 		jne	boot_rom_ram		| ROM or SRAM
    157 		| FALLTHROUGH			| SASI or Floppy
    158 
    159 boot_sasi_floppy:
    160 		| Floppy or SASI
    161 		cmpiw	#0x90,%d0
    162 		jlt	boot_dev_not_supp	| SASI
    163 
    164 		|
    165 		| Boot from floppy
    166 		|
    167 boot_floppy:
    168 		| Make PDA+MODE
    169 		lslw	#8,%d0			| %d0=$00009X00 (X is unit#)
    170 		moveql	#0x70,%d1
    171 		orw	%d0,%d1			| %d1=$00009X70 = (PDA<<8)+MODE
    172 		movel	%d1,RELOC(FDMODE)
    173 check_fd_format:
    174 		| Check fd format.
    175 		| Obtain min & max sector # of track(cylinder) 0.
    176 		| On x68k, we can consider only double-sided floppy.
    177 		moveql	#0,%d2
    178 init_loop:
    179 		| On 1st time, clear %d3-%d5 with zero.
    180 		| On 2nd time, initialize %d3-%d5 with first %d2.
    181 		movel	%d2,%d3			| %d3: initial NCHR
    182 		movel	%d2,%d4			| %d4: minimum NCHR
    183 		movel	%d2,%d5			| %d5: maximum NCHR
    184 loop:
    185 		| B_READID with MSB of %d2 set obtains detected CHRN to %d2.
    186 		moveql	#1,%d2			| %d2 = 0x00000001
    187 		rorl	#1,%d2			| %d2 = 0x80000000
    188 		IOCS(__B_READID)
    189 						| %d2 = 0xCCHHRRNN
    190 		rorl	#8,%d2			| %d2 = 0xNNCCHHRR
    191 
    192 		| On 1st time, goto init_loop with %d2 (%d2 is not zero).
    193 		| On 2nd time, fall through because %d3 is not zero.
    194 		tstl	%d3
    195 		jeq	init_loop
    196 
    197 		cmpl	%d4,%d2			| if (%d2 < %d4)
    198 		jge	1f			|
    199 		movel	%d2,%d4			|  min = %d2
    200 1:
    201 		cmpl	%d5,%d2			| if (%d2 > %d5)
    202 		jle	1f			|
    203 		movel	%d2,%d5			|  max = %d2
    204 1:
    205 		cmpl	%d3,%d2			| if (%d2 == %d3) break
    206 		jne	loop
    207 
    208 		| Assume 2HD
    209 		oriw	#0x0100,%d5		| FDSEC.maxsec.H = 1
    210 		moveml	%d4-%d5,RELOC(FDSEC)	| Store
    211 		| end of check_fd_format
    212 
    213 		| read 8KB myself from floppy
    214 						| %d1: (PDA<<8)+MODE already
    215 		movel	%d4,%d2			| %d2: read pos = first sector
    216 		moveql	#0x20,%d3		| %d3: read bytes = (0x20 << 8)
    217 		lsll	#8,%d3			|  = 0x2000 = 8192
    218 		leal	%a5@,%a1		| %a1: dest buffer
    219 		IOCS(__B_READ)
    220 
    221 		| Jump to full parimary loader
    222 		jmp	RELOC(first_kbyte)
    223 
    224 boot_rom_ram:
    225 		| ROM(SCSI) or SRAM
    226 		cmpib	#0xed,%d1
    227 		jeq	boot_dev_not_supp	| SRAM
    228 
    229 		|
    230 		| Boot from SCSI
    231 		|
    232 boot_scsi:
    233 		| get block length of the SCSI disk
    234 		leal	RELOC(SCSI_CAP),%a1
    235 		SCSIIOCS(__S_READCAP)
    236 		tstl	%d0
    237 		jeq	boot_scsi1
    238 		BOOT_ERROR("READCAP failed")
    239 boot_scsi1:
    240 		movel	RELOC(SCSI_CAP+4),%d0	| %d0 = blocksize in bytes
    241 		lsrl	#2,%d0			| %d0 = blocksize in longword
    242 		moveql	#25,%d5
    243 		bfffo	%d0{#0:#32},%d1		| 25:256 24:512 23:1024 22:2048
    244 		subl	%d1,%d5			|  0:256  1:512  2:1024  3:2048
    245 		movel	%d5,RELOC(SCSI_BLKLEN)	| %d5 = sector length index
    246 
    247 		| Find out the start position of the boot partition.
    248 		| There seems to be no interface or consensus about this and
    249 		| so that we would have to do it heuristicly.
    250 		|
    251 		| ROM firmware:
    252 		|	pass read pos (in block #, aka sector #) in %d2.
    253 		|	Human68k-style partition table does not exist.
    254 		|	%d2 is 4 at the maximum.
    255 		| SCSI IPLs (genuine and SxSI):
    256 		|	pass read pos (in kilobytes) in %d2.
    257 		|	%d2 is bigger than 0x20.
    258 		|	partition table on the memory is destroyed.
    259 		| BOOT MENU Ver.2.22:
    260 		|	passes partition table entry address in %a0.
    261 		|	%d2 is cleared to zero
    262 		| No other IPLs are supported.
    263 
    264 		tstl	%d2
    265 		jne	1f
    266 		| If no information in %d2, probably from BOOT MENU.
    267 		| %a0 points the on-memory partition table entry.
    268 		movel	%a0@(0x0008),%d2	| %d2 = pos in kbyte
    269 1:
    270 		moveql	#0x20,%d3
    271 		cmpl	%d3,%d2
    272 		jcs	1f			| jump if %d2 > 0x20
    273 		| SCSI IPL or BOOT MENU.
    274 		| At this point, %d2 is pos in kbyte in all cases.
    275 		lsll	#8,%d2			| %d2 = pos in longword
    276 		divul	%d0,%d2			| %d2 = pos in sector
    277 1:
    278 		| At this point, %d2 is pos in sector in all cases.
    279 		| TDSIZE = 8192, TDSIZE / 4 = 0x800 = (0x20 << 6).
    280 		lsll	#6,%d3			| %d3 = TDSIZE in longword
    281 		divul	%d0,%d3			| %d0 = TDSIZE in sector
    282 		| Read full primary bootloader
    283 		moveal	%a5,%a1			| %a1 = dest buffer
    284 		jbsr	scsiread
    285 
    286 		| Selected start sector should not <= 4.  There should be
    287 		| partition table.  If so, repoints to zero(?).
    288 		moveql	#5,%d0
    289 		cmpl	%d0,%d2
    290 		bcc	1f
    291 		moveql	#0,%d2
    292 1:
    293 		movel	%d2,RELOC(SCSI_PARTTOP)
    294 
    295 		| Jump to full parimary loader
    296 		jmp	RELOC(first_kbyte)
    297 
    298 |
    299 | scsiread
    300 |	Read SCSI disk using __S_READ as possible.  If __S_READ cannot be
    301 |	used (due to read length or offset), use __S_READEXT instead.
    302 | input:
    303 |	%d2.l: pos in sector
    304 |	%d3.l: len in sector (must be < 65536)
    305 |	%d4.l: target SCSI ID
    306 |	%d5.l: sector length index (0:256, 1:512, 2:1024, 3:2048, ...)
    307 |	%a1.l: buffer address
    308 | destroy:
    309 |	%d0,%d1
    310 scsiread:
    311 		| if (len >= 256 || pos + len >= 0x200000)
    312 		|   use READEXT
    313 		| else
    314 		|   use READ
    315 
    316 		moveql	#__S_READEXT,%d1
    317 
    318 		cmpiw	#256,%d3
    319 		jge	scsiread_core		| if (d3 >= 256) use READEXT
    320 
    321 		movel	%d2,%d0
    322 		addl	%d3,%d0			| %d0 = pos + len
    323 		jcs	scsiread_core		| if overflow, use READEXT
    324 		bftst	%d0{#0:#11}		| if (pos + len >= 0x200000)
    325 		jne	scsiread_core		|  use REAEXT
    326 
    327 		moveql	#__S_READ,%d1		| else use READ
    328 scsiread_core:
    329 		IOCS(__SCSIDRV)
    330 		rts
    331 
    332 boot_dev_not_supp:
    333 		BOOT_ERROR("not supported device");
    334 
    335 |
    336 | void __dead BOOT_ERROR(const char *msg);
    337 |	Print an error message, wait for key press, and reboot.
    338 |	Called from C.
    339 ENTRY_NOPROFILE(BOOT_ERROR)
    340 		addql	#4,%sp			| throw away return address
    341 		| FALLTHROUGH
    342 |
    343 | BOOT_ERROR(msg)
    344 |	Print an error message, wait for key press, and reboot.
    345 |	Called from asm.
    346 boot_error:
    347 		leal	%pc@(msg_progname),%a1
    348 		IOCS(__B_PRINT)
    349 		moveal	%sp@+,%a1
    350 		IOCS(__B_PRINT)
    351 ENTRY_NOPROFILE(exit)
    352 ENTRY_NOPROFILE(_rtt)
    353 		leal	%pc@(msg_reboot),%a1
    354 		IOCS(__B_PRINT)
    355 
    356 		| wait for a key press (or release of a modifier)
    357 		IOCS(__B_KEYINP)
    358 
    359 		| issue software reset
    360 		trap	#10
    361 		| NOTREACHED
    362 msg_reboot:
    363 		.asciz	"\r\n[Hit key to reboot]"
    364 		.even
    365 
    366 		.globl	first_kbyte
    367 first_kbyte:
    368 |--------------------------------------------------------------------------
    369 |
    370 #if defined(SELFTEST)
    371 		jbsr	selftest_ashldi3
    372 		jbsr	selftest_ashrdi3
    373 		jbsr	selftest_memcmp
    374 		jbsr	selftest_memmove
    375 		jbsr	selftest_memset
    376 #endif
    377 
    378 		jmp	_C_LABEL(bootmain)
    379 		| NOTREACHED
    380 
    381 |
    382 | uint32_t badbadd(void *addr)
    383 |	returns 1 if reading addr occurs bus error.  Otherwise it returns 0.
    384 ENTRY_NOPROFILE(badbaddr)
    385 		leal	0x0008:W,%a1		| bus error vector
    386 		moveql	#1,%d0
    387 		leal	%pc@(badbaddr1),%a0
    388 		movew	%sr,%sp@-
    389 		oriw	#0x0700,%sr		| keep out interrupts
    390 		movel	%a1@,%sp@-
    391 		movel	%a0,%a1@		| set bus error vector
    392 		movel	%sp,%d1			| save sp
    393 		moveal	%sp@(10),%a0
    394 		tstb	%a0@			| try read...
    395 		moveql	#0,%d0			| this is skipped on bus error
    396 badbaddr1:
    397 		moveal	%d1,%sp			| restore sp
    398 		movel	%sp@+,%a1@
    399 		movew	%sp@+,%sr
    400 		rts
    401 
    402 |
    403 | int raw_read(uint32_t blkpos, uint32_t bytelen, void *buf)
    404 |	blkpos:  read start position in 512 byte block unit (always?).
    405 |	bytelen: read length in bytes.
    406 |	         caller already avoids bytelen == 0 so that no checks here.
    407 |	         must be a multiple of sector size on scsi.
    408 |	buf:     destination buffer address
    409 |
    410 ENTRY_NOPROFILE(raw_read)
    411 		moveal	%sp,%a1
    412 		moveml	%d2-%d7/%a2-%a6,%sp@-
    413 		moveml	%a1@,%d0/%d2-%d3/%a1	| %d0 (return address)
    414 						| %d2 blkpos
    415 						| %d3 bytelen
    416 						| %a1 buf
    417 		| At this point boot device is either floppy or SCSI.
    418 		tstb	%pc@(BOOT_INFO+1)
    419 		jeq	raw_read_floppy
    420 		| FALLTHROUGH
    421 
    422 raw_read_scsi:
    423 						| %d2 = pos from device top
    424 						|  in 512 bytes/block
    425 		lsll	#1,%d2			| %d2 = in 256 bytes/block
    426 		movel	%pc@(SCSI_BLKLEN),%d5	| %d5 = sector length index
    427 		lsrl	%d5,%d2			| %d2 = pos from device top
    428 						|  in media sector size
    429 
    430 		divull	%pc@(SCSI_CAP+4),%d0:%d3| %d3 = bytelen / sectsize
    431 						| %d0 = bytelen % sectsize
    432 		tstl	%d0
    433 		jeq	.Lraw1
    434 		BOOT_ERROR("Err1")		| ASSERT(bytelen%sectsize==0)
    435 .Lraw1:
    436 		movel	%pc@(SCSI_ID),%d4	| %d4 = SCSI ID
    437 		jbsr	scsiread
    438 
    439 raw_read_exit:
    440 		moveml	%sp@+,%d2-%d7/%a2-%a6
    441 		rts
    442 
    443 raw_read_floppy:
    444 		| nhead = FDSEC.maxsec.H - FDSEC.minsec.H + 1
    445 		|       = 2;
    446 		| nsect = FDSEC.maxsec.R - FDSEC.minsec.R + 1;
    447 		|
    448 		| sect = (blkpos % nsect) + FDSEC.minsec.R;
    449 		| head = ((blkpos / nsect) % nhead) + FDSEC.minsec.H;
    450 		| cyl  = ((blkpos / nsect) / nhead) + FDSEC.minsec.C;
    451 		|
    452 		| NCHR = (FDSEC.minsec.N << 24) |
    453 		|      (cyl << 16) |
    454 		|      (head << 8) |
    455 		|      sect;
    456 
    457 		| calc nsect.
    458 		moveql	#1,%d0			| %d0 = 1
    459 		addb	%pc@(FDSEC+maxR),%d0	| %d0 = 1 + maxsec.R
    460 		subb	%pc@(FDSEC+minR),%d0	| %d0 = 1 + maxsec.R - minsec.R
    461 						|     = nsect
    462 
    463 		| Convert blkpos to N/C/H/R.
    464 		divuw	%d0,%d2			| %d2.hw = blkpos % nsect
    465 						| %d2.lw = blkpos / nsect
    466 		| Here, %d2.hw becomes sector number and .lw becomes cyl+head.
    467 		| %d2.lw = %0000_0000_CCCC_CCCH in binary form.  LSB of
    468 		| (blkpos / nsect) is head number because we support only
    469 		| double-sided floppy here.
    470 						| %d2.w = %0000_0000_CCCC_CCCH
    471 		lslw	#7,%d2			| %d2.w = %0CCC_CCCC_H000_0000
    472 		lsrb	#7,%d2			| %d2.w = %0CCC_CCCC_0000_000H
    473 						| i.e,
    474 						| %d2 = $00rrCCHH
    475 		swap	%d2			| %d2 = $CCHH00rr
    476 		lslw	#8,%d2			| %d2 = $CCHHrr00
    477 		| two bytes from odd FDSEC+minR is (minR << 8 | maxN) and
    478 		| minN == maxN always.
    479 		addw	%pc@(FDSEC+minR),%d2	| %d2 = $CCHHRRNN
    480 		rorl	#8,%d2			| %d2 = $NNCCHHRR
    481 
    482 		movel	%pc@(FDMODE),%d1	| %d1 = PDA+MODE
    483 		IOCS(__B_READ)
    484 		andil	#0xf8ffff00,%d0		| Check status (must be zero)
    485 		jeq	raw_read_exit
    486 		BOOT_ERROR("B_READ failed");
    487 
    488 |
    489 | BSS
    490 |
    491 		BSS(BOOT_INFO, 4)	| whole result of IOCS BOOTINF
    492 
    493 		BSS(FDMODE, 4)
    494 		BSS(FDSEC, 8)		| +0: (minN) sector length
    495 					| +1: (minC) track number
    496 					| +2: (minH) head
    497 					| +3: (minR) sector number
    498 					| +4: (maxN) sector length
    499 					| +5: (maxC) track number
    500 					| +6: (maxH) head
    501 					| +7: (maxR) sector number
    502 
    503 		BSS(SCSI_ID, 4)		| SCSI ID, if booted from SCSI
    504 		BSS(SCSI_CAP, 8)	| result of SCSI READCAP
    505 					|  +0.L: total number of logical blocks
    506 					|  +4.L: block length in bytes
    507 		BSS(SCSI_PARTTOP, 4)	| top sector # of this partition
    508 		BSS(SCSI_BLKLEN ,4)	| sector length index
    509 					|  0:256, 1:512, 2:1024, 3:2048, ..
    510