; ---------------------------------------------------------------------------- ; Initialization routines and segment mappings for os2ahci driver, plus ; some low-level functions that were implemented in assembler include devhdr.inc ; ----------------------------------------------------------------------------- ; Public symbols PUBLIC _asm_strat ; low-level strategy routine PUBLIC _readl ; MMIO read (32 bits) PUBLIC _writel ; MMIO write (32 bits) PUBLIC _memset ; C memset() implementation PUBLIC _memcpy ; C memcpy() implementation PUBLIC _restart_hook ; port restart context hook PUBLIC _reset_hook ; port reset context hook PUBLIC _engine_hook ; engine trigger context hook PUBLIC _dev_hdr ; device driver header PUBLIC _end_of_data ; end of all data (label) PUBLIC _end_of_code ; end of all code (label) ; ---------------------------------------------------------------------------- ; Device Driver Header DEVHDR SEGMENT WORD PUBLIC 'DATA' _dev_hdr dd -1 ; no headers after this one dw DEVLEV_3 + DEV_CHAR_DEV ; flags for ADD drivers dw _asm_strat ; strategy routine dw 0 ; no IDC routine db "OS2AHCI$" ; name of character device dq 0 ; 8 reserved bytes dd DEV_ADAPTER_DD + DEV_INITCOMPLETE + 20h ; ADD flags dw 0 DEVHDR ENDS ; ---------------------------------------------------------------------------- ; Segment Definitions. We need to reference all segments here, even if they ; are not used, to allow proper grouping of all segments into one group for ; data and one group for code as well as proper ordering of the segments ; within each group to make sure the end of segment markers are at the end ; of each group. ; On top of that, we need to make sure that the end of segment marker is in ; a data segment of class 'BSS' because BSS segments are always put towards ; the end of the executable when linking with high-level language objects ; such as C. _DATA SEGMENT WORD PUBLIC 'DATA' readl_dbg_fmt db "readl(%04x:%04x) = 0x%08lx [addr %% 0x80 = 0x%02x]" db 10, 0 writel_dbg_fmt db "writel(%04x:%04x, 0x%08lx) [addr %% 0x80 = 0x%02x]" db 10, 0 _DATA ENDS CONST SEGMENT WORD PUBLIC 'CONST' CONST ENDS _BSS SEGMENT WORD PUBLIC 'BSS' _BSS ENDS c_common SEGMENT WORD PUBLIC 'BSS' EXTRN _debug : WORD ; global debug flag c_common ENDS _z_data SEGMENT WORD PUBLIC 'BSS' _end_of_data db 0 _z_data ENDS _TEXT SEGMENT WORD PUBLIC 'CODE' EXTRN _c_strat : NEAR ; C strategy routine EXTRN _printf : NEAR ; C printf routine EXTRN _restart_ctxhook : NEAR ; C restart context hook EXTRN _reset_ctxhook : NEAR ; C reset context hook EXTRN _engine_ctxhook : NEAR ; C engine context hook _TEXT ENDS CODE SEGMENT WORD PUBLIC 'CODE' CODE ENDS RMCode SEGMENT WORD PUBLIC 'CODE' RMCode ENDS LIBCODE SEGMENT WORD PUBLIC 'CODE' LIBCODE ENDS _z_text SEGMENT WORD PUBLIC 'CODE' _end_of_code LABEL NEAR _z_text ENDS DGROUP GROUP DEVHDR, _DATA, CONST, _BSS, c_common, _z_data TGROUP GROUP _TEXT, CODE, _z_text ; ---------------------------------------------------------------------------- ; Start of code _TEXT SEGMENT WORD PUBLIC 'CODE' ASSUME DS:DGROUP ASSUME ES:NOTHING ASSUME SS:NOTHING ; Device driver main entry point (strategy routine) _asm_strat PROC FAR ; push request packet address PUSH ES PUSH BX CLD ; call C strategy routine CALL _c_strat POP BX POP ES MOV WORD PTR ES:[BX+3], AX RET _asm_strat ENDP .386 ; Read long value from MMIO address; need to do this here to get real ; 32-bit operations because at least the AHCI device in VirtualBox doesn't ; seem to support reading 32-bit MMIO registers in two 16-bit steps. ; ; C prototype: u32 readl(void _far *addr); _readl PROC NEAR ENTER 0, 0 ; load 'addr' into ES:EAX LES AX, [BP+4] AND EAX, 0000FFFFh ; read MMIO register into EDX and return it in DX:AX MOV EDX, ES:[EAX] ; print debug message if debug level is 3+ CMP _debug, 3 JB no_debug PUSH EDX ; save value read from MMIO port MOV BX, AX ; addr & 0x7f (port reg index) AND BX, 7FH PUSH BX PUSH EDX ; value read from MMIO address PUSH AX ; offset of MMIO address PUSH ES ; segment of MMIO address PUSH OFFSET readl_dbg_fmt CALL _printf ADD SP, 12 POP EDX ; restore value read from MMIO port no_debug: MOV EAX, EDX SHR EDX, 16 LEAVE RET _readl ENDP ; Write long value to MMIO address; need to do this here to get real ; 32-bit operations because at least the AHCI device in VirtualBox doesn't ; seem to support reading 32-bit MMIO registers in two 16-bit steps and the ; assumption is that this is the same with writing 32-bit MMIO registers. ; ; C prototype: void writel(void _far *addr, u32 val); _writel PROC NEAR ENTER 0, 0 ; load 'addr' into ES:EAX LES AX, [BP+4] AND EAX, 0000FFFFh ; load 'val' into EDX, preserving the upper 16 bits of EBP MOV EBX, EBP AND EBP, 0000FFFFh MOV EDX, [EBP+8] MOV EBP, EBX ; store 'val' at MMIO address in ES:EAX MOV DWORD PTR ES:[EAX], EDX ; print debug message if debug level is 3+ CMP _debug, 3 JB no_debug MOV BX, AX ; addr & 0x7f (port reg index) AND BX, 7FH PUSH BX PUSH EDX ; value written to MMIO address PUSH AX ; offset of MMIO address PUSH ES ; segment of MMIO address PUSH OFFSET writel_dbg_fmt CALL _printf ADD SP, 12 no_debug: LEAVE RET _writel ENDP ; Halfway-decent 32-bit implementation of memset(). ; ; C prototype: void *memset(void _far *s, int c, size_t n); _memset PROC NEAR ENTER 0, 0 PUSH DI ; load 's' into ES:EDI LES DI, [BP+4] AND EDI, 0000FFFFh ; load 'c' into EAX, replicating the low 8 bits all over MOV BL, [BP+8] MOV BH, BL MOV AX, BX SHL EAX, 16 MOV AX, BX ; load 'n / 4' into ECX MOV CX, [BP+10] SHR CX, 2 AND ECX, 0000FFFFh ; fill 's' with 32-bit chunks of EAX REP STOSD ; set remaining bytes (n % 4)' MOV CX, [BP+10] AND CX, 0003h REP STOSB ; return address of 's' LES AX, [BP+4] PUSH ES POP DX POP DI LEAVE RET _memset ENDP ; Halfway-decent 32-bit implementation of memcpy(). ; ; C prototype: void *memcpy(void _far *d, void _far *s, size_t n); _memcpy PROC NEAR ENTER 0, 0 INT 3 PUSH SI PUSH DI PUSH DS ; load 'd' into ES:EDI LES DI, [BP+4] AND EDI, 0000FFFFh ; load 's' into DS:ESI LDS SI, [BP+8] AND ESI, 0000FFFFh ; load 'n / 4' into ECX MOV CX, [BP+12] SHR CX, 2 AND ECX, 0000FFFFh ; copy 's' to 'd' in 32-bit chunks REP MOVSD ; copy remaining bytes (n % 4)' MOV CX, [BP+12] AND CX, 0003h REP MOVSB ; return address of 'd' LES AX, [BP+4] PUSH ES POP DX POP DS POP DI POP SI LEAVE RET _memcpy ENDP ; Port restart context hook; context hooks need to save all registers ; and get the parameter passed to DevHelp_ArmCtxHook() in EAX, thus ; we need a stub which calls the real context hook. _restart_hook PROC FAR PUSHAD PUSH EAX CALL _restart_ctxhook POP EAX POPAD RET _restart_hook ENDP ; Port reset context hook; context hooks need to save all registers ; and get the parameter passed to DevHelp_ArmCtxHook() in EAX, thus ; we need a stub which calls the real context hook. _reset_hook PROC FAR PUSHAD PUSH EAX CALL _reset_ctxhook POP EAX POPAD RET _reset_hook ENDP ; Engine trigger context hook; context hooks need to save all registers ; and get the parameter passed to DevHelp_ArmCtxHook() in EAX, thus ; we need a stub which calls the real context hook. _engine_hook PROC FAR PUSHAD PUSH EAX CALL _engine_ctxhook POP EAX POPAD RET _engine_hook ENDP .286 _TEXT ENDS END