boot.S revision 1.13 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