[ipxe-devel] [PATCH] ipxe/autoboot: Boot only from requested device
Alex Williamson
alex.williamson at redhat.com
Wed Feb 12 21:44:04 UTC 2014
Per the BIOS Boot Specification, the initialization phase of the ROM
is called with the PFA (PCI Function Address) in the %ax register.
The intention is that the ROM code will store that device address
somewhere and use it for booting from that device when the Boot Entry
Vector (BEV) is called. iPXE does store the PFA, but it doesn't use
it for anything other than console messages. Instead, iPXE will try
to boot from every device claimed by the driver, typically in PCI bus
scan order. This mostly negates the benefit of the BIOS IPL as iPXE
only provides driver level granularity.
To fix this, allow the init code to store the PFA in a location that
the runtime code can get to it. We can then create a preferred boot
tag on the device_description and prioritize autoboot to first attempt
boot only from these devices. If preferred devices are present but
fail to boot, do not attempt to autoboot remaining devices. The
remaining devices are still available for interactive or scripted
boot.
Signed-off-by: Alex Williamson <alex.williamson at redhat.com>
---
Changes since RFC:
- Found how to properly store and retrieve data across real/protected
- Switch to "preferred" flag on device_description to avoid creating
a PCI-specific solution
- Scan all netdevs for preferred match for cases where vlans might
create multiple netdevs from the same device (per Haggai)
Again, my assembly is based on copying existing code, please check
register usage. Thanks
src/arch/i386/prefix/romprefix.S | 12 +++++++++++-
src/drivers/bus/pci.c | 25 ++++++++++++++++++++++++-
src/include/ipxe/device.h | 2 ++
src/usr/autoboot.c | 35 ++++++++++++++++-------------------
4 files changed, 53 insertions(+), 21 deletions(-)
diff --git a/src/arch/i386/prefix/romprefix.S b/src/arch/i386/prefix/romprefix.S
index 091673d..495c82e 100644
--- a/src/arch/i386/prefix/romprefix.S
+++ b/src/arch/i386/prefix/romprefix.S
@@ -742,7 +742,17 @@ exec: /* Set %ds = %cs */
pushw $1f
lret
.section ".text16", "awx", @progbits
-1: /* Call main() */
+1:
+ /* Retrieve PCI bus:dev.fn */
+ movw init_pci_busdevfn, %cx
+
+ /* Set up %ds for access to .data16 */
+ movw %bx, %ds
+
+ /* Store PCI bus:dev.fn */
+ movw %cx, pci_busdevfn
+
+ /* Call main() */
pushl $main
pushw %cs
call prot_call
diff --git a/src/drivers/bus/pci.c b/src/drivers/bus/pci.c
index 4a8d00b..28be3eb 100644
--- a/src/drivers/bus/pci.c
+++ b/src/drivers/bus/pci.c
@@ -37,6 +37,13 @@ FILE_LICENCE ( GPL2_OR_LATER );
*
*/
+/** Preferred boot device
+ *
+ * This can be set by the prefix.
+ */
+uint16_t __bss16 ( pci_busdevfn );
+#define pci_busdevfn __use_data16 ( pci_busdevfn )
+
static void pcibus_remove ( struct root_device *rootdev );
/**
@@ -313,10 +320,20 @@ void pci_remove ( struct pci_device *pci ) {
* find.
*/
static int pcibus_probe ( struct root_device *rootdev ) {
- struct pci_device *pci = NULL;
+ struct pci_device *pci = NULL, *preferred;
int busdevfn = 0;
int rc;
+ preferred = malloc ( sizeof ( *preferred ) );
+ if ( ! preferred )
+ return -ENOMEM;
+
+ pci_init ( preferred, pci_busdevfn );
+ if ( pci_read_config ( preferred ) ) {
+ free ( preferred );
+ preferred = NULL;
+ }
+
for ( busdevfn = 0 ; 1 ; busdevfn++ ) {
/* Allocate struct pci_device */
@@ -345,6 +362,10 @@ static int pcibus_probe ( struct root_device *rootdev ) {
/* Look for a driver */
if ( ( rc = pci_probe ( pci ) ) == 0 ) {
+ /* Tag whether this is a preferred boot device */
+ if ( preferred && pci->busdevfn == preferred->busdevfn )
+ pci->dev.desc.preferred = 1;
+
/* pcidev registered, we can drop our ref */
pci = NULL;
} else {
@@ -353,10 +374,12 @@ static int pcibus_probe ( struct root_device *rootdev ) {
}
}
+ free ( preferred );
free ( pci );
return 0;
err:
+ free ( preferred );
free ( pci );
pcibus_remove ( rootdev );
return rc;
diff --git a/src/include/ipxe/device.h b/src/include/ipxe/device.h
index c59697c..a78354d 100644
--- a/src/include/ipxe/device.h
+++ b/src/include/ipxe/device.h
@@ -37,6 +37,8 @@ struct device_description {
unsigned long ioaddr;
/** IRQ */
unsigned int irq;
+ /** Preferred boot device */
+ int preferred;
};
/** PCI bus type */
diff --git a/src/usr/autoboot.c b/src/usr/autoboot.c
index c95a256..7b32cae 100644
--- a/src/usr/autoboot.c
+++ b/src/usr/autoboot.c
@@ -36,6 +36,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/features.h>
#include <ipxe/image.h>
#include <ipxe/timer.h>
+#include <ipxe/device.h>
#include <usr/ifmgmt.h>
#include <usr/route.h>
#include <usr/imgmgmt.h>
@@ -74,15 +75,6 @@ __weak int pxe_menu_boot ( struct net_device *netdev __unused ) {
}
/**
- * Identify the boot network device
- *
- * @ret netdev Boot network device
- */
-static struct net_device * find_boot_netdev ( void ) {
- return NULL;
-}
-
-/**
* Parse next-server and filename into a URI
*
* @v next_server Next-server address
@@ -440,21 +432,26 @@ int netboot ( struct net_device *netdev ) {
* Boot the system
*/
int autoboot ( void ) {
- struct net_device *boot_netdev;
struct net_device *netdev;
- int rc = -ENODEV;
-
- /* If we have an identifable boot device, try that first */
- if ( ( boot_netdev = find_boot_netdev() ) )
- rc = netboot ( boot_netdev );
+ int found_preferred = 0, rc = -ENODEV;
- /* If that fails, try booting from any of the other devices */
+ /* If we have preferred boot devices, use them */
for_each_netdev ( netdev ) {
- if ( netdev == boot_netdev )
- continue;
- rc = netboot ( netdev );
+ if ( netdev->dev->desc.preferred ) {
+ rc = netboot ( netdev );
+ found_preferred = 1;
+ }
+ }
+
+ if ( found_preferred ) {
+ printf ( "No more boot network devices\n" );
+ return rc;
}
+ /* If there are no preferred devices, boot from anything */
+ for_each_netdev ( netdev )
+ rc = netboot ( netdev );
+
printf ( "No more network devices\n" );
return rc;
}
More information about the ipxe-devel
mailing list