1 /* boot.S: The initial boot code for the Sparc port of Linux. 2
3 Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu) 4
5 This file has to serve three purposes. 6
7 1) determine the prom-version and cpu/architecture 8 2) print enough useful info before we start to execute 9 c-code that I can possibly begin to debug things 10 3) Hold the vector of trap entry points 11
12 The Sparc offers many challenges to kernel design. Here I will 13 document those I have come across thus far. Upon bootup the boot 14 prom loads your a.out image into memory. This memory the prom has 15 already mapped for you, however as far as I can tell the virtual 16 address cache is not turned on although the MMU is translating 17 things. You get loaded at 0xf8004000 exactly. So, when you link 18 a boot-loadable object you want to do something like: 19
20 ld -e start -T f8004000 -o mykernel myobj1.o myobj2.o .... 21
22 to produce a proper image. 23
24 At boot time you are given (as far as I can tell at this time) 25 one key to figure out what machine you are one and what devices 26 are available. The prom when it loads you leaves a pointer to 27 the 'rom vector' in register %o0 right before it jumps to your 28 starting address. This is a pointer to a struct that is full of 29 pointer to functions (ie. printf, halt, reboot), pointers to 30 linked lists (ie. memory mappings), and pointer to empirical 31 constants (ie. stdin and stdout magic cookies + rom version). 32 Starting with this piece of information you can figure out 33 just about anything you want about the machine you are on. 34 */ 35
36 #include "boot.h"
37 #include "version.h"
38
39 .data
40
41 /* First thing to go in the data segment is the interrupt stack. */ 42
43 .globl _intstack
44 .globl _eintstack
45 _intstack:
46 .skip 4 * NBPG ! 16k = 128 128-byte stack frames
47 _eintstack:
48
49
50
51 /* 52 The following are used with the prom_vector node-ops to figure out 53 the cpu-type 54 */ 55
56 .globl _cputyp
57
58 _cputyp:
59 .word 1
60
61 _cputypval:
62 .asciz "sun4c"
63 .ascii " "
64
65 /* 66 * Sun people can't spell worth damn. "compatability" indeed. 67 * At least we *know* we can't spell, and use a spell-checker. 68 */ 69 _cputypvar:
70 .asciz "compatability"
71
72 _cputypvallen = _cputypvar - _cputypval
73
74 /* This hold the prom-interface-version number for either v0 or v2. */ 75
76 .align 4
77 .globl _prom_iface_vers
78
79 _prom_iface_vers: .skip 4
80
81 /* WARNING: evil messages follow */ 82
83 .align 4
84
85 sun4_notsup:
86 .asciz "Sparc-Linux: sun4 support not implemented yet\n\n"
87 .align 4
88
89 sun4m_notsup:
90 .asciz "Sparc-Linux: sun4m support does not exist\n\n"
91 .align 4
92
93 sun4d_notsup:
94 .asciz "Sparc-Linux: sun4d support does not exist\n\n"
95 .align 4
96
97 you_lose:
98 .asciz "You lose..... Thanks for playing...\n"
99 .align 4
100
101 /* 102 Fill up the prom vector, note in particular the kind first element, 103 no joke. 104 */ 105
106 .globl _prom_vector_p
107
108 _prom_vector_p: .skip 4
109 prom_magic: .skip 4 ! magic mushroom, beware...
110 prom_rom_vers: .skip 4 ! interface version (v0 or v2)
111 prom_pluginvers: .skip 4 ! XXX help help help ???
112 prom_revision: .skip 4 ! PROM revision (ie. 1.4)
113 prom_bootstr: .skip 4 ! what we are invoked with
114 prom_putchar: .skip 4 ! void putchar(int ch) BLOCKING.
115 prom_getchar: .skip 4 ! int getchar(void) BLOCKING.
116 prom_nputchar: .skip 4 ! int putchar(int ch) non-block
117 prom_ngetchar: .skip 4 ! int getchar(void) non-block
118 prom_halt: .skip 4 ! void halt(void) solaris friend
119 prom_eval: .skip 4 ! void eval(int len, char* string)
120 prom_v0mem_desc: .skip 4 ! V0 memory descriptor list ptr.
121 prom_nodefuncs: .skip 4 ! Magical Node functions
122 prom_v0devfuncs: .skip 4 ! V0 device operations
123 prom_putstring: .skip 4 ! prom putstring()
124 prom_bootme: .skip 4 ! reset()
125 prom_printf: .skip 4 ! minimal printf()
126
127 /* The prom_abort pointer MUST be mapped in all contexts, because if you 128 don't then if a user process is running when you press the abort key 129 sequence, all sorts of bad things can happen 130 */ 131
132 prom_abort: .skip 4 ! "L1-A" magic cookie
133 ! must be mapped in ALL contexts
134 prom_ticks: .skip 4 ! number of ticks since reset
135
136 /* prom_sync is a place where the kernel should place a pointer to a kernel 137 function that when called will sync all pending information to the drives 138 and then promptly return. If the kernel gets aborted with 'L1-A' one can 139 give the 'sync' command to the boot prompt and this magic cookie gets 140 executed. Nice feature eh? 141 */ 142
143 prom_sync: .skip 4 ! hook in prom for "sync" func
144 prom_v0bootarg: .skip 4 ! v0 prom boot arguments
145 prom_v2bootarg: .skip 4 ! same as above for v2 proms
146 prom_ethaddr_func: .skip 4 ! extract ethernet device address
147 prom_v2devfunc: .skip 4 ! ptr to v2 style device ops.
148 prom_xtra_array: .skip 4 ! who knows :-( help help
149 prom_setcontext: .skip 4 ! set context on sun4c
150 prom_stdin: .skip 4 ! prom stdin magic cookie
151 prom_stdout: .skip 4 ! prom stdout magic cookie
152
153
154 .align 4
155
156 .globl boot_msg
157
158 /* memory descriptor property strings, v2 = yuk yuk yuk */ 159 /* XXX how to figure out vm mapped by prom? May have to scan magic addresses */ 160
161 mem_prop_physavail: .asciz "available"
162 mem_prop_phystot: .asciz "reg"
163
164 /* v2_memory descriptor struct kludged here for assembly, if it ain't broke */ 165
166 .align 4
167 v2_mem_struct: .skip 0xff
168
169 .align 4
170 v2_printf_physavail: .asciz "Physical Memory Available: 0x%x bytes"
171 v2_printf_phystot: .asciz "Physical Memory: 0x%x bytes"
172
173 /* A place to store property strings returned from the prom 'node' funcs */ 174
175 .align 4
176 prop_string_buf: .skip 32
177
178 prop_name: .asciz "name"
179 .align 4
180
181 current_node: .skip 4
182 .align 4
183
184
185 /* nice little boot message */ 186
187 boot_msg:
188 .ascii "Booting Sparc-Linux V0.00PRE-ALPHA "
189 .ascii WHO_COMPILED_ME
190 .asciz " \n"
191 .align 4
192
193 .globl boot_msg2
194
195 boot_msg2:
196 .asciz "Booting Sparclinux V0.00 PRE-ALPHA on a (SUN4C)\n\n"
197
198 .align 4
199
200 pstring1:
201 .asciz "Prom Magic Cookie: 0x%x "
202 .align 4
203
204 pstring2:
205 .asciz "Interface Version: v%d\n"
206 .align 4
207
208 pstring3:
209 .asciz "Prom Revision: V%d\n\n"
210 .align 4
211
212 pstring4:
213 .ascii "Total Physical Memory: %d bytes\nVM mapped by Prom: %d bytes\n"
214 .asciz "Available Physical Memory: %d bytes\n"
215 .align 4
216
217
218 newline:
219 .asciz "\n"
220 .align 4
221
222 .text
223
224 .globl _msgbuf
225 msgbufsize = NBPG ! 1 page for msg buffer
226 _msgbuf = KERNBASE + NBPG
227
228
229 IE_reg_addr = _msgbuf + msgbufsize ! this page not used; points to IEreg
230
231 /* 232 ignore the following variable settings, I used them when I had 233 no stinkin idea what the linker was doing with the symbols to 234 get them in the right place for load time 235 */ 236
237 whereis_kernbase = KERNBASE
238 whereis_prom_vector_p = _prom_vector_p-KERNBASE
239
240 /* Ok, things start to get interesting. We get linked such that 'start' 241 is the entry symbol. However, it is real low in kernel address space 242 and as such a nifty place to place the trap table. We achieve this goal 243 by just jumping to 'dostart' for the first trap's entry as the sparc 244 never receives the zero trap as it is real special. 245
246 Each trap entry point is the size of 4 sparc instructions (or 4 bytes 247 * 4 insns = 16 bytes). There are 128 hardware traps (some undefined 248 or unimplemented) and 128 software traps (ditto). 249
250 One of the instructions must be a branch. More often than not this 251 will be to a trap handler entry point because it is completely 252 impossible to handle any trap in 4 insns. I welcome anyone to 253 challenge this theory. :-) 254
255 On entry into this table the hardware has loaded the program counter 256 at which the trap occurred into register %l1 and the next program 257 counter into %l2, this way we can return from the trap with a simple 258
259 jmp %l1; rett %l2 260
261 after properly servicing the trap. It wouldn't be a bad idea to load 262 some more information into the local regs since we have technically 263 2 or 3 instructions to play with besides the jmp to the 'real' trap 264 handler (one can even go in the delay slot). For now I am going to put 265 the %psr (processor status register) and the trap-type value in %l0 266 and %l3 respectively. 267
268 TODO: Write cheesy macros to make this table more manageable. 269 Ugh, this shit is long... 270
271 */ 272
273 .globl start
274 .globl _trapbase
275 start:
276 _trapbase:
277 b dostart; nop; nop; nop ! we never get trap #0 it is special
278 ! TRAP code should go here, TODO :>
279
280 _msgbufmapped:
281 .word 1
282
283
284 /* The following two things point to window management tables. The first 285 one is used to quickly look up how many user windows there are from 286 trap-land. The second is used in a trap handler to determine if a rett 287 instruction will land us smack inside the invalid window that possibly 288 the trap was called to fix-up. 289 */ 290
291 .data
292 .skip 32 ! alignment byte & negative indices
293 lnx_uw: .skip 32 ! u_char uwtab[-31..31];
294 lnx_winmask: .skip 32 ! u_char wmask[0..31];
295
296 .text
297
298
299 /* Cool, here we go. Pick up the romvec pointer in %o0 and stash it in 300 %g7 and at _prom_vector_p. And also quickly check whether we are on 301 a v0 or v2 prom. 302 */ 303
304 dostart: mov %o0, %g7
305 st %o0, [_prom_vector_p] ! we will need it later
306 ld [%g7 + 0x4], %o2
307 cmp %o2, 2 ! a v2 prom?
308 be found_v2
309 nop
310
311 /* Old sun4's pass our load address into %o0 instead of the prom 312 pointer. On sun4's you have to hard code the romvec pointer into 313 your code. Sun probably still does that because they don't even 314 trust their own "OpenBoot" specifications. 315 */ 316
317 set 0x4000, %g6
318 cmp %o0, %g6 ! an old sun4?
319 beq no_sun4_here
320 nop
321
322 st %g0, [_prom_iface_vers] ! useless, disappear soon
323 b not_v2
324 nop
325
326 found_v2:
327 set 0x2, %o5
328 st %o5, [_prom_iface_vers]
329
330 not_v2:
331
332 /* Get the machine type via the mysterious romvec node operations. 333 Here we can find out whether we are on a sun4 sun4c, sun4m, or 334 a sun4m. The "nodes" are set up as a bunch of n-ary trees which 335 you can traverse to get information about devices and such. The 336 information acquisition happens via the node-ops which are defined 337 in the linux_openprom.h header file. Of particular interest is the 338 'nextnode(int node)' function as it does the smart thing when 339 presented with a value of '0', it gives you the first node in the 340 tree. These node integers probably offset into some internal prom 341 pointer table the openboot has. It's completely undocumented, so 342 I'm not about to go sifting through the prom address space, but may 343 do so if I get suspicious enough. :-) 344 */ 345
346 mov 0, %o0 ! next_node(0) = first_node
347 ld [%g7 + 0x1c], %o4
348 ld [%o4], %o4
349 call %o4
350 nop
351
352 set _cputypvar, %o1 ! first node has cpu-arch
353 set _cputypval, %o2 ! information, the string
354 ld [%g7 + 0x1c], %o4 ! 'compatibility' tells
355 ld [%o4 + 0x0c], %o4 ! that we want 'sun4x' where
356 call %o4 ! x is one of '', 'c', 'm',
357 nop ! 'd' or 'e'. %o2 holds pointer
358 ! to a buf where above string
359 ! will get stored by the prom.
360 set _cputypval, %o2
361 ldub [%o2 + 4], %o0
362 cmp %o0, 'c' ! we already know we are not
363 beq is_sun4c ! on a plain sun4 because of
364 nop ! the check for 0x4000 in %o0
365 cmp %o0, 'm' ! at start:
366 beq is_sun4m
367 nop
368 b no_sun4d_here ! god bless the person who
369 nop ! tried to run this on sun4d
370
371 is_sun4m:
372 is_sun4c: ! OK, this is a sun4c, yippie
373 mov %g7, %g6 ! load up the promvec offsets
374 st %g6, [prom_magic] ! magic mushroom :>
375 add %g7, 0x4, %g6
376 st %g6, [prom_rom_vers]
377 add %g7, 0x8, %g6
378 st %g6, [prom_pluginvers]
379 add %g7, 0xc, %g6
380 st %g6, [prom_revision]
381 add %g7, 0x10, %g6
382 st %g6, [prom_v0mem_desc]
383 add %g7, 0x1c, %g6
384 st %g6, [prom_nodefuncs]
385 add %g7, 0x20, %g6
386 st %g6, [prom_bootstr]
387 add %g7, 0x24, %g6
388 st %g6, [prom_v0devfuncs]
389 add %g7, 0x48, %g6
390 st %g6, [prom_stdin]
391 add %g7, 0x4c, %g6
392 st %g6, [prom_stdout]
393 add %g7, 0x54, %g6
394 st %g6, [prom_putchar]
395 add %g7, 0x50, %g6
396 st %g6, [prom_getchar]
397 add %g7, 0x5c, %g6
398 st %g6, [prom_nputchar]
399 add %g7, 0x58, %g6
400 st %g6, [prom_ngetchar]
401 add %g7, 0x60, %g6
402 st %g6, [prom_putstring]
403 add %g7, 0x64, %g6
404 st %g6, [prom_bootme]
405 add %g7, 0x68, %g6
406 st %g6, [prom_printf]
407 add %g7, 0x6c, %g6
408 st %g6, [prom_abort]
409 add %g7, 0x70, %g6
410 st %g6, [prom_ticks]
411 add %g7, 0x74, %g6
412 st %g6, [prom_halt]
413 add %g7, 0x78, %g6
414 st %g6, [prom_sync]
415 add %g7, 0x7c, %g6
416 st %g6, [prom_eval]
417 add %g7, 0x80, %g6
418 st %g6, [prom_v0bootarg]
419 add %g7, 0x84, %g6
420 st %g6, [prom_ethaddr_func]
421 add %g7, 0x88, %g6
422 st %g6, [prom_v2bootarg]
423 add %g7, 0x98, %g6
424 st %g6, [prom_v2devfunc]
425 add %g7, 0xc8, %g6
426 st %g6, [prom_xtra_array]
427 add %g7, 0x104, %g6
428 st %g6, [prom_setcontext]
429
430 /* That was easy, now lets try to print some message on the screen. 431 We have to be careful because the prom addressed things weird and 432 we aren't really mapped into memory as far as the rom routines are 433 concerned. So all addresses we have ourselves and would like the 434 prom to actually use must be calculated as (addr - KERNBASE) in order 435 for anything to work at all. We will map ourselves later before we 436 call any c-code to avoid this hassle. 437 */ 438
439 set boot_msg-KERNBASE, %o0
440 ld [prom_printf-KERNBASE], %o2
441 ld [%o2], %o1
442 call %o1 ! print boot message #1
443 nop
444
445 _newline: set newline-KERNBASE, %o0
446 ld [prom_printf-KERNBASE], %o2
447 ld [%o2], %o1
448 call %o1
449 nop
450
451 b 0f
452 nop ! damn delay slots...
453
454 0: nop ! duh
455 set pstring1-KERNBASE, %o0
456 ld [prom_printf-KERNBASE], %o3
457 ld [%o3], %o2
458 ld [prom_magic-KERNBASE], %o3
459 ld [%o3], %o1
460 call %o2
461 nop; nop; nop
462
463 set pstring2-KERNBASE, %o0
464 ld [prom_printf-KERNBASE], %o3
465 ld [%o3], %o2
466 ld [_prom_iface_vers], %o3
467 ld [%o3], %o1
468 call %o2
469 nop; nop; nop
470
471 /* Print out various bits of memory information. At this point 472 I just cycle through the documented v0_prom memory lists for 473 the values. They are linked lists and allow for description of 474 non-contiguous physical memory configurations, thus the 'memloop' 475 things to traverse the linked lists. 476 */ 477
478 /* Things are different for v0 and v2. v2 requires traversing the node trees 479 and that really sucks. 480 */ 481
482 /* Another Note: 483 The prom printf() function can take up to 5 arguments in registers 484 %o1 -- %o5 , the format string goes in %o0. It is your usual libc 485 printf() believe it or not. 486 */ 487
488 cmp %o0, 0x2
489 be v2_mem_probe
490 nop
491
492 set pstring4-KERNBASE, %o0
493 ld [prom_printf-KERNBASE], %o5
494 ld [%o5], %o4
495 ld [_prom_vector_p], %l1
496 ld [%l1+16], %l2
497 ld [%l2], %l3
498 ld [%l3 + 8], %o1 ! 'nbytes' memory accumulator
499
500 ld [_prom_vector_p], %l1
501 ld [%l1 + 16], %l2
502 ld [%l2], %l3
503 ld [%l3], %l4
504
505 memloop:
506 cmp %l4, 0
507 be mv_to_vmprom ! is there more?
508 nop
509
510 ld [%l4 + 0x8], %l6 ! apparently so...
511 add %o1, %l6, %o1
512 b memloop
513 ld [%l4], %l4
514
515 mv_to_vmprom:
516
517 ld [_prom_vector_p], %l0
518 ld [%l0 + 20], %l1
519 ld [%l1], %l2
520 ld [%l2 + 8], %o2 ! memory accumulator
521
522 ld [_prom_vector_p], %l0
523 ld [%l0 + 20], %l1
524 ld [%l1], %l2
525 ld [%l2], %l4
526
527 memloop2:
528 cmp %l4, 0
529 be mv_to_vmprom2 ! is there more?
530 nop
531 ld [%l4 + 0x8], %l6 ! apparently so...
532 add %o2, %l6, %o2
533 b memloop2
534 ld [%l4], %l4
535
536 mv_to_vmprom2:
537
538 ld [_prom_vector_p], %l0
539 ld [%l0 + 24], %l1
540 ld [%l1], %l2
541 ld [%l2 + 8], %o3 ! memory accumulator
542
543 ld [_prom_vector_p], %l0
544 ld [%l0 + 24], %l1
545 ld [%l1], %l2
546 ld [%l2], %l4
547
548 memloop3:
549 cmp %l4, 0
550 be mv_to_vmprom3 ! is there more?
551 nop
552 ld [%l4 + 0x8], %l6 ! apparently so...
553 add %o3, %l6, %o3
554 b memloop3
555 ld [%l4], %l4
556
557 mv_to_vmprom3:
558
559 call %o4
560 nop; nop; nop
561
562
563 set newline-KERNBASE, %o0
564 ld [prom_printf-KERNBASE], %o2
565 ld [%o2], %o1
566 call %o1
567 nop
568
569 b halt_me
570 nop
571
572 no_sun4_here:
573 ld [%g7 + 0x68], %o1
574 set sun4_notsup, %o0
575 call %o1
576 nop
577 b rest_of_boot ! next stage...
578 nop
579
580 v2_mem_probe:
581 set you_lose-KERNBASE, %o0 ! I just print this
582 ld [prom_printf-KERNBASE], %o1 ! crap to debug my node
583 ld [%o1], %o2 ! routines :-)
584 call %o2
585 nop
586
587 st %g0, [current_node]
588 set prop_string_buf, %o2
589 or %g0, %g0, %o0
590 ld [prop_name], %o1
591 or %g0, 31, %o3
592
593 node_find_loop:
594 ld [prom_nodefuncs], %o4
595 ld [%o4 + 0xc], %o4
596 call %o4
597 nop
598 ld [prop_string_buf], %l3
599 cmp %l3, 'm'
600 bne node_find_loop2
601 ld [prop_string_buf + 1], %l3
602 cmp %l3, 'e'
603 bne node_find_loop2
604 ld [prop_string_buf + 2], %l3
605 cmp %l3, 'm'
606 bne node_find_loop2
607 nop
608 b found_mem_node
609 nop
610
611 node_find_loop2:
612 ld [current_node], %o0 ! get next node
613 ld [prom_nodefuncs], %o1
614 ld [%o1], %o1
615 call %o1
616 nop
617 st %o0, [current_node]
618 set prop_string_buf, %o2
619 set prop_name, %o1
620 b node_find_loop
621 or %g0, 31, %o3
622
623 found_mem_node:
624 set v2_mem_struct-KERNBASE, %o2
625 set 0xff, %o3
626 set mem_prop_physavail-KERNBASE, %o1
627 ld [current_node], %o0
628 ld [prom_nodefuncs], %o4
629 ld [%o4 + 0xc], %o4
630 call %o4
631 nop
632
633 set v2_printf_physavail-KERNBASE, %o0
634 ld [v2_mem_struct + 0x8], %o1
635 ld [prom_printf], %o4
636 ld [%o4], %o4
637 call %o4
638
639 set v2_mem_struct-KERNBASE, %o2
640 set 0xff, %o3
641 set mem_prop_phystot-KERNBASE, %o1
642 ld [current_node], %o0
643 ld [prom_nodefuncs], %o4
644 ld [%o4 + 0xc], %o4
645 call %o4
646 nop
647
648 set v2_printf_physavail-KERNBASE, %o0
649 ld [v2_mem_struct + 0x8], %o1
650 ld [prom_printf], %o4
651 ld [%o4], %o4
652 call %o4
653 nop
654
655 b rest_of_boot
656 nop
657
658 rest_of_boot:
659 call halt_me
660 nop ! who cares at this point
661
662 /* There, happy now adrian? */ 663
664 no_sun4d_here:
665 ld [%g7 + 0x68], %o1
666 set sun4d_notsup, %o0
667 call %o1
668 nop
669 b halt_me
670 nop
671
672 halt_me:
673 ld [%g7 + 0x74], %o0
674 call %o0 ! get us out of here...
675 nop ! apparently solaris is better
676
677 _strlen:
678 mov %o0, %l1
679 mov %g0, %l3
680 ldub [%l1], %l2
681 sll %l2, 24, %l2
682 sra %l2, 24, %l2
683 len_loop:
684 cmp %l2, 0
685 be len_loop_end
686 nop
687 add %l3, 0x1, %l3
688 add %l1, 0x1, %l1
689 ldub [%l1], %l2
690 sll %l2, 24, %l2
691 sra %l2, 24, %l2
692 b len_loop
693 nop
694
695 len_loop_end:
696 mov %l3, %o0
697 ret
698 nop
699
700
701
702
703