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