1f50a7f3dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 24f5c791aSLennart Poettering /* 34f5c791aSLennart Poettering * Export SMBIOS/DMI info via sysfs to userspace 44f5c791aSLennart Poettering * 54f5c791aSLennart Poettering * Copyright 2007, Lennart Poettering 64f5c791aSLennart Poettering */ 74f5c791aSLennart Poettering 84f5c791aSLennart Poettering #include <linux/module.h> 94f5c791aSLennart Poettering #include <linux/kernel.h> 104f5c791aSLennart Poettering #include <linux/init.h> 114f5c791aSLennart Poettering #include <linux/dmi.h> 124f5c791aSLennart Poettering #include <linux/device.h> 135a0e3ad6STejun Heo #include <linux/slab.h> 144f5c791aSLennart Poettering 1563744756SJean Delvare struct dmi_device_attribute{ 1663744756SJean Delvare struct device_attribute dev_attr; 1763744756SJean Delvare int field; 1863744756SJean Delvare }; 1963744756SJean Delvare #define to_dmi_dev_attr(_dev_attr) \ 2063744756SJean Delvare container_of(_dev_attr, struct dmi_device_attribute, dev_attr) 2163744756SJean Delvare 2263744756SJean Delvare static ssize_t sys_dmi_field_show(struct device *dev, 2363744756SJean Delvare struct device_attribute *attr, 2463744756SJean Delvare char *page) 2563744756SJean Delvare { 2663744756SJean Delvare int field = to_dmi_dev_attr(attr)->field; 2763744756SJean Delvare ssize_t len; 2863744756SJean Delvare len = scnprintf(page, PAGE_SIZE, "%s\n", dmi_get_system_info(field)); 2963744756SJean Delvare page[len-1] = '\n'; 3063744756SJean Delvare return len; 3163744756SJean Delvare } 3263744756SJean Delvare 3363744756SJean Delvare #define DMI_ATTR(_name, _mode, _show, _field) \ 3463744756SJean Delvare { .dev_attr = __ATTR(_name, _mode, _show, NULL), \ 3563744756SJean Delvare .field = _field } 3663744756SJean Delvare 374f5c791aSLennart Poettering #define DEFINE_DMI_ATTR_WITH_SHOW(_name, _mode, _field) \ 3863744756SJean Delvare static struct dmi_device_attribute sys_dmi_##_name##_attr = \ 3963744756SJean Delvare DMI_ATTR(_name, _mode, sys_dmi_field_show, _field); 404f5c791aSLennart Poettering 414f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(bios_vendor, 0444, DMI_BIOS_VENDOR); 424f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(bios_version, 0444, DMI_BIOS_VERSION); 434f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(bios_date, 0444, DMI_BIOS_DATE); 444f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(sys_vendor, 0444, DMI_SYS_VENDOR); 45f5152f4dSErwan Velu DEFINE_DMI_ATTR_WITH_SHOW(bios_release, 0444, DMI_BIOS_RELEASE); 46f5152f4dSErwan Velu DEFINE_DMI_ATTR_WITH_SHOW(ec_firmware_release, 0444, DMI_EC_FIRMWARE_RELEASE); 474f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(product_name, 0444, DMI_PRODUCT_NAME); 484f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(product_version, 0444, DMI_PRODUCT_VERSION); 494f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(product_serial, 0400, DMI_PRODUCT_SERIAL); 504f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(product_uuid, 0400, DMI_PRODUCT_UUID); 51b23908d3SSimon Glass DEFINE_DMI_ATTR_WITH_SHOW(product_sku, 0444, DMI_PRODUCT_SKU); 52e0733e97SJean Delvare DEFINE_DMI_ATTR_WITH_SHOW(product_family, 0444, DMI_PRODUCT_FAMILY); 534f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(board_vendor, 0444, DMI_BOARD_VENDOR); 544f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(board_name, 0444, DMI_BOARD_NAME); 554f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(board_version, 0444, DMI_BOARD_VERSION); 564f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(board_serial, 0400, DMI_BOARD_SERIAL); 574f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(board_asset_tag, 0444, DMI_BOARD_ASSET_TAG); 584f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(chassis_vendor, 0444, DMI_CHASSIS_VENDOR); 594f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(chassis_type, 0444, DMI_CHASSIS_TYPE); 604f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(chassis_version, 0444, DMI_CHASSIS_VERSION); 614f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(chassis_serial, 0400, DMI_CHASSIS_SERIAL); 624f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(chassis_asset_tag, 0444, DMI_CHASSIS_ASSET_TAG); 634f5c791aSLennart Poettering 644f5c791aSLennart Poettering static void ascii_filter(char *d, const char *s) 654f5c791aSLennart Poettering { 664f5c791aSLennart Poettering /* Filter out characters we don't want to see in the modalias string */ 674f5c791aSLennart Poettering for (; *s; s++) 684f5c791aSLennart Poettering if (*s > ' ' && *s < 127 && *s != ':') 694f5c791aSLennart Poettering *(d++) = *s; 704f5c791aSLennart Poettering 714f5c791aSLennart Poettering *d = 0; 724f5c791aSLennart Poettering } 734f5c791aSLennart Poettering 744f5c791aSLennart Poettering static ssize_t get_modalias(char *buffer, size_t buffer_size) 754f5c791aSLennart Poettering { 76*f97a2103SHans de Goede /* 77*f97a2103SHans de Goede * Note new fields need to be added at the end to keep compatibility 78*f97a2103SHans de Goede * with udev's hwdb which does matches on "`cat dmi/id/modalias`*". 79*f97a2103SHans de Goede */ 804f5c791aSLennart Poettering static const struct mafield { 814f5c791aSLennart Poettering const char *prefix; 824f5c791aSLennart Poettering int field; 834f5c791aSLennart Poettering } fields[] = { 844f5c791aSLennart Poettering { "bvn", DMI_BIOS_VENDOR }, 854f5c791aSLennart Poettering { "bvr", DMI_BIOS_VERSION }, 864f5c791aSLennart Poettering { "bd", DMI_BIOS_DATE }, 87f5152f4dSErwan Velu { "br", DMI_BIOS_RELEASE }, 88f5152f4dSErwan Velu { "efr", DMI_EC_FIRMWARE_RELEASE }, 894f5c791aSLennart Poettering { "svn", DMI_SYS_VENDOR }, 904f5c791aSLennart Poettering { "pn", DMI_PRODUCT_NAME }, 914f5c791aSLennart Poettering { "pvr", DMI_PRODUCT_VERSION }, 924f5c791aSLennart Poettering { "rvn", DMI_BOARD_VENDOR }, 934f5c791aSLennart Poettering { "rn", DMI_BOARD_NAME }, 944f5c791aSLennart Poettering { "rvr", DMI_BOARD_VERSION }, 954f5c791aSLennart Poettering { "cvn", DMI_CHASSIS_VENDOR }, 964f5c791aSLennart Poettering { "ct", DMI_CHASSIS_TYPE }, 974f5c791aSLennart Poettering { "cvr", DMI_CHASSIS_VERSION }, 98*f97a2103SHans de Goede { "sku", DMI_PRODUCT_SKU }, 994f5c791aSLennart Poettering { NULL, DMI_NONE } 1004f5c791aSLennart Poettering }; 1014f5c791aSLennart Poettering 1024f5c791aSLennart Poettering ssize_t l, left; 1034f5c791aSLennart Poettering char *p; 1044f5c791aSLennart Poettering const struct mafield *f; 1054f5c791aSLennart Poettering 1064f5c791aSLennart Poettering strcpy(buffer, "dmi"); 1074f5c791aSLennart Poettering p = buffer + 3; left = buffer_size - 4; 1084f5c791aSLennart Poettering 1094f5c791aSLennart Poettering for (f = fields; f->prefix && left > 0; f++) { 1104f5c791aSLennart Poettering const char *c; 1114f5c791aSLennart Poettering char *t; 1124f5c791aSLennart Poettering 1134f5c791aSLennart Poettering c = dmi_get_system_info(f->field); 1144f5c791aSLennart Poettering if (!c) 1154f5c791aSLennart Poettering continue; 1164f5c791aSLennart Poettering 1174f5c791aSLennart Poettering t = kmalloc(strlen(c) + 1, GFP_KERNEL); 1184f5c791aSLennart Poettering if (!t) 1194f5c791aSLennart Poettering break; 1204f5c791aSLennart Poettering ascii_filter(t, c); 1214f5c791aSLennart Poettering l = scnprintf(p, left, ":%s%s", f->prefix, t); 1224f5c791aSLennart Poettering kfree(t); 1234f5c791aSLennart Poettering 1244f5c791aSLennart Poettering p += l; 1254f5c791aSLennart Poettering left -= l; 1264f5c791aSLennart Poettering } 1274f5c791aSLennart Poettering 1284f5c791aSLennart Poettering p[0] = ':'; 1294f5c791aSLennart Poettering p[1] = 0; 1304f5c791aSLennart Poettering 1314f5c791aSLennart Poettering return p - buffer + 1; 1324f5c791aSLennart Poettering } 1334f5c791aSLennart Poettering 1344f5c791aSLennart Poettering static ssize_t sys_dmi_modalias_show(struct device *dev, 1354f5c791aSLennart Poettering struct device_attribute *attr, char *page) 1364f5c791aSLennart Poettering { 1374f5c791aSLennart Poettering ssize_t r; 1384f5c791aSLennart Poettering r = get_modalias(page, PAGE_SIZE-1); 1394f5c791aSLennart Poettering page[r] = '\n'; 1404f5c791aSLennart Poettering page[r+1] = 0; 1414f5c791aSLennart Poettering return r+1; 1424f5c791aSLennart Poettering } 1434f5c791aSLennart Poettering 14434b51f39SJean Delvare static struct device_attribute sys_dmi_modalias_attr = 14534b51f39SJean Delvare __ATTR(modalias, 0444, sys_dmi_modalias_show, NULL); 1464f5c791aSLennart Poettering 1474f5c791aSLennart Poettering static struct attribute *sys_dmi_attributes[DMI_STRING_MAX+2]; 1484f5c791aSLennart Poettering 1494f5c791aSLennart Poettering static struct attribute_group sys_dmi_attribute_group = { 1504f5c791aSLennart Poettering .attrs = sys_dmi_attributes, 1514f5c791aSLennart Poettering }; 1524f5c791aSLennart Poettering 153a4dbd674SDavid Brownell static const struct attribute_group* sys_dmi_attribute_groups[] = { 1544f5c791aSLennart Poettering &sys_dmi_attribute_group, 1554f5c791aSLennart Poettering NULL 1564f5c791aSLennart Poettering }; 1574f5c791aSLennart Poettering 1587eff2e7aSKay Sievers static int dmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) 1594f5c791aSLennart Poettering { 1607eff2e7aSKay Sievers ssize_t len; 1614f5c791aSLennart Poettering 1627eff2e7aSKay Sievers if (add_uevent_var(env, "MODALIAS=")) 1637eff2e7aSKay Sievers return -ENOMEM; 1647eff2e7aSKay Sievers len = get_modalias(&env->buf[env->buflen - 1], 1657eff2e7aSKay Sievers sizeof(env->buf) - env->buflen); 1667eff2e7aSKay Sievers if (len >= (sizeof(env->buf) - env->buflen)) 1677eff2e7aSKay Sievers return -ENOMEM; 1687eff2e7aSKay Sievers env->buflen += len; 1694f5c791aSLennart Poettering return 0; 1704f5c791aSLennart Poettering } 1714f5c791aSLennart Poettering 1724f5c791aSLennart Poettering static struct class dmi_class = { 1734f5c791aSLennart Poettering .name = "dmi", 1744f5c791aSLennart Poettering .dev_release = (void(*)(struct device *)) kfree, 1754f5c791aSLennart Poettering .dev_uevent = dmi_dev_uevent, 1764f5c791aSLennart Poettering }; 1774f5c791aSLennart Poettering 1784f5c791aSLennart Poettering static struct device *dmi_dev; 1794f5c791aSLennart Poettering 1804f5c791aSLennart Poettering /* Initialization */ 1814f5c791aSLennart Poettering 1824f5c791aSLennart Poettering #define ADD_DMI_ATTR(_name, _field) \ 1834f5c791aSLennart Poettering if (dmi_get_system_info(_field)) \ 18463744756SJean Delvare sys_dmi_attributes[i++] = &sys_dmi_##_name##_attr.dev_attr.attr; 1854f5c791aSLennart Poettering 186ce8c628aSJean Delvare /* In a separate function to keep gcc 3.2 happy - do NOT merge this in 187ce8c628aSJean Delvare dmi_id_init! */ 188ce8c628aSJean Delvare static void __init dmi_id_init_attr_table(void) 1894f5c791aSLennart Poettering { 190ce8c628aSJean Delvare int i; 1914f5c791aSLennart Poettering 1924f5c791aSLennart Poettering /* Not necessarily all DMI fields are available on all 1934f5c791aSLennart Poettering * systems, hence let's built an attribute table of just 1944f5c791aSLennart Poettering * what's available */ 1954f5c791aSLennart Poettering i = 0; 1964f5c791aSLennart Poettering ADD_DMI_ATTR(bios_vendor, DMI_BIOS_VENDOR); 1974f5c791aSLennart Poettering ADD_DMI_ATTR(bios_version, DMI_BIOS_VERSION); 1984f5c791aSLennart Poettering ADD_DMI_ATTR(bios_date, DMI_BIOS_DATE); 199f5152f4dSErwan Velu ADD_DMI_ATTR(bios_release, DMI_BIOS_RELEASE); 200f5152f4dSErwan Velu ADD_DMI_ATTR(ec_firmware_release, DMI_EC_FIRMWARE_RELEASE); 2014f5c791aSLennart Poettering ADD_DMI_ATTR(sys_vendor, DMI_SYS_VENDOR); 2024f5c791aSLennart Poettering ADD_DMI_ATTR(product_name, DMI_PRODUCT_NAME); 2034f5c791aSLennart Poettering ADD_DMI_ATTR(product_version, DMI_PRODUCT_VERSION); 2044f5c791aSLennart Poettering ADD_DMI_ATTR(product_serial, DMI_PRODUCT_SERIAL); 2054f5c791aSLennart Poettering ADD_DMI_ATTR(product_uuid, DMI_PRODUCT_UUID); 206c61872c9SMika Westerberg ADD_DMI_ATTR(product_family, DMI_PRODUCT_FAMILY); 207b23908d3SSimon Glass ADD_DMI_ATTR(product_sku, DMI_PRODUCT_SKU); 2084f5c791aSLennart Poettering ADD_DMI_ATTR(board_vendor, DMI_BOARD_VENDOR); 2094f5c791aSLennart Poettering ADD_DMI_ATTR(board_name, DMI_BOARD_NAME); 2104f5c791aSLennart Poettering ADD_DMI_ATTR(board_version, DMI_BOARD_VERSION); 2114f5c791aSLennart Poettering ADD_DMI_ATTR(board_serial, DMI_BOARD_SERIAL); 2124f5c791aSLennart Poettering ADD_DMI_ATTR(board_asset_tag, DMI_BOARD_ASSET_TAG); 2134f5c791aSLennart Poettering ADD_DMI_ATTR(chassis_vendor, DMI_CHASSIS_VENDOR); 2144f5c791aSLennart Poettering ADD_DMI_ATTR(chassis_type, DMI_CHASSIS_TYPE); 2154f5c791aSLennart Poettering ADD_DMI_ATTR(chassis_version, DMI_CHASSIS_VERSION); 2164f5c791aSLennart Poettering ADD_DMI_ATTR(chassis_serial, DMI_CHASSIS_SERIAL); 2174f5c791aSLennart Poettering ADD_DMI_ATTR(chassis_asset_tag, DMI_CHASSIS_ASSET_TAG); 2184f5c791aSLennart Poettering sys_dmi_attributes[i++] = &sys_dmi_modalias_attr.attr; 219ce8c628aSJean Delvare } 220ce8c628aSJean Delvare 221ce8c628aSJean Delvare static int __init dmi_id_init(void) 222ce8c628aSJean Delvare { 223ce8c628aSJean Delvare int ret; 224ce8c628aSJean Delvare 225ce8c628aSJean Delvare if (!dmi_available) 226ce8c628aSJean Delvare return -ENODEV; 227ce8c628aSJean Delvare 228ce8c628aSJean Delvare dmi_id_init_attr_table(); 2294f5c791aSLennart Poettering 2304f5c791aSLennart Poettering ret = class_register(&dmi_class); 2314f5c791aSLennart Poettering if (ret) 2324f5c791aSLennart Poettering return ret; 2334f5c791aSLennart Poettering 2344f5c791aSLennart Poettering dmi_dev = kzalloc(sizeof(*dmi_dev), GFP_KERNEL); 2354f5c791aSLennart Poettering if (!dmi_dev) { 2364f5c791aSLennart Poettering ret = -ENOMEM; 2374f5c791aSLennart Poettering goto fail_class_unregister; 2384f5c791aSLennart Poettering } 2394f5c791aSLennart Poettering 2404f5c791aSLennart Poettering dmi_dev->class = &dmi_class; 2416ff4dd36SKay Sievers dev_set_name(dmi_dev, "id"); 2424f5c791aSLennart Poettering dmi_dev->groups = sys_dmi_attribute_groups; 2434f5c791aSLennart Poettering 2444f5c791aSLennart Poettering ret = device_register(dmi_dev); 2454f5c791aSLennart Poettering if (ret) 2469b41b92bSAllen Hung goto fail_put_dmi_dev; 2474f5c791aSLennart Poettering 2484f5c791aSLennart Poettering return 0; 2494f5c791aSLennart Poettering 2509b41b92bSAllen Hung fail_put_dmi_dev: 2519b41b92bSAllen Hung put_device(dmi_dev); 2524f5c791aSLennart Poettering 2539b41b92bSAllen Hung fail_class_unregister: 2544f5c791aSLennart Poettering class_unregister(&dmi_class); 2554f5c791aSLennart Poettering 2564f5c791aSLennart Poettering return ret; 2574f5c791aSLennart Poettering } 2584f5c791aSLennart Poettering 2594f5c791aSLennart Poettering arch_initcall(dmi_id_init); 260