📄 pcipnp.8
字号:
;
; PCI plug & play
;
; (C)1997-2001 Pascal Dornier / PC Engines; All rights reserved.
; This file is licensed pursuant to the COMMON PUBLIC LICENSE 0.5.
;
; pd 000830 add PCI_NORST2 option -> don't touch base register
; for second motherboard device (e.g. USB)
; pd 991115 fix PCI I/O allocation: some devices (e.g. ESS Tech)
; have 16 bit base registers.
; pd 990329 fix PCI I/O allocation: 4 byte -> 64 bit registers...
; pd 990216 rewrite I/O allocation
; pd 980728 change to call postcode routine
; pd 980728 fix PCI_NORST option -> skip entire device, not just
; function
;
; To make this BIOS more suitable for future hot plug PCI support,
; the PCI bus address space allocation has been designed to use
; minimum granularity and fixed size allocation for bridges, rather
; than packing things as tightly as possible. This also simplifies
; the code quite a bit.
;
; Limitations:
;
; - PCI bridge code not yet tested.
; - Fixed size allocation for bridges (better to support future
; hot plug, simpler). As implemented, this does not work for
; more than 2 levels of bridges.
; - VGA through bridge is not supported (bad idea for performance).
; - Except for VGA BIOS, expansion ROMs are not supported.
; Assume first image, 32 KB size.
; - Memory allocation below 1MB does not handle memory holes.
;
; PCI inherent limitations:
;
; - Devices behind bridges don't support memory that must be
; allocated below 1MB.
;
; PCI configuration space structure
;
p_id equ 0 ;vendor, device ID
p_cmd equ 4 ;command register
p_stat equ 6 ;status register
p_class equ 8 ;class code, revision ID
p_linesz equ 12 ;cache line size
p_lat equ 13 ;latency timer
p_hedt equ 14 ;header type
p_bist equ 15 ;built-in self test
p_base equ 10h ;base address registers
pb_bus equ 18h ;bridge: primary bus number
pb_bus2 equ 19h ;bridge: secondary bus number
pb_bus3 equ 1ah ;bridge: subordinate bus number
pb_lat2 equ 1bh ;bridge: secondary latency timer
pb_io equ 1ch ;bridge: I/O limit
pb_stat equ 1eh ;bridge: secondary status
pb_mem equ 20h ;bridge: memory base low
pb_memp equ 24h ;bridge: prefetchable memory base
p_cis equ 28h ;end of base address registers
pb_mem2 equ 28h ;bridge: memory base high
pb_memp2 equ 2ch ;bridge: prefetchable memory base high
p_rom equ 30h ;expansion rom base
pb_io2 equ 34h ;bridge: I/O base high
pb_rom equ 38h ;bridge: ROM
p_line equ 3ch ;IRQ assigned to function
p_pin equ 3dh ;0 = not used, 1=A, 2=B, 3=C, 4=D
p_mingnt equ 3eh ;minimum grant time
pb_ctl equ 3eh ;bridge: control
p_maxlat equ 3fh ;maximum latency
;
; working variables for PNP
;
;+ must directly follow previous variable !
;
struc tmp_pci
p_int: dw ?,? ;PCI interrupt lines, LSB = INTA,
;MSB = INTD
p_irqpt: dw ? ;pointer to IRQ table entry
p_mem: dw ? ;regular memory (64K steps)
p_memlim: dw ? ;+ memory limit
p_memp: dw ? ;prefetchable memory (64K steps)
p_memplim: dw ? ;+ prefetchable memory limit
p_io: dw ? ;I/O address (16 byte steps)
p_iolim: dw ? ;+ I/O limit
p_memr: dw ? ;memory below 1MB (segment value)
p_memrlim: dw ? ;+ limit
p_capa dw ? ;low: 0 if back to back mode supported
;high: 0 for fast, 2 for medium, 4/6
;slow devsel
p_bus: db ? ;current bus
p_lastbus: db ? ;+ last bus number
ends
;
; get byte [EBX] -> AL
;
pci_getb: mov eax,ebx ;set index
mov dx,pci_ad
and al,0fch ;mask low bits
out dx,eax
mov dl,bl ;I/O port index
or dl,0fch ;pci_dat assume $fc + bit mask
in al,dx
ret
;
; get word [EBX] -> AX
;
pci_getw: mov eax,ebx ;set index
mov dx,pci_ad
and al,0fch ;mask low bits
out dx,eax
mov dl,bl ;I/O port index
or dl,0fch ;pci_dat assume $fc + bit mask
in ax,dx
ret
;
; get double word [EBX] -> EAX
;
pci_getd: mov eax,ebx ;set index
mov dx,pci_ad
out dx,eax
mov dl,low(pci_dat)
in eax,dx
ret
;
; assign interrupts to device [EBX]
;
pci_airq: mov bl,p_pin ;which interrupt pin does device use ?
call pci_getb
mov ah,0
sub al,1 ;0 -> FF
mov si,ax
jb pci_airq3 ;0 -> FF = no interrupt
cmp al,3
mov al,0ffh
ja pci_airq3 ;out of range -> FF = no interrupt
mov al,[si+p_int] ;get interrupt connected to this line
pci_airq3: mov bl,p_line ;store interrupt number
;V fall through
;
; set byte AL -> [EBX]
;
pci_setb: push ax
mov eax,ebx ;set index
mov dx,pci_ad
and al,0fch ;mask low bits
out dx,eax
pop ax
mov dl,bl ;I/O port index
or dl,0fch ;assume pci_dat $fc + bit mask
out dx,al
ret
;
; set word AX -> [EBX]
;
pci_setw: push ax
mov eax,ebx ;set index
mov dx,pci_ad
and al,0fch ;mask low bits
out dx,eax
pop ax
mov dl,bl ;I/O port index
or dl,0fch ;assume pci_dat $fc + bit mask
out dx,ax
ret
;
; set double word EAX -> [EBX]
;
pci_setd: xchg eax,ebx
mov dx,pci_ad
out dx,eax
xchg eax,ebx
mov dl,low(pci_dat)
out dx,eax
ret
;
; allocate space for a device, CL = ending index
; (different for normal device / bridge)
;
pci_dev: mov bl,p_base
pci_dev1: mov eax,0ffffffffh ;find out how many valid bits
call pci_setd ;write
call pci_getd ;and read back
mov ch,al ;save register type
test al,1 ;I/O ?
jz pci_dev1a ;:no
and ax,ax ;high bit set ? (D15)
jns pci_dev9 ;no: this register doesn't work
jmp pci_io ;allocate I/O device
pci_dev1a: and eax,eax ;high bit set ? (D31)
jns pci_dev9 ;no: this register doesn't work !
test al,2 ;below 1 MB ?
jnz pci_memr
mov si,p_memp
test al,8 ;prefetchable ?
jnz pci_dev2 ;:yes
mov si,p_mem
; allocate memory (normal or prefetchable)
;
; we allocate with a minimum of 64KB granularity
pci_dev2: shr eax,16 ;/ 64KB
not ax ;get requested block size
cmp ax,P_MEMINC-1 ;round up to minimum increment
ja pci_dev3
mov ax,P_MEMINC-1
pci_dev3: test [si],ax ;do we need to round up base ?
jz pci_dev4 ;:no
or [si],ax ;round up base
inc word [si]
jz pci_err2 ;:overflow
pci_dev4: inc ax ;size + 1
add ax,[si] ;update base
jb pci_err2 ;overflow: error
cmp ax,[si+2] ;exceed limit ?
ja pci_err2 ;:yes
xchg ax,[si] ;set new base, get starting base
shl eax,16 ;-> high word, low base is 0
pci_dev7: call pci_setd ;set base register
pci_dev8: test ch,4 ;64 bit base register ?
jz pci_dev9 ;:32 bit
add bl,4
xor eax,eax ;clear high register
pci_dev8a: call pci_setd
pci_dev9: add bl,4
cmp bl,cl ;end of registers ?
jb pci_dev1 ;:try another
ret
pci_err2: mov al,0c2h ;& error code
jmp pci_err9
pci_err3: mov al,0c3h ;& error code
jmp pci_err9
pci_err4: mov al,0c4h ;& error code
jmp pci_err9
;
; Allocate <1MB memory space
;
pci_memr: test al,4 ;reserved type ?
jnz pci_err4 ;:yes
not eax ;find out requested block size
shr eax,4 ;/16
test eax,0ffff0000h ;error if device wants more than 1MB !
jnz pci_err3
cmp ax,P_MEMRINC-1 ;round up to minimum increment
ja pci_memr2
mov ax,P_MEMRINC-1
;&&& need to add base rounding
pci_memr2: add ax,[p_memr] ;update base
jb pci_err2 ;overflow: error
cmp ax,[p_memrlim] ;exceed limit ?
ja pci_err2 ;:yes
xchg [p_memr],ax ;get old base, set new
shl eax,4 ;-> get back into place
;(high EAX is 0 from 1MB test)
jmp short pci_dev7 ;set base register, continue
;
; Allocate I/O space
;
; Note that I/O is allocated in 256 byte blocks starting at
; $1000, $1400, etc. (aliases of chipset registers 0000..00FF)
;
; Space in x100 .. x3FF is reserved for ISA bus. Space in 0000..00FF
; and 0480..04FF is reserved for chipset peripherals. 0CF8..0CFF is
; the PCI config address. So we usually start at 1000.
;
pci_io: not ax ;find out requested block size
test ax,0ff00h ;> 256 bytes ?
jnz pci_err5 ;:error
or ax,P_IOINC-1 ;minimum allocation
test [p_io],ax ;do we need to round up ?
jz pci_ior2 ;:no
or [p_io],ax ;base must be multiple of block size
inc word [p_io]
test word [p_io],0300h ;block overrun ?
jz pci_ior2 ;:no
or word [p_io],03ffh ;next 1024 byte block
inc word [p_io]
jz pci_err5 ;:overflow
pci_ior2:
inc ax ;mask -> count
add ax,[p_io] ;base + block size -> future base
test ax,0300h ;block overrun ?
jz pci_ior3 ;:no
or ax,03ffh ;next 1024 byte block
inc ax
jz pci_err5 ;:overflow
pci_ior3: cmp ax,[p_iolim] ;exceeded limit ?
ja pci_err5 ;:overflow
xchg [p_io],ax ;get old base, set new
and eax,0ffffh ;clear high half of EAX
jmp pci_dev8a ;set base register, continue
pci_err5: mov al,0c5h ;& error code
jmp pci_err9
;
; round up [SI], mask CL, set limit using DX if primary bus,
; DI if secondary bus
;
pci_rnd: mov ax,[si]
test al,cl
jz pci_rnd1
or al,cl
inc ax
jz pci_err1
mov [si],ax
pci_rnd1: cmp byte [p_bus],0 ;primary bus ?
jnz pci_rnd2
add ax,dx ;primary bus limit
jmp short pci_rnd3
pci_rnd2: add ax,di ;secondary bus limit
pci_rnd3: jb pci_err1 ;:too much
mov [si+2],ax ;set limit
ret
pci_err1: jmp pci_bus9 ;allocation error
;
; allocate space for a bridge
;
pci_bri: inc byte [p_lastbus] ;get a new bus number
mov ax,[p_bus] ;+ lastbus
mov bl,pb_bus ;set primary, secondary bus number
call pci_setw
mov ax,P_SECLAT * 256 + 0ffh ;subordinate = 255 for config
mov bl,pb_bus3 ;+ pb_lat2
call pci_setw
; save variables for recursion
mov al,[p_lastbus] ;save old bus number, set new
xchg al,[p_bus]
push ax
push [p_memlim]
push [p_memplim]
push [p_iolim]
;bridges don't have fine granularity - start at 1MB / 4K boundaries
mov si,p_mem ;memory: 1MB boundaries
mov dx,P_MEMINC1 ;primary bus
mov di,P_MEMINC2 ;secondary bus
mov cl,0fh
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -