| .level 1.1 |
| |
| #include "common.h" |
| |
| ;! |
| ;! Execution begins here. |
| ;! |
| ;! We are called by the PDC as: |
| ;! |
| ;! $START$(interactive, endaddr) |
| ;! |
| ;! Where: |
| ;! |
| ;! interactive - 0 if not interactive, 1 if interactive. |
| ;! |
| .section .init |
| .EXPORT $START$,entry |
| .export _start |
| $START$: |
| _start: |
| b,n continue ; This branch instruction may be used |
| ; by 'palo' to determine whether a boot |
| ; loader file really is a boot loader, so |
| ; if you change it palo may start failing. |
| .word 0 ; This word is available for inserting a |
| ; checksum when necessary, as on an ISO |
| ; image where we have to preserve the |
| ; integrity of this ELF executable file. |
| .align 16 |
| .string PALOMAGIC ; Use strings command to get palo ipl version |
| .string PALOVERSION |
| |
| .align 16 |
| continue: |
| ssm 0,%r0 |
| |
| ;! nuke the W bit, saving original value |
| .level 2.0 |
| #define PSW_W_SM 0x200 |
| #define PSW_W_BIT 36 |
| rsm PSW_W_SM, %r1 |
| |
| .level 1.1 |
| extrw,u %r1, PSW_W_BIT-32, 1, %r1 |
| |
| ;! find out where we were loaded |
| blr 0,%arg1 |
| ldo ($START$-bbb)(%arg1), %arg1 |
| bbb: |
| ldil L%($global$), %dp |
| ldo R%($global$)(%dp), %dp |
| |
| /* find size of first memory module -- assume it is well aligned */ |
| #define IMM_MAX_MEM 0x39c |
| ldil L%(IMM_MAX_MEM), %r3 |
| ldo R%(IMM_MAX_MEM)(%r3), %r3 |
| ldw 0(%r3), %r3 |
| |
| /* due to sign extension problems and PDC, limit RAM to 1G */ |
| /* this isn't efficient assembly but it doesn't matter here either */ |
| #define ARTIFICIAL_LIMIT (1*1024*1024*1024) |
| ldil L%(ARTIFICIAL_LIMIT), %r4 |
| ldo R%(ARTIFICIAL_LIMIT)(%r4), %r4 |
| cmpb,<< %r3, %r4, limit_ok |
| nop |
| copy %r4, %r3 |
| |
| limit_ok: |
| /* stack is at top of initial mem module */ |
| #define STACK_SIZE (64 * 1024) |
| ldil L%(STACK_SIZE), %r4 |
| ldo R%(STACK_SIZE)(%r4), %r4 |
| |
| sub %r3, %r4, %sp |
| copy %sp, %arg2 |
| |
| /* heap lives just below stack and grows down -- see iplmain() */ |
| |
| ;! save %sp for calling iplmain() |
| copy %sp, %r10 |
| |
| ;! push a stack frame |
| ldo 64(%sp), %sp |
| |
| ;! save arg0 from the firmware and original PSW-W bit value |
| stw %arg0, -64(%sp) |
| stw %r1, -60(%sp) |
| |
| ldil L%$START$, %arg0 |
| ldo R%$START$(%arg0), %arg0 |
| |
| /* arg2 = _end - $$START$$ */ |
| .import _end |
| ldil L%_end, %r5 |
| ldo R%_end(%r5), %r5 |
| sub %r5, %arg0, %arg2 |
| |
| ;! copy us to our correct destination |
| /* memmove($START$, where-loaded, #bytes) */ |
| bl memmove, %r2 |
| nop |
| |
| /* restore saved arg0 from firmware and original PSW-W bit */ |
| ldw -64(%sp), %arg0 |
| ldw -60(%sp), %arg2 |
| |
| .import iplmain,code |
| ;! call iplmain via a register so we can jump from PIC space |
| ;! to non-PIC space |
| ldil L%iplmain, %r3 |
| ldo R%iplmain(%r3), %r3 |
| |
| ;! we will "return" to non-PIC space |
| ldil L%ret_iplmain, %r2 |
| ldo R%ret_iplmain(%r2), %r2 |
| |
| ;! second arg is initial SP |
| copy %r10, %arg1 |
| |
| call_iplmain: |
| ;! kernel-entry = iplmain(interactive, free-ptr, started-wide) |
| bv 0(%r3) |
| nop |
| ret_iplmain: |
| |
| ldil L%ret_kernel, %r2 |
| ldo R%ret_kernel(%r2), %r2 |
| |
| ;! kernel(any-big-number, command-line, rd-start, rd-end) |
| ldil L%commandline, %arg0 |
| ldo R%commandline(%arg0), %arg0 |
| copy %arg0, %arg1 |
| ldil L%(rd_start), %arg2 |
| ldw R%(rd_start)(%arg2), %arg2 |
| ldil L%(rd_end), %arg3 |
| ldw R%(rd_end)(%arg3), %arg3 |
| |
| bv 0(%ret0) |
| nop |
| /* shouldn't ever return */ |
| ret_kernel: |
| nop |
| /* if it does, it won't go past here */ |
| infinite_loop: |
| b . |
| nop |
| |
| .section .init |
| .export memmove |
| .export pa_memcpy |
| /* void *memmove(void * dest,const void *src,size_t count) |
| * |
| * combines overkill cache flushing with copying because |
| * this loop is used to copy the bootloader to its final |
| * location where it will then be executed. You probably |
| * shouldn't use this for generic C memory copying. |
| * |
| * note that memmove() can handle overlapping copies, but |
| * that's not currently used because copying a running program |
| * on top of itself is unwise. |
| */ |
| memmove: |
| pa_memcpy: |
| copy %arg0, %ret0 /* arg0 must be returned */ |
| cmpb,<<,n %arg1, %arg0, mmreverse |
| ldo -1(%arg2), %arg2 |
| cmpib,=,n -1, %arg2, mmret /* if count==0 exit */ |
| mmloop1: |
| ldb,ma 1(%arg1), %r19 |
| ldo -1(%arg2), %arg2 |
| stb %r19, 0(%arg0) |
| fdc 0(%arg0) |
| fic 0(%sr0,%arg0) |
| ldo 1(%arg0), %arg0 |
| cmpib,<> -1, %arg2, mmloop1 |
| nop |
| bv,n %r0(%rp) |
| mmreverse: |
| add,l %arg0, %arg2, %arg0 |
| add,l %arg1, %arg2, %arg1 |
| ldo -1(%arg2), %arg2 |
| ldo -1(%arg0), %arg0 |
| cmpib,=,n -1, %arg2, mmret |
| mmloop2: |
| ldb,mb -1( %arg1), %r19 |
| ldo -1(%arg2), %arg2 |
| stb %r19, 0(%arg0) |
| fdc 0(%arg0) |
| fic 0(%sr0,%arg0) |
| ldo -1(%arg0), %arg0 |
| cmpib,<> -1, %arg2, mmloop2 |
| nop |
| mmret: |
| bv,n %r0(%rp) |
| nop |
| |
| /************************ 64-bit real-mode calls ***********************/ |
| /* Called in narrow mode; switches to wide mode for PDC or IODC call */ |
| #define REG_SZ 8 |
| .text |
| .export real64_call_asm |
| .level 2.0 |
| /* unsigned int real64_call_asm(unsigned long long *sp, |
| * unsigned long long *arg0p, |
| * unsigned long long fn) |
| * sp is value of stack pointer to adopt before calling PDC |
| * arg0p points to where saved arg values may be found |
| * fn is the PDC/IODC function to call |
| */ |
| real64_call_asm: |
| stw %rp, -20(%sp) /* save RP */ |
| stw %sp, -8(%arg0) /* save SP on real-mode stack */ |
| copy %arg0, %sp /* adopt the real-mode SP */ |
| |
| /* switch to wide mode */ |
| 1: mfia %ret0 /* clear upper part of pcoq */ |
| ldo 2f-1b(%ret0), %ret0 |
| depdi 0, 31, 32, %ret0 |
| bv (%ret0) |
| ssm 0x200, %r3 /* set W-bit */ |
| 2: |
| |
| copy %arg2, %r31 /* save fn */ |
| depd %arg3, 31, 32, %r31 /* merge in hi part */ |
| |
| ldo -2*REG_SZ(%sp), %r29 /* set up the new ap */ |
| |
| /* load up the arg registers from the saved arg area */ |
| /* 64-bit calling convention passes first 8 args in registers */ |
| ldw 0*REG_SZ+4(%arg1), %arg0 /* note overwriting arg0 */ |
| ldw 2*REG_SZ+4(%arg1), %arg2 |
| ldw 3*REG_SZ+4(%arg1), %arg3 |
| ldw 4*REG_SZ+4(%arg1), %r22 |
| ldw 5*REG_SZ+4(%arg1), %r21 |
| ldw 6*REG_SZ+4(%arg1), %r20 |
| ldw 7*REG_SZ+4(%arg1), %r19 |
| ldw 1*REG_SZ+4(%arg1), %arg1 /* do this one last! */ |
| |
| ldil L%r64_ret, %rp |
| ldo R%r64_ret(%rp), %rp |
| bv 0(%r31) |
| nop |
| |
| r64_ret: |
| mtsm %r3 /* return to narrow mode */ |
| |
| ldw -8(%sp), %sp /* restore SP */ |
| ldw -20(%sp), %rp /* restore RP */ |
| bv 0(%rp) |
| nop |
| |
| |
| /* setjmp and longjmp, ignores FPU and signals */ |
| .level 1.1 |
| |
| .export setjmp, code |
| setjmp: |
| stw %r3, 0(%r26) |
| stw %r4, 8(%r26) |
| stw %r5, 12(%r26) |
| stw %r6, 16(%r26) |
| stw %r7, 20(%r26) |
| stw %r8, 24(%r26) |
| stw %r9, 28(%r26) |
| stw %r10, 32(%r26) |
| stw %r11, 36(%r26) |
| stw %r12, 40(%r26) |
| stw %r13, 44(%r26) |
| stw %r14, 48(%r26) |
| stw %r15, 52(%r26) |
| stw %r16, 56(%r26) |
| stw %r17, 60(%r26) |
| stw %r18, 64(%r26) |
| stw %r19, 68(%r26) |
| stw %r27, 72(%r26) |
| stw %r30, 76(%r26) |
| |
| stw %rp, 80(%r26) |
| copy %r0, %r28 |
| bv,n %r0(%rp) |
| |
| |
| .export longjmp, code |
| longjmp: |
| ldi 1, %r28 |
| |
| ldw 0(%r26), %r3 |
| ldw 8(%r26), %r4 |
| ldw 12(%r26), %r5 |
| ldw 16(%r26), %r6 |
| ldw 20(%r26), %r7 |
| ldw 24(%r26), %r8 |
| ldw 28(%r26), %r9 |
| ldw 32(%r26), %r10 |
| ldw 36(%r26), %r11 |
| ldw 40(%r26), %r12 |
| ldw 44(%r26), %r13 |
| ldw 48(%r26), %r14 |
| ldw 52(%r26), %r15 |
| ldw 56(%r26), %r16 |
| ldw 60(%r26), %r17 |
| ldw 64(%r26), %r18 |
| ldw 68(%r26), %r19 |
| ldw 72(%r26), %r27 |
| ldw 76(%r26), %r30 |
| |
| ldw 80(%r26), %rp |
| bv,n %r0(%rp) |
| |
| |
| .data |
| .align 4 |
| .export $global$, data |
| $global$: |
| .word 4 |
| .import commandline,data |
| .export rd_start,data |
| .export rd_end,data |
| rd_start: |
| .blockz 4 |
| rd_end: |
| .blockz 4 |
| |
| .section .bss |
| .export real_stack |
| .align 64 |
| real_stack: |
| .block 8192 |