diff --git a/drivers/firmware/google/Kconfig b/drivers/firmware/google/Kconfig index c52566c8e5de..11a22d176280 100644 --- a/drivers/firmware/google/Kconfig +++ b/drivers/firmware/google/Kconfig @@ -19,6 +19,13 @@ config GOOGLE_SMI driver provides an interface for reading and writing NVRAM variables. +config GOOGLE_COREBOOT_TABLE + tristate "Coreboot Table Access" + depends on OF + help + This option enable the coreboot_table module, which provide other + firmware modules to access coreboot table. + config GOOGLE_MEMCONSOLE_X86 tristate "Firmware Memory Console - X86" depends on X86 && ACPI && DMI @@ -29,7 +36,7 @@ config GOOGLE_MEMCONSOLE_X86 config GOOGLE_MEMCONSOLE_OF tristate "Firmware Memory Console - OF" - depends on OF + depends on GOOGLE_COREBOOT_TABLE help This option enables the kernel to search for a firmware log on coreboot platforms using device tree. If found, this log is exported diff --git a/drivers/firmware/google/Makefile b/drivers/firmware/google/Makefile index 182041a2c226..d5be65468e94 100644 --- a/drivers/firmware/google/Makefile +++ b/drivers/firmware/google/Makefile @@ -3,6 +3,7 @@ memc-x86-y := memconsole-x86.o memconsole.o vpd-sysfs-y := vpd.o vpd_decode.o obj-$(CONFIG_GOOGLE_SMI) += gsmi.o +obj-$(CONFIG_GOOGLE_COREBOOT_TABLE) += coreboot_table.o obj-$(CONFIG_GOOGLE_MEMCONSOLE_OF) += memc-of.o obj-$(CONFIG_GOOGLE_MEMCONSOLE_X86) += memc-x86.o obj-$(CONFIG_GOOGLE_VPD) += vpd-sysfs.o diff --git a/drivers/firmware/google/coreboot_table.c b/drivers/firmware/google/coreboot_table.c new file mode 100644 index 000000000000..22bf50bc8535 --- /dev/null +++ b/drivers/firmware/google/coreboot_table.c @@ -0,0 +1,145 @@ +/* + * coreboot_table.c: module providing coreboot table access. + * + * Copyright 2015 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License v2.0 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +struct coreboot_table_header { + char signature[4]; + u32 header_bytes; + u32 header_checksum; + u32 table_bytes; + u32 table_checksum; + u32 table_entries; +}; + +struct coreboot_table_entry { + u32 tag; + u32 size; +}; + +static struct coreboot_table_header __iomem *ptr_header = + (void*)(-EPROBE_DEFER); + +/* + * This function parses the coreboot table for an entry that contains the base + * address of the given entry tag. The coreboot table consists of a header + * directly followed by a number of small, variable-sized entries, which each + * contain an identifying tag and their length as the first two fields. + */ +int coreboot_table_find(int tag, void *data, size_t data_size) +{ + struct coreboot_table_header header; + struct coreboot_table_entry entry; + void *ptr_entry; + int i; + + if (IS_ERR(ptr_header)) + return PTR_ERR(ptr_header); + + memcpy_fromio(&header, ptr_header, sizeof(header)); + + if (strncmp(header.signature, "LBIO", sizeof(header.signature))) { + pr_warn("coreboot_table: coreboot table missing or corrupt!\n"); + return -ENODEV; + } + + ptr_entry = (void *)ptr_header + header.header_bytes; + + for (i = 0; i < header.table_entries; i++) { + memcpy_fromio(&entry, ptr_entry, sizeof(entry)); + if (entry.tag == tag) { + if (data_size < entry.size) + return -EINVAL; + memcpy_fromio(data, ptr_entry, entry.size); + return 0; + } + ptr_entry += entry.size; + } + + return 0; +} +EXPORT_SYMBOL(coreboot_table_find); + +static int coreboot_table_of_probe(struct platform_device *pdev) +{ + int ret = 0; + struct device_node* fw_dn; + + fw_dn = of_find_compatible_node(NULL, NULL, "coreboot"); + if (!fw_dn) { + ret = -ENODEV; + goto fail; + } + + ptr_header = of_iomap(fw_dn, 0); + of_node_put(fw_dn); + + if (!ptr_header) { + ret = -ENOMEM; + goto fail; + } + + return 0; +fail: + ptr_header = ERR_PTR(ret); + return ret; +} + +static int coreboot_table_of_remove(struct platform_device *pdev) +{ + if (!IS_ERR(ptr_header)) + iounmap(ptr_header); + return 0; +} + +static struct platform_driver coreboot_table_driver = { + .probe = coreboot_table_of_probe, + .remove = coreboot_table_of_remove, + .driver = { + .name = "coreboot_table", + }, +}; + +static int __init platform_coreboot_table_init(void) +{ + struct platform_device* pdev; + int ret = 0; + + pdev = platform_device_register_simple("coreboot_table", -1, NULL, 0); + if (pdev == NULL) { + ret = -ENODEV; + goto fail; + } + + ret = platform_driver_register(&coreboot_table_driver); + if (ret) + goto fail; + + return 0; +fail: + ptr_header = ERR_PTR(ret); + return ret; +} + +module_init(platform_coreboot_table_init); + +MODULE_AUTHOR("Google, Inc."); +MODULE_LICENSE("GPL"); diff --git a/drivers/firmware/google/coreboot_table.h b/drivers/firmware/google/coreboot_table.h new file mode 100644 index 000000000000..367e9b4b90fe --- /dev/null +++ b/drivers/firmware/google/coreboot_table.h @@ -0,0 +1,32 @@ +/* + * coreboot_table.c: Internal header for coreboot table access. + * + * Copyright 2015 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License v2.0 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __COREBOOT_TABLE_H +#define __COREBOOT_TABLE_H + +#include + +/* List of coreboot entry structures that is used */ +struct lb_cbmem_ref { + uint32_t tag; + uint32_t size; + + uint64_t cbmem_addr; +}; + +/* Retrieve coreboot table entry with tag *tag* and copy it to data */ +int coreboot_table_find(int tag, void *data, size_t data_size); + +#endif /* __COREBOOT_TABLE_H */ diff --git a/drivers/firmware/google/memconsole-of.c b/drivers/firmware/google/memconsole-of.c index 7758d018afb9..b54807fb2edc 100644 --- a/drivers/firmware/google/memconsole-of.c +++ b/drivers/firmware/google/memconsole-of.c @@ -11,79 +11,22 @@ #include #include "memconsole.h" +#include "coreboot_table.h" #define CB_TAG_CBMEM_CONSOLE 0x17 -struct coreboot_table_header { - char signature[4]; - u32 header_bytes; - u32 header_checksum; - u32 table_bytes; - u32 table_checksum; - u32 table_entries; -}; - -struct coreboot_table_entry { - u32 tag; - u32 size; - u64 physaddr; -}; - -/* - * This function parses the coreboot table for an entry that contains the base - * address of the firmware console. The coreboot table consists of a header - * directly followed by a number of small, variable-sized entries, which each - * contain an identifying tag and their length as the first two fields. - */ -static bool memconsole_find(struct platform_device *pdev) -{ - bool ret = false; - struct coreboot_table_header __iomem header; - struct coreboot_table_header *ptr_header; - struct coreboot_table_entry __iomem entry; - struct coreboot_table_entry *ptr_entry; - int i; - struct device_node *coreboot_node = pdev->dev.of_node; - - if (!coreboot_node) - return false; - - ptr_header = of_iomap(coreboot_node, 0); - if (!ptr_header) - goto out_node; - - memcpy_fromio(&header, ptr_header, sizeof(header)); - - if (strncmp(header.signature, "LBIO", sizeof(header.signature))) { - - pr_warn("memconsole: coreboot table missing or corrupt!\n"); - goto out_unmap; - } - - ptr_entry = (void *)ptr_header + header.header_bytes; - - for (i = 0; i < header.table_entries; i++) { - memcpy_fromio(&entry, ptr_entry, sizeof(entry)); - - if (entry.tag == CB_TAG_CBMEM_CONSOLE) { - ret = memconsole_coreboot_init(entry.physaddr); - break; - } - - ptr_entry = (void *)ptr_entry + entry.size; - } - -out_unmap: - iounmap(ptr_header); -out_node: - of_node_put(coreboot_node); - return ret; -} - static int memconsole_of_probe(struct platform_device *pdev) { - if (!memconsole_find(pdev)) - return -ENODEV; + int ret; + struct lb_cbmem_ref entry; + + ret = coreboot_table_find(CB_TAG_CBMEM_CONSOLE, &entry, sizeof(entry)); + if (ret) + return ret; + + ret = memconsole_coreboot_init(entry.cbmem_addr); + if (ret) + return ret; return memconsole_sysfs_init(); } @@ -104,19 +47,11 @@ static struct platform_driver memconsole_driver = { static int __init platform_memconsole_init(void) { - struct device_node *fw_dn; struct platform_device *pdev; - /* - * Coreboot systems with OF support might have memconsole. - * memconsole_find checks to see if CB_TAG_CBMEM_CONSOLE entry is found. - */ - fw_dn = of_find_compatible_node(NULL, NULL, "coreboot"); - if (!fw_dn) - return -ENODEV; - pdev = platform_device_register_simple("memconsole", -1, NULL, 0); - pdev->dev.of_node = fw_dn; + if (pdev == NULL) + return -ENODEV; platform_driver_register(&memconsole_driver); diff --git a/drivers/firmware/google/memconsole-x86.c b/drivers/firmware/google/memconsole-x86.c index 5d45d6696f75..6b800da83e02 100644 --- a/drivers/firmware/google/memconsole-x86.c +++ b/drivers/firmware/google/memconsole-x86.c @@ -243,11 +243,11 @@ static bool __init memconsole_find(void) if (coreboot_system) { physaddr = get_address_from_acpi(CBMEM_CONSOLE_ACPI_NAME); - if (physaddr && memconsole_coreboot_init(physaddr)) + if (physaddr && memconsole_coreboot_init(physaddr) == 0) return true; physaddr = check_cbmem(); - if (physaddr && memconsole_coreboot_init(physaddr)) + if (physaddr && memconsole_coreboot_init(physaddr) == 0) return true; } diff --git a/drivers/firmware/google/memconsole.c b/drivers/firmware/google/memconsole.c index d38635c37699..48217510e1e5 100644 --- a/drivers/firmware/google/memconsole.c +++ b/drivers/firmware/google/memconsole.c @@ -44,14 +44,14 @@ void memconsole_setup(void *baseaddr, size_t length) } EXPORT_SYMBOL(memconsole_setup); -bool memconsole_coreboot_init(phys_addr_t physaddr) +int memconsole_coreboot_init(phys_addr_t physaddr) { struct cbmem_cons __iomem *tmp_cbmc; tmp_cbmc = ioremap_cache(physaddr, sizeof(*tmp_cbmc)); if (tmp_cbmc == NULL) - return false; + return -ENOMEM; cbmem_console = ioremap_cache(physaddr, tmp_cbmc->buffer_size + sizeof(*cbmem_console)); /* Don't forget counting the header. */ @@ -59,11 +59,11 @@ bool memconsole_coreboot_init(phys_addr_t physaddr) iounmap(tmp_cbmc); if (cbmem_console == NULL) - return false; + return -ENOMEM; memconsole_setup(cbmem_console->buffer_body, min(cbmem_console->buffer_cursor, cbmem_console->buffer_size)); - return true; + return 0; } EXPORT_SYMBOL(memconsole_coreboot_init); diff --git a/drivers/firmware/google/memconsole.h b/drivers/firmware/google/memconsole.h index ad385c458cde..a54cce72af67 100644 --- a/drivers/firmware/google/memconsole.h +++ b/drivers/firmware/google/memconsole.h @@ -13,7 +13,7 @@ #include /* Initialize the memory console given physical address of console buffer */ -bool memconsole_coreboot_init(phys_addr_t physaddr); +int memconsole_coreboot_init(phys_addr_t physaddr); /* Initialize the memory console from raw (virtual) base address and length. */ void memconsole_setup(void *baseaddr, size_t length);