kolibrios-gitea/kernel/trunk/blkdev/hd_drv.inc

568 lines
16 KiB
PHP
Raw Permalink Normal View History

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2024. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; HDD driver
struct HD_DATA
hdpos dw ?
hdid dw ?
hdbase dw ?
hd48 dw ?
sectors dq ?
ends
;-----------------------------------------------------------------
iglobal
align 4
ide_callbacks:
dd ide_callbacks.end - ide_callbacks
dd 0 ; no close function
dd 0 ; no closemedia function
dd ide_querymedia
dd ide_read
dd ide_write
dd 0 ; no flush function
dd 0 ; use default cache size
.end:
hd0_data HD_DATA 1, 0
hd1_data HD_DATA 2, 16
hd2_data HD_DATA 3, 0
hd3_data HD_DATA 4, 16
hd4_data HD_DATA 5, 0
hd5_data HD_DATA 6, 16
hd6_data HD_DATA 7, 0
hd7_data HD_DATA 8, 16
hd8_data HD_DATA 9, 0
hd9_data HD_DATA 10, 16
hd10_data HD_DATA 11, 0
hd11_data HD_DATA 12, 16
ide_mutex_table:
dd ide_channel1_mutex
dd ide_channel2_mutex
dd ide_channel3_mutex
dd ide_channel4_mutex
dd ide_channel5_mutex
dd ide_channel6_mutex
endg
;-----------------------------------------------------------------
uglobal
ide_mutex MUTEX
ide_channel1_mutex MUTEX
ide_channel2_mutex MUTEX
ide_channel3_mutex MUTEX
ide_channel4_mutex MUTEX
ide_channel5_mutex MUTEX
ide_channel6_mutex MUTEX
blockSize:
rb 4
sector:
rb 6
allow_dma_access db ?
IDE_common_irq_param db ?
eventPointer dd ?
eventID dd ?
endg
;-----------------------------------------------------------------
ide_read:
mov al, 25h ; READ DMA EXT
jmp ide_read_write
ide_write:
mov al, 35h ; WRITE DMA EXT
proc ide_read_write stdcall uses esi edi ebx, \
hd_data, buffer, startsector:qword, numsectors
; hd_data = pointer to hd*_data
; buffer = pointer to buffer with/for data
; startsector = 64-bit start sector
; numsectors = pointer to number of sectors on input,
; must be filled with number of sectors really read/written
locals
sectors_todo dd ?
channel_lock dd ?
endl
mov bl, al
; get number of requested sectors and say that no sectors were read yet
mov ecx, [numsectors]
mov eax, [ecx]
mov dword [ecx], 0
mov [sectors_todo], eax
; acquire the global lock
mov ecx, ide_mutex
call mutex_lock
mov ecx, [hd_data]
movzx ecx, [ecx+HD_DATA.hdpos]
dec ecx
shr ecx, 1
shl ecx, 2
mov ecx, [ecx + ide_mutex_table]
mov [channel_lock], ecx
call mutex_lock
; prepare worker procedures variables
mov esi, [buffer]
mov edi, esi
mov ecx, [hd_data]
movzx eax, [ecx+HD_DATA.hdbase]
mov [hdbase], eax
mov ax, [ecx+HD_DATA.hdid]
mov [hdid], eax
mov eax, dword [startsector]
mov [sector], eax
cmp [ecx+HD_DATA.hd48], 0
jz .LBA28
mov ax, word [startsector+4]
mov [sector+4], ax
movzx ecx, [ecx+HD_DATA.hdpos]
mov [hdpos], ecx
dec ecx
shr ecx, 2
imul ecx, sizeof.IDE_DATA
add ecx, IDE_controller_1
mov [IDE_controller_pointer], ecx
mov eax, [hdpos]
dec eax
and eax, 11b
shr eax, 1
add eax, ecx
cmp [eax+IDE_DATA.dma_hdd_channel_1], 1
jz .next
dec ebx ; READ/WRITE SECTOR(S) EXT
; LBA48 supports max 10000h sectors per time
; loop until all sectors will be processed
.next:
mov ecx, 8000h
cmp ecx, [sectors_todo]
jbe @f
mov ecx, [sectors_todo]
@@:
mov [blockSize], ecx
push ecx
call IDE_transfer
pop ecx
jc .out
mov eax, [numsectors]
add [eax], ecx
sub [sectors_todo], ecx
jz .out
add [sector], ecx
adc word [sector+4], 0
jmp .next
.LBA28:
add eax, [sectors_todo]
add eax, 0xF0000000
jc .out
sub bl, 5 ; READ/WRITE SECTOR(S)
; LBA28 supports max 256 sectors per time
; loop until all sectors will be processed
.next28:
mov ecx, 256
cmp ecx, [sectors_todo]
jbe @f
mov ecx, [sectors_todo]
@@:
mov [blockSize], ecx
push ecx
call IDE_transfer.LBA28
pop ecx
jc .out
mov eax, [numsectors]
add [eax], ecx
sub [sectors_todo], ecx
jz .out
add [sector], ecx
jmp .next28
; loop is done, either due to error or because everything is done
; release the global lock and return the corresponding status
.out:
sbb eax, eax
push eax
mov ecx, [channel_lock]
call mutex_unlock
mov ecx, ide_mutex
call mutex_unlock
pop eax
ret
endp
;-----------------------------------------------------------------
proc ide_querymedia stdcall, hd_data, mediainfo
mov eax, [mediainfo]
mov edx, [hd_data]
mov [eax+DISKMEDIAINFO.Flags], 0
mov [eax+DISKMEDIAINFO.SectorSize], 512
mov ecx, dword[edx+HD_DATA.sectors]
mov dword[eax+DISKMEDIAINFO.Capacity], ecx
mov ecx, dword[edx+HD_DATA.sectors+4]
mov dword[eax+DISKMEDIAINFO.Capacity+4], ecx
xor eax, eax
ret
endp
;-----------------------------------------------------------------
; input: esi -> buffer, bl = command, [sector], [blockSize]
; output: esi -> next block in buffer
; for pio read esi equal edi
IDE_transfer:
mov edx, [hdbase]
add edx, 6
mov al, byte [hdid]
add al, 224
out dx, al ; select the desired drive
call save_hd_wait_timeout
inc edx
@@:
call check_hd_wait_timeout
jc .hd_error
in al, dx
test al, 128 ; ready for command?
jnz @b
pushfd ; fill the ports
cli
mov edx, [hdbase]
inc edx
inc edx
mov al, [blockSize+1]
out dx, al ; Sector count (15:8)
inc edx
mov eax, [sector+3]
out dx, al ; LBA (31:24)
inc edx
shr eax, 8
out dx, al ; LBA (39:32)
inc edx
shr eax, 8
out dx, al ; LBA (47:40)
sub edx, 3
mov al, [blockSize]
out dx, al ; Sector count (7:0)
inc edx
mov eax, [sector]
out dx, al ; LBA (7:0)
inc edx
shr eax, 8
out dx, al ; LBA (15:8)
inc edx
shr eax, 8
out dx, al ; LBA (23:16)
inc edx
mov al, byte [hdid]
add al, 224
out dx, al
test bl, 1
jz .PIO
; DMA
mov dword [esp], 0x1000
call kernel_alloc
mov edi, eax
push eax
shl dword [blockSize], 9
mov eax, esi
add eax, [blockSize]
push eax
; check buffer pages physical addresses and fill the scatter-gather list
; buffer may be not aligned and may have size not divisible by page size
; [edi] = block physical address, [edi+4] = block size in bytes
; block addresses can not cross 10000h borders
mov ecx, esi
and ecx, 0xFFF
jz .aligned
mov eax, esi
call get_pg_addr
add eax, ecx
neg ecx
add ecx, 0x1000
mov [edi], eax
cmp ecx, [blockSize]
jnc .end
mov [edi+4], ecx
add esi, 0x1000
add edi, 8
sub [blockSize], ecx
.aligned:
mov eax, esi
call get_pg_addr
mov ecx, eax
mov [edi], eax
and ecx, 0xFFFF
neg ecx
add ecx, 0x10000
cmp [blockSize], ecx
jnc @f
mov ecx, [blockSize]
and ecx, 0xF000
jz .end
@@:
push ecx
@@:
add esi, 0x1000
add eax, 0x1000
sub ecx, 0x1000
jz @f
mov edx, eax
mov eax, esi
call get_pg_addr
cmp eax, edx
jz @b
@@:
pop edx
sub edx, ecx
mov [edi+4], edx
add edi, 8
sub [blockSize], edx
jnz .aligned
sub edi, 8
jmp @f
.end:
mov ecx, [blockSize]
mov [edi+4], ecx
@@:
mov byte [edi+7], 80h ; list end
pop esi
pop edi
; select controller Primary or Secondary
mov ecx, [IDE_controller_pointer]
mov dx, [ecx+IDE_DATA.RegsBaseAddres]
mov eax, [hdpos]
dec eax
test eax, 10b
jz @f
add edx, 8
@@:
add edx, 2 ; Bus Master IDE Status register
mov al, 6
out dx, al ; clear Error bit and Interrupt bit
add edx, 2 ; Bus Master IDE PRD Table Address
mov eax, edi
call get_pg_addr
out dx, eax ; send scatter-gather list physical address
push edx
mov edx, [hdbase]
add edx, 7 ; ATACommand
mov al, bl
out dx, al ; Start hard drive
pop edx
sub edx, 4 ; Bus Master IDE Command register
mov al, 1 ; set direction
cmp bl, 35h ; write
jz @f
add al, 8 ; read
@@:
out dx, al ; Start Bus Master
mov [IDE_common_irq_param], 14
mov eax, [hdpos]
dec eax
test eax, 10b
jz @f
inc [IDE_common_irq_param]
@@:
push edi esi ebx
xor ecx, ecx
xor esi, esi
call create_event
mov [eventPointer], eax
mov [eventID], edx
sti
mov ebx, edx
mov ecx, 300
call wait_event_timeout
test eax, eax
jnz @f
dbgstr 'IDE DMA IRQ timeout'
mov [IDE_common_irq_param], 0
mov eax, [eventPointer]
mov ebx, [eventID]
call destroy_event
mov [eventPointer], 0
@@:
pop ebx esi
call kernel_free
cmp [eventPointer], 0
jz .hd_error
ret
.LBA28:
mov edx, [hdbase]
add edx, 6
mov al, byte [hdid]
add al, 224
out dx, al ; select the desired drive
call save_hd_wait_timeout
inc edx
@@:
call check_hd_wait_timeout
jc .hd_error
in al, dx
test al, 128 ; ready for command?
jnz @b
pushfd ; fill the ports
cli
mov edx, [hdbase]
inc edx
inc edx
mov al, [blockSize]
out dx, al ; Sector count (7:0)
inc edx
mov eax, [sector]
out dx, al ; LBA (7:0)
inc edx
shr eax, 8
out dx, al ; LBA (15:8)
inc edx
shr eax, 8
out dx, al ; LBA (23:16)
inc edx
shr eax, 8
add al, byte [hdid]
add al, 224
out dx, al ; LBA (27:24)
.PIO:
inc edx ; ATACommand
mov al, bl
out dx, al ; Start hard drive
popfd
.sectorTransfer:
call save_hd_wait_timeout
in al, dx
in al, dx
in al, dx
in al, dx
@@:
call check_hd_wait_timeout
jc .hd_error
in al, dx
test al, 8 ; ready for transfer?
jz @b
cmp [hd_setup], 1 ; do not mark error for setup request
jz @f
test al, 1 ; previous command ended up with an error
jnz .pio_error
@@:
pushfd
cli
cld
mov ecx, 256
mov edx, [hdbase]
cmp bl, 30h
jnc .write
rep insw
jmp @f
.write:
rep outsw
@@:
popfd
add edx, 7
dec dword [blockSize]
jnz .sectorTransfer
ret
.pio_error:
dbgstr 'IDE PIO transfer error'
.hd_error:
cmp bl, 30h
jnc hd_write_error
;-----------------------------------------------------------------
hd_read_error:
dbgstr 'HD read error'
stc
ret
;-----------------------------------------------------------------
hd_write_error:
dbgstr 'HD write error'
stc
ret
;-----------------------------------------------------------------
save_hd_wait_timeout:
mov eax, [timer_ticks]
add eax, 300 ; 3 sec timeout
mov [hd_wait_timeout], eax
ret
;-----------------------------------------------------------------
check_hd_wait_timeout:
mov eax, [timer_ticks]
cmp [hd_wait_timeout], eax
jc @f
ret
@@:
dbgstr 'IDE device timeout'
stc
ret
;-----------------------------------------------------------------
align 4
IDE_irq_14_handler:
IDE_irq_15_handler:
IDE_common_irq_handler:
; Most of the time, we are here because we have requested
; a DMA transfer for the corresponding drive.
; However,
; (a) we can be here because IDE IRQ is shared with some other device,
; that device has actually raised IRQ,
; it has nothing to do with IDE;
; (b) we can be here because IDE controller just does not want
; to be silent and reacts to something even though
; we have, in theory, disabled IRQs.
; If the interrupt corresponds to our current request,
; remove the interrupt request and raise the event for the waiting code.
; In the case (a), just return zero - not our interrupt.
; In the case (b), remove the interrupt request and hope for the best.
; DEBUGF 1, 'K : IDE_irq_handler %x\n', [IDE_common_irq_param]:2
mov ecx, [esp+4]
mov dx, [ecx+IDE_DATA.RegsBaseAddres]
add edx, 2 ; Bus Master IDE Status register
in al, dx
test al, 4
jnz .interrupt_from_primary
add edx, 8
in al, dx
test al, 4
jnz .interrupt_from_secondary
xor eax, eax ; not our interrupt
ret
.interrupt_from_primary:
out dx, al ; clear Interrupt bit
sub edx, 2
xor eax, eax
out dx, al ; clear Bus Master IDE Command register
mov dx, [ecx+IDE_DATA.BAR0_val]
add edx, 7
in al, dx ; read status register
cmp [IDE_common_irq_param], 14
jz .raise
.exit_our:
mov al, 1
ret
.interrupt_from_secondary:
out dx, al ; clear Interrupt bit
sub edx, 2
xor eax, eax
out dx, al ; clear Bus Master IDE Command register
mov dx, [ecx+IDE_DATA.BAR2_val]
add edx, 7
in al, dx ; read status register
cmp [IDE_common_irq_param], 15
jnz .exit_our
.raise:
cmp ecx, [IDE_controller_pointer]
jnz .exit_our
pushad
mov eax, [eventPointer]
mov ebx, [eventID]
xor edx, edx
xor esi, esi
call raise_event
popad
mov al, 1 ; remove the interrupt request
ret