Nightloader/i386/legacy-boot/src-asm/drivers/pci/enumerate.asm

378 lines
11 KiB
NASM
Executable File

; Arguments:
; [Furthest from EBP]
; 2. U16 Function (0 .. 7)
; 1. U16 Device Number (0 .. 31)
; 0. U16 Bus Number (0 .. 255)
; [Nearest to EBP]
;
; Returns:
; eax: If there is NO device at the location: 0
; If there IS a device at the location: 1
pci_check_device_exists:
.prolog:
push esi
sub esp, 32
mov [esi + 24], ebx
mov [esi + 28], edx
.gather_arguments:
mov ax, [ebp - 2]
mov [esi + 4], al
mov ax, [ebp - 4]
and al, 0x1f
mov [esi + 5], al
mov ax, [ebp - 6]
and al, 0x07
mov [esi + 6], al
; Configuration Space Address (4 bytes):
; [Highest Bit]
; 5. Enable Bit (1)
; 4. Reserved (8)
; 3. Bus Number (8)
; 2. Device Number (4)
; 1. Function Number (3)
; 0. Register Offset (8)
; [Lowest Bit]
.make_address:
xor edx, edx
; Set the Enable Bit
inc edx
; Make space for 8 Reserved Bits and 8 Bits for the Bus Number.
shl edx, 15
; Bus Number
mov dl, [esi + 4]
; Device Number
shl edx, 5
or dl, [esi + 5]
; Function Number
shl edx, 3
or edx, [esi + 6]
; Make place for the register offset (which isn't used here).
shl edx, 8
mov eax, edx
mov dx, PCI_CONFIG_ADDRESS_PORT
out dx, eax
mov dx, PCI_CONFIG_DATA_PORT
in eax, dx
cmp ax, 0xffff
je .no_device
xor eax, eax
inc eax
jmp .epilog
.no_device:
xor eax, eax
.epilog:
mov ebx, [esi + 24]
mov edx, [esi + 28]
add esp, 32
pop esi
ret
; Arguments:
; [FURTHEST FROM EBP]
; 3. U16 PCI Device Class & Subclass
; (Higher byte: Class, lower byte: Subclass)
; 2. U16 PCI Device Address
; (Higher byte: Bus Address,
; lower byte: Device Address)
; 1. Ptr32 Pointer to PCI driver area of 65536 bytes
; [NEAREST TO EBP]
pci_append_device:
push dword esi
sub esp, 64
mov esi, esp
mov [esi + (64 - 4)], eax
mov [esi + (64 - 8)], ebx
mov [esi + (64 - 12)], ecx
mov [esi + (64 - 20)], edi
.get_device_count:
mov ebx, [ebp - 4]
xor ecx, ecx
mov cx, [ebx + PCI_DEVICE_COUNT_OFFSET]
mov [esi + 4], cx
.calculate_device_offset:
; Every device slot has 16 bytes, multiply index with 16
shl ecx, 4
; Point into driver device area (add device area start on top)
mov ebx, [ebp - 4]
add ebx, PCI_DEVICE_AREA_OFFSET
add ebx, ecx
.set_device:
mov cx, [ebp - 6]
mov [ebx], cx
mov cx, [ebp - 8]
mov [ebx + 2], cx
.increment_device_count:
mov ebx, [ebp - 4]
inc word [ebx + PCI_DEVICE_COUNT_OFFSET]
.epilog:
mov eax, [esi + (64 - 4)]
mov ebx, [esi + (64 - 8)]
mov ecx, [esi + (64 - 12)]
mov edi, [esi + (64 - 20)]
add esp, 64
pop dword esi
ret
; Arguments:
; [FURTHER FROM EBP]
; 2. U16 Device Number
; 1. U16 Bus Number
; [NEAREST TO EBP]
;
; Returns:
; ax PCI Device Class & Subclass
; (Lower byte: Subclass, Higher byte: Class)
pci_get_device_class_at_address:
push dword esi
sub esp, 64
mov esi, esp
mov [esi + (64 - 8)], ebx
mov [esi + (64 - 12)], ecx
mov [esi + (64 - 16)], edx
mov [esi + (64 - 20)], edi
.create_address:
xor eax, eax
; Enable Bit
or al, 1
; Make space for 8 Reserved Bits and 8 Bytes of bus number.
shl eax, 15
; Bus Number
mov al, [ebp - 2]
; Device Number
; Make space for device number
shl eax, 5
mov dl, [ebp - 4]
and dl, 0x1f
or al, dl
shl eax, 11
; Include the register offset of the configuration space
; register which contains the class and subclass
or eax, 8
.input_output:
mov dx, PCI_CONFIG_ADDRESS_PORT
out dx, eax
mov dx, PCI_CONFIG_DATA_PORT
in eax, dx
; Remove the parts of the register which aren't part of the
; PCI class- and subclass-code
shr eax, 16
xchg ah, al
.epilog:
mov ebx, [esi + (64 - 8)]
mov ecx, [esi + (64 - 12)]
mov edx, [esi + (64 - 16)]
mov edi, [esi + (64 - 20)]
add esp, 64
pop dword esi
ret
; Arguments:
; [FURTHER FROM EBP]
; 2. U16 Bus Number (0 .. 255)
; 1. Ptr32 Pointer to PCI driver area of 65536 bytes
; [NEAREST TO EBP]
;
; Returns:
; - Number of devics on the bus (eax)
pci_enumerate_bus:
push dword esi
sub esp, 64
mov esi, esp
mov [esi + (64 - 8)], ebx
mov [esi + (64 - 12)], ecx
mov [esi + (64 - 16)], edx
mov [esi + (64 - 20)], edi
; CH: Bus Number
; CL: Device index within bus
; [esi + 4]: Number of existing devices
; [esi + 12]: PCI driver area pointer
mov [esi], dword 0
mov [esi + 4], dword 0
.initialize_device_loop:
mov ch, [ebp - 4]
xor cl, cl
.device_loop:
cmp cl, 32
jae .epilog
.test_whether_device_exists:
mov ax, [ebp - 6] ; Bus Number
mov dx, cx
and dx, 0xff
push ebp
mov ebp, esp
push ax ; Bus Number
push dx ; Device Index
push word 0 ; Function Number
call pci_check_device_exists
mov esp, ebp
pop ebp
cmp ax, 0
je .no_device
.call_append_device:
mov bx, cx
and bx, 0xff
mov ax, cx
shr ax, 8
push ebp
mov ebp, esp
push ax ; Bus Number
push bx ; Device Index
call pci_get_device_class_at_address
mov esp, ebp
pop ebp
mov edx, [ebp - 4]
push ebp
mov ebp, esp
push edx ; Pointer to PCI Driver Area
push cx ; Device Address
push ax ; Class, Subclass
call pci_append_device
mov esp, ebp
pop ebp
mov ebx, [ebp - 4]
.no_device:
inc cl
jmp .device_loop
.epilog:
mov ebx, [esi + (64 - 8)]
mov ecx, [esi + (64 - 12)]
mov edx, [esi + (64 - 16)]
mov edi, [esi + (64 - 20)]
mov eax, [esi + 4]
add esp, 64
pop dword esi
ret
; Arguments:
; [FURTHEST FROM EBP]
; 2. U32 Maximum number of devices to enumerate
; (0 to eliminate limit)
; 1. Ptr32 Pointer to PCI driver area of 65536 bytes
; [NEAREST TO EBP]
; Returns:
; eax: Number of devices read
pci_enumerate:
.prolog:
push dword esi
sub esp, 64
mov esi, esp
mov [esi + (64 - 4)], eax
mov [esi + (64 - 8)], ebx
mov [esi + (64 - 12)], ecx
mov [esi + (64 - 16)], edx
mov [esi + (64 - 20)], edi
.get_arguments:
; [esi]: Number of devices found
; ecx: Bus Index
mov edx, [ebp - 4]
mov [esi + 4], edx
; Set the device count to 0 explicitly, just for safety.
mov ebx, [ebp - 4]
add ebx, PCI_DEVICE_COUNT_OFFSET
mov [ebx], word 0
.bus_loop:
xor ecx, ecx
mov [esi], dword 0
.bus_loop_body:
; Test whether the device count is unlimited,
; ignore the device count check if it is.
cmp [ebp - 8], dword 0
je .call_bus_enumerator
mov edx, [esi]
cmp edx, [ebp - 8]
jae .epilog
.call_bus_enumerator:
push ebp
mov ebp, esp
push dword [esi + 4]
push ecx
call pci_enumerate_bus
mov esp, ebp
pop ebp
; Add the number of devices found on the bus
; to the number of devices found in total.
add [esi], eax
mov ebx, [esi + 4]
add [ebx + PCI_DEVICE_COUNT_OFFSET], ax
; Go to the next bus (if there is one)
inc ecx
cmp ecx, 256
jb .bus_loop_body
.epilog:
mov ebx, [esi + (64 - 8)]
mov ecx, [esi + (64 - 12)]
mov edx, [esi + (64 - 16)]
mov edi, [esi + (64 - 20)]
mov eax, [esi]
add esp, 64
pop dword esi
ret