1 // SPDX-License-Identifier: GPL-2.0-only
5 * Module providing coreboot table access.
7 * Copyright 2017 Google Inc.
8 * Copyright 2017 Samuel Holland <samuel@sholland.org>
11 #include <linux/acpi.h>
12 #include <linux/device.h>
13 #include <linux/err.h>
14 #include <linux/init.h>
16 #include <linux/kernel.h>
17 #include <linux/module.h>
19 #include <linux/platform_device.h>
20 #include <linux/slab.h>
22 #include "coreboot_table.h"
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)
27 static int coreboot_bus_match(struct device
*dev
, const struct device_driver
*drv
)
29 struct coreboot_device
*device
= CB_DEV(dev
);
30 const struct coreboot_driver
*driver
= CB_DRV(drv
);
31 const struct coreboot_device_id
*id
;
33 if (!driver
->id_table
)
36 for (id
= driver
->id_table
; id
->tag
; id
++) {
37 if (device
->entry
.tag
== id
->tag
)
44 static int coreboot_bus_probe(struct device
*dev
)
47 struct coreboot_device
*device
= CB_DEV(dev
);
48 struct coreboot_driver
*driver
= CB_DRV(dev
->driver
);
51 ret
= driver
->probe(device
);
56 static void coreboot_bus_remove(struct device
*dev
)
58 struct coreboot_device
*device
= CB_DEV(dev
);
59 struct coreboot_driver
*driver
= CB_DRV(dev
->driver
);
62 driver
->remove(device
);
65 static int coreboot_bus_uevent(const struct device
*dev
, struct kobj_uevent_env
*env
)
67 struct coreboot_device
*device
= CB_DEV(dev
);
68 u32 tag
= device
->entry
.tag
;
70 return add_uevent_var(env
, "MODALIAS=coreboot:t%08X", tag
);
73 static const struct bus_type coreboot_bus_type
= {
75 .match
= coreboot_bus_match
,
76 .probe
= coreboot_bus_probe
,
77 .remove
= coreboot_bus_remove
,
78 .uevent
= coreboot_bus_uevent
,
81 static void coreboot_device_release(struct device
*dev
)
83 struct coreboot_device
*device
= CB_DEV(dev
);
88 int __coreboot_driver_register(struct coreboot_driver
*driver
,
91 driver
->drv
.bus
= &coreboot_bus_type
;
92 driver
->drv
.owner
= owner
;
94 return driver_register(&driver
->drv
);
96 EXPORT_SYMBOL(__coreboot_driver_register
);
98 void coreboot_driver_unregister(struct coreboot_driver
*driver
)
100 driver_unregister(&driver
->drv
);
102 EXPORT_SYMBOL(coreboot_driver_unregister
);
104 static int coreboot_table_populate(struct device
*dev
, void *ptr
)
108 struct coreboot_device
*device
;
109 struct coreboot_table_entry
*entry
;
110 struct coreboot_table_header
*header
= ptr
;
112 ptr_entry
= ptr
+ header
->header_bytes
;
113 for (i
= 0; i
< header
->table_entries
; i
++) {
116 if (entry
->size
< sizeof(*entry
)) {
117 dev_warn(dev
, "coreboot table entry too small!\n");
121 device
= kzalloc(sizeof(device
->dev
) + entry
->size
, GFP_KERNEL
);
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
);
130 switch (device
->entry
.tag
) {
131 case LB_TAG_CBMEM_ENTRY
:
132 dev_set_name(&device
->dev
, "cbmem-%08x",
133 device
->cbmem_entry
.id
);
136 dev_set_name(&device
->dev
, "coreboot%d", i
);
140 ret
= device_register(&device
->dev
);
142 put_device(&device
->dev
);
146 ptr_entry
+= entry
->size
;
152 static int coreboot_table_probe(struct platform_device
*pdev
)
155 struct coreboot_table_header
*header
;
156 struct resource
*res
;
157 struct device
*dev
= &pdev
->dev
;
161 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
165 len
= resource_size(res
);
166 if (!res
->start
|| !len
)
169 /* Check just the header first to make sure things are sane */
170 header
= memremap(res
->start
, sizeof(*header
), MEMREMAP_WB
);
174 len
= header
->header_bytes
+ header
->table_bytes
;
175 ret
= strncmp(header
->signature
, "LBIO", sizeof(header
->signature
));
178 dev_warn(dev
, "coreboot table missing or corrupt!\n");
182 ptr
= memremap(res
->start
, len
, MEMREMAP_WB
);
186 ret
= coreboot_table_populate(dev
, ptr
);
193 static int __cb_dev_unregister(struct device
*dev
, void *dummy
)
195 device_unregister(dev
);
199 static void coreboot_table_remove(struct platform_device
*pdev
)
201 bus_for_each_dev(&coreboot_bus_type
, NULL
, NULL
, __cb_dev_unregister
);
205 static const struct acpi_device_id cros_coreboot_acpi_match
[] = {
210 MODULE_DEVICE_TABLE(acpi
, cros_coreboot_acpi_match
);
214 static const struct of_device_id coreboot_of_match
[] = {
215 { .compatible
= "coreboot" },
218 MODULE_DEVICE_TABLE(of
, coreboot_of_match
);
221 static struct platform_driver coreboot_table_driver
= {
222 .probe
= coreboot_table_probe
,
223 .remove
= coreboot_table_remove
,
225 .name
= "coreboot_table",
226 .acpi_match_table
= ACPI_PTR(cros_coreboot_acpi_match
),
227 .of_match_table
= of_match_ptr(coreboot_of_match
),
231 static int __init
coreboot_table_driver_init(void)
235 ret
= bus_register(&coreboot_bus_type
);
239 ret
= platform_driver_register(&coreboot_table_driver
);
241 bus_unregister(&coreboot_bus_type
);
248 static void __exit
coreboot_table_driver_exit(void)
250 platform_driver_unregister(&coreboot_table_driver
);
251 bus_unregister(&coreboot_bus_type
);
254 module_init(coreboot_table_driver_init
);
255 module_exit(coreboot_table_driver_exit
);
257 MODULE_AUTHOR("Google, Inc.");
258 MODULE_DESCRIPTION("Module providing coreboot table access");
259 MODULE_LICENSE("GPL");