Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

platform/x86: dell-smbios: Add a sysfs interface for SMBIOS tokens

Currently userspace tools can access system tokens via the dcdbas
kernel module and a SMI call that will cause the platform to execute
SMM code.

With a goal in mind of deprecating the dcdbas kernel module a different
method for accessing these tokens from userspace needs to be created.

This is intentionally marked to only be readable as a process with
CAP_SYS_ADMIN as it can contain sensitive information about the
platform's configuration.

While adding this interface I found that some tokens are duplicated.
These need to be ignored from sysfs to avoid duplicate files.

MAINTAINERS was missing for this driver. Add myself and Pali to
maintainers list for it.

Signed-off-by: Mario Limonciello <mario.limonciello@dell.com>
Reviewed-by: Edward O'Callaghan <quasisec@google.com>
Signed-off-by: Darren Hart (VMware) <dvhart@infradead.org>

authored by

Mario Limonciello and committed by
Darren Hart (VMware)
33b9ca1e 980f481d

+240 -1
+21
Documentation/ABI/testing/sysfs-platform-dell-smbios
··· 1 + What: /sys/devices/platform/<platform>/tokens/* 2 + Date: November 2017 3 + KernelVersion: 4.15 4 + Contact: "Mario Limonciello" <mario.limonciello@dell.com> 5 + Description: 6 + A read-only description of Dell platform tokens 7 + available on the machine. 8 + 9 + Each token attribute is available as a pair of 10 + sysfs attributes readable by a process with 11 + CAP_SYS_ADMIN. 12 + 13 + For example the token ID "5" would be available 14 + as the following attributes: 15 + 16 + 0005_location 17 + 0005_value 18 + 19 + Tokens will vary from machine to machine, and 20 + only tokens available on that machine will be 21 + displayed.
+7
MAINTAINERS
··· 3978 3978 S: Maintained 3979 3979 F: drivers/net/fddi/defxx.* 3980 3980 3981 + DELL SMBIOS DRIVER 3982 + M: Pali Rohár <pali.rohar@gmail.com> 3983 + M: Mario Limonciello <mario.limonciello@dell.com> 3984 + L: platform-driver-x86@vger.kernel.org 3985 + S: Maintained 3986 + F: drivers/platform/x86/dell-smbios.* 3987 + 3981 3988 DELL LAPTOP DRIVER 3982 3989 M: Matthew Garrett <mjg59@srcf.ucam.org> 3983 3990 M: Pali Rohár <pali.rohar@gmail.com>
+212 -1
drivers/platform/x86/dell-smbios.c
··· 16 16 17 17 #include <linux/kernel.h> 18 18 #include <linux/module.h> 19 + #include <linux/capability.h> 19 20 #include <linux/dmi.h> 20 21 #include <linux/err.h> 21 22 #include <linux/gfp.h> 22 23 #include <linux/mutex.h> 24 + #include <linux/platform_device.h> 23 25 #include <linux/slab.h> 24 26 #include <linux/io.h> 25 27 #include "../../firmware/dcdbas.h" ··· 41 39 static int da_command_address; 42 40 static int da_command_code; 43 41 static int da_num_tokens; 42 + static struct platform_device *platform_device; 44 43 static struct calling_interface_token *da_tokens; 44 + static struct device_attribute *token_location_attrs; 45 + static struct device_attribute *token_value_attrs; 46 + static struct attribute **token_attrs; 45 47 46 48 int dell_smbios_error(int value) 47 49 { ··· 163 157 da_num_tokens += tokens; 164 158 } 165 159 160 + static void zero_duplicates(struct device *dev) 161 + { 162 + int i, j; 163 + 164 + for (i = 0; i < da_num_tokens; i++) { 165 + if (da_tokens[i].tokenID == 0) 166 + continue; 167 + for (j = i+1; j < da_num_tokens; j++) { 168 + if (da_tokens[j].tokenID == 0) 169 + continue; 170 + if (da_tokens[i].tokenID == da_tokens[j].tokenID) { 171 + dev_dbg(dev, "Zeroing dup token ID %x(%x/%x)\n", 172 + da_tokens[j].tokenID, 173 + da_tokens[j].location, 174 + da_tokens[j].value); 175 + da_tokens[j].tokenID = 0; 176 + } 177 + } 178 + } 179 + } 180 + 166 181 static void __init find_tokens(const struct dmi_header *dm, void *dummy) 167 182 { 168 183 switch (dm->type) { ··· 196 169 break; 197 170 } 198 171 } 172 + 173 + static int match_attribute(struct device *dev, 174 + struct device_attribute *attr) 175 + { 176 + int i; 177 + 178 + for (i = 0; i < da_num_tokens * 2; i++) { 179 + if (!token_attrs[i]) 180 + continue; 181 + if (strcmp(token_attrs[i]->name, attr->attr.name) == 0) 182 + return i/2; 183 + } 184 + dev_dbg(dev, "couldn't match: %s\n", attr->attr.name); 185 + return -EINVAL; 186 + } 187 + 188 + static ssize_t location_show(struct device *dev, 189 + struct device_attribute *attr, char *buf) 190 + { 191 + int i; 192 + 193 + if (!capable(CAP_SYS_ADMIN)) 194 + return -EPERM; 195 + 196 + i = match_attribute(dev, attr); 197 + if (i > 0) 198 + return scnprintf(buf, PAGE_SIZE, "%08x", da_tokens[i].location); 199 + return 0; 200 + } 201 + 202 + static ssize_t value_show(struct device *dev, 203 + struct device_attribute *attr, char *buf) 204 + { 205 + int i; 206 + 207 + if (!capable(CAP_SYS_ADMIN)) 208 + return -EPERM; 209 + 210 + i = match_attribute(dev, attr); 211 + if (i > 0) 212 + return scnprintf(buf, PAGE_SIZE, "%08x", da_tokens[i].value); 213 + return 0; 214 + } 215 + 216 + static struct attribute_group smbios_attribute_group = { 217 + .name = "tokens" 218 + }; 219 + 220 + static struct platform_driver platform_driver = { 221 + .driver = { 222 + .name = "dell-smbios", 223 + }, 224 + }; 225 + 226 + static int build_tokens_sysfs(struct platform_device *dev) 227 + { 228 + char buffer_location[13]; 229 + char buffer_value[10]; 230 + char *location_name; 231 + char *value_name; 232 + size_t size; 233 + int ret; 234 + int i, j; 235 + 236 + /* (number of tokens + 1 for null terminated */ 237 + size = sizeof(struct device_attribute) * (da_num_tokens + 1); 238 + token_location_attrs = kzalloc(size, GFP_KERNEL); 239 + if (!token_location_attrs) 240 + return -ENOMEM; 241 + token_value_attrs = kzalloc(size, GFP_KERNEL); 242 + if (!token_value_attrs) 243 + goto out_allocate_value; 244 + 245 + /* need to store both location and value + terminator*/ 246 + size = sizeof(struct attribute *) * ((2 * da_num_tokens) + 1); 247 + token_attrs = kzalloc(size, GFP_KERNEL); 248 + if (!token_attrs) 249 + goto out_allocate_attrs; 250 + 251 + for (i = 0, j = 0; i < da_num_tokens; i++) { 252 + /* skip empty */ 253 + if (da_tokens[i].tokenID == 0) 254 + continue; 255 + /* add location */ 256 + sprintf(buffer_location, "%04x_location", 257 + da_tokens[i].tokenID); 258 + location_name = kstrdup(buffer_location, GFP_KERNEL); 259 + if (location_name == NULL) 260 + goto out_unwind_strings; 261 + sysfs_attr_init(&token_location_attrs[i].attr); 262 + token_location_attrs[i].attr.name = location_name; 263 + token_location_attrs[i].attr.mode = 0444; 264 + token_location_attrs[i].show = location_show; 265 + token_attrs[j++] = &token_location_attrs[i].attr; 266 + 267 + /* add value */ 268 + sprintf(buffer_value, "%04x_value", 269 + da_tokens[i].tokenID); 270 + value_name = kstrdup(buffer_value, GFP_KERNEL); 271 + if (value_name == NULL) 272 + goto loop_fail_create_value; 273 + sysfs_attr_init(&token_value_attrs[i].attr); 274 + token_value_attrs[i].attr.name = value_name; 275 + token_value_attrs[i].attr.mode = 0444; 276 + token_value_attrs[i].show = value_show; 277 + token_attrs[j++] = &token_value_attrs[i].attr; 278 + continue; 279 + 280 + loop_fail_create_value: 281 + kfree(value_name); 282 + goto out_unwind_strings; 283 + } 284 + smbios_attribute_group.attrs = token_attrs; 285 + 286 + ret = sysfs_create_group(&dev->dev.kobj, &smbios_attribute_group); 287 + if (ret) 288 + goto out_unwind_strings; 289 + return 0; 290 + 291 + out_unwind_strings: 292 + for (i = i-1; i > 0; i--) { 293 + kfree(token_location_attrs[i].attr.name); 294 + kfree(token_value_attrs[i].attr.name); 295 + } 296 + kfree(token_attrs); 297 + out_allocate_attrs: 298 + kfree(token_value_attrs); 299 + out_allocate_value: 300 + kfree(token_location_attrs); 301 + 302 + return -ENOMEM; 303 + } 304 + 305 + static void free_group(struct platform_device *pdev) 306 + { 307 + int i; 308 + 309 + sysfs_remove_group(&pdev->dev.kobj, 310 + &smbios_attribute_group); 311 + for (i = 0; i < da_num_tokens; i++) { 312 + kfree(token_location_attrs[i].attr.name); 313 + kfree(token_value_attrs[i].attr.name); 314 + } 315 + kfree(token_attrs); 316 + kfree(token_value_attrs); 317 + kfree(token_location_attrs); 318 + } 319 + 199 320 200 321 static int __init dell_smbios_init(void) 201 322 { ··· 372 197 ret = -ENOMEM; 373 198 goto fail_buffer; 374 199 } 200 + ret = platform_driver_register(&platform_driver); 201 + if (ret) 202 + goto fail_platform_driver; 203 + 204 + platform_device = platform_device_alloc("dell-smbios", 0); 205 + if (!platform_device) { 206 + ret = -ENOMEM; 207 + goto fail_platform_device_alloc; 208 + } 209 + ret = platform_device_add(platform_device); 210 + if (ret) 211 + goto fail_platform_device_add; 212 + 213 + /* duplicate tokens will cause problems building sysfs files */ 214 + zero_duplicates(&platform_device->dev); 215 + 216 + ret = build_tokens_sysfs(platform_device); 217 + if (ret) 218 + goto fail_create_group; 375 219 376 220 return 0; 221 + 222 + fail_create_group: 223 + platform_device_del(platform_device); 224 + 225 + fail_platform_device_add: 226 + platform_device_put(platform_device); 227 + 228 + fail_platform_device_alloc: 229 + platform_driver_unregister(&platform_driver); 230 + 231 + fail_platform_driver: 232 + free_page((unsigned long)buffer); 377 233 378 234 fail_buffer: 379 235 kfree(da_tokens); ··· 413 207 414 208 static void __exit dell_smbios_exit(void) 415 209 { 416 - kfree(da_tokens); 210 + if (platform_device) { 211 + free_group(platform_device); 212 + platform_device_unregister(platform_device); 213 + platform_driver_unregister(&platform_driver); 214 + } 417 215 free_page((unsigned long)buffer); 216 + kfree(da_tokens); 418 217 } 419 218 420 219 subsys_initcall(dell_smbios_init);