diff options
author | Patrick Rudolph <siro@das-labor.org> | 2017-06-06 19:30:55 +0200 |
---|---|---|
committer | Patrick Rudolph <siro@das-labor.org> | 2018-01-31 08:36:52 +0000 |
commit | 00c0cd2c45d503716dfd7d37ebff6c6768712a52 (patch) | |
tree | e40ed3fcf602454d9f1ee1e2d434f1af4d34bd96 /src | |
parent | 6be6df00144402cdc5ace0489d7255186609537c (diff) |
device/pci_rom: Write _ROM method for VGA devices
Write _ROM method and store PCI Option ROM in CBMEM.
Allows an EFI compatible OS to retrieve the Option ROM without the need
to access the PCI BAR. As the Option ROM is no longer present in the
legacy VGA area it's required for mobile platforms. On hybrid devices,
like Lenovo Thinkpads supporting NVIDIA Optimus it's the only way to
retrieve the Option ROM, even with legacy BIOS, as there's no PCI BAR to
map.
Tested on:
* Lenovo T530
* Linux Kernel 4.13.7
* nouveau
Change-Id: I548b730fb64833083cc05af5b21dd6959804224b
Signed-off-by: Patrick Rudolph <siro@das-labor.org>
Reviewed-on: https://review.coreboot.org/20548
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Aaron Durbin <adurbin@chromium.org>
Diffstat (limited to 'src')
-rw-r--r-- | src/commonlib/include/commonlib/cbmem_id.h | 10 | ||||
-rw-r--r-- | src/device/pci_device.c | 1 | ||||
-rw-r--r-- | src/device/pci_rom.c | 59 | ||||
-rw-r--r-- | src/include/device/pci_rom.h | 2 |
4 files changed, 71 insertions, 1 deletions
diff --git a/src/commonlib/include/commonlib/cbmem_id.h b/src/commonlib/include/commonlib/cbmem_id.h index 2595655b4d..9a66246001 100644 --- a/src/commonlib/include/commonlib/cbmem_id.h +++ b/src/commonlib/include/commonlib/cbmem_id.h @@ -71,6 +71,10 @@ #define CBMEM_ID_WIFI_CALIBRATION 0x57494649 #define CBMEM_ID_EC_HOSTEVENT 0x63ccbbc3 #define CBMEM_ID_EXT_VBT 0x69866684 +#define CBMEM_ID_ROM0 0x524f4d30 +#define CBMEM_ID_ROM1 0x524f4d31 +#define CBMEM_ID_ROM2 0x524f4d32 +#define CBMEM_ID_ROM3 0x524f4d33 #define CBMEM_ID_TO_NAME_TABLE \ { CBMEM_ID_ACPI, "ACPI " }, \ @@ -122,5 +126,9 @@ { CBMEM_ID_VPD, "VPD " }, \ { CBMEM_ID_WIFI_CALIBRATION, "WIFI CLBR " }, \ { CBMEM_ID_EC_HOSTEVENT, "EC HOSTEVENT"}, \ - { CBMEM_ID_EXT_VBT, "EXT VBT"}, + { CBMEM_ID_EXT_VBT, "EXT VBT"}, \ + { CBMEM_ID_ROM0, "VGA ROM #0 "}, \ + { CBMEM_ID_ROM1, "VGA ROM #1 "}, \ + { CBMEM_ID_ROM2, "VGA ROM #2 "}, \ + { CBMEM_ID_ROM3, "VGA ROM #3 "}, #endif /* _CBMEM_ID_H_ */ diff --git a/src/device/pci_device.c b/src/device/pci_device.c index ffc679facf..dbe76b54f5 100644 --- a/src/device/pci_device.c +++ b/src/device/pci_device.c @@ -744,6 +744,7 @@ struct device_operations default_pci_ops_dev = { .enable_resources = pci_dev_enable_resources, #if IS_ENABLED(CONFIG_HAVE_ACPI_TABLES) .write_acpi_tables = pci_rom_write_acpi_tables, + .acpi_fill_ssdt_generator = pci_rom_ssdt, #endif .init = pci_dev_init, .scan_bus = 0, diff --git a/src/device/pci_rom.c b/src/device/pci_rom.c index a411f06acf..15b0c4c23f 100644 --- a/src/device/pci_rom.c +++ b/src/device/pci_rom.c @@ -25,6 +25,8 @@ #include <device/pci_ops.h> #include <string.h> #include <cbfs.h> +#include <cbmem.h> +#include <arch/acpigen.h> /* Rmodules don't like weak symbols. */ u32 __attribute__((weak)) map_oprom_vendev(u32 vendev) { return vendev; } @@ -267,4 +269,61 @@ pci_rom_write_acpi_tables(struct device *device, return current; } + +void pci_rom_ssdt(struct device *device) +{ + static size_t ngfx; + + /* Only handle VGA devices */ + if ((device->class >> 8) != PCI_CLASS_DISPLAY_VGA) + return; + + /* Only handle enabled devices */ + if (!device->enabled) + return; + + /* Probe for option rom */ + const struct rom_header *rom = pci_rom_probe(device); + if (!rom || !rom->size) { + printk(BIOS_WARNING, "%s: Missing PCI Option ROM\n", + dev_path(device)); + return; + } + + const char *scope = acpi_device_path(device); + if (!scope) { + printk(BIOS_ERR, "%s: Missing ACPI scope\n", dev_path(device)); + return; + } + + /* Supports up to four devices. */ + if ((CBMEM_ID_ROM0 + ngfx) > CBMEM_ID_ROM3) { + printk(BIOS_ERR, "%s: Out of CBMEM IDs.\n", dev_path(device)); + return; + } + + /* Prepare memory */ + const size_t cbrom_length = rom->size * 512; + if (!cbrom_length) { + printk(BIOS_ERR, "%s: ROM has zero length!\n", + dev_path(device)); + return; + } + + void *cbrom = cbmem_add(CBMEM_ID_ROM0 + ngfx, cbrom_length); + if (!cbrom) { + printk(BIOS_ERR, "%s: Failed to allocate CBMEM.\n", + dev_path(device)); + return; + } + /* Increment CBMEM id for next device */ + ngfx++; + + memcpy(cbrom, rom, cbrom_length); + + /* write _ROM method */ + acpigen_write_scope(scope); + acpigen_write_rom(cbrom, cbrom_length); + acpigen_pop_len(); /* pop scope */ +} #endif diff --git a/src/include/device/pci_rom.h b/src/include/device/pci_rom.h index c5ace4c788..a4aa52aa09 100644 --- a/src/include/device/pci_rom.h +++ b/src/include/device/pci_rom.h @@ -43,6 +43,8 @@ pci_rom_write_acpi_tables(struct device *device, unsigned long current, struct acpi_rsdp *rsdp); +void pci_rom_ssdt(struct device *device); + u32 map_oprom_vendev(u32 vendev); #endif |