// 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 struct efi_config *efi_early;
__pure const struct efi_config *__efi_early(void)
{
return efi_early;
}
#define BOOT_SERVICES(bits) \
static void setup_boot_services##bits(struct efi_config *c) \
{ \
efi_system_table_##bits##_t *table; \
\
table = (typeof(table))sys_table; \
\
c->runtime_services = table->runtime; \
c->boot_services = table->boottime; \
c->text_output = table->con_out; \
}
BOOT_SERVICES(32);
BOOT_SERVICES(64);
void efi_char16_printk(efi_system_table_t *table, efi_char16_t *str)
{
efi_call_proto(efi_simple_text_output_protocol, output_string,
efi_early->text_output, str);
}
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 = (void *)(unsigned long)efi_table_attr(efi_pci_io_protocol,
romimage, pci);
romsize = efi_table_attr(efi_pci_io_protocol, romsize, pci);
if (!romimage || !romsize || romsize > SZ_16M)
return EFI_INVALID_PARAMETER;
size = romsize + sizeof(*rom);
status = efi_call_early(allocate_pool, EFI_LOADER_DATA, size, &rom);
if (status != EFI_SUCCESS) {
efi_printk(sys_table, "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(efi_pci_io_protocol, pci.read, pci,
EfiPciIoWidthUint16, PCI_VENDOR_ID, 1,
&rom->vendor);
if (status != EFI_SUCCESS) {
efi_printk(sys_table, "Failed to read rom->vendor\n");
goto free_struct;
}
status = efi_call_proto(efi_pci_io_protocol, pci.read, pci,
EfiPciIoWidthUint16, PCI_DEVICE_ID, 1,
&rom->devid);
if (status != EFI_SUCCESS) {
efi_printk(sys_table, "Failed to read rom->devid\n");
goto free_struct;
}
status = efi_call_proto(efi_pci_io_protocol, get_location, pci,
&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_call_early(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;
int i;
status = efi_call_early(locate_handle,
EFI_LOCATE_BY_PROTOCOL,
&pci_proto, NULL, &size, pci_handle);
if (status == EFI_BUFFER_TOO_SMALL) {
status = efi_call_early(allocate_pool,
EFI_LOADER_DATA,
size, (void **)&pci_handle);
if (status != EFI_SUCCESS) {
efi_printk(sys_table, "Failed to allocate memory for 'pci_handle'\n");
return;
}
status = efi_call_early(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;
nr_pci = size / (efi_is_64bit() ? sizeof(u64) : sizeof(u32));