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_const(d, struct coreboot_driver, drv) 26 27 static int coreboot_bus_match(struct device *dev, const struct device_driver *drv) 28 { 29 struct coreboot_device *device = CB_DEV(dev); 30 const 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 44 static 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 56 static 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 65 static 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 73 static 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 81 static void coreboot_device_release(struct device *dev) 82 { 83 struct coreboot_device *device = CB_DEV(dev); 84 85 kfree(device); 86 } 87 88 int __coreboot_driver_register(struct coreboot_driver *driver, 89 struct module *owner) 90 { 91 driver->drv.bus = &coreboot_bus_type; 92 driver->drv.owner = owner; 93 94 return driver_register(&driver->drv); 95 } 96 EXPORT_SYMBOL(__coreboot_driver_register); 97 98 void coreboot_driver_unregister(struct coreboot_driver *driver) 99 { 100 driver_unregister(&driver->drv); 101 } 102 EXPORT_SYMBOL(coreboot_driver_unregister); 103 104 static int coreboot_table_populate(struct device *dev, void *ptr) 105 { 106 int i, ret; 107 void *ptr_entry; 108 struct coreboot_device *device; 109 struct coreboot_table_entry *entry; 110 struct coreboot_table_header *header = ptr; 111 112 ptr_entry = ptr + header->header_bytes; 113 for (i = 0; i < header->table_entries; i++) { 114 entry = ptr_entry; 115 116 if (entry->size < sizeof(*entry)) { 117 dev_warn(dev, "coreboot table entry too small!\n"); 118 return -EINVAL; 119 } 120 121 device = kzalloc(sizeof(device->dev) + entry->size, GFP_KERNEL); 122 if (!device) 123 return -ENOMEM; 124 125 device->dev.parent = dev; 126 device->dev.bus = &coreboot_bus_type; 127 device->dev.release = coreboot_device_release; 128 memcpy(device->raw, ptr_entry, entry->size); 129 130 switch (device->entry.tag) { 131 case LB_TAG_CBMEM_ENTRY: 132 dev_set_name(&device->dev, "cbmem-%08x", 133 device->cbmem_entry.id); 134 break; 135 default: 136 dev_set_name(&device->dev, "coreboot%d", i); 137 break; 138 } 139 140 ret = device_register(&device->dev); 141 if (ret) { 142 put_device(&device->dev); 143 return ret; 144 } 145 146 ptr_entry += entry->size; 147 } 148 149 return 0; 150 } 151 152 static int coreboot_table_probe(struct platform_device *pdev) 153 { 154 resource_size_t len; 155 struct coreboot_table_header *header; 156 struct resource *res; 157 struct device *dev = &pdev->dev; 158 void *ptr; 159 int ret; 160 161 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 162 if (!res) 163 return -EINVAL; 164 165 len = resource_size(res); 166 if (!res->start || !len) 167 return -EINVAL; 168 169 /* Check just the header first to make sure things are sane */ 170 header = memremap(res->start, sizeof(*header), MEMREMAP_WB); 171 if (!header) 172 return -ENOMEM; 173 174 len = header->header_bytes + header->table_bytes; 175 ret = strncmp(header->signature, "LBIO", sizeof(header->signature)); 176 memunmap(header); 177 if (ret) { 178 dev_warn(dev, "coreboot table missing or corrupt!\n"); 179 return -ENODEV; 180 } 181 182 ptr = memremap(res->start, len, MEMREMAP_WB); 183 if (!ptr) 184 return -ENOMEM; 185 186 ret = coreboot_table_populate(dev, ptr); 187 188 memunmap(ptr); 189 190 return ret; 191 } 192 193 static int __cb_dev_unregister(struct device *dev, void *dummy) 194 { 195 device_unregister(dev); 196 return 0; 197 } 198 199 static void coreboot_table_remove(struct platform_device *pdev) 200 { 201 bus_for_each_dev(&coreboot_bus_type, NULL, NULL, __cb_dev_unregister); 202 } 203 204 #ifdef CONFIG_ACPI 205 static const struct acpi_device_id cros_coreboot_acpi_match[] = { 206 { "GOOGCB00", 0 }, 207 { "BOOT0000", 0 }, 208 { } 209 }; 210 MODULE_DEVICE_TABLE(acpi, cros_coreboot_acpi_match); 211 #endif 212 213 #ifdef CONFIG_OF 214 static const struct of_device_id coreboot_of_match[] = { 215 { .compatible = "coreboot" }, 216 {} 217 }; 218 MODULE_DEVICE_TABLE(of, coreboot_of_match); 219 #endif 220 221 static struct platform_driver coreboot_table_driver = { 222 .probe = coreboot_table_probe, 223 .remove_new = coreboot_table_remove, 224 .driver = { 225 .name = "coreboot_table", 226 .acpi_match_table = ACPI_PTR(cros_coreboot_acpi_match), 227 .of_match_table = of_match_ptr(coreboot_of_match), 228 }, 229 }; 230 231 static int __init coreboot_table_driver_init(void) 232 { 233 int ret; 234 235 ret = bus_register(&coreboot_bus_type); 236 if (ret) 237 return ret; 238 239 ret = platform_driver_register(&coreboot_table_driver); 240 if (ret) { 241 bus_unregister(&coreboot_bus_type); 242 return ret; 243 } 244 245 return 0; 246 } 247 248 static void __exit coreboot_table_driver_exit(void) 249 { 250 platform_driver_unregister(&coreboot_table_driver); 251 bus_unregister(&coreboot_bus_type); 252 } 253 254 module_init(coreboot_table_driver_init); 255 module_exit(coreboot_table_driver_exit); 256 257 MODULE_AUTHOR("Google, Inc."); 258 MODULE_DESCRIPTION("Module providing coreboot table access"); 259 MODULE_LICENSE("GPL"); 260