// SPDX-License-Identifier: GPL-2.0-only
/* -----------------------------------------------------------------------
*
* Copyright 2011 Intel Corporation; author Matt Fleming
*
* ----------------------------------------------------------------------- */
#include <linux/efi.h>
#include <linux/pci.h>
#include <asm/efi.h>
#include <asm/e820/types.h>
#include <asm/setup.h>
#include <asm/desc.h>
#include <asm/boot.h>
#include "../string.h"
#include "eboot.h"
static efi_system_table_t *sys_table;
static bool efi_is64 = IS_ENABLED(CONFIG_X86_64);
__pure efi_system_table_t *efi_system_table(void)
{
return sys_table;
}
__pure bool efi_is_64bit(void)
{
return efi_is64;
}
static efi_status_t
preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom)
{
struct pci_setup_rom *rom = NULL;
efi_status_t status;
unsigned long size;
uint64_t romsize;
void *romimage;
/*
* Some firmware images contain EFI function pointers at the place where
* the romimage and romsize fields are supposed to be. Typically the EFI
* code is mapped at high addresses, translating to an unrealistically
* large romsize. The UEFI spec limits the size of option ROMs to 16
* MiB so we reject any ROMs over 16 MiB in size to catch this.
*/
romimage = efi_table_attr(pci, romimage);
romsize = efi_table_attr(pci, romsize);
if (!romimage || !romsize || romsize > SZ_16M)
return EFI_INVALID_PARAMETER;
size = romsize + sizeof(*rom);
status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, size,
(void **)&rom);
if (status != EFI_SUCCESS) {
efi_printk("Failed to allocate memory for 'rom'\n");
return status;
}
memset(rom, 0, sizeof(*rom));
rom->data.type = SETUP_PCI;
rom->data.len = size - sizeof(struct setup_data);
rom->data.next = 0;
rom->pcilen = pci->romsize;
*__rom = rom;
status = efi_call_proto(pci, pci.read, EfiPciIoWidthUint16,
PCI_VENDOR_ID, 1, &rom->vendor);
if (status != EFI_SUCCESS) {
efi_printk("Failed to read rom->vendor\n");
goto free_struct;
}
status = efi_call_proto(pci, pci.read, EfiPciIoWidthUint16,
PCI_DEVICE_ID, 1, &rom->devid);
if (status != EFI_SUCCESS) {
efi_printk("Failed to read rom->devid\n");
goto free_struct;
}
status = efi_call_proto(pci, get_location, &rom->segment, &rom->bus,
&rom->device, &rom->function);
if (status != EFI_SUCCESS)
goto free_struct;
memcpy(rom->romdata, romimage, romsize);
return status;
free_struct:
efi_bs_call(free_pool, rom);
return status;
}
/*
* There's no way to return an informative status from this function,
* because any analysis (and printing of error messages) needs to be
* done directly at the EFI function call-site.
*
* For example, EFI_INVALID_PARAMETER could indicate a bug or maybe we
* just didn't find any PCI devices, but there's no way to tell outside
* the context of the call.
*/
static void setup_efi_pci(struct boot_params *params)
{
efi_status_t status;
void **pci_handle = NULL;
efi_guid_t pci_proto = EFI_PCI_IO_PROTOCOL_GUID;
unsigned long size = 0;
unsigned long nr_pci;
struct setup_data *data;
efi_handle_t h;
int i;
status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL,
&pci_proto, NULL, &size, pci_handle);
if (status == EFI_BUFFER_TOO_SMALL) {
status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, size,
(void **)&pci_handle);
if (status != EFI_SUCCESS) {
efi_printk("Failed to allocate memory for 'pci_handle'\n");
return;
}
status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL,
&pci_proto, NULL, &size, pci_handle);
}
if (status != EFI_SUCCESS)
goto free_handle;
data = (struct setup_data *)(unsigned long)params->hdr.setup_data;
while (data && data->next)
data = (struct setup_data *)(unsigned long)data->next;
for_each_efi_handle(h, pci_handle, size, i) {
efi_pci_io_protocol_t *pci = NULL;
struct pci_setup_rom *rom;
status = efi_bs_call(handle_protocol, h, &pci_proto,
(void **)&pci);
if (status != EFI_SUCCESS || !pci)
continue;
status = preserve_pci_rom_image(pci, &rom);
if (status != EFI_SUCCESS)
continue;
if (data)
data->next = (unsigned long)rom;
else
params->hdr.setup_data = (unsigned