Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * coreboot_table.c
0004  *
0005  * Module providing coreboot table access.
0006  *
0007  * Copyright 2017 Google Inc.
0008  * Copyright 2017 Samuel Holland <samuel@sholland.org>
0009  */
0010 
0011 #include <linux/acpi.h>
0012 #include <linux/device.h>
0013 #include <linux/err.h>
0014 #include <linux/init.h>
0015 #include <linux/io.h>
0016 #include <linux/kernel.h>
0017 #include <linux/module.h>
0018 #include <linux/of.h>
0019 #include <linux/platform_device.h>
0020 #include <linux/slab.h>
0021 
0022 #include "coreboot_table.h"
0023 
0024 #define CB_DEV(d) container_of(d, struct coreboot_device, dev)
0025 #define CB_DRV(d) container_of(d, struct coreboot_driver, drv)
0026 
0027 static int coreboot_bus_match(struct device *dev, struct device_driver *drv)
0028 {
0029     struct coreboot_device *device = CB_DEV(dev);
0030     struct coreboot_driver *driver = CB_DRV(drv);
0031 
0032     return device->entry.tag == driver->tag;
0033 }
0034 
0035 static int coreboot_bus_probe(struct device *dev)
0036 {
0037     int ret = -ENODEV;
0038     struct coreboot_device *device = CB_DEV(dev);
0039     struct coreboot_driver *driver = CB_DRV(dev->driver);
0040 
0041     if (driver->probe)
0042         ret = driver->probe(device);
0043 
0044     return ret;
0045 }
0046 
0047 static void coreboot_bus_remove(struct device *dev)
0048 {
0049     struct coreboot_device *device = CB_DEV(dev);
0050     struct coreboot_driver *driver = CB_DRV(dev->driver);
0051 
0052     if (driver->remove)
0053         driver->remove(device);
0054 }
0055 
0056 static struct bus_type coreboot_bus_type = {
0057     .name       = "coreboot",
0058     .match      = coreboot_bus_match,
0059     .probe      = coreboot_bus_probe,
0060     .remove     = coreboot_bus_remove,
0061 };
0062 
0063 static void coreboot_device_release(struct device *dev)
0064 {
0065     struct coreboot_device *device = CB_DEV(dev);
0066 
0067     kfree(device);
0068 }
0069 
0070 int coreboot_driver_register(struct coreboot_driver *driver)
0071 {
0072     driver->drv.bus = &coreboot_bus_type;
0073 
0074     return driver_register(&driver->drv);
0075 }
0076 EXPORT_SYMBOL(coreboot_driver_register);
0077 
0078 void coreboot_driver_unregister(struct coreboot_driver *driver)
0079 {
0080     driver_unregister(&driver->drv);
0081 }
0082 EXPORT_SYMBOL(coreboot_driver_unregister);
0083 
0084 static int coreboot_table_populate(struct device *dev, void *ptr)
0085 {
0086     int i, ret;
0087     void *ptr_entry;
0088     struct coreboot_device *device;
0089     struct coreboot_table_entry *entry;
0090     struct coreboot_table_header *header = ptr;
0091 
0092     ptr_entry = ptr + header->header_bytes;
0093     for (i = 0; i < header->table_entries; i++) {
0094         entry = ptr_entry;
0095 
0096         device = kzalloc(sizeof(struct device) + entry->size, GFP_KERNEL);
0097         if (!device)
0098             return -ENOMEM;
0099 
0100         dev_set_name(&device->dev, "coreboot%d", i);
0101         device->dev.parent = dev;
0102         device->dev.bus = &coreboot_bus_type;
0103         device->dev.release = coreboot_device_release;
0104         memcpy(&device->entry, ptr_entry, entry->size);
0105 
0106         ret = device_register(&device->dev);
0107         if (ret) {
0108             put_device(&device->dev);
0109             return ret;
0110         }
0111 
0112         ptr_entry += entry->size;
0113     }
0114 
0115     return 0;
0116 }
0117 
0118 static int coreboot_table_probe(struct platform_device *pdev)
0119 {
0120     resource_size_t len;
0121     struct coreboot_table_header *header;
0122     struct resource *res;
0123     struct device *dev = &pdev->dev;
0124     void *ptr;
0125     int ret;
0126 
0127     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0128     if (!res)
0129         return -EINVAL;
0130 
0131     len = resource_size(res);
0132     if (!res->start || !len)
0133         return -EINVAL;
0134 
0135     /* Check just the header first to make sure things are sane */
0136     header = memremap(res->start, sizeof(*header), MEMREMAP_WB);
0137     if (!header)
0138         return -ENOMEM;
0139 
0140     len = header->header_bytes + header->table_bytes;
0141     ret = strncmp(header->signature, "LBIO", sizeof(header->signature));
0142     memunmap(header);
0143     if (ret) {
0144         dev_warn(dev, "coreboot table missing or corrupt!\n");
0145         return -ENODEV;
0146     }
0147 
0148     ptr = memremap(res->start, len, MEMREMAP_WB);
0149     if (!ptr)
0150         return -ENOMEM;
0151 
0152     ret = bus_register(&coreboot_bus_type);
0153     if (!ret) {
0154         ret = coreboot_table_populate(dev, ptr);
0155         if (ret)
0156             bus_unregister(&coreboot_bus_type);
0157     }
0158     memunmap(ptr);
0159 
0160     return ret;
0161 }
0162 
0163 static int __cb_dev_unregister(struct device *dev, void *dummy)
0164 {
0165     device_unregister(dev);
0166     return 0;
0167 }
0168 
0169 static int coreboot_table_remove(struct platform_device *pdev)
0170 {
0171     bus_for_each_dev(&coreboot_bus_type, NULL, NULL, __cb_dev_unregister);
0172     bus_unregister(&coreboot_bus_type);
0173     return 0;
0174 }
0175 
0176 #ifdef CONFIG_ACPI
0177 static const struct acpi_device_id cros_coreboot_acpi_match[] = {
0178     { "GOOGCB00", 0 },
0179     { "BOOT0000", 0 },
0180     { }
0181 };
0182 MODULE_DEVICE_TABLE(acpi, cros_coreboot_acpi_match);
0183 #endif
0184 
0185 #ifdef CONFIG_OF
0186 static const struct of_device_id coreboot_of_match[] = {
0187     { .compatible = "coreboot" },
0188     {}
0189 };
0190 MODULE_DEVICE_TABLE(of, coreboot_of_match);
0191 #endif
0192 
0193 static struct platform_driver coreboot_table_driver = {
0194     .probe = coreboot_table_probe,
0195     .remove = coreboot_table_remove,
0196     .driver = {
0197         .name = "coreboot_table",
0198         .acpi_match_table = ACPI_PTR(cros_coreboot_acpi_match),
0199         .of_match_table = of_match_ptr(coreboot_of_match),
0200     },
0201 };
0202 module_platform_driver(coreboot_table_driver);
0203 MODULE_AUTHOR("Google, Inc.");
0204 MODULE_LICENSE("GPL");