/** * Write to PCI configuration space * * @v pci PCI device * @v location Encoded offset and width * @v value Value * @ret rc Return status code */ int efipci_write ( struct pci_device *pci, unsigned long location, unsigned long value ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *root; EFI_HANDLE handle; EFI_STATUS efirc; int rc; /* Identify root bridge */ if ( ( rc = efipci_root ( pci, &handle, &root ) ) != 0 ) goto err_root; /* Read from configuration space */ if ( ( efirc = root->Pci.Write ( root, EFIPCI_WIDTH ( location ), efipci_address ( pci, location ), 1, &value ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( pci, "EFIPCI " PCI_FMT " config write to offset %02lx " "failed: %s\n", PCI_ARGS ( pci ), EFIPCI_OFFSET ( location ), strerror ( rc ) ); goto err_write; } err_write: bs->CloseProtocol ( handle, &efi_pci_root_bridge_io_protocol_guid, efi_image_handle, handle ); err_root: return rc; }
/** * Handle fatal errors * * @v fmt Error message format string * @v ... Arguments */ void die ( const char *fmt, ... ) { EFI_BOOT_SERVICES *bs; EFI_RUNTIME_SERVICES *rs; va_list args; /* Print message */ va_start ( args, fmt ); vprintf ( fmt, args ); va_end ( args ); /* Reboot or exit as applicable */ if ( efi_systab ) { /* Exit */ bs = efi_systab->BootServices; bs->Exit ( efi_image_handle, EFI_LOAD_ERROR, 0, NULL ); printf ( "Failed to exit\n" ); rs = efi_systab->RuntimeServices; rs->ResetSystem ( EfiResetWarm, 0, 0, NULL ); printf ( "Failed to reboot\n" ); } else { /* Wait for keypress */ printf ( "Press a key to reboot..." ); getchar(); printf ( "\n" ); /* Reboot system */ reboot(); } /* Should be impossible to reach this */ __builtin_unreachable(); }
/** * Probe SNP root bus * * @v rootdev SNP bus root device * * Look at the loaded image's device handle and see if the simple network * protocol exists. If so, register a driver for it. */ static int snpbus_probe ( struct root_device *rootdev ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; EFI_STATUS efirc; int rc; void *snp; efirc = bs->OpenProtocol ( efi_loaded_image->DeviceHandle, &efi_simple_network_protocol_guid, &snp, efi_image_handle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); if ( efirc ) { DBG ( "Could not find Simple Network Protocol!\n" ); return -ENODEV; } snponly_dev.snp = snp; /* Add to device hierarchy */ strncpy ( snponly_dev.dev.name, "EFI SNP", ( sizeof ( snponly_dev.dev.name ) - 1 ) ); snponly_dev.dev.parent = &rootdev->dev; list_add ( &snponly_dev.dev.siblings, &rootdev->dev.children); INIT_LIST_HEAD ( &snponly_dev.dev.children ); /* Create network device */ if ( ( rc = snpnet_probe ( &snponly_dev ) ) != 0 ) goto err; return 0; err: list_del ( &snponly_dev.dev.siblings ); return rc; }
/** * Identify autoboot device * */ void efi_set_autoboot ( void ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; union { EFI_SIMPLE_NETWORK_PROTOCOL *snp; void *interface; } snp; EFI_SIMPLE_NETWORK_MODE *mode; EFI_STATUS efirc; /* Look for an SNP instance on the image's device handle */ if ( ( efirc = bs->OpenProtocol ( efi_loaded_image->DeviceHandle, &efi_simple_network_protocol_guid, &snp.interface, efi_image_handle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ DBGC ( efi_loaded_image, "EFI found no autoboot device\n" ); return; } /* Record autoboot device */ mode = snp.snp->Mode; set_autoboot_ll_addr ( &mode->CurrentAddress, mode->HwAddressSize ); DBGC ( efi_loaded_image, "EFI found autoboot link-layer address:\n" ); DBGC_HDA ( efi_loaded_image, 0, &mode->CurrentAddress, mode->HwAddressSize ); /* Close protocol */ bs->CloseProtocol ( efi_loaded_image->DeviceHandle, &efi_simple_network_protocol_guid, efi_image_handle, NULL ); }
/** * Detach driver from device * * @v efidev EFI device */ void snpnet_stop ( struct efi_device *efidev ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct net_device *netdev = efidev_get_drvdata ( efidev ); struct snp_nic *snp = netdev->priv; EFI_HANDLE device = efidev->device; EFI_STATUS efirc; int rc; /* Unregister network device */ unregister_netdev ( netdev ); /* Stop SNP protocol */ if ( ( efirc = snp->snp->Stop ( snp->snp ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( device, "SNP %s could not stop: %s\n", efi_handle_name ( device ), strerror ( rc ) ); /* Nothing we can do about this */ } /* Free network device */ list_del ( &snp->dev.siblings ); netdev_nullify ( netdev ); netdev_put ( netdev ); /* Close SNP protocol */ bs->CloseProtocol ( device, &efi_simple_network_protocol_guid, efi_image_handle, device ); }
/** * Initialise EFI environment * * @v image_handle Image handle * @v systab System table * @ret efirc EFI return status code */ EFI_STATUS efi_init ( EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *systab ) { EFI_BOOT_SERVICES *bs; struct efi_protocol *prot; struct efi_config_table *tab; EFI_STATUS efirc; /* Store image handle and system table pointer for future use */ efi_image_handle = image_handle; efi_systab = systab; /* Sanity checks */ if ( ! systab ) return EFI_NOT_AVAILABLE_YET; if ( ! systab->ConOut ) return EFI_NOT_AVAILABLE_YET; if ( ! systab->BootServices ) { DBGC ( systab, "EFI provided no BootServices entry point\n" ); return EFI_NOT_AVAILABLE_YET; } if ( ! systab->RuntimeServices ) { DBGC ( systab, "EFI provided no RuntimeServices entry " "point\n" ); return EFI_NOT_AVAILABLE_YET; } DBGC ( systab, "EFI handle %p systab %p\n", image_handle, systab ); /* Look up used protocols */ bs = systab->BootServices; for_each_table_entry ( prot, EFI_PROTOCOLS ) { if ( ( efirc = bs->LocateProtocol ( &prot->u.guid, NULL, prot->protocol ) ) == 0 ) { DBGC ( systab, "EFI protocol %s is at %p\n", uuid_ntoa ( &prot->u.uuid ), *(prot->protocol)); } else { DBGC ( systab, "EFI does not provide protocol %s\n", uuid_ntoa ( &prot->u.uuid ) ); /* All protocols are required */ return efirc; } } /* Look up used configuration tables */ for_each_table_entry ( tab, EFI_CONFIG_TABLES ) { if ( ( *(tab->table) = efi_find_table ( &tab->u.guid ) ) ) { DBGC ( systab, "EFI configuration table %s is at %p\n", uuid_ntoa ( &tab->u.uuid ), *(tab->table) ); } else { DBGC ( systab, "EFI does not provide configuration " "table %s\n", uuid_ntoa ( &tab->u.uuid ) ); if ( tab->required ) return EFI_NOT_AVAILABLE_YET; } } return 0; }
/** * Reallocate external memory * * @v old_ptr Memory previously allocated by umalloc(), or UNULL * @v new_size Requested size * @ret new_ptr Allocated memory, or UNULL * * Calling realloc() with a new size of zero is a valid way to free a * memory block. */ static userptr_t efi_urealloc ( userptr_t old_ptr, size_t new_size ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; EFI_PHYSICAL_ADDRESS phys_addr; unsigned int new_pages, old_pages; userptr_t new_ptr = UNOWHERE; size_t old_size; EFI_STATUS efirc; int rc; /* Allocate new memory if necessary. If allocation fails, * return without touching the old block. */ if ( new_size ) { new_pages = ( EFI_SIZE_TO_PAGES ( new_size ) + 1 ); if ( ( efirc = bs->AllocatePages ( AllocateAnyPages, EfiBootServicesData, new_pages, &phys_addr ) ) != 0 ) { rc = -EEFI ( efirc ); DBG ( "EFI could not allocate %d pages: %s\n", new_pages, strerror ( rc ) ); return UNULL; } assert ( phys_addr != 0 ); new_ptr = phys_to_user ( phys_addr + EFI_PAGE_SIZE ); copy_to_user ( new_ptr, -EFI_PAGE_SIZE, &new_size, sizeof ( new_size ) ); DBG ( "EFI allocated %d pages at %llx\n", new_pages, phys_addr ); } /* Copy across relevant part of the old data region (if any), * then free it. Note that at this point either (a) new_ptr * is valid, or (b) new_size is 0; either way, the memcpy() is * valid. */ if ( old_ptr && ( old_ptr != UNOWHERE ) ) { copy_from_user ( &old_size, old_ptr, -EFI_PAGE_SIZE, sizeof ( old_size ) ); memcpy_user ( new_ptr, 0, old_ptr, 0, ( (old_size < new_size) ? old_size : new_size )); old_pages = ( EFI_SIZE_TO_PAGES ( old_size ) + 1 ); phys_addr = user_to_phys ( old_ptr, -EFI_PAGE_SIZE ); if ( ( efirc = bs->FreePages ( phys_addr, old_pages ) ) != 0 ){ rc = -EEFI ( efirc ); DBG ( "EFI could not free %d pages at %llx: %s\n", old_pages, phys_addr, strerror ( rc ) ); /* Not fatal; we have leaked memory but successfully * allocated (if asked to do so). */ } DBG ( "EFI freed %d pages at %llx\n", old_pages, phys_addr ); } return new_ptr; }
/** * Delay for a fixed number of microseconds * * @v usecs Number of microseconds for which to delay */ static void efi_udelay ( unsigned long usecs ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; EFI_STATUS efirc; if ( ( efirc = bs->Stall ( usecs ) ) != 0 ) { DBG ( "EFI could not delay for %ldus: %s\n", usecs, efi_strerror ( efirc ) ); /* Probably screwed */ } }
/** * Block callback * * @v unique_id NII NIC * @v acquire Acquire lock */ static EFIAPI VOID nii_block ( UINT64 unique_id, UINT32 acquire ) { struct nii_nic *nii = ( ( void * ) ( intptr_t ) unique_id ); EFI_BOOT_SERVICES *bs = efi_systab->BootServices; /* This functionality (which is copied verbatim from the * SnpDxe implementation of this function) appears to be * totally brain-dead, since it produces no actual blocking * behaviour. */ if ( acquire ) { nii->saved_tpl = bs->RaiseTPL ( TPL_NOTIFY ); } else { bs->RestoreTPL ( nii->saved_tpl ); } }
VOID EFIAPI OvrRestoreTPL(IN EFI_TPL OldTpl) { gOrgBS.RestoreTPL(OldTpl); // do not print - it's called by UEFI events and timers (from timer interrupts) many times //PRINT("->RestoreTPL(OldTpl=%d)\n", OldTpl); return; }
EFI_TPL EFIAPI OvrRaiseTPL(IN EFI_TPL NewTpl) { EFI_TPL Status; Status = gOrgBS.RaiseTPL(NewTpl); // do not print - it's called by UEFI events and timers (from timer interrupts) many times //PRINT("->RaiseTPL(NewTpl=%d) = %d\n", NewTpl, Status); return Status; }
EFI_STATUS EFIAPI OvrCheckEvent( IN EFI_EVENT Event ) { EFI_STATUS Status; Status = gOrgBS.CheckEvent(Event); //PRINT("->CheckEvent(%p) = %r\n", Event, Status); return Status; }
VOID EFIAPI OvrSetMem( IN VOID *Buffer, IN UINTN Size, IN UINT8 Value ) { gOrgBS.SetMem(Buffer, Size, Value); //PRINT("->SetMem(%p, 0x%x, 0x%x)\n", Buffer, Size, Value); return; }
EFI_STATUS EFIAPI OvrUnloadImage( IN EFI_HANDLE ImageHandle ) { EFI_STATUS Status; Status = gOrgBS.UnloadImage(ImageHandle); PRINT("->UnloadImage(%p) = %r\n", ImageHandle, Status); return Status; }
VOID EFIAPI OvrCopyMem( IN VOID *Destination, IN VOID *Source, IN UINTN Length ) { gOrgBS.CopyMem(Destination, Source, Length); //PRINT("->CopyMem(%p, %p, 0x%x)\n", Destination, Source, Length); return; }
EFI_STATUS EFIAPI OvrGetNextMonotonicCount( OUT UINT64 *Count ) { EFI_STATUS Status; Status = gOrgBS.GetNextMonotonicCount(Count); PRINT("->GetNextMonotonicCount(0x%x) = %r\n", *Count, Status); return Status; }
EFI_STATUS EFIAPI OvrStall( IN UINTN Microseconds ) { EFI_STATUS Status; Status = gOrgBS.Stall(Microseconds); // do not print - too many calls //PRINT("->Stall(%d) = %r\n", Microseconds, Status); return Status; }
EFI_STATUS EFIAPI OvrFreePages( IN EFI_PHYSICAL_ADDRESS Memory, IN UINTN Pages ) { EFI_STATUS Status; Status = gOrgBS.FreePages(Memory, Pages); // PRINT("->FreePages(0x%lx, 0x%x) = %r\n", Memory, Pages, Status); return Status; }
EFI_STATUS EFIAPI OvrHandleProtocol( IN EFI_HANDLE Handle, IN EFI_GUID *Protocol, OUT VOID **Interface ) { EFI_STATUS Status; Status = gOrgBS.HandleProtocol(Handle, Protocol, Interface); PRINT("->HandleProtocol(%p, %s, %p) = %r\n", Handle, GuidStr(Protocol), *Interface, Status); return Status; }
/** * Probe EFI image * * @v image EFI file * @ret rc Return status code */ static int efi_image_probe ( struct image *image ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; EFI_HANDLE handle; EFI_STATUS efirc; /* Attempt loading image */ if ( ( efirc = bs->LoadImage ( FALSE, efi_image_handle, NULL, user_to_virt ( image->data, 0 ), image->len, &handle ) ) != 0 ) { /* Not an EFI image */ DBGC ( image, "EFIIMAGE %p could not load: %s\n", image, efi_strerror ( efirc ) ); return -ENOEXEC; } /* Unload the image. We can't leave it loaded, because we * have no "unload" operation. */ bs->UnloadImage ( handle ); return 0; }
/** * Hold off watchdog timer * * @v retry Retry timer * @v over Failure indicator */ static void efi_watchdog_expired ( struct retry_timer *timer, int over __unused ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; static CHAR16 data[] = WATCHDOG_DATA; EFI_STATUS efirc; int rc; DBGC2 ( timer, "EFI holding off watchdog timer\n" ); /* Restart this holdoff timer */ start_timer_fixed ( timer, ( WATCHDOG_HOLDOFF_SECS * TICKS_PER_SEC ) ); /* Reset watchdog timer */ if ( ( efirc = bs->SetWatchdogTimer ( WATCHDOG_TIMEOUT_SECS, WATCHDOG_CODE, sizeof ( data ), data ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( timer, "EFI could not set watchdog timer: %s\n", strerror ( rc ) ); return; } }
EFI_STATUS EFIAPI OvrDisconnectController( IN EFI_HANDLE ControllerHandle, IN EFI_HANDLE DriverImageHandle, IN EFI_HANDLE ChildHandle ) { EFI_STATUS Status; Status = gOrgBS.DisconnectController(ControllerHandle, DriverImageHandle, ChildHandle); PRINT("->DisconnectController(%p, %p, %p) = %r\n", ControllerHandle, DriverImageHandle, ChildHandle, Status); return Status; }
EFI_STATUS EFIAPI OvrFreePool( IN VOID *Buffer ) { EFI_STATUS Status; Status = gOrgBS.FreePool(Buffer); // do not print to console - requires FreePool - recursion // do not print to serial - too many calls from UEFI //DebugPrint(1, "->FreePool(%p) = %r\n", Buffer, Status); return Status; }
EFI_STATUS EFIAPI OvrStartImage( IN EFI_HANDLE ImageHandle, OUT UINTN *ExitDataSize, OUT CHAR16 **ExitData ) { EFI_STATUS Status; Status = gOrgBS.StartImage(ImageHandle, ExitDataSize, ExitData); PRINT("->StartImage(%p, 0x%x, %p) = %r\n", ImageHandle, *ExitDataSize, *ExitData, Status); return Status; }
EFI_STATUS EFIAPI OvrSetTimer( IN EFI_EVENT Event, IN EFI_TIMER_DELAY Type, IN UINT64 TriggerTime ) { EFI_STATUS Status; Status = gOrgBS.SetTimer(Event, Type, TriggerTime); PRINT("->SetTimer(%p, %d, 0x%x) = %r\n", Event, Type, TriggerTime, Status); return Status; }
EFI_STATUS EFIAPI OvrWaitForEvent( IN UINTN NumberOfEvents, IN EFI_EVENT *Event, OUT UINTN *Index ) { EFI_STATUS Status; Status = gOrgBS.WaitForEvent(NumberOfEvents, Event, Index); // PRINT("->WaitForEvent(%d, %p, %d) = %r\n", NumberOfEvents, *Event, *Index, Status); return Status; }
EFI_STATUS EFIAPI OvrUninstallProtocolInterface( IN EFI_HANDLE Handle, IN EFI_GUID *Protocol, IN VOID *Interface ) { EFI_STATUS Status; Status = gOrgBS.UninstallProtocolInterface(Handle, Protocol, Interface); PRINT("->UninstallProtocolInterface(%p, %s, %p) = %r\n", Handle, GuidStr(Protocol), Interface, Status); return Status; }
EFI_STATUS EFIAPI OvrRegisterProtocolNotify( IN EFI_GUID *Protocol, IN EFI_EVENT Event, OUT VOID **Registration ) { EFI_STATUS Status; Status = gOrgBS.RegisterProtocolNotify(Protocol, Event, Registration); PRINT("->RegisterProtocolNotify(%s, %p, %p) = %r\n", GuidStr(Protocol), Event, *Registration, Status); return Status; }
EFI_STATUS EFIAPI OvrInstallConfigurationTable( IN EFI_GUID *Guid, IN VOID *Table ) { EFI_STATUS Status; Status = gOrgBS.InstallConfigurationTable(Guid, Table); // TODO: table guids to Lib.c PRINT("->InstallConfigurationTable(%s, %p) = %r\n", GuidStr(Guid), Table, Status); return Status; }
EFI_STATUS EFIAPI OvrLocateProtocol( IN EFI_GUID *Protocol, IN VOID *Registration, OUT VOID **Interface ) { EFI_STATUS Status; VOID *InterfaceIn = *Interface; Status = gOrgBS.LocateProtocol(Protocol, Registration, Interface); PRINT("->LocateProtocol(%s, %p, %p/%p) = %r\n", GuidStr(Protocol), Registration, InterfaceIn, *Interface, Status); return Status; }