1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * coreboot_table.c 4 * 5 * Module providing coreboot table access. 6 * 7 * Copyright 2017 Google Inc. 8 * Copyright 2017 Samuel Holland <samuel@sholland.org> 9 */ 10 11#include <linux/acpi.h> 12#include <linux/device.h> 13#include <linux/err.h> 14#include <linux/init.h> 15#include <linux/io.h> 16#include <linux/kernel.h> 17#include <linux/module.h> 18#include <linux/of.h> 19#include <linux/platform_device.h> 20#include <linux/slab.h> 21 22#include "coreboot_table.h" 23 24#define CB_DEV(d) container_of(d, struct coreboot_device, dev) 25#define CB_DRV(d) container_of(d, struct coreboot_driver, drv) 26 27static int coreboot_bus_match(struct device *dev, struct device_driver *drv) 28{ 29 struct coreboot_device *device = CB_DEV(dev); 30 struct coreboot_driver *driver = CB_DRV(drv); 31 const struct coreboot_device_id *id; 32 33 if (!driver->id_table) 34 return 0; 35 36 for (id = driver->id_table; id->tag; id++) { 37 if (device->entry.tag == id->tag) 38 return 1; 39 } 40 41 return 0; 42} 43 44static int coreboot_bus_probe(struct device *dev) 45{ 46 int ret = -ENODEV; 47 struct coreboot_device *device = CB_DEV(dev); 48 struct coreboot_driver *driver = CB_DRV(dev->driver); 49 50 if (driver->probe) 51 ret = driver->probe(device); 52 53 return ret; 54} 55 56static void coreboot_bus_remove(struct device *dev) 57{ 58 struct coreboot_device *device = CB_DEV(dev); 59 struct coreboot_driver *driver = CB_DRV(dev->driver); 60 61 if (driver->remove) 62 driver->remove(device); 63} 64 65static int coreboot_bus_uevent(const struct device *dev, struct kobj_uevent_env *env) 66{ 67 struct coreboot_device *device = CB_DEV(dev); 68 u32 tag = device->entry.tag; 69 70 return add_uevent_var(env, "MODALIAS=coreboot:t%08X", tag); 71} 72 73static const struct bus_type coreboot_bus_type = { 74 .name = "coreboot", 75 .match = coreboot_bus_match, 76 .probe = coreboot_bus_probe, 77 .remove = coreboot_bus_remove, 78 .uevent = coreboot_bus_uevent, 79}; 80 81static void coreboot_device_release(struct device *dev) 82{ 83 struct coreboot_device *device = CB_DEV(dev); 84 85 kfree(device); 86} 87 88int coreboot_driver_register(struct coreboot_driver *driver) 89{ 90 driver->drv.bus = &coreboot_bus_type; 91 92 return driver_register(&driver->drv); 93} 94EXPORT_SYMBOL(coreboot_driver_register); 95 96void coreboot_driver_unregister(struct coreboot_driver *driver) 97{ 98 driver_unregister(&driver->drv); 99} 100EXPORT_SYMBOL(coreboot_driver_unregister); 101 102static int coreboot_table_populate(struct device *dev, void *ptr) 103{ 104 int i, ret; 105 void *ptr_entry; 106 struct coreboot_device *device; 107 struct coreboot_table_entry *entry; 108 struct coreboot_table_header *header = ptr; 109 110 ptr_entry = ptr + header->header_bytes; 111 for (i = 0; i < header->table_entries; i++) { 112 entry = ptr_entry; 113 114 if (entry->size < sizeof(*entry)) { 115 dev_warn(dev, "coreboot table entry too small!\n"); 116 return -EINVAL; 117 } 118 119 device = kzalloc(sizeof(device->dev) + entry->size, GFP_KERNEL); 120 if (!device) 121 return -ENOMEM; 122 123 device->dev.parent = dev; 124 device->dev.bus = &coreboot_bus_type; 125 device->dev.release = coreboot_device_release; 126 memcpy(device->raw, ptr_entry, entry->size); 127 128 switch (device->entry.tag) { 129 case LB_TAG_CBMEM_ENTRY: 130 dev_set_name(&device->dev, "cbmem-%08x", 131 device->cbmem_entry.id); 132 break; 133 default: 134 dev_set_name(&device->dev, "coreboot%d", i); 135 break; 136 } 137 138 ret = device_register(&device->dev); 139 if (ret) { 140 put_device(&device->dev); 141 return ret; 142 } 143 144 ptr_entry += entry->size; 145 } 146 147 return 0; 148} 149 150static int coreboot_table_probe(struct platform_device *pdev) 151{ 152 resource_size_t len; 153 struct coreboot_table_header *header; 154 struct resource *res; 155 struct device *dev = &pdev->dev; 156 void *ptr; 157 int ret; 158 159 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 160 if (!res) 161 return -EINVAL; 162 163 len = resource_size(res); 164 if (!res->start || !len) 165 return -EINVAL; 166 167 /* Check just the header first to make sure things are sane */ 168 header = memremap(res->start, sizeof(*header), MEMREMAP_WB); 169 if (!header) 170 return -ENOMEM; 171 172 len = header->header_bytes + header->table_bytes; 173 ret = strncmp(header->signature, "LBIO", sizeof(header->signature)); 174 memunmap(header); 175 if (ret) { 176 dev_warn(dev, "coreboot table missing or corrupt!\n"); 177 return -ENODEV; 178 } 179 180 ptr = memremap(res->start, len, MEMREMAP_WB); 181 if (!ptr) 182 return -ENOMEM; 183 184 ret = coreboot_table_populate(dev, ptr); 185 186 memunmap(ptr); 187 188 return ret; 189} 190 191static int __cb_dev_unregister(struct device *dev, void *dummy) 192{ 193 device_unregister(dev); 194 return 0; 195} 196 197static void coreboot_table_remove(struct platform_device *pdev) 198{ 199 bus_for_each_dev(&coreboot_bus_type, NULL, NULL, __cb_dev_unregister); 200} 201 202#ifdef CONFIG_ACPI 203static const struct acpi_device_id cros_coreboot_acpi_match[] = { 204 { "GOOGCB00", 0 }, 205 { "BOOT0000", 0 }, 206 { } 207}; 208MODULE_DEVICE_TABLE(acpi, cros_coreboot_acpi_match); 209#endif 210 211#ifdef CONFIG_OF 212static const struct of_device_id coreboot_of_match[] = { 213 { .compatible = "coreboot" }, 214 {} 215}; 216MODULE_DEVICE_TABLE(of, coreboot_of_match); 217#endif 218 219static struct platform_driver coreboot_table_driver = { 220 .probe = coreboot_table_probe, 221 .remove_new = coreboot_table_remove, 222 .driver = { 223 .name = "coreboot_table", 224 .acpi_match_table = ACPI_PTR(cros_coreboot_acpi_match), 225 .of_match_table = of_match_ptr(coreboot_of_match), 226 }, 227}; 228 229static int __init coreboot_table_driver_init(void) 230{ 231 int ret; 232 233 ret = bus_register(&coreboot_bus_type); 234 if (ret) 235 return ret; 236 237 ret = platform_driver_register(&coreboot_table_driver); 238 if (ret) { 239 bus_unregister(&coreboot_bus_type); 240 return ret; 241 } 242 243 return 0; 244} 245 246static void __exit coreboot_table_driver_exit(void) 247{ 248 platform_driver_unregister(&coreboot_table_driver); 249 bus_unregister(&coreboot_bus_type); 250} 251 252module_init(coreboot_table_driver_init); 253module_exit(coreboot_table_driver_exit); 254 255MODULE_AUTHOR("Google, Inc."); 256MODULE_LICENSE("GPL"); 257