Browse Source

[efi] Mark SNP devices as children of EFI PCI device

Re-open the EFI_PCI_IO_PROTOCOL specifying an Attributes value of
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.  This causes the SNP devices to
be marked as children of the EFI PCI device (as shown in the "devtree"
command).

On at least one IBM blade system, this is required in order to have
the relevant drivers automatically attach to the SNP controller at
device creation time.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 13 years ago
parent
commit
b9326c3655
3 changed files with 70 additions and 0 deletions
  1. 6
    0
      src/include/ipxe/efi/efi_pci.h
  2. 49
    0
      src/interface/efi/efi_pci.c
  3. 15
    0
      src/interface/efi/efi_snp.c

+ 6
- 0
src/include/ipxe/efi/efi_pci.h View File

@@ -27,6 +27,8 @@ struct efi_pci_device {
27 27
 	EFI_PCI_IO_PROTOCOL *pci_io;
28 28
 	/** Device path */
29 29
 	EFI_DEVICE_PATH_PROTOCOL *path;
30
+	/** EFI driver */
31
+	struct efi_driver *efidrv;
30 32
 };
31 33
 
32 34
 extern struct efi_pci_device * efipci_create ( struct efi_driver *efidrv,
@@ -34,6 +36,10 @@ extern struct efi_pci_device * efipci_create ( struct efi_driver *efidrv,
34 36
 extern EFI_STATUS efipci_enable ( struct efi_pci_device *efipci );
35 37
 extern struct efi_pci_device * efipci_find_efi ( EFI_HANDLE device );
36 38
 extern struct efi_pci_device * efipci_find ( struct device *dev );
39
+extern EFI_STATUS efipci_child_add ( struct efi_pci_device *efipci,
40
+				     EFI_HANDLE device );
41
+extern void efipci_child_del ( struct efi_pci_device *efipci,
42
+			       EFI_HANDLE device );
37 43
 extern void efipci_destroy ( struct efi_driver *efidrv,
38 44
 			     struct efi_pci_device *efipci );
39 45
 

+ 49
- 0
src/interface/efi/efi_pci.c View File

@@ -139,6 +139,7 @@ struct efi_pci_device * efipci_create ( struct efi_driver *efidrv,
139 139
 	if ( ! efipci )
140 140
 		goto err_zalloc;
141 141
 	efipci->device = device;
142
+	efipci->efidrv = efidrv;
142 143
 
143 144
 	/* See if device is a PCI device */
144 145
 	if ( ( efirc = bs->OpenProtocol ( device,
@@ -262,6 +263,54 @@ struct efi_pci_device * efipci_find ( struct device *dev ) {
262 263
 	return NULL;
263 264
 }
264 265
 
266
+/**
267
+ * Add EFI device as child of EFI PCI device
268
+ *
269
+ * @v efipci		EFI PCI device
270
+ * @v device		EFI child device
271
+ * @ret efirc		EFI status code
272
+ */
273
+EFI_STATUS efipci_child_add ( struct efi_pci_device *efipci,
274
+			      EFI_HANDLE device ) {
275
+	EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
276
+	struct efi_driver *efidrv = efipci->efidrv;
277
+	union {
278
+		EFI_PCI_IO_PROTOCOL *pci_io;
279
+		void *interface;
280
+	} pci_io;
281
+	EFI_STATUS efirc;
282
+
283
+	/* Re-open the PCI_IO_PROTOCOL */
284
+	if ( ( efirc = bs->OpenProtocol ( efipci->device,
285
+					  &efi_pci_io_protocol_guid,
286
+					  &pci_io.interface,
287
+					  efidrv->driver.DriverBindingHandle,
288
+					  device,
289
+					  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
290
+					  ) ) != 0 ) {
291
+		DBGC ( efipci, "EFIPCI " PCI_FMT " could not add child: %s\n",
292
+		       PCI_ARGS ( &efipci->pci ), efi_strerror ( efirc ) );
293
+		return efirc;
294
+	}
295
+
296
+	return 0;
297
+}
298
+
299
+/**
300
+ * Remove EFI device as child of PCI device
301
+ *
302
+ * @v efipci		EFI PCI device
303
+ * @v device		EFI child device
304
+ * @ret efirc		EFI status code
305
+ */
306
+void efipci_child_del ( struct efi_pci_device *efipci, EFI_HANDLE device ) {
307
+	EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
308
+	struct efi_driver *efidrv = efipci->efidrv;
309
+
310
+	bs->CloseProtocol ( efipci->device, &efi_pci_io_protocol_guid,
311
+			    efidrv->driver.DriverBindingHandle, device );
312
+}
313
+
265 314
 /**
266 315
  * Destroy EFI PCI device
267 316
  *

+ 15
- 0
src/interface/efi/efi_snp.c View File

@@ -46,6 +46,8 @@ struct efi_snp_device {
46 46
 	struct list_head list;
47 47
 	/** The underlying iPXE network device */
48 48
 	struct net_device *netdev;
49
+	/** The underlying EFI PCI device */
50
+	struct efi_pci_device *efipci;
49 51
 	/** EFI device handle */
50 52
 	EFI_HANDLE handle;
51 53
 	/** The SNP structure itself */
@@ -795,6 +797,7 @@ static int efi_snp_probe ( struct net_device *netdev ) {
795 797
 		goto err_alloc_snp;
796 798
 	}
797 799
 	snpdev->netdev = netdev_get ( netdev );
800
+	snpdev->efipci = efipci;
798 801
 
799 802
 	/* Sanity check */
800 803
 	if ( netdev->ll_protocol->ll_addr_len > sizeof ( EFI_MAC_ADDRESS ) ) {
@@ -865,6 +868,15 @@ static int efi_snp_probe ( struct net_device *netdev ) {
865 868
 		goto err_install_protocol_interface;
866 869
 	}
867 870
 
871
+	/* Add as child of PCI device */
872
+	if ( ( efirc = efipci_child_add ( efipci, snpdev->handle ) ) != 0 ) {
873
+		DBGC ( snpdev, "SNPDEV %p could not become child of " PCI_FMT
874
+		       ": %s\n", snpdev, PCI_ARGS ( &efipci->pci ),
875
+		       efi_strerror ( efirc ) );
876
+		rc = EFIRC_TO_RC ( efirc );
877
+		goto err_efipci_child_add;
878
+	}
879
+
868 880
 	/* Add to list of SNP devices */
869 881
 	list_add ( &snpdev->list, &efi_snp_devices );
870 882
 
@@ -872,6 +884,8 @@ static int efi_snp_probe ( struct net_device *netdev ) {
872 884
 	       snpdev, netdev->name, snpdev->handle );
873 885
 	return 0;
874 886
 
887
+	efipci_child_del ( efipci, snpdev->handle );
888
+ err_efipci_child_add:
875 889
 	bs->UninstallMultipleProtocolInterfaces (
876 890
 			snpdev->handle,
877 891
 			&efi_simple_network_protocol_guid, &snpdev->snp,
@@ -916,6 +930,7 @@ static void efi_snp_remove ( struct net_device *netdev ) {
916 930
 	}
917 931
 
918 932
 	/* Uninstall the SNP */
933
+	efipci_child_del ( snpdev->efipci, snpdev->handle );
919 934
 	list_del ( &snpdev->list );
920 935
 	bs->UninstallMultipleProtocolInterfaces (
921 936
 			snpdev->handle,

Loading…
Cancel
Save