Subversion Repositories Kolibri OS

Rev

Rev 9138 | Rev 9140 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
9020 rgimad 1
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2
;;                                                              ;;
3
;; Copyright (C) KolibriOS team 2004-2021. All rights reserved. ;;
4
;; Distributed under terms of the GNU General Public License    ;;
5
;;                                                              ;;
6
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
7
 
8
$Revision$
9
 
10
PCI_REG_STATUS_COMMAND = 0x0004
11
PCI_REG_BAR5 = 0x0024
12
 
9134 rgimad 13
; different SATA device signatures
14
SATA_SIG_ATA	= 0x00000101	; SATA drive
15
SATA_SIG_ATAPI	= 0xEB140101	; SATAPI drive
16
SATA_SIG_SEMB	= 0xC33C0101	; Enclosure management bridge
17
SATA_SIG_PM	= 0x96690101	; Port multiplier
18
 
19
; Device type constants
20
AHCI_DEV_NULL   = 0
21
AHCI_DEV_SATA   = 1
22
AHCI_DEV_SEMB   = 2
23
AHCI_DEV_PM     = 3
24
AHCI_DEV_SATAPI = 4
25
 
26
; ATA commands
27
ATA_IDENTIFY    = 0xEC
28
 
9139 rgimad 29
; ATA constants
30
ATA_DEV_BUSY    = 0x80
31
ATA_DEV_DRQ     = 0x08
32
 
9134 rgimad 33
; ATAPI commands
34
ATAPI_IDENTIFY  = 0xA1
35
 
9023 rgimad 36
; bit_ prefix means that its index of bit
37
; format: bit_AHCI_STR_REG_BIT
38
bit_AHCI_HBA_CAP2_BOH   = 0        ; Supports BIOS/OS Handoff
9020 rgimad 39
 
9023 rgimad 40
bit_AHCI_HBA_BOHC_BOS  = 0         ; BIOS-Owned Semaphore (BIOS owns controller)
41
bit_AHCI_HBA_BOHC_OOS  = 1         ; OS-Owned Semaphore (OS owns controller)
42
bit_AHCI_HBA_BOHC_BB   = 4         ; BIOS Busy (polling bit while BIOS cleans up
9020 rgimad 43
 
9023 rgimad 44
bit_AHCI_HBA_GHC_AHCI_ENABLE      = 31  ; Enable AHCI mode
45
bit_AHCI_HBA_GHC_RESET            = 0   ; Reset HBA
46
bit_AHCI_HBA_GHC_INTERRUPT_ENABLE = 1   ; Enable interrupts from the HBA
47
 
9065 rgimad 48
bit_AHCI_HBA_PxCMD_ST    = 0
49
bit_AHCI_HBA_PxCMD_FRE   = 4
50
bit_AHCI_HBA_PxCMD_FR    = 14
51
bit_AHCI_HBA_PxCMD_CR    = 15
9139 rgimad 52
bit_AHCI_HBA_PxIS_TFES   = 30
9065 rgimad 53
 
9130 rgimad 54
AHCI_HBA_PxCMD_ST    = 1 shl 0
55
AHCI_HBA_PxCMD_FRE   = 1 shl 4
56
AHCI_HBA_PxCMD_FR    = 1 shl 14
57
AHCI_HBA_PxCMD_CR    = 1 shl 15
58
 
9074 rgimad 59
bit_AHCI_H2D_FLAG_CMD    = 7
60
 
9037 rgimad 61
AHCI_HBA_PxSSTS_DET         = 0xF
62
AHCI_HBA_PORT_IPM_ACTIVE    = 1
63
AHCI_HBA_PxSSTS_DET_PRESENT = 3
64
 
9023 rgimad 65
AHCI_MAX_PORTS = 32        ;
9064 rgimad 66
;HBA_MEMORY_SIZE = 0x1100
9020 rgimad 67
 
9139 rgimad 68
AHCI_PORT_TIMEOUT = 1000000
69
 
9064 rgimad 70
; Frame Information Structure Types
71
FIS_TYPE_REG_H2D    = 0x27 ; Register FIS - host to device
72
FIS_TYPE_REG_D2H    = 0x34 ; Register FIS - device to host
73
FIS_TYPE_DMA_ACT    = 0x39 ; DMA activate FIS - device to host
74
FIS_TYPE_DMA_SETUP  = 0x41 ; DMA setup FIS - bidirectional
75
FIS_TYPE_DATA       = 0x46 ; Data FIS - bidirectional
76
FIS_TYPE_BIST       = 0x58 ; BIST activate FIS - bidirectional
77
FIS_TYPE_PIO_SETUP  = 0x5F ; PIO setup FIS - device to host
78
FIS_TYPE_DEV_BITS   = 0xA1 ; Set device bits FIS - device to host
79
 
9020 rgimad 80
struct AHCI_DATA
81
        abar    dd ?       ; pointer to HBA Memory (BAR5) mapped to virtual kernelspace memory
82
        pcidev  dd ?       ; pointer to corresponding PCIDEV structure
83
ends
84
 
85
; Generic Host Control registers
86
struct HBA_MEM
9064 rgimad 87
        cap                   dd ?                    ; 0x00, Host capabilities
88
        ghc                   dd ?                    ; 0x04, Global host control
89
        is                    dd ?                    ; 0x08, Interrupt status
90
        pi                    dd ?                    ; 0x0C, Port implemented
91
        version               dd ?                    ; 0x10, Version
9020 rgimad 92
        ccc_ctl               dd ?                    ; 0x14, Command completion coalescing control
93
        ccc_pts               dd ?                    ; 0x18, Command completion coalescing ports
94
        em_loc                dd ?                    ; 0x1C, Enclosure management location
95
        em_ctl                dd ?                    ; 0x20, Enclosure management control
9064 rgimad 96
        cap2                  dd ?                    ; 0x24, Host capabilities extended
9020 rgimad 97
        bohc                  dd ?                    ; 0x28, BIOS/OS handoff control and status
9072 rgimad 98
        reserved              rb (0xA0-HBA_MEM.reserved)        ; 0x2C - 0x9F, Reserved
99
        vendor                rb (0x100-HBA_MEM.vendor)         ; 0xA0 - 0xFF, Vendor specific
9023 rgimad 100
        ports                 rb (sizeof.HBA_PORT*AHCI_MAX_PORTS) ; 0x100 - 0x10FF, Port control registers, max AHCI_MAX_PORTS
9020 rgimad 101
ends
102
 
103
; Port Control registers
104
struct HBA_PORT
9064 rgimad 105
        command_list_base_l      dd ?                 ; 0x00, command list base address, 1K-byte aligned
106
        command_list_base_h      dd ?                 ; 0x04, command list base address upper 32 bits, used on 64 bit systems
107
        fis_base_l               dd ?                 ; 0x08, FIS base address, 256-byte aligned
108
        fis_base_h               dd ?                 ; 0x0C, FIS base address upper 32 bits, used on 64 bit systems
109
        interrupt_status         dd ?                 ; 0x10
110
        interrupt_enable         dd ?                 ; 0x14
111
        command                  dd ?                 ; 0x18, command and status
112
        reserved0                dd ?                 ; 0x1C
113
        task_file_data           dd ?                 ; 0x20
114
        signature                dd ?                 ; 0x24
115
        sata_status              dd ?                 ; 0x28, SATA status (SCR0:SStatus)
116
        sata_control             dd ?                 ; 0x2C, SATA control (SCR2:SControl)
117
        sata_error               dd ?                 ; 0x30, SATA error (SCR1:SError)
118
        sata_active              dd ?                 ; 0x34, SATA active (SCR3:SActive)
119
        command_issue            dd ?                 ; 0x38
120
        sata_notification        dd ?                 ; 0x3C, SATA notification (SCR4:SNotification)
121
        fis_based_switch_control dd ?                 ; 0x40
122
        reserved1                rd 11                ; 0x44 - 0x6F
123
        vendor                   rd 4                 ; 0x70 - 0x7F, vendor specific
9020 rgimad 124
ends
125
 
9074 rgimad 126
; Command header structure, size = 32 bytes
9068 rgimad 127
struct HBA_CMD_HDR
9131 rgimad 128
    flags1       db ? ; 0bPWACCCCC, P - Prefetchable, W - Write (1: H2D, 0: D2H)
9068 rgimad 129
                       ; A - ATAPI, C - Command FIS length in DWORDS, 2 ~ 16
130
 
9131 rgimad 131
    flags2       db ? ; 0bPPPPRCB(Re), P - Port multiplier port, R - Reserved,
9068 rgimad 132
                       ; C - Clear busy upon R_OK, B - BIST, Re - Reset
133
 
134
    prdtl         dw ? ; Physical region descriptor table length in entries
135
    prdbc         dd ? ; Physical region descriptor byte count transferred
136
    ctba          dd ? ; Command table descriptor base address
137
    ctbau         dd ? ; Command table descriptor base address upper 32 bits
9072 rgimad 138
                  rd 4 ; Reserved
9068 rgimad 139
ends
140
 
9074 rgimad 141
; Physical region descriptor table entry, size = 16 bytes
9069 rgimad 142
struct HBA_PRDT_ENTRY
143
    dba           dd ?  ; Data base address
144
    dbau          dd ?  ; Data base address upper 32 bits
9072 rgimad 145
                  dd ?  ; Reserved
9131 rgimad 146
    flags        dd ?  ; 0bIR..RD..D, I (1 bit) - Interrupt on completion,
9069 rgimad 147
                        ; R (9 bits) - Reserved, D (22 bits) - Byte count, 4M max
148
ends
149
 
150
struct HBA_CMD_TBL
151
    cfis          rb 64 ; 0x00, Command FIS
152
    acmd          rb 16 ; 0x40, ATAPI command, 12 or 16 bytes
9072 rgimad 153
                  rb 48 ; 0x50, Reserved
9069 rgimad 154
    prdt_entry    HBA_PRDT_ENTRY  ; 0x80, Physical region descriptor table entries, 0 ~ 65535
155
                        ; so, this structure is variable-length
156
ends
157
 
9068 rgimad 158
; Contains virtual mappings for port phys memory regions
159
struct PORT_DATA
160
    clb           dd ? ; Command list base
161
    fb            dd ? ; FIS base
162
    ctba_arr      rd 32 ; ctba_arr[0] = clb[0].ctba, ... and so on.
163
    port          dd ? ; address of correspoding HBA_PORT structure
9074 rgimad 164
    portno        dd ? ; port index, 0..31
9134 rgimad 165
    drive_type    db ? ; drive type
9068 rgimad 166
ends
167
 
9064 rgimad 168
; Register FIS – Host to Device
169
struct FIS_REG_H2D
170
        fis_type      db ?       ; FIS_TYPE_REG_H2D
9131 rgimad 171
        flags        db ?       ; 0bCRRRPPPP, C - 1: Command, 0: Control
9064 rgimad 172
                                 ; R - Reserved, P - Port multiplier
173
 
174
        command       db ?       ; Command register
175
        featurel      db ?       ; Feature register, 7:0
176
 
177
        lba0          db ?       ; LBA low register, 7:0
178
        lba1          db ?       ; LBA mid register, 15:8
179
        lba2          db ?       ; LBA high register, 23:16
180
        device        db ?       ; Device register
181
 
182
        lba3          db ?       ; LBA register, 31:24
183
        lba4          db ?       ; LBA register, 39:32
184
        lba5          db ?       ; LBA register, 47:40
185
        featureh      db ?       ; Feature register, 15:8
186
 
187
        countl        db ?       ; Count register, 7:0
188
        counth        db ?       ; Count register, 15:8
189
        icc           db ?       ; Isochronous command completion
190
        control       db ?       ; Control register
191
 
9072 rgimad 192
                      rb 4       ; Reserved
9064 rgimad 193
ends
194
 
195
; Register FIS – Device to Host
196
struct FIS_REG_D2H
197
    fis_type      db ?           ; FIS_TYPE_REG_D2H
198
 
9131 rgimad 199
    flags        db ?           ; 0bRIRPPPP, P - Port multiplier, R - Reserved
9064 rgimad 200
                                 ; I - Interrupt bit
201
 
202
    status        db ?           ; Status register
203
    error         db ?           ; Error register
204
 
205
    lba0          db ?           ; LBA low register, 7:0
206
    lba1          db ?           ; LBA mid register, 15:8
207
    lba2          db ?           ; LBA high register, 23:16
208
    device        db ?           ; Device register
209
 
210
    lba3          db ?           ; LBA register, 31:24
211
    lba4          db ?           ; LBA register, 39:32
212
    lba5          db ?           ; LBA register, 47:40
9072 rgimad 213
                  db ?           ; Reserved
9064 rgimad 214
 
215
    countl        db ?           ; Count register, 7:0
216
    counth        db ?           ; Count register, 15:8
9072 rgimad 217
                  rb 2           ; Reserved
9064 rgimad 218
 
9072 rgimad 219
                  rb 4           ; Reserved
9064 rgimad 220
ends
221
 
222
; Data FIS – Bidirectional
223
struct FIS_DATA
224
    fis_type      db ?           ; FIS_TYPE_DATA
9131 rgimad 225
    flags        db ?           ; 0bRRRRPPPP, R - Reserved, P - Port multiplier
9072 rgimad 226
                  rb 2           ; Reserved
9064 rgimad 227
    ; DWORD 1 ~ N (?)
228
    data          rd 1           ; Payload
229
ends
230
 
231
; PIO Setup – Device to Host
232
struct FIS_PIO_SETUP
233
    fis_type      db ?           ; FIS_TYPE_PIO_SETUP
234
 
9131 rgimad 235
    flags        db ?           ; 0bRIDRPPPP, P - Port multiplier, R - Reserved
9064 rgimad 236
                                 ; I - Interrupt bit, D - Data transfer direction, 1 - device to host
237
 
238
    status        db ?           ; Status register
239
    error         db ?           ; Error register
240
 
241
    lba0          db ?           ; LBA low register, 7:0
242
    lba1          db ?           ; LBA mid register, 15:8
243
    lba2          db ?           ; LBA high register, 23:16
244
    device        db ?           ; Device register
245
 
246
    lba3          db ?           ; LBA register, 31:24
247
    lba4          db ?           ; LBA register, 39:32
248
    lba5          db ?           ; LBA register, 47:40
9072 rgimad 249
                  db ?           ; Reserved
9064 rgimad 250
 
251
    countl        db ?           ; Count register, 7:0
252
    counth        db ?           ; Count register, 15:8
9072 rgimad 253
                  db ?           ; Reserved
9064 rgimad 254
    e_status      db ?           ; New value of status register
255
 
256
    tc            dw ?           ; Transfer count
9072 rgimad 257
                  rb 2           ; Reserved
9064 rgimad 258
ends
259
 
260
; DMA Setup – Device to Host
261
struct FIS_DMA_SETUP
262
    fis_type      db ?           ; FIS_TYPE_DMA_SETUP
9131 rgimad 263
    flags        db ?           ; 0bAIDRPPPP, A - Auto-activate. Specifies if DMA Activate FIS is needed,
9064 rgimad 264
                                 ; I - Interrupt bit, D - Data transfer direction, 1 - device to host,
265
                                 ; R - Reserved, P - Port multiplier
266
 
9072 rgimad 267
                  rb 2           ; Reserved
9064 rgimad 268
    DMAbufferID   dq ?           ; DMA Buffer Identifier.
269
                                 ; Used to Identify DMA buffer in host memory.
270
                                 ; SATA Spec says host specific and not in Spec.
271
                                 ; Trying AHCI spec might work.
272
 
9072 rgimad 273
                  dd ?           ; Reserved
274
    DMAbufOffset  dd ?           ; Byte offset into buffer. First 2 bits must be 0
9064 rgimad 275
    TransferCount dd ?           ; Number of bytes to transfer. Bit 0 must be 0
9072 rgimad 276
                  dd ?           ; Reserved
9064 rgimad 277
ends
278
 
279
; Set device bits FIS - device to host
280
struct FIS_DEV_BITS
281
    fis_type      db ?           ; FIS_TYPE_DEV_BITS
9131 rgimad 282
    flags        db ?           ; 0bNIRRPPPP, N - Notification, I - Interrupt,
9064 rgimad 283
                                 ; R - Reserved, P - Port multiplier
284
 
285
    status        db ?           ; Status register
286
    error         db ?           ; Error register
287
 
288
    protocol      dd ?           ; Protocol
289
ends
290
 
9069 rgimad 291
struct HBA_FIS
292
    dsfis         FIS_DMA_SETUP  ; 0x00, DMA Setup FIS
9072 rgimad 293
                  rb 4           ; padding
9069 rgimad 294
 
295
    psfis         FIS_PIO_SETUP  ; 0x20, PIO Setup FIS
9072 rgimad 296
                  rb 12          ; padding
9069 rgimad 297
 
298
    rfis          FIS_REG_D2H    ; 0x40, Register - Device to Host FIS
9072 rgimad 299
                  rb 4           ; padding
9069 rgimad 300
 
301
    sdbfis        FIS_DEV_BITS   ; 0x58, Set Device Bit FIS
302
 
303
    ufis          rb 64          ; 0x60
304
 
9072 rgimad 305
                  rb (0x100 - 0xA0) ; 0xA0, Reserved
9069 rgimad 306
ends
307
 
9064 rgimad 308
; --------------------------------------------------
9020 rgimad 309
uglobal
310
align 4
311
        ahci_controller AHCI_DATA
9068 rgimad 312
        port_data_arr   rb (sizeof.PORT_DATA*AHCI_MAX_PORTS)
9020 rgimad 313
endg
314
 
9064 rgimad 315
; -----------------------------------------------------------------------
9020 rgimad 316
; detect ahci controller and initialize
317
align 4
9068 rgimad 318
ahci_init:
9020 rgimad 319
        mov     ecx, ahci_controller
320
        mov     esi, pcidev_list
321
.find_ahci_ctr:
322
        mov     esi, [esi + PCIDEV.fd]
323
        cmp     esi, pcidev_list
324
        jz      .ahci_ctr_not_found
325
        mov     eax, [esi + PCIDEV.class]
326
        ;DEBUGF  1, "K: device class = %x\n", eax
327
        shr     eax, 8 ; shift right because lowest 8 bits if ProgIf field
328
        cmp     eax, 0x0106 ; 0x01 - Mass Storage Controller class,  0x06 - Serial ATA Controller subclass
329
        jz      .ahci_ctr_found
330
        jmp     .find_ahci_ctr
331
 
332
.ahci_ctr_not_found:
333
        DEBUGF  1, "K: AHCI controller not found\n"
334
        ret
335
 
336
.ahci_ctr_found:
337
        mov     [ahci_controller + AHCI_DATA.pcidev], esi
338
 
339
        mov     eax, [esi+PCIDEV.class]
340
        movzx   ebx, byte [esi+PCIDEV.bus]
341
        movzx   ecx, byte [esi+PCIDEV.devfn]
342
        shr     ecx, 3 ; get rid of 3 lowest bits (function code), the rest bits is device code
343
        movzx   edx, byte [esi+PCIDEV.devfn]
344
        and     edx, 00000111b ; get only 3 lowest bits (function code)
345
        DEBUGF  1, "K: found AHCI controller, (class, subcl, progif) = %x, bus = %x, device = %x, function = %x\n", eax, ebx, ecx, edx
346
 
9023 rgimad 347
        ; get BAR5 value, it is physical address
9037 rgimad 348
        movzx   ebx, [esi + PCIDEV.bus]
349
        movzx   ebp, [esi + PCIDEV.devfn]
350
        stdcall pci_read32, ebx, ebp, PCI_REG_BAR5
351
        DEBUGF  1, "K: AHCI controller MMIO = %x\n", eax
352
        mov     edi, eax
9020 rgimad 353
 
9037 rgimad 354
        ; get the size of MMIO region
355
        stdcall pci_write32, ebx, ebp, PCI_REG_BAR5, 0xFFFFFFFF
356
        stdcall pci_read32, ebx, ebp, PCI_REG_BAR5
357
        not     eax
358
        inc     eax
359
        DEBUGF  1, "K: AHCI: MMIO region size = 0x%x bytes\n", eax
360
 
361
        ; Map MMIO region to virtual memory
362
        stdcall map_io_mem, edi, eax, PG_SWR + PG_NOCACHE
9020 rgimad 363
        mov     [ahci_controller + AHCI_DATA.abar], eax
364
        DEBUGF  1, "K: AHCI controller BAR5 mapped to virtual addr %x\n", eax
365
 
9037 rgimad 366
        ; Restore the original BAR5 value
367
        stdcall pci_write32, ebx, ebp, PCI_REG_BAR5, edi
368
 
9023 rgimad 369
        ; Enable dma bus mastering, memory space access, clear the "disable interrupts" bit
370
        ; Usually, it is already done before us
9024 rgimad 371
        movzx   ebx, [esi + PCIDEV.bus]
372
        movzx   ebp, [esi + PCIDEV.devfn]
373
        stdcall pci_read32, ebx, ebp, PCI_REG_STATUS_COMMAND
9020 rgimad 374
        DEBUGF  1, "K: AHCI: pci_status_command = %x\nEnabling interrupts, DMA bus mastering and memory space access\n", eax
375
        or      eax, 0x06 ; pci.command |= 0x06 (dma bus mastering + memory space access)
376
        btr     eax, 10 ; clear the "disable interrupts" bit
377
        DEBUGF  1, "K: AHCI: pci_status_command = %x\n", eax
9024 rgimad 378
        stdcall pci_write32, ebx, ebp, PCI_REG_STATUS_COMMAND, eax
9020 rgimad 379
 
9023 rgimad 380
        ; ; Print some register values to debug board
381
        ; mov     esi, [ahci_controller + AHCI_DATA.abar]
9064 rgimad 382
        ; DEBUGF  1, "K: AHCI: HBA.cap = %x, HBA.ghc = %x, HBA_MEM.version = %x\n", [esi + HBA_MEM.cap], [esi + HBA_MEM.ghc], [esi + HBA_MEM.version]
9020 rgimad 383
 
9023 rgimad 384
        ;-------------------------------------------------------
385
        ; Request BIOS/OS ownership handoff, if supported. (TODO check correctness)
386
        mov     esi, [ahci_controller + AHCI_DATA.abar]
9064 rgimad 387
        ;mov     ebx, [esi + HBA_MEM.cap2]
9023 rgimad 388
        ;DEBUGF  1, "K: AHCI: HBA_MEM.cap2 = %x\n", ebx
9064 rgimad 389
        bt      [esi + HBA_MEM.cap2], bit_AHCI_HBA_CAP2_BOH
9020 rgimad 390
        jnc     .end_handoff
9023 rgimad 391
        DEBUGF  1, "K: AHCI: requesting AHCI ownership change...\n"
9024 rgimad 392
        bts     [esi + HBA_MEM.bohc], bit_AHCI_HBA_BOHC_OOS
9020 rgimad 393
 
394
.wait_not_bos:
9024 rgimad 395
        bt      [esi + HBA_MEM.bohc], bit_AHCI_HBA_BOHC_BOS
9020 rgimad 396
        jc      .wait_not_bos
397
 
398
        mov     ebx, 3
399
        call    delay_hs
400
 
9023 rgimad 401
        ; if Bios Busy is still set after 30 mS, wait 2 seconds.
9024 rgimad 402
        bt      [esi + HBA_MEM.bohc], bit_AHCI_HBA_BOHC_BB
9020 rgimad 403
        jnc     @f
404
 
405
        mov     ebx, 200
406
        call    delay_hs
407
@@:
9023 rgimad 408
        DEBUGF  1, "K: AHCI: ownership change completed.\n"
9020 rgimad 409
 
410
.end_handoff:
9023 rgimad 411
        ;-------------------------------------------------------
9020 rgimad 412
 
9023 rgimad 413
        ; enable the AHCI and reset it
9064 rgimad 414
        bts     [esi + HBA_MEM.ghc], bit_AHCI_HBA_GHC_AHCI_ENABLE
415
        bts     [esi + HBA_MEM.ghc], bit_AHCI_HBA_GHC_RESET
9020 rgimad 416
 
9023 rgimad 417
        ; wait for reset to complete
418
.wait_reset:
9064 rgimad 419
        bt      [esi + HBA_MEM.ghc], bit_AHCI_HBA_GHC_RESET
9023 rgimad 420
        jc      .wait_reset
9020 rgimad 421
 
9023 rgimad 422
        ; enable the AHCI and interrupts
9064 rgimad 423
        bts     [esi + HBA_MEM.ghc], bit_AHCI_HBA_GHC_AHCI_ENABLE
424
        bts     [esi + HBA_MEM.ghc], bit_AHCI_HBA_GHC_INTERRUPT_ENABLE
9023 rgimad 425
        mov     ebx, 2
426
        call    delay_hs
427
 
9064 rgimad 428
        DEBUGF  1, "K: AHCI: caps: %x %x, ver: %x, ghc: %x, pi: %x\n", [esi + HBA_MEM.cap], [esi + HBA_MEM.cap2], [esi + HBA_MEM.version], [esi + HBA_MEM.ghc], [esi + HBA_MEM.pi]
9020 rgimad 429
 
9037 rgimad 430
        ; TODO:
431
        ; calculate irq line
432
        ; ahciHBA->ghc |= AHCI_GHC_IE;
433
        ; IDT::RegisterInterruptHandler(irq, InterruptHandler);
9064 rgimad 434
        ; ahciHBA->is = 0xffffffff;
9037 rgimad 435
 
436
        xor     ebx, ebx
437
.detect_drives:
438
        cmp     ebx, AHCI_MAX_PORTS
439
        jae     .end_detect_drives
440
 
441
        ; if port with index ebx is not implemented then go to next
9064 rgimad 442
        mov     ecx, [esi + HBA_MEM.pi]
9037 rgimad 443
        bt      ecx, ebx
444
        jnc     .continue_detect_drives
445
 
446
        mov     edi, ebx
9074 rgimad 447
        imul    edi, sizeof.HBA_PORT
9037 rgimad 448
        add     edi, HBA_MEM.ports
449
        add     edi, esi
450
        ; now edi - base of HBA_MEM.ports[ebx]
451
 
9130 rgimad 452
        DEBUGF  1, "K: AHCI: port %d, cmd = %x, ssts = %x\n", ebx, [edi + HBA_PORT.command], [edi + HBA_PORT.sata_status]
9037 rgimad 453
 
9130 rgimad 454
        ; If port is not idle force it to be idle
455
        mov     eax, [edi + HBA_PORT.command]
456
        and     eax, (AHCI_HBA_PxCMD_ST or AHCI_HBA_PxCMD_CR or AHCI_HBA_PxCMD_FRE or AHCI_HBA_PxCMD_FR)
457
        test    eax, eax
458
        jz      @f
459
 
460
        mov     eax, edi
461
        DEBUGF  1, "ahci_stop_cmd..\n"
462
        call    ahci_stop_cmd
463
@@:
464
        ; TODO: what is purpose of this block of code ?
465
        ; Reset port, disable slumber and partial state
466
        ; mov     [edi + HBA_PORT.sata_control], 0x301
467
        ; push    ebx
468
        ; mov     ebx, 5 ; wait 50 ms
469
        ; call    delay_hs
470
        ; pop     ebx
471
        ; mov     [edi + HBA_PORT.sata_control], 0x300
472
 
473
        ; if(abar->cap & HBA_MEM_CAP_SSS)
474
        ; {
475
        ; abar->ports[i].cmd |= (HBA_PxCMD_SUD | HBA_PxCMD_POD | HBA_PxCMD_ICC);
476
        ; Sleep(10);
477
        ; }
478
        ; rewritten to:
479
        bt      [esi + HBA_MEM.cap], 27 ; check Supports Staggered Spin-up bit in capabilities
480
        jnc     @f
9134 rgimad 481
        DEBUGF  1, "Supports Staggered Spin-up, spinning up the port..\n"
9130 rgimad 482
        or      [edi + HBA_PORT.command], (0x0002 or 0x0004 or 0x10000000)
483
        push    ebx
484
        mov     ebx, 1 ; wait 10 ms
485
        call    delay_hs
486
        pop     ebx
487
@@:
488
        ; Clear interrupt status and error status
489
        mov     [edi + HBA_PORT.sata_error], 0xFFFFFFFF
490
        mov     [edi + HBA_PORT.interrupt_status], 0xFFFFFFFF
491
 
492
        ; ------------------------------------------
493
 
9037 rgimad 494
        mov     ecx, [edi + HBA_PORT.sata_status]
495
        shr     ecx, 8
496
        and     ecx, 0x0F
497
        cmp     ecx, AHCI_HBA_PORT_IPM_ACTIVE
498
        jne     .continue_detect_drives
499
 
500
        mov     ecx, [edi + HBA_PORT.sata_status]
501
        and     ecx, AHCI_HBA_PxSSTS_DET
502
        cmp     ecx, AHCI_HBA_PxSSTS_DET_PRESENT
9068 rgimad 503
        jne     .continue_detect_drives
9037 rgimad 504
 
9134 rgimad 505
        ; DEBUGF  1, "K: AHCI: found drive at port %d, cmd = 0x%x, ssts = 0x%x, signature = 0x%x\n", ebx, [edi + HBA_PORT.command], [edi + HBA_PORT.sata_status], [edi + HBA_PORT.signature]
9037 rgimad 506
 
9068 rgimad 507
        mov     ecx, ebx
9074 rgimad 508
        imul    ecx, sizeof.PORT_DATA
9068 rgimad 509
        add     ecx, port_data_arr
510
        stdcall ahci_port_rebase, edi, ebx, ecx
511
 
9134 rgimad 512
        ; DEBUGF  1, "K: AHCI: After REBASING, signature = 0x%x\n", [edi + HBA_PORT.signature]
513
 
9135 rgimad 514
        ; Determine drive type by checking port signature
9134 rgimad 515
.switch_sig:
516
        cmp     [edi + HBA_PORT.signature], SATA_SIG_ATA
9135 rgimad 517
        mov     eax, AHCI_DEV_SATA
518
        jz      .end_switch_sig
519
 
9134 rgimad 520
        cmp     [edi + HBA_PORT.signature], SATA_SIG_ATAPI
9135 rgimad 521
        mov     eax, AHCI_DEV_SATAPI
522
        jz      .end_switch_sig
523
 
9134 rgimad 524
        cmp     [edi + HBA_PORT.signature], SATA_SIG_SEMB
9135 rgimad 525
        mov     eax, AHCI_DEV_SEMB
526
        jz      .end_switch_sig
527
 
9134 rgimad 528
        cmp     [edi + HBA_PORT.signature], SATA_SIG_PM
9135 rgimad 529
        mov     eax, AHCI_DEV_PM
530
        jz      .end_switch_sig
531
 
9134 rgimad 532
        DEBUGF  1, "Unknown device signature\n"
9135 rgimad 533
        mov     eax, AHCI_DEV_NULL
9134 rgimad 534
.end_switch_sig:
9136 rgimad 535
        mov     [ecx + PORT_DATA.drive_type], al
9134 rgimad 536
 
537
        DEBUGF  1, "K: AHCI: found drive on port %u: TYPE = %u\n", ebx, [ecx + PORT_DATA.drive_type]
538
 
9074 rgimad 539
        stdcall ahci_port_identify, ecx
540
 
9037 rgimad 541
.continue_detect_drives:
542
        inc     ebx
543
        jmp     .detect_drives
544
 
9064 rgimad 545
 
9037 rgimad 546
 
547
.end_detect_drives:
548
 
549
 
9020 rgimad 550
        ret
9065 rgimad 551
; -------------------------------------------------
9020 rgimad 552
 
9074 rgimad 553
modelstr  rb 42
554
; Identify drive on port ; TODO check
555
; in: pdata - address of PORT_DATA structure
556
proc ahci_port_identify stdcall, pdata: dword
557
        locals
558
            cmdslot dd ?
559
            cmdheader dd ?
560
            cmdtable  dd ?
561
            buf_phys  dd ?
562
            buf_virt  dd ?
563
        endl
564
 
565
        pushad
566
 
567
        mov     esi, [pdata] ; esi - address of PORT_DATA struct of port
568
        mov     edi, [esi + PORT_DATA.port] ; edi - address of HBA_PORT struct of port
569
 
570
        mov     eax, edi
571
        call    ahci_find_cmdslot
572
 
573
        cmp     eax, -1
574
        jne      .cmdslot_found
575
 
576
        DEBUGF  1, "No free cmdslot on port %u\n", [esi + PORT_DATA.portno]
577
 
578
.cmdslot_found:
579
        mov     [cmdslot], eax
9134 rgimad 580
        ; DEBUGF  1, "Found free cmdslot %u on port %u\n", [cmdslot], [esi + PORT_DATA.portno]
9074 rgimad 581
 
582
        shl     eax, BSF sizeof.HBA_CMD_HDR
583
        add     eax, [esi + PORT_DATA.clb]
584
        mov     [cmdheader], eax ; address of virtual mapping of command header
585
        mov     eax, [cmdslot]
586
        mov     eax, [esi + eax*4 + PORT_DATA.ctba_arr]
587
        mov     [cmdtable], eax ; address of virtual mapping of command table of command header
588
 
589
        stdcall _memset, eax, 0, sizeof.HBA_CMD_TBL
590
 
591
        call    alloc_page
592
        mov     [buf_phys], eax
593
 
594
        stdcall map_io_mem, eax, 4096, PG_NOCACHE + PG_SWR  ; map to virt memory so we can work with it
595
        mov     [buf_virt], eax
596
 
597
        mov     eax, [cmdtable]
598
        mov     ebx, [buf_phys]
599
        mov     dword [eax + HBA_CMD_TBL.prdt_entry + HBA_PRDT_ENTRY.dba], ebx
600
        mov     dword [eax + HBA_CMD_TBL.prdt_entry + HBA_PRDT_ENTRY.dbau], 0
9131 rgimad 601
        and     [eax + HBA_CMD_TBL.prdt_entry + HBA_PRDT_ENTRY.flags], not 0x3FFFFF ; zero out lower 22 bits, they used for byte count
602
        or      [eax + HBA_CMD_TBL.prdt_entry + HBA_PRDT_ENTRY.flags], 512 - 1 ; reason why -1 see in spec on this field
603
        ; or      [eax + HBA_CMD_TBL.prdt_entry + HBA_PRDT_ENTRY.flags], 1 shl 31 ; enable interrupt on completion
604
 
9074 rgimad 605
        mov     eax, [cmdheader]
9131 rgimad 606
        and     [eax + HBA_CMD_HDR.flags1], not 0x1F ; zero out lower 5 bits, they will be used for cfl
607
        or      [eax + HBA_CMD_HDR.flags1], (sizeof.FIS_REG_H2D / 4) ; set command fis length in dwords
608
        movzx   bx, [eax + HBA_CMD_HDR.flags1]
609
        btr     bx, 6 ; flag W = 0
610
        mov     [eax + HBA_CMD_HDR.flags1], bl
611
        movzx   bx, [eax + HBA_CMD_HDR.flags2]
612
        btr     bx, 2 ; flag C = 0
613
        mov     [eax + HBA_CMD_HDR.flags2], bl
9074 rgimad 614
        mov     [eax + HBA_CMD_HDR.prdtl], 1
615
 
616
        mov     eax, [cmdtable]
617
        mov     byte [eax + HBA_CMD_TBL.cfis + FIS_REG_H2D.fis_type], FIS_TYPE_REG_H2D
9131 rgimad 618
        movzx   ebx, byte [eax + HBA_CMD_TBL.cfis + FIS_REG_H2D.flags]
9074 rgimad 619
        bts     ebx, bit_AHCI_H2D_FLAG_CMD ; Set Command bit in H2D FIS.
9131 rgimad 620
        mov     byte [eax + HBA_CMD_TBL.cfis + FIS_REG_H2D.flags], bl
9134 rgimad 621
 
622
        mov     byte [eax + HBA_CMD_TBL.cfis + FIS_REG_H2D.command], ATA_IDENTIFY
623
        cmp     [esi + PORT_DATA.drive_type], AHCI_DEV_SATAPI
624
        jne     @f
625
        mov     byte [eax + HBA_CMD_TBL.cfis + FIS_REG_H2D.command], ATAPI_IDENTIFY
626
@@:
9074 rgimad 627
        mov     byte [eax + HBA_CMD_TBL.cfis + FIS_REG_H2D.device], 0
628
 
9139 rgimad 629
        ; Wait on previous command to complete, before issuing new command.
630
        stdcall ahci_port_wait, edi, AHCI_PORT_TIMEOUT
631
        ; DEBUGF  1, "eax = %x\n", eax
632
        ; TODO check eax error value
9074 rgimad 633
 
634
        mov     eax, [cmdslot]
635
        bts     [edi + HBA_PORT.command_issue], eax ; Issue the command
636
 
9139 rgimad 637
        ; Wait for command completion
638
        stdcall ahci_port_cmd_wait, edi, eax;, AHCI_PORT_CMD_TIMEOUT
639
        ; DEBUGF  1, " eax = %x\n", eax
640
        ; TODO check eax error value
9074 rgimad 641
 
9134 rgimad 642
        ; DEBUGF  1, "sata_error register = 0x%x\n", [edi + HBA_PORT.sata_error]
9130 rgimad 643
 
644
        mov     esi, [buf_virt]
9074 rgimad 645
        add     esi, 27*2
646
        mov     edi, modelstr
647
        mov     ecx, ((46-27)+1)*2
648
        cld
649
        rep movsb
650
        mov     byte [edi], 0
651
 
9138 rgimad 652
        stdcall swap_bytes_in_words, modelstr, (46-27)+1
9131 rgimad 653
        DEBUGF  1, "IDENTIFICATION RESULT: MODEL = %s\n", modelstr
9074 rgimad 654
 
9138 rgimad 655
        mov     esi, [buf_virt]
656
        mov     eax, [esi + 200]
657
        mov     edx, [esi + 200 + 4]
658
        DEBUGF 1, "lba48 mode sector count = 0x%x:%x\n", edx, eax
659
 
660
        shrd    eax, edx, 11 ; i.e *512 / 1024 / 1024, 512 - sector size
661
        DEBUGF  1, "disk capacity = %u MiB ", eax
662
        shrd    eax, edx, 10 ; / 1024
663
        DEBUGF  1, "= %u GiB\n", eax
9074 rgimad 664
.ret:
665
        popad
666
        ret
667
endp
668
 
9134 rgimad 669
 
9065 rgimad 670
; Start command engine
671
; in: eax - address of HBA_PORT structure
9068 rgimad 672
ahci_start_cmd:
9065 rgimad 673
.wait_cr: ; Wait until CR (bit15) is cleared
674
        bt      [eax + HBA_PORT.command], bit_AHCI_HBA_PxCMD_CR
675
        jc      .wait_cr
676
 
677
        ; Set FRE (bit4) and ST (bit0)
678
        bts     [eax + HBA_PORT.command], bit_AHCI_HBA_PxCMD_FRE
679
        bts     [eax + HBA_PORT.command], bit_AHCI_HBA_PxCMD_ST
9068 rgimad 680
        ; maybe here call ahci flush cmd ? TODO (see seakernel)
9065 rgimad 681
        ret
682
 
683
; Stop command engine
684
; in: eax - address of HBA_PORT structure
9068 rgimad 685
ahci_stop_cmd:
9065 rgimad 686
        btr     [eax + HBA_PORT.command], bit_AHCI_HBA_PxCMD_ST ; Clear ST (bit0)
687
        btr     [eax + HBA_PORT.command], bit_AHCI_HBA_PxCMD_FRE ; Clear FRE (bit4)
688
.wait_fr_cr: ; Wait until FR (bit14), CR (bit15) are cleared
689
        bt      [eax + HBA_PORT.command], bit_AHCI_HBA_PxCMD_FR
690
        jc      .wait_fr_cr
691
        bt      [eax + HBA_PORT.command], bit_AHCI_HBA_PxCMD_CR
692
        jc      .wait_fr_cr
693
 
694
        ret
695
 
9139 rgimad 696
; waits until the port is no longer busy before issuing a new command
697
; in: [port] - address of HBA_PORT structure
698
; [timeout] - timeout (in iterations)
699
; out: eax = 0 if success, 1 if timeout expired
700
proc ahci_port_wait stdcall, port: dword, timeout: dword
701
        push    ebx ecx
702
        mov     ebx, [port]
703
        xor     ecx, ecx
704
.wait:
705
        cmp     ecx, [timeout]
706
        jae     .wait_end
707
        mov     eax, [ebx + HBA_PORT.task_file_data]
708
        and     eax, ATA_DEV_BUSY or ATA_DEV_DRQ
709
        test    eax, eax
710
        jz      .wait_end
711
        inc     ecx
712
        jmp     .wait
713
.wait_end:
714
        xor     eax, eax
715
        DEBUGF  1, "port wait counter = %u\n", ecx
716
        cmp     ecx, [timeout] ; if they equal it means port is hung
717
        setz    al
718
        pop     ecx ebx
9068 rgimad 719
        ret
9139 rgimad 720
endp
9065 rgimad 721
 
722
 
9139 rgimad 723
; Wait for command completion
724
; in: [port] - address of HBA_PORT structure
725
;     [cmdslot] - number of command slot
726
; out: eax = 0 if success, 1 if error
727
proc ahci_port_cmd_wait stdcall, port: dword, cmdslot: dword ;, timeout: dword
728
        push    ebx ecx edx
729
        mov     ebx, [port]
730
        mov     edx, [cmdslot]
731
        xor     eax, eax
732
        xor     ecx, ecx
733
.wait:
734
        bt      [ebx + HBA_PORT.command_issue], edx
735
        jnc     .wait_end
736
        bt      [ebx + HBA_PORT.interrupt_status], bit_AHCI_HBA_PxIS_TFES ; check for Task File Error
737
        jc      .error
738
        inc     ecx
739
        jmp     .wait
740
.wait_end:
741
        DEBUGF  1, "port cmd wait counter = %u\n", ecx
742
        bt      [ebx + HBA_PORT.interrupt_status], bit_AHCI_HBA_PxIS_TFES ; check for Task File Error
743
        jc      .error
744
        jmp     .ret
745
.error:
746
        mov     eax, 1
747
.ret:
748
        pop     edx ecx ebx
9068 rgimad 749
        ret
9139 rgimad 750
endp
9065 rgimad 751
 
9139 rgimad 752
; ; The commands may not take effect until the command
753
; ; register is read again by software, because reasons.
754
; ; in: eax - address of HBA_PORT structure
755
; ; out: eax - command register value
756
; ahci_flush_cmd:
757
;         mov     eax, [eax + HBA_PORT.command]
758
;         ret
759
 
760
; ; Send command to port
761
; ; in: eax - address of HBA_PORT structure
762
; ;     ebx - index of command slot
763
; ahci_send_cmd:
764
;         push    ecx
765
;         mov     [eax + HBA_PORT.interrupt_status], 0xFFFFFFFF
766
 
767
;         mov     cl, bl
768
;         mov     [eax + HBA_PORT.command_issue], 1
769
;         shl     [eax + HBA_PORT.command_issue], cl
770
 
771
;         call    ahci_flush_cmd
772
;         pop     ecx
773
;         ret
774
 
9068 rgimad 775
; ---------------------------------------------------------------------------
776
; in: port - address of HBA_PORT structure
777
;     portno - port index (0..31)
778
;     pdata - address of PORT_DATA structure
779
proc ahci_port_rebase stdcall, port: dword, portno: dword, pdata: dword
780
        locals
781
            phys_page1  dd ?
782
            virt_page1  dd ?
783
            phys_page23 dd ?
784
            virt_page23 dd ?
785
            tmp         dd ?
786
        endl
787
 
788
        pushad
789
 
790
        DEBUGF  1, "Rebasing port %u\n", [portno]
791
 
792
        mov     eax, [port]
793
        call    ahci_stop_cmd
794
 
795
        ; Command list entry size = 32
796
        ; Command list entry maxim count = 32
797
        ; Command list maxim size = 32*32 = 1K per port
798
        call    alloc_page
799
        mov     [phys_page1], eax
800
 
801
        stdcall map_io_mem, eax, 4096, PG_NOCACHE + PG_SWR  ; map to virt memory so we can work with it
802
        mov     [virt_page1], eax
803
 
804
        mov     esi, [port]
805
        mov     ebx, [phys_page1]
806
        mov     [esi + HBA_PORT.command_list_base_l], ebx ; set the command list base
807
        mov     [esi + HBA_PORT.command_list_base_h], 0  ; zero upper 32 bits of addr cause we are 32 bit os
808
 
809
        mov     edi, [pdata]
810
        mov     ebx, [virt_page1]
811
        mov     [edi + PORT_DATA.clb], ebx ; set pdata->clb
812
 
813
        mov     eax, [port]
814
        mov     [edi + PORT_DATA.port], eax ; set pdata->port
9074 rgimad 815
        mov     eax, [portno]               ; set pdata->portno
816
        mov     [edi + PORT_DATA.portno], eax
9068 rgimad 817
 
818
        stdcall _memset, ebx, 0, 1024 ; zero out the command list
819
 
820
        ; FIS entry size = 256 bytes per port
821
        mov     eax, [phys_page1]
822
        add     eax, 1024
823
        mov     [esi + HBA_PORT.fis_base_l], eax
824
        mov     [esi + HBA_PORT.fis_base_h], 0
825
 
826
        mov     eax, [virt_page1]
827
        add     eax, 1024
828
        mov     [edi + PORT_DATA.fb], eax ; set pdata->fb
829
        stdcall _memset, eax, 0, 256 ; zero out
830
 
831
        stdcall alloc_pages, 2
832
        mov     [phys_page23], eax
833
        stdcall map_io_mem, eax, 2*4096, PG_NOCACHE + PG_SWR
834
        mov     [virt_page23], eax
835
 
836
        ; Command table size = 256*32 = 8K per port
837
        mov     edx, [edi + PORT_DATA.clb] ; cmdheader array base
838
        xor     ecx, ecx
839
 
840
.for1:
841
        cmp     ecx, 32
842
        jae     .for1_end
843
 
844
        mov     ebx, ecx
845
        shl     ebx, BSF sizeof.HBA_CMD_HDR
846
        add     ebx, edx ; ebx = cmdheader[ecx]
847
 
848
        mov     [ebx + HBA_CMD_HDR.prdtl], 8 ; 8 prdt entries per command table
849
 
850
        ; 256 bytes per command table, 64+16+48+16*8
851
 
852
        push    edx
853
 
854
        ; cmdheader[ecx].ctba = phys_page23 + ecx*256
855
        mov     [ebx + HBA_CMD_HDR.ctba], ecx
856
        shl     [ebx + HBA_CMD_HDR.ctba], BSF 256 ; *= 256
857
        mov     eax, [ebx + HBA_CMD_HDR.ctba]
858
        mov     edx, [phys_page23]
859
        add     [ebx + HBA_CMD_HDR.ctba], edx
860
 
861
        add     eax, [virt_page23]
862
        mov     [tmp], eax  ; tmp = virt_page23 + ecx*256
9069 rgimad 863
        lea     eax, [ecx*4 + edi + PORT_DATA.ctba_arr] ; eax = pdata->ctba_arr[ecx]
9068 rgimad 864
        mov     edx, [tmp]
865
        mov     [eax], edx  ; pdata->ctba_arr[ecx] = virt_page23 + ecx*256
866
 
867
        pop     edx
868
 
869
        mov     [ebx + HBA_CMD_HDR.ctbau], 0
870
        stdcall _memset, [eax], 0, 256 ; zero out
871
 
872
        inc     ecx
873
        jmp     .for1
874
.for1_end:
875
 
876
        mov     eax, [port]
877
        call    ahci_start_cmd
878
 
879
        DEBUGF  1, "End rebasing port %u\n", [portno]
880
        popad
881
        ret
882
endp
883
 
9069 rgimad 884
; ----------------------------------------------------------- ; TODO check
885
; Find a free command list slot
886
; in: eax - address of HBA_PORT structure
887
; out: eax - if not found -1, else slot index
888
ahci_find_cmdslot:
889
        push    ebx ecx edx esi
890
        ; If not set in SACT and CI, the slot is free
891
        mov     ebx, [eax + HBA_PORT.sata_active]
892
        or      ebx, [eax + HBA_PORT.command_issue] ; ebx = slots
9068 rgimad 893
 
9069 rgimad 894
        mov     esi, [ahci_controller + AHCI_DATA.abar]
895
        mov     edx, [esi + HBA_MEM.cap]
896
        shr     edx, 8
897
        and     edx, 0xf
9134 rgimad 898
        ; DEBUGF  1, "Number of Command Slots on each port = %u\n", edx
9069 rgimad 899
        xor     ecx, ecx
900
.for1:
901
        cmp     ecx, edx
902
        jae     .for1_end
9068 rgimad 903
 
9069 rgimad 904
        ; if ((slots&1) == 0) return i;
905
        bt      ebx, 0
906
        jc      .cont1
907
 
908
        mov     eax, ecx
909
        jmp     .ret
910
 
911
.cont1:
912
        shr     ebx, 1
913
        inc     ecx
914
        jmp     .for1
915
.for1_end:
916
        DEBUGF  1, "Cannot find free command list entry\n"
917
        mov     eax, -1
918
.ret:
919
        pop     esi edx ecx ebx
920
        ret
921
 
922
 
9068 rgimad 923
proc _memset stdcall, dest:dword, val:byte, cnt:dword ; doesnt clobber any registers
924
        ;DEBUGF  DBG_INFO, "memset(%x, %u, %u)\n", [dest], [val], [cnt]
925
        push    eax ecx edi
926
        mov     edi, dword [dest]
927
        mov     al,  byte [val]
928
        mov     ecx, dword [cnt]
9069 rgimad 929
        rep stosb
9068 rgimad 930
        pop     edi ecx eax
931
        ret
932
endp
9138 rgimad 933
 
934
; Swaps byte order in words
935
; base - address of first word
936
; len - how many words to swap bytes in
937
; doesnt clobber any registers
938
proc swap_bytes_in_words stdcall, base: dword, len: dword
939
        push    eax ebx ecx
940
        xor     ecx, ecx
941
        mov     ebx, [base]
942
.loop:
943
        cmp     ecx, [len]
944
        jae     .loop_end
945
        mov     ax, word [ebx + ecx*2]
946
        xchg    ah, al
947
        mov     word [ebx + ecx*2], ax
948
        inc     ecx
949
        jmp     .loop
950
.loop_end:
951
        pop     ecx ebx eax
952
        ret
953
endp