diff options
author | Gabriel Somlo <somlo@cmu.edu> | 2016-01-28 09:23:13 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2016-02-09 20:37:39 -0500 |
commit | 246c46ebaeaef17814dc5a8830d16e7f1b01116b (patch) | |
tree | 0a55991f6e99de4f2895b7921569a55a3b8d5837 | |
parent | 75f3e8e47f381074801d0034874d20c638d9e3d9 (diff) |
firmware: create directory hierarchy for sysfs fw_cfg entries
Each fw_cfg entry of type "file" has an associated 56-char,
nul-terminated ASCII string which represents its name. While
the fw_cfg device doesn't itself impose any specific naming
convention, QEMU developers have traditionally used path name
semantics (i.e. "etc/acpi/rsdp") to descriptively name the
various fw_cfg "blobs" passed into the guest.
This patch attempts, on a best effort basis, to create a
directory hierarchy representing the content of fw_cfg file
names, under /sys/firmware/qemu_fw_cfg/by_name.
Upon successful creation of all directories representing the
"dirname" portion of a fw_cfg file, a symlink will be created
to represent the "basename", pointing at the appropriate
/sys/firmware/qemu_fw_cfg/by_key entry. If a file name is not
suitable for this procedure (e.g., if its basename or dirname
components collide with an already existing dirname component
or basename, respectively) the corresponding fw_cfg blob is
skipped and will remain available in sysfs only by its selector
key value.
Signed-off-by: Gabriel Somlo <somlo@cmu.edu>
Cc: Andy Lutomirski <luto@amacapital.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | Documentation/ABI/testing/sysfs-firmware-qemu_fw_cfg | 42 | ||||
-rw-r--r-- | drivers/firmware/qemu_fw_cfg.c | 109 |
2 files changed, 148 insertions, 3 deletions
diff --git a/Documentation/ABI/testing/sysfs-firmware-qemu_fw_cfg b/Documentation/ABI/testing/sysfs-firmware-qemu_fw_cfg index e9e58d4ea60a..011dda4f8e8a 100644 --- a/Documentation/ABI/testing/sysfs-firmware-qemu_fw_cfg +++ b/Documentation/ABI/testing/sysfs-firmware-qemu_fw_cfg | |||
@@ -56,3 +56,45 @@ Description: | |||
56 | entry via the control register, and reading a number | 56 | entry via the control register, and reading a number |
57 | of bytes equal to the blob size from the data | 57 | of bytes equal to the blob size from the data |
58 | register. | 58 | register. |
59 | |||
60 | --- Listing fw_cfg blobs by file name --- | ||
61 | |||
62 | While the fw_cfg device does not impose any specific naming | ||
63 | convention on the blobs registered in the file directory, | ||
64 | QEMU developers have traditionally used path name semantics | ||
65 | to give each blob a descriptive name. For example: | ||
66 | |||
67 | "bootorder" | ||
68 | "genroms/kvmvapic.bin" | ||
69 | "etc/e820" | ||
70 | "etc/boot-fail-wait" | ||
71 | "etc/system-states" | ||
72 | "etc/table-loader" | ||
73 | "etc/acpi/rsdp" | ||
74 | "etc/acpi/tables" | ||
75 | "etc/smbios/smbios-tables" | ||
76 | "etc/smbios/smbios-anchor" | ||
77 | ... | ||
78 | |||
79 | In addition to the listing by unique selector key described | ||
80 | above, the fw_cfg sysfs driver also attempts to build a tree | ||
81 | of directories matching the path name components of fw_cfg | ||
82 | blob names, ending in symlinks to the by_key entry for each | ||
83 | "basename", as illustrated below (assume current directory is | ||
84 | /sys/firmware): | ||
85 | |||
86 | qemu_fw_cfg/by_name/bootorder -> ../by_key/38 | ||
87 | qemu_fw_cfg/by_name/etc/e820 -> ../../by_key/35 | ||
88 | qemu_fw_cfg/by_name/etc/acpi/rsdp -> ../../../by_key/41 | ||
89 | ... | ||
90 | |||
91 | Construction of the directory tree and symlinks is done on a | ||
92 | "best-effort" basis, as there is no guarantee that components | ||
93 | of fw_cfg blob names are always "well behaved". I.e., there is | ||
94 | the possibility that a symlink (basename) will conflict with | ||
95 | a dirname component of another fw_cfg blob, in which case the | ||
96 | creation of the offending /sys/firmware/qemu_fw_cfg/by_name | ||
97 | entry will be skipped. | ||
98 | |||
99 | The authoritative list of entries will continue to be found | ||
100 | under the /sys/firmware/qemu_fw_cfg/by_key directory. | ||
diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 83e8a5c8f887..19f6851be87f 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c | |||
@@ -334,9 +334,103 @@ static struct bin_attribute fw_cfg_sysfs_attr_raw = { | |||
334 | .read = fw_cfg_sysfs_read_raw, | 334 | .read = fw_cfg_sysfs_read_raw, |
335 | }; | 335 | }; |
336 | 336 | ||
337 | /* kobjects representing top-level and by_key folders */ | 337 | /* |
338 | * Create a kset subdirectory matching each '/' delimited dirname token | ||
339 | * in 'name', starting with sysfs kset/folder 'dir'; At the end, create | ||
340 | * a symlink directed at the given 'target'. | ||
341 | * NOTE: We do this on a best-effort basis, since 'name' is not guaranteed | ||
342 | * to be a well-behaved path name. Whenever a symlink vs. kset directory | ||
343 | * name collision occurs, the kernel will issue big scary warnings while | ||
344 | * refusing to add the offending link or directory. We follow up with our | ||
345 | * own, slightly less scary error messages explaining the situation :) | ||
346 | */ | ||
347 | static int fw_cfg_build_symlink(struct kset *dir, | ||
348 | struct kobject *target, const char *name) | ||
349 | { | ||
350 | int ret; | ||
351 | struct kset *subdir; | ||
352 | struct kobject *ko; | ||
353 | char *name_copy, *p, *tok; | ||
354 | |||
355 | if (!dir || !target || !name || !*name) | ||
356 | return -EINVAL; | ||
357 | |||
358 | /* clone a copy of name for parsing */ | ||
359 | name_copy = p = kstrdup(name, GFP_KERNEL); | ||
360 | if (!name_copy) | ||
361 | return -ENOMEM; | ||
362 | |||
363 | /* create folders for each dirname token, then symlink for basename */ | ||
364 | while ((tok = strsep(&p, "/")) && *tok) { | ||
365 | |||
366 | /* last (basename) token? If so, add symlink here */ | ||
367 | if (!p || !*p) { | ||
368 | ret = sysfs_create_link(&dir->kobj, target, tok); | ||
369 | break; | ||
370 | } | ||
371 | |||
372 | /* does the current dir contain an item named after tok ? */ | ||
373 | ko = kset_find_obj(dir, tok); | ||
374 | if (ko) { | ||
375 | /* drop reference added by kset_find_obj */ | ||
376 | kobject_put(ko); | ||
377 | |||
378 | /* ko MUST be a kset - we're about to use it as one ! */ | ||
379 | if (ko->ktype != dir->kobj.ktype) { | ||
380 | ret = -EINVAL; | ||
381 | break; | ||
382 | } | ||
383 | |||
384 | /* descend into already existing subdirectory */ | ||
385 | dir = to_kset(ko); | ||
386 | } else { | ||
387 | /* create new subdirectory kset */ | ||
388 | subdir = kzalloc(sizeof(struct kset), GFP_KERNEL); | ||
389 | if (!subdir) { | ||
390 | ret = -ENOMEM; | ||
391 | break; | ||
392 | } | ||
393 | subdir->kobj.kset = dir; | ||
394 | subdir->kobj.ktype = dir->kobj.ktype; | ||
395 | ret = kobject_set_name(&subdir->kobj, "%s", tok); | ||
396 | if (ret) { | ||
397 | kfree(subdir); | ||
398 | break; | ||
399 | } | ||
400 | ret = kset_register(subdir); | ||
401 | if (ret) { | ||
402 | kfree(subdir); | ||
403 | break; | ||
404 | } | ||
405 | |||
406 | /* descend into newly created subdirectory */ | ||
407 | dir = subdir; | ||
408 | } | ||
409 | } | ||
410 | |||
411 | /* we're done with cloned copy of name */ | ||
412 | kfree(name_copy); | ||
413 | return ret; | ||
414 | } | ||
415 | |||
416 | /* recursively unregister fw_cfg/by_name/ kset directory tree */ | ||
417 | static void fw_cfg_kset_unregister_recursive(struct kset *kset) | ||
418 | { | ||
419 | struct kobject *k, *next; | ||
420 | |||
421 | list_for_each_entry_safe(k, next, &kset->list, entry) | ||
422 | /* all set members are ksets too, but check just in case... */ | ||
423 | if (k->ktype == kset->kobj.ktype) | ||
424 | fw_cfg_kset_unregister_recursive(to_kset(k)); | ||
425 | |||
426 | /* symlinks are cleanly and automatically removed with the directory */ | ||
427 | kset_unregister(kset); | ||
428 | } | ||
429 | |||
430 | /* kobjects & kset representing top-level, by_key, and by_name folders */ | ||
338 | static struct kobject *fw_cfg_top_ko; | 431 | static struct kobject *fw_cfg_top_ko; |
339 | static struct kobject *fw_cfg_sel_ko; | 432 | static struct kobject *fw_cfg_sel_ko; |
433 | static struct kset *fw_cfg_fname_kset; | ||
340 | 434 | ||
341 | /* register an individual fw_cfg file */ | 435 | /* register an individual fw_cfg file */ |
342 | static int fw_cfg_register_file(const struct fw_cfg_file *f) | 436 | static int fw_cfg_register_file(const struct fw_cfg_file *f) |
@@ -363,6 +457,9 @@ static int fw_cfg_register_file(const struct fw_cfg_file *f) | |||
363 | if (err) | 457 | if (err) |
364 | goto err_add_raw; | 458 | goto err_add_raw; |
365 | 459 | ||
460 | /* try adding "/sys/firmware/qemu_fw_cfg/by_name/" symlink */ | ||
461 | fw_cfg_build_symlink(fw_cfg_fname_kset, &entry->kobj, entry->f.name); | ||
462 | |||
366 | /* success, add entry to global cache */ | 463 | /* success, add entry to global cache */ |
367 | fw_cfg_sysfs_cache_enlist(entry); | 464 | fw_cfg_sysfs_cache_enlist(entry); |
368 | return 0; | 465 | return 0; |
@@ -417,18 +514,21 @@ static int fw_cfg_sysfs_probe(struct platform_device *pdev) | |||
417 | 514 | ||
418 | /* NOTE: If we supported multiple fw_cfg devices, we'd first create | 515 | /* NOTE: If we supported multiple fw_cfg devices, we'd first create |
419 | * a subdirectory named after e.g. pdev->id, then hang per-device | 516 | * a subdirectory named after e.g. pdev->id, then hang per-device |
420 | * by_key subdirectories underneath it. However, only | 517 | * by_key (and by_name) subdirectories underneath it. However, only |
421 | * one fw_cfg device exist system-wide, so if one was already found | 518 | * one fw_cfg device exist system-wide, so if one was already found |
422 | * earlier, we might as well stop here. | 519 | * earlier, we might as well stop here. |
423 | */ | 520 | */ |
424 | if (fw_cfg_sel_ko) | 521 | if (fw_cfg_sel_ko) |
425 | return -EBUSY; | 522 | return -EBUSY; |
426 | 523 | ||
427 | /* create by_key subdirectory of /sys/firmware/qemu_fw_cfg/ */ | 524 | /* create by_key and by_name subdirs of /sys/firmware/qemu_fw_cfg/ */ |
428 | err = -ENOMEM; | 525 | err = -ENOMEM; |
429 | fw_cfg_sel_ko = kobject_create_and_add("by_key", fw_cfg_top_ko); | 526 | fw_cfg_sel_ko = kobject_create_and_add("by_key", fw_cfg_top_ko); |
430 | if (!fw_cfg_sel_ko) | 527 | if (!fw_cfg_sel_ko) |
431 | goto err_sel; | 528 | goto err_sel; |
529 | fw_cfg_fname_kset = kset_create_and_add("by_name", NULL, fw_cfg_top_ko); | ||
530 | if (!fw_cfg_fname_kset) | ||
531 | goto err_name; | ||
432 | 532 | ||
433 | /* initialize fw_cfg device i/o from platform data */ | 533 | /* initialize fw_cfg device i/o from platform data */ |
434 | err = fw_cfg_do_platform_probe(pdev); | 534 | err = fw_cfg_do_platform_probe(pdev); |
@@ -457,6 +557,8 @@ err_dir: | |||
457 | err_rev: | 557 | err_rev: |
458 | fw_cfg_io_cleanup(); | 558 | fw_cfg_io_cleanup(); |
459 | err_probe: | 559 | err_probe: |
560 | fw_cfg_kset_unregister_recursive(fw_cfg_fname_kset); | ||
561 | err_name: | ||
460 | fw_cfg_kobj_cleanup(fw_cfg_sel_ko); | 562 | fw_cfg_kobj_cleanup(fw_cfg_sel_ko); |
461 | err_sel: | 563 | err_sel: |
462 | return err; | 564 | return err; |
@@ -466,6 +568,7 @@ static int fw_cfg_sysfs_remove(struct platform_device *pdev) | |||
466 | { | 568 | { |
467 | pr_debug("fw_cfg: unloading.\n"); | 569 | pr_debug("fw_cfg: unloading.\n"); |
468 | fw_cfg_sysfs_cache_cleanup(); | 570 | fw_cfg_sysfs_cache_cleanup(); |
571 | fw_cfg_kset_unregister_recursive(fw_cfg_fname_kset); | ||
469 | fw_cfg_kobj_cleanup(fw_cfg_sel_ko); | 572 | fw_cfg_kobj_cleanup(fw_cfg_sel_ko); |
470 | fw_cfg_io_cleanup(); | 573 | fw_cfg_io_cleanup(); |
471 | return 0; | 574 | return 0; |