at master 5.9 kB view raw
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 27static 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 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 struct module *owner) 90{ 91 driver->drv.bus = &coreboot_bus_type; 92 driver->drv.owner = owner; 93 94 return driver_register(&driver->drv); 95} 96EXPORT_SYMBOL(__coreboot_driver_register); 97 98void coreboot_driver_unregister(struct coreboot_driver *driver) 99{ 100 driver_unregister(&driver->drv); 101} 102EXPORT_SYMBOL(coreboot_driver_unregister); 103 104static 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 152static 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 193static int __cb_dev_unregister(struct device *dev, void *dummy) 194{ 195 device_unregister(dev); 196 return 0; 197} 198 199static 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 205static const struct acpi_device_id cros_coreboot_acpi_match[] = { 206 { "GOOGCB00", 0 }, 207 { "BOOT0000", 0 }, 208 { } 209}; 210MODULE_DEVICE_TABLE(acpi, cros_coreboot_acpi_match); 211#endif 212 213#ifdef CONFIG_OF 214static const struct of_device_id coreboot_of_match[] = { 215 { .compatible = "coreboot" }, 216 {} 217}; 218MODULE_DEVICE_TABLE(of, coreboot_of_match); 219#endif 220 221static struct platform_driver coreboot_table_driver = { 222 .probe = coreboot_table_probe, 223 .remove = 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 231static 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 248static void __exit coreboot_table_driver_exit(void) 249{ 250 platform_driver_unregister(&coreboot_table_driver); 251 bus_unregister(&coreboot_bus_type); 252} 253 254module_init(coreboot_table_driver_init); 255module_exit(coreboot_table_driver_exit); 256 257MODULE_AUTHOR("Google, Inc."); 258MODULE_DESCRIPTION("Module providing coreboot table access"); 259MODULE_LICENSE("GPL");