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 69ASENTRY_NOPROFILE(start) 70ASENTRY_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 77msg_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 85entry: 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 91disklabel: 92 .space 404 93disklabel_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!! 121clrbss: | 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 128chkmpu: 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?"); 139mpuok: 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 159boot_sasi_floppy: 160 | Floppy or SASI 161 cmpiw #0x90,%d0 162 jlt boot_dev_not_supp | SASI 163 164 | 165 | Boot from floppy 166 | 167boot_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) 173check_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 178init_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 184loop: 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 2001: 201 cmpl %d5,%d2 | if (%d2 > %d5) 202 jle 1f | 203 movel %d2,%d5 | max = %d2 2041: 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 224boot_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 | 232boot_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") 239boot_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 2691: 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 2771: 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 2921: 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 310scsiread: 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 328scsiread_core: 329 IOCS(__SCSIDRV) 330 rts 331 332boot_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. 339ENTRY_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. 346boot_error: 347 leal %pc@(msg_progname),%a1 348 IOCS(__B_PRINT) 349 moveal %sp@+,%a1 350 IOCS(__B_PRINT) 351ENTRY_NOPROFILE(exit) 352ENTRY_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 362msg_reboot: 363 .asciz "\r\n[Hit key to reboot]" 364 .even 365 366 .globl first_kbyte 367first_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. 384ENTRY_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 396badbaddr1: 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| 410ENTRY_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 422raw_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 439raw_read_exit: 440 moveml %sp@+,%d2-%d7/%a2-%a6 441 rts 442 443raw_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