diff options
author | Michael Brown | 2018-03-20 12:22:30 +0100 |
---|---|---|
committer | Michael Brown | 2018-03-20 12:31:45 +0100 |
commit | e8e9ca36130bed2ed241a38fa57e9a35c8ac61f3 (patch) | |
tree | e641dcd858ae604f7b98e184bbf4774d7a602630 | |
parent | [lacp] Check the partner's own state when checking for blocked links (diff) | |
download | ipxe-e8e9ca36130bed2ed241a38fa57e9a35c8ac61f3.tar.gz ipxe-e8e9ca36130bed2ed241a38fa57e9a35c8ac61f3.tar.xz ipxe-e8e9ca36130bed2ed241a38fa57e9a35c8ac61f3.zip |
[efi] Provide Map_Mem() and associated UNDI callbacks
Some drivers are known to call the optional Map_Mem() callback without
first checking that the callback exists. Provide a usable basic
implementation of Map_Mem() along with the other callbacks that become
mandatory if Map_Mem() is provided.
Note that in theory the PCI I/O protocol is allowed to require
multiple calls to Map(), with each call handling only a subset of the
overall mapped range. However, the reference implementation in EDK2
assumes that a single Map() will always suffice, so we can probably
make the same simplifying assumption here.
Tested with the Intel E3522X2.EFI driver (which, incidentally, fails
to cleanly remove one of its mappings).
Originally-implemented-by: Maor Dickman <maord@mellanox.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
-rw-r--r-- | src/drivers/net/efi/nii.c | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/src/drivers/net/efi/nii.c b/src/drivers/net/efi/nii.c index 1700b4bd..2d87e0c6 100644 --- a/src/drivers/net/efi/nii.c +++ b/src/drivers/net/efi/nii.c @@ -25,6 +25,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> #include <strings.h> +#include <stdlib.h> #include <unistd.h> #include <errno.h> #include <ipxe/netdevice.h> @@ -137,6 +138,16 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ #define PCI_MAX_BAR 6 +/** An NII memory mapping */ +struct nii_mapping { + /** List of mappings */ + struct list_head list; + /** Mapped address */ + UINT64 addr; + /** Mapping cookie created by PCI I/O protocol */ + VOID *mapping; +}; + /** An NII NIC */ struct nii_nic { /** EFI device */ @@ -179,6 +190,9 @@ struct nii_nic { struct io_buffer *txbuf; /** Current receive buffer */ struct io_buffer *rxbuf; + + /** Mapping list */ + struct list_head mappings; }; /** Maximum number of received packets per poll */ @@ -280,7 +294,19 @@ static int nii_pci_open ( struct nii_nic *nii ) { */ static void nii_pci_close ( struct nii_nic *nii ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + struct nii_mapping *map; + struct nii_mapping *tmp; + + /* Remove any stale mappings */ + list_for_each_entry_safe ( map, tmp, &nii->mappings, list ) { + DBGC ( nii, "NII %s removing stale mapping %#llx\n", + nii->dev.name, ( ( unsigned long long ) map->addr ) ); + nii->pci_io->Unmap ( nii->pci_io, map->mapping ); + list_del ( &map->list ); + free ( map ); + } + /* Close protocols */ bs->CloseProtocol ( nii->pci_device, &efi_pci_io_protocol_guid, efi_image_handle, nii->efidev->device ); } @@ -332,6 +358,139 @@ static EFIAPI VOID nii_io ( UINT64 unique_id, UINT8 op, UINT8 len, UINT64 addr, } /** + * Map callback + * + * @v unique_id NII NIC + * @v addr Address of memory to be mapped + * @v len Length of memory to be mapped + * @v dir Direction of data flow + * @v mapped Device mapped address to fill in + */ +static EFIAPI VOID nii_map ( UINT64 unique_id, UINT64 addr, UINT32 len, + UINT32 dir, UINT64 mapped ) { + struct nii_nic *nii = ( ( void * ) ( intptr_t ) unique_id ); + EFI_PHYSICAL_ADDRESS *phys = ( ( void * ) ( intptr_t ) mapped ); + EFI_PCI_IO_PROTOCOL_OPERATION op; + struct nii_mapping *map; + UINTN count = len; + EFI_STATUS efirc; + int rc; + + /* Return a zero mapped address on failure */ + *phys = 0; + + /* Determine PCI mapping operation */ + switch ( dir ) { + case TO_AND_FROM_DEVICE: + op = EfiPciIoOperationBusMasterCommonBuffer; + break; + case FROM_DEVICE: + op = EfiPciIoOperationBusMasterWrite; + break; + case TO_DEVICE: + op = EfiPciIoOperationBusMasterRead; + break; + default: + DBGC ( nii, "NII %s unsupported mapping direction %d\n", + nii->dev.name, dir ); + goto err_dir; + } + + /* Allocate a mapping record */ + map = zalloc ( sizeof ( *map ) ); + if ( ! map ) + goto err_alloc; + map->addr = addr; + + /* Create map */ + if ( ( efirc = nii->pci_io->Map ( nii->pci_io, op, + ( ( void * ) ( intptr_t ) addr ), + &count, phys, &map->mapping ) ) != 0){ + rc = -EEFI ( efirc ); + DBGC ( nii, "NII %s map operation failed: %s\n", + nii->dev.name, strerror ( rc ) ); + goto err_map; + } + + /* Add to list of mappings */ + list_add ( &map->list, &nii->mappings ); + DBGC2 ( nii, "NII %s mapped %#llx+%#x->%#llx\n", + nii->dev.name, ( ( unsigned long long ) addr ), + len, ( ( unsigned long long ) *phys ) ); + return; + + list_del ( &map->list ); + err_map: + free ( map ); + err_alloc: + err_dir: + return; +} + +/** + * Unmap callback + * + * @v unique_id NII NIC + * @v addr Address of mapped memory + * @v len Length of mapped memory + * @v dir Direction of data flow + * @v mapped Device mapped address + */ +static EFIAPI VOID nii_unmap ( UINT64 unique_id, UINT64 addr, UINT32 len, + UINT32 dir __unused, UINT64 mapped ) { + struct nii_nic *nii = ( ( void * ) ( intptr_t ) unique_id ); + struct nii_mapping *map; + + /* Locate mapping record */ + list_for_each_entry ( map, &nii->mappings, list ) { + if ( map->addr == addr ) { + nii->pci_io->Unmap ( nii->pci_io, map->mapping ); + list_del ( &map->list ); + free ( map ); + DBGC2 ( nii, "NII %s unmapped %#llx+%#x->%#llx\n", + nii->dev.name, ( ( unsigned long long ) addr ), + len, ( ( unsigned long long ) mapped ) ); + return; + } + } + + DBGC ( nii, "NII %s non-existent mapping %#llx+%#x->%#llx\n", + nii->dev.name, ( ( unsigned long long ) addr ), + len, ( ( unsigned long long ) mapped ) ); +} + +/** + * Sync callback + * + * @v unique_id NII NIC + * @v addr Address of mapped memory + * @v len Length of mapped memory + * @v dir Direction of data flow + * @v mapped Device mapped address + */ +static EFIAPI VOID nii_sync ( UINT64 unique_id __unused, UINT64 addr, + UINT32 len, UINT32 dir, UINT64 mapped ) { + const void *src; + void *dst; + + /* Do nothing if this is an identity mapping */ + if ( addr == mapped ) + return; + + /* Determine direction */ + if ( dir == FROM_DEVICE ) { + src = ( ( void * ) ( intptr_t ) mapped ); + dst = ( ( void * ) ( intptr_t ) addr ); + } else { + src = ( ( void * ) ( intptr_t ) addr ); + dst = ( ( void * ) ( intptr_t ) mapped ); + } + + /* Copy data */ + memcpy ( dst, src, len ); +} + +/** * Delay callback * * @v unique_id NII NIC @@ -499,6 +658,9 @@ static int nii_start_undi ( struct nii_nic *nii ) { cpb.Delay = ( ( intptr_t ) nii_delay ); cpb.Block = ( ( intptr_t ) nii_block ); cpb.Mem_IO = ( ( intptr_t ) nii_io ); + cpb.Map_Mem = ( ( intptr_t ) nii_map ); + cpb.UnMap_Mem = ( ( intptr_t ) nii_unmap ); + cpb.Sync_Mem = ( ( intptr_t ) nii_sync ); cpb.Unique_ID = ( ( intptr_t ) nii ); /* Issue command */ @@ -1063,6 +1225,7 @@ int nii_start ( struct efi_device *efidev ) { netdev_init ( netdev, &nii_operations ); nii = netdev->priv; nii->efidev = efidev; + INIT_LIST_HEAD ( &nii->mappings ); netdev->ll_broadcast = nii->broadcast; efidev_set_drvdata ( efidev, netdev ); |