; $NetBSD: start.S,v 1.1 2014/02/24 07:23:43 skrll Exp $ ; Copyright (c) 2003 ITOH Yasufumi. ; All rights reserved. ; ; Redistribution and use in source and binary forms, with or without ; modification, are permitted provided that the following conditions ; are met: ; 1. Redistributions of source code must retain the above copyright ; notice, this list of conditions and the following disclaimer. ; 2. Redistributions in binary forms are unlimited. ; ; THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' ; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ; THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ; PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS ; BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ; SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ; CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ; ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF ; THE POSSIBILITY OF SUCH DAMAGE. .level 1.0 .code .origin 0 ; ; LIF (Logical Interchange Format) header ; lifhdr: .byte 0x80,0x00 ; LIF magic .string "NetBSD" ; volume label (6 chars, fill with space) .origin 0xf0 ; 0xf0 lif_ipl_addr: .word top-lifhdr ; start at 4KB (must be 2KB aligned) lif_ipl_size: .word 0x00001000 ; size 4KB (must be 2KB aligned) lif_ipl_entry: .word $START$-top ; entry offset ; ipl part 1 starts here .origin 4096 top: ; ; IPL startup ; ; arg0 = interact flag (1: interactive, 0: otherwise) ; arg1 = address of first doubleword past the end of ipl part 1 ; .export $START$,entry $START$: b,n start ; 0: entry address ; version of interface of primary to secondary boot BOOT_IF_VERSION: .equ 0 ; version 0: arg0 = interact flag, arg1 = version (0), ; arg2 = end addr, arg3 = selected boot partition ; r1, r3 - r22, r28, r29, r31 = cleared to zeros cksum: .word 0 ; 4: checksum will be stored here version: .word BOOT_IF_VERSION ; 8: version of interface rsvd1: .word 0 ; 12: future use rsvd2: .word 0 ; 16: future use rsvd3: .word 0 ; 20: future use start: ; set data pointer for relocatable data access blr %r0,%r27 ; get PSW at entry ssm 0,%r4 .export $global$,data $global$: ; save parameters for main copy %arg0,%r3 tmpdiskbufsz: .equ 0x1000 ; 4KB tmpdiskbuf_labelsec: .equ 0x0200 ; dbtob(LABELSECTOR) tmpdiskbuf_labelsecsz: .equ 512 tmpdiskbuf_part2: .equ 0x0400 tmpdiskbuf_part2sz: .equ 0x0400 tmpdiskbuf_part3: .equ 0x0A00 tmpdiskbuf_part3sz: .equ 0x0600 part1sz: .equ 0x1000 ; get next free address .import _end,data addil L%_end-$global$,%r27;%r1 ldo R%_end-$global$(%r1),%r1 ; 32bit environment (and this code) requires stack is 64byte aligned. ldi 64-1,%r2 add %r1,%r2,%r1 andcm %r1,%r2,%r6 ; r6 = tmp disk buffer ldo tmpdiskbufsz+64(%r6),%sp ; tmp stack bl print,%rp ldo str_startup-$global$(%r27),%arg0 ; ; read part 2 and 3 of ipl (see README.ipl) ; ; read disk blocks which contains ipl part 2 and part 3 copy %r6,%arg0 ldi 0,%arg2 ; offset = 0 bl boot_input,%rp ldi tmpdiskbufsz,%arg1 ; read size ; part 2 address ldo top-$global$+part1sz(%r27),%r19 ; copy part 2 ldo tmpdiskbuf_part2(%r6),%r20 addi,tr tmpdiskbuf_part2sz/4,%r0,%r2 ; loop count, skip next cpipl2: stws,ma %r1,4(0,%r19) ; write to dst addib,uv,n -1,%r2,cpipl2 ; check loop condition ldws,ma 4(0,%r20),%r1 ; read from src ; copy part 3 ; (r19 already has destination address of part 3) ldo tmpdiskbuf_part3(%r6),%r20 addi,tr tmpdiskbuf_part3sz/4,%r0,%r2 ; loop count, skip next cpipl3: stws,ma %r1,4(0,%r19) ; write to dst addib,uv,n -1,%r2,cpipl3 ; check loop condition ldws,ma 4(0,%r20),%r1 ; read from src ; flush data cache / invalidate instruction cache ldo top-$global$+part1sz(%r27),%r1 ; part 2 address ldi 16,%r20 ; 16: cache line size flipl: fdc 0(0,%r1) ; flush data cache line at r1 comb,< %r1,%r19,flipl fic,m %r20(0,%r1) ; flush instruction cache line at r1, r1 += 16 sync ; I/O operation is guaranteed to finish ; in eight instructions after sync ; ; Now, whole the IPL is loaded ; ; clear BSS .import _edata,data addil L%_edata-$global$,%r27;%r1 ldo R%_edata-$global$(%r1),%r1 clrbss: comb,< %r1,%r6,clrbss stws,ma %r0,4(0,%r1) ; we have read disklabel -- save it for later use .import labelsector,data addil L%labelsector-$global$,%r27;%r1 ldo R%labelsector-$global$(%r1),%r20 ldo tmpdiskbuf_labelsec(%r6),%r21 addi,tr tmpdiskbuf_labelsecsz/4,%r0,%r2 ; loop count, skip next cplbl: stws,ma %r1,4(0,%r20) ; write to dst addib,uv,n -1,%r2,cplbl ; check loop condition ldws,ma 4(0,%r21),%r1 ; read from src ; set stack ; (r6 points at free space, 64byte aligned) ; 32bit environment (and this code) requires stack is 64byte aligned. ldo 64(%r6),%sp ; 64 > 48: frame marker (32) + args(up to 4) ; stack usage ; 12bytes arguments ; 32 frame marker ; parameters for main copy %r3,%arg0 copy %r4,%arg2 .import ipl_main,entry bl ipl_main,%rp copy %sp,%arg1 ; main returned --- perform reset bl print,%rp ldo str_reset-$global$(%r27),%arg0 ; FALLTHROUGH IOMOD_CMD: .equ 0xFFFC0000 + (4*12) IOMOD_CMD_STOP: .equ 0 IOMOD_CMD_RESET: .equ 5 ; void reboot(void) ; void halt(void) .export reboot,entry .export halt,entry reboot: addi,tr IOMOD_CMD_RESET,%r0,%r1 ; %r1 = IOMOD_CMD_RESET, skip next halt: ldi IOMOD_CMD_STOP,%r1 iomcmd: ldil L%IOMOD_CMD,%r2 ldo R%IOMOD_CMD(%r2),%r2 stwas %r1,0(%r2) b,n . str_startup: .string "\r\n\n" .stringz "NetBSD/hppa FFS/LFS Primary Bootstrap\r\n\n" str_reset: .stringz "\r\nresetting..." .align 4 ; void dispatch(unsigned interactive, unsigned top, unsigned end, int part, ; unsigned entry) .export dispatch,entry dispatch: ; flush data cache / invalidate instruction cache ldi 16,%r20 ; 16: cache line size flush: fdc 0(0,%arg1) ; flush data cache line at arg1 comb,< %arg1,%arg2,flush fic,m %r20(0,%arg1) ; flush instruction cache line at arg1, arg1+=16 sync copy %r0,%r1 ; I/O operation is guaranteed to finish copy %r0,%r3 ; in eight instructions after sync copy %r0,%r4 copy %r0,%r5 ; while waiting, clear unused registers copy %r0,%r6 ; for future compatibility copy %r0,%r7 copy %r0,%r8 copy %r0,%r9 copy %r0,%r10 copy %r0,%r11 copy %r0,%r12 copy %r0,%r13 copy %r0,%r14 copy %r0,%r15 copy %r0,%r16 copy %r0,%r17 copy %r0,%r18 copy %r0,%r19 copy %r0,%r20 copy %r0,%r21 copy %r0,%r22 copy %r0,%r28 ; r23-r26: arg3-arg0, r27: dp copy %r0,%r29 ; r30: sp copy %r0,%r31 ldw -52(%sp),%arg1 ; arg4: exec address ldo reboot-$global$(%r27),%rp ; reboot if returns bv %r0(%arg1) ; execute ldi BOOT_IF_VERSION,%arg1 ; ; IODC subroutines ; PZ_MEM_CONSOLE: .equ 0x3a0 PZ_MEM_BOOT: .equ 0x3d0 PZ_MEM_KEYBOARD: .equ 0x400 DEV_PATH: .equ 0x00 DEV_LAYERS: .equ 0x08 DEV_HPA: .equ 0x20 ; hard physical address space DEV_SPA: .equ 0x24 ; soft physical address space DEV_IODC_ENTRY: .equ 0x28 DEV_CLASS: .equ 0x2c DEV_CL_DUPLEX: .equ 0x7 ; full-duplex console class IODC_ENTRY_IO_BOOTIN: .equ 0 IODC_ENTRY_IO_CONSOLEIN: .equ 2 IODC_ENTRY_IO_CONSOLEOUT: .equ 3 ; call_iodc ; inputs: ; %ret0 IODC base in page zero ; %rp return address ; %r29 arg 8 ; %r19 arg 7 ; %r20 arg 6 ; %r21 arg 5 ; %r25 arg 1 ; outputs ; all scratch regs undefined, unless defined by the IODC call call_iodc: ; set common arguments in registers addil L%retbuf-$global$,%r27;%r1 ldo R%retbuf-$global$(%r1),%r22 ; arg4: return buffer ldo DEV_LAYERS(%ret0),%arg3 ; arg3: layer ldw DEV_SPA(%ret0),%arg2 ; arg2: spa ldw DEV_HPA(%ret0),%arg0 ; arg0: hpa ; check if narrow or wide mode ssm 0,%r1 ; get PSW bb,< %r1,4,call_iodc_64 ; if W, call in 64bit mode ldw DEV_IODC_ENTRY(%ret0),%r1 ; ENTRY_IO address ; narrow mode stw %r29,-68(%sp) ; arg8: maxsize / lang stw %r19,-64(%sp) ; arg7: size stw %r20,-60(%sp) ; arg6: buf stw %r21,-56(%sp) ; arg5: devaddr / unused bv %r0(%r1) ; call ENTRY_IO stw %r22,-52(%sp) ; arg4: return buffer call_iodc_64: .allow 2.0 ; On PA64 convention, arg0 - arg7 are passed in registers. ; Parameters are placed in INCREASING order. ; The argument pointer points at the first stack parameter. ; stack usage: ; 64bytes allocated for register arguments arg0-arg7 ; 8 arg8 (argument pointer points here) ; 16 frame marker std %r29,-16-8(%sp) ; arg8: maxsize / lang ; std %sp,-8(%sp) ; psp in frame marker bv %r0(%r1) ; call ENTRY_IO ldo -16-8(%sp),%r29 ; argument pointer .allow ; ; console output ; ; void putch(int) ; void print(const char *string) .align 4 .export putch,entry .export print,entry putch: stwm %arg0,128(%sp) ; fake up a string on the stack stb %r0,-124(%sp) ; (see stack usage below) addi,tr -125,%sp,%arg0 ; string address, skip next print: .proc .callinfo frame=128,save_rp,no_unwind .entry ldo 128(%sp),%sp stw %rp,-128-20(%sp) ; stack usage: ; 36byte IODC buffer (assume %sp was 64byte aligned) ; 4 saved reg ; 88 arguments, frame marker ; 32bit: 36 (arguments) + 32 (frame marker) ; 64bit: 72 (arguments) + 16 (frame marker) prbufsiz: .equ 36 ; save callee-saves stw %r3,-92(%sp) copy %arg0,%r3 prloop: copy %r0,%r19 ldi prbufsiz,%r20 ldo -128(%sp),%r1 strloop: ldb 0(%r3),%r2 comb,= %r2,%r0,endstr stbs,ma %r2,1(0,%r1) ldo 1(%r19),%r19 comb,<> %r19,%r20,strloop ldo 1(%r3),%r3 endstr: comb,=,n %r19,%r0,endpr ; see IODC 3-51 ; arg0 hpa ; arg1 option (ENTRY_IO_CONSOLEOUT (3)) ; arg2 spa ; arg3 ID_addr (pointer to LAYER) ; arg4 R_addr (pointer to return buffer (64word?)) ; arg5 unused (0) ; arg6 memaddr (64byte-aligned) string buffer ; arg7 reqsize ; arg8 lang (0) ldi PZ_MEM_CONSOLE,%ret0 ; IODC base in page zero copy %r0,%r29 ; arg8: lang ; copy %r19,%r19 ; arg7: size ldo -128(%sp),%r20 ; arg6: buf ; copy %r0,%r21 ; arg5: unused bl call_iodc,%rp ldi IODC_ENTRY_IO_CONSOLEOUT,%arg1 ; arg1: option b,n prloop endpr: ; restore callee-saves ldw -92(%sp),%r3 ; return subroutine ldw -128-20(%sp),%rp bv %r0(%rp) .exit ldo -128(%sp),%sp .procend ; ; console input ; ; int getch(void) .align 4 .export getch,entry getch: .proc .callinfo frame=192,save_rp,no_unwind .entry stw %rp,-20(%sp) ldo 192(%sp),%sp ; stack usage: ; 64byte IODC buffer (assume %sp was 64byte aligned) ; 40 unused ; 88 arguments, frame marker ; 32bit: 36 (arguments) + 32 (frame marker) ; 64bit: 72 (arguments) + 16 (frame marker) ; check if console is full or half duplex ldw PZ_MEM_CONSOLE+DEV_CLASS(%r0),%r1 ; device class extru %r1,31,4,%r1 ; right 4bits are valid ldi PZ_MEM_CONSOLE,%ret0 comib,=,n DEV_CL_DUPLEX,%r1,getch_console ; use CONSOLE if full ldi PZ_MEM_KEYBOARD,%ret0 ; otherwise KEYBOARD getch_console: ; see IODC 3-50 ; arg0 hpa ; arg1 option (ENTRY_IO_CONSOLEIN (2)) ; arg2 spa ; arg3 ID_addr (pointer to LAYER) ; arg4 R_addr (pointer to return buffer (64word?)) ; arg5 unused (0) ; arg6 memaddr (64byte-aligned, must have 64byte) data buffer ; arg7 reqsize ; arg8 lang (0) ; copy %rp,%rp ; IODC base in page zero copy %r0,%r29 ; arg8: lang ldi 1,%r19 ; arg7: size (1) ldo -192(%sp),%r20 ; arg6: buf ; copy %r0,%r21 ; arg5: unused bl call_iodc,%rp ldi IODC_ENTRY_IO_CONSOLEIN,%arg1 ; arg1: option ; make return value comb,<> %ret0,%r0,getch_ret ; return -1 on error ldi -1,%ret0 ldi 1,%r19 ; check if narrow or wide mode ssm 0,%r1 ; get PSW bb,< %r1,4,getch_64 addil L%retbuf-$global$,%r27;%r1 ldw R%retbuf-$global$(%r1),%r2 ; ret[0] comclr,<> %r19,%r2,%ret0 ; return 0 if no char available getch_retc: ldb -192(%sp),%ret0 ; otherwise return the char getch_ret: ; return subroutine ldw -192-20(%sp),%rp bv %r0(%rp) .exit ldo -192(%sp),%sp getch_64: .allow 2.0 ldd R%retbuf-$global$(%r1),%r2 ; ret[0] (64bit) b getch_retc cmpclr,*<> %r19,%r2,%ret0 ; return 0 if no char available .allow .procend ; ; read boot device ; ; void boot_input(void *buf, unsigned len, unsigned pos) .align 4 .export boot_input,entry boot_input: .proc .callinfo frame=128,save_rp,no_unwind .entry stw %rp,-20(%sp) ldo 128(%sp),%sp ; stack usage: ; 40byte unused (alignment) ; 88 arguments, frame marker ; 32bit: 36 (arguments) + 32 (frame marker) ; 64bit: 72 (arguments) + 16 (frame marker) ; see IODC 3-46 ; arg0 hpa ; arg1 option (ENTRY_IO_BOOTIN (0)) ; arg2 spa ; arg3 ID_addr (pointer to LAYER) ; arg4 R_addr (pointer to return buffer (64word?)) ; arg5 devaddr ; arg6 memaddr (64byte-aligned) string buffer ; arg7 reqsize ; arg8 maxsize ldi PZ_MEM_BOOT,%ret0 ; IODC base in page zero copy %arg1,%r29 ; arg8: maxsize copy %arg1,%r19 ; arg7: size copy %arg0,%r20 ; arg6: buf copy %arg2,%r21 ; arg5: devaddr bl call_iodc,%rp ldi IODC_ENTRY_IO_BOOTIN,%arg1 ; arg1: option ; return subroutine ldw -128-20(%sp),%rp bv %r0(%rp) .exit ldo -128(%sp),%sp .procend ; ; utilities ; optimized for size ; ; int strcmp(const char *str1, const char *str2) .align 4 .export strcmp,entry strcmp: .proc .callinfo frame=0,no_calls .entry ldbs,ma 1(0,%arg0),%r1 strcmp_loop: comb,= %r1,%r0,strcmp_eos ldbs,ma 1(0,%arg1),%r19 comb,=,n %r1,%r19,strcmp_loop ldbs,ma 1(0,%arg0),%r1 strcmp_eos: bv %r0(%rp) .exit sub %r1,%r19,%ret0 .procend ; void memcpy(void *dst, const void *src, unsigned len) .align 4 .export memcpy,entry .export memmove,entry memcpy: memmove: .proc .callinfo no_unwind ; multiple exit points .entry ; copy %arg0,%ret0 ; uncomment this to conform ANSI comb,<<,n %arg0,%arg1,memcpy0 ; copy forward or backward? add %arg0,%arg2,%arg0 ; dst end address add,tr %arg1,%arg2,%arg1 ; src end address, skip next memcpy_bwd: stbs,mb %r1,-1(0,%arg0) ; write to dst addib,uv,n -1,%arg2,memcpy_bwd ; check loop condition ldbs,mb -1(0,%arg1),%r1 ; read from src bv,n %r0(%rp) ; return subroutine memcpy_fwd: stbs,ma %r1,1(0,%arg0) ; write to dst memcpy0: addib,uv,n -1,%arg2,memcpy_fwd ; check loop condition ldbs,ma 1(0,%arg1),%r1 ; read from src .exit bv,n %r0(%rp) ; return subroutine .procend ; ; string table ; placed here to save space ; .export str_seekseq, data .export str_startup, data .export str_bit_firmware, data .export str_crlf, data .export str_space, data .export str_rubout, data str_seekseq: .stringz "repositioning media...\r\n" str_bit_firmware: .stringz "bit firmware\r\n" str_rubout: .byte 0x08, 0x20, 0x08, 0x00 ; "\b \b" .export str_bootpart, data str_bootpart: .string "boot partition (a-p, ! to reboot) [a]:" str_space: .stringz " " .export str_booting_part, data str_booting_part: .string "\r\nbooting from partition _" str_crlf: .stringz "\r\n" .export str_warn_2GB, data str_warn_2GB: .stringz "boot partition exceeds 2GB boundary\r\n" .export str_warn_unused, data str_warn_unused: .stringz "unused partition\r\n" .export str_nolabel, data str_nolabel: .stringz "no disklabel\r\n" .export str_filesystem, data str_filesystem: .stringz "filesystem: _FS\r\n" .export str_nofs, data str_nofs: .stringz "no filesystem found\r\n" .export str_lookup, data .export str_loading, data .export str_at, data .export str_dddot, data .export str_done, data str_lookup: .stringz "looking up " str_loading: .stringz "loading " str_at: .stringz " at 0x" str_dddot: .stringz "..." str_done: .stringz "done\r\n" .export str_boot1, data .export str_boot2, data .export str_boot3, data str_boot1: .stringz "boot.hp700" str_boot2: .stringz "boot" str_boot3: .stringz "usr/mdec/boot" .export str_noboot, data str_noboot: .stringz "no secondary boot found\r\n" .export str_ukfmt, data str_ukfmt: .stringz ": unknown format -- exec from top\r\n" .bss .align 64 retbuf: .block 32*8 ; *4 for narrow mode / *8 for wide mode .export diskbuf,data .align 64 diskbuf: .block 2048