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 27 static 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 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 { 90 driver->drv.bus = &coreboot_bus_type; 91 92 return driver_register(&driver->drv); 93 } 94 EXPORT_SYMBOL(coreboot_driver_register); 95 96 void coreboot_driver_unregister(struct coreboot_driver *driver) 97 { 98 driver_unregister(&driver->drv); 99 } 100 EXPORT_SYMBOL(coreboot_driver_unregister); 101 102 static 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 150 static 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 191 static int __cb_dev_unregister(struct device *dev, void *dummy) 192 { 193 device_unregister(dev); 194 return 0; 195 } 196 197 static 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 203 static const struct acpi_device_id cros_coreboot_acpi_match[] = { 204 { "GOOGCB00", 0 }, 205 { "BOOT0000", 0 }, 206 { } 207 }; 208 MODULE_DEVICE_TABLE(acpi, cros_coreboot_acpi_match); 209 #endif 210 211 #ifdef CONFIG_OF 212 static const struct of_device_id coreboot_of_match[] = { 213 { .compatible = "coreboot" }, 214 {} 215 }; 216 MODULE_DEVICE_TABLE(of, coreboot_of_match); 217 #endif 218 219 static 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 229 static 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 246 static void __exit coreboot_table_driver_exit(void) 247 { 248 platform_driver_unregister(&coreboot_table_driver); 249 bus_unregister(&coreboot_bus_type); 250 } 251 252 module_init(coreboot_table_driver_init); 253 module_exit(coreboot_table_driver_exit); 254 255 MODULE_AUTHOR("Google, Inc."); 256 MODULE_LICENSE("GPL"); 257