Unable to detect second PCI NIC on QEMU [resolved]

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
0b1
Member
Member
Posts: 35
Joined: Sun Feb 04, 2018 8:04 pm

Unable to detect second PCI NIC on QEMU [resolved]

Post by 0b1 »

Help!

Does anybody have experience setting up multiple NICs on QEMU?

I develop on a windows environment for Intel. I have a 64 bit kernel that scans the PCI slots and will detect whichever FIRST model I specify on the QEMU command line. If I specify a second NIC, it is always ignored.

Any example command lines for multiple NICs would be greatly appreciated!

(There are several examples, but they are complicated! Using vlans, TUNs, etc -- AND they dont' work for me either).

I am running QEMU on Windows 10, eg:

This correctly detects the 8139 card on the PCI bus.
qemu-system-x86_64.exe" C:\_dev\_pet\src\makescripts\..\bin\bootable.raw -cpu qemu64,+pdpe1gb -m 256M -net nic,model=rtl8139

This correctly detects the 8254 card on the PCI bus.
qemu-system-x86_64.exe" C:\_dev\_pet\src\makescripts\..\bin\bootable.raw -cpu qemu64,+pdpe1gb -m 256M -net nic

These only show the first card specified; the second card is ignored:
qemu-system-x86_64.exe" C:\_dev\_pet\src\makescripts\..\bin\bootable.raw -cpu qemu64,+pdpe1gb -m 256M -net nic -net nic,model=rtl8139

qemu-system-x86_64.exe" C:\_dev\_pet\src\makescripts\..\bin\bootable.raw -cpu qemu64,+pdpe1gb -m 256M -net nic,model=rtl8139 -net nic

I have tried versions 2.11.0 and 2.12 of QEMU and both behave the same way.
I have tried the newer syntax for specifying network cards -- same result.

The documentation is a little sketchy, so any and all help appreciated!
Last edited by 0b1 on Sat Jun 23, 2018 6:43 am, edited 1 time in total.
Code or code not. There is no try.
0b1
Member
Member
Posts: 35
Joined: Sun Feb 04, 2018 8:04 pm

Re: Unable to detect second PCI NIC on QEMU

Post by 0b1 »

Update --

It turned out my PCI scan ended one device earlier than the second NIC's location.

Because my OS maxmizes use of registers over the stack, this led me to come up with a simplified PCI scan...

Code: Select all


_pci64_scan:
	;scans the PCI buses for devices and places the found bus/dev_slot/class_subclass into the PCI Map.
	;inputs: none
	;outputs: none
	;scan can be simplified so that bus + dev are on the same 32 bit register using the mask below
	; E000 0000 BBBB BBBB DDDD DFFF rrrr rr00   e=Enable Bit
	; 1000 0000 0000 0000 0000 0000 0000 0000  0x‭80000000‬  <== first bus:device:function #
	; 1000 0000 1111 1111 1111 1000 0000 0000  ‭0x80FFF800‬  <== last bus:device:function #
        ; assumes function 0 on each b:d. 

	MOV RDI, PCI_MAP_ADDR				
	MOV ECX, 0x80000000					;starting B:D:F, shifted
	.loop_next_device:						
		MOV EAX, ECX 		
		OR EAX, PCI_REGISTER_CLASS << 2					;insert register to be queried
		CALL pci_read_register						 
		SHR EAX, 16									;Move the retrieved Class/Subclass code to AX
		CMP AX, 0xFFFF								     
		JE  .not_a_device							
			MOV [RDI + pci_map_entry.class_sub], AX	  	
			MOV [RDI + pci_map_entry.location], ECX                   ;Store location word as-is. Saves bit shifting when reused
			MOV EAX, ECX 		
			OR EAX, PCI_REGISTER_DEVICEID << 2
			CALL pci_read_register
			MOV [RDI + pci_map_entry.device_id], EAX 
			ADD RDI, pci_map_entry_size 
			CMP RDI, PCI_MAP_ADDR + (pci_map_entry_size * MAX_MAP_ENTRIES)
			JNL .loop_next_device_done					;maximum map size reached, exit the loop
		.not_a_device:
		ADD ECX, 0x800								;next B:D:F
		CMP ECX, 0x80FFF800                                                        ;final B:D:F
		JNG .loop_next_device
	.loop_next_device_done:	
	RET
_pci64_scan_done:


pci_read_register:  			
	;inputs:
	;	EAX:  BUS/DEVICE/SLOT/REGISTER
	;outputs:
	; 	EAX:  register contents
	; trashes:
	; 	DX	
	MOV DX, PCI_CONFIG_ADDRESS
	OUT DX, EAX
	MOV DX, PCI_CONFIG_DATA
	IN EAX, DX
	RET	
pci_read_register_done:



(iaw Defensive programming practice, called routines in my implementation do not save and restore registers. The calling code holds the responsibility for saving any registers it needs saved.)
Code or code not. There is no try.
nullplan
Member
Member
Posts: 1801
Joined: Wed Aug 30, 2017 8:24 am

Re: Unable to detect second PCI NIC on QEMU [resolved]

Post by nullplan »

Assembly? Why? Besides, your code lacks support for multi-function devices. The logic is: Start at B:D:F 0, keep increasing the device part of that, but if you find a device with the MF bit set (bit 23 in register 3), you also query each function. Although you could just change your existing code to increase ECX by 0x100 if the MF bit is set, else by 0x800 (instead of using 0x800 always).

And while we're optimizing: It is probably safe to assume that the firmware already initialized all the PCI bridges, so from your perspective the bus numbers are just a block. Then it would be better not to query all the busses that don't exist, right? Therefore, start a variable "maxbus" at 0, and whenever you see a PCI bridge (class code 6), update maxbus to its subordinate bus if that is higher than maxbus. Then break out of the loop once all devices on maxbus are queried.

And your note regarding defensive programming only works if you practice what you preach. Right now, _pci64_scan does not save its registers before calling pci_read_register. That's just another reason to use C for this.
Carpe diem!
Post Reply