/* * drivers/firmware/qemu_fw_cfg.c * * Copyright 2015 Carnegie Mellon University * * Expose entries from QEMU's firmware configuration (fw_cfg) device in * sysfs (read-only, under "/sys/firmware/qemu_fw_cfg/..."). * * The fw_cfg device may be instantiated via either an ACPI node (on x86 * and select subsets of aarch64), a Device Tree node (on arm), or using * a kernel module (or command line) parameter with the following syntax: * * [qemu_fw_cfg.]ioport=<size>@<base>[:<ctrl_off>:<data_off>[:<dma_off>]] * or * [qemu_fw_cfg.]mmio=<size>@<base>[:<ctrl_off>:<data_off>[:<dma_off>]] * * where: * <size> := size of ioport or mmio range * <base> := physical base address of ioport or mmio range * <ctrl_off> := (optional) offset of control register * <data_off> := (optional) offset of data register * <dma_off> := (optional) offset of dma register * * e.g.: * qemu_fw_cfg.ioport=12@0x510:0:1:4 (the default on x86) * or * qemu_fw_cfg.mmio=16@0x9020000:8:0:16 (the default on arm)
*/
/* qemu fw_cfg device is sync today, but spec says it may become async */ staticvoid fw_cfg_wait_for_control(struct fw_cfg_dma_access *d)
{ for (;;) {
u32 ctrl = be32_to_cpu(READ_ONCE(d->control));
/* do not reorder the read to d->control */
rmb(); if ((ctrl & ~FW_CFG_DMA_CTL_ERROR) == 0) return;
d = kmalloc(sizeof(*d), GFP_KERNEL); if (!d) {
ret = -ENOMEM; goto end;
}
/* fw_cfg device does not need IOMMU protection, so use physical addresses */
*d = (struct fw_cfg_dma_access) {
.address = cpu_to_be64(address ? virt_to_phys(address) : 0),
.length = cpu_to_be32(length),
.control = cpu_to_be32(control)
};
dma = virt_to_phys(d);
iowrite32be((u64)dma >> 32, fw_cfg_reg_dma); /* force memory to sync before notifying device via MMIO */
wmb();
iowrite32be(dma, fw_cfg_reg_dma + 4);
fw_cfg_wait_for_control(d);
if (be32_to_cpu(READ_ONCE(d->control)) & FW_CFG_DMA_CTL_ERROR) {
ret = -EIO;
}
end:
kfree(d);
return ret;
} #endif
/* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ static ssize_t fw_cfg_read_blob(u16 key, void *buf, loff_t pos, size_t count)
{
u32 glk = -1U;
acpi_status status;
/* If we have ACPI, ensure mutual exclusion against any potential * device access by the firmware, e.g. via AML methods:
*/
status = acpi_acquire_global_lock(ACPI_WAIT_FOREVER, &glk); if (ACPI_FAILURE(status) && status != AE_NOT_CONFIGURED) { /* Should never get here */
WARN(1, "fw_cfg_read_blob: Failed to lock ACPI!\n");
memset(buf, 0, count); return -EINVAL;
}
#ifdef CONFIG_VMCORE_INFO /* write chunk of given fw_cfg blob (caller responsible for sanity-check) */ static ssize_t fw_cfg_write_blob(u16 key, void *buf, loff_t pos, size_t count)
{
u32 glk = -1U;
acpi_status status;
ssize_t ret = count;
/* If we have ACPI, ensure mutual exclusion against any potential * device access by the firmware, e.g. via AML methods:
*/
status = acpi_acquire_global_lock(ACPI_WAIT_FOREVER, &glk); if (ACPI_FAILURE(status) && status != AE_NOT_CONFIGURED) { /* Should never get here */
WARN(1, "%s: Failed to lock ACPI!\n", __func__); return -EINVAL;
}
mutex_lock(&fw_cfg_dev_lock); if (pos == 0) {
ret = fw_cfg_dma_transfer(buf, count, key << 16
| FW_CFG_DMA_CTL_SELECT
| FW_CFG_DMA_CTL_WRITE);
} else {
fw_cfg_sel_endianness(key);
ret = fw_cfg_dma_transfer(NULL, pos, FW_CFG_DMA_CTL_SKIP); if (ret < 0) goto end;
ret = fw_cfg_dma_transfer(buf, count, FW_CFG_DMA_CTL_WRITE);
}
data = kmalloc(sizeof(struct fw_cfg_vmcoreinfo), GFP_KERNEL); if (!data) return -ENOMEM;
*data = (struct fw_cfg_vmcoreinfo) {
.guest_format = cpu_to_le16(FW_CFG_VMCOREINFO_FORMAT_ELF),
.size = cpu_to_le32(VMCOREINFO_NOTE_SIZE),
.paddr = cpu_to_le64(paddr_vmcoreinfo_note())
}; /* spare ourself reading host format support for now since we * don't know what else to format - host may ignore ours
*/
ret = fw_cfg_write_blob(be16_to_cpu(f->select), data,
0, sizeof(struct fw_cfg_vmcoreinfo));
/* release: destructor, to be called via kobject_put() */ staticvoid fw_cfg_sysfs_release_entry(struct kobject *kobj)
{ struct fw_cfg_sysfs_entry *entry = to_entry(kobj);
kfree(entry);
}
/* kobj_type: ties together all properties required to register an entry */ staticconststruct kobj_type fw_cfg_sysfs_entry_ktype = {
.default_groups = fw_cfg_sysfs_entry_groups,
.sysfs_ops = &fw_cfg_sysfs_attr_ops,
.release = fw_cfg_sysfs_release_entry,
};
/* * Create a kset subdirectory matching each '/' delimited dirname token * in 'name', starting with sysfs kset/folder 'dir'; At the end, create * a symlink directed at the given 'target'. * NOTE: We do this on a best-effort basis, since 'name' is not guaranteed * to be a well-behaved path name. Whenever a symlink vs. kset directory * name collision occurs, the kernel will issue big scary warnings while * refusing to add the offending link or directory. We follow up with our * own, slightly less scary error messages explaining the situation :)
*/ staticint fw_cfg_build_symlink(struct kset *dir, struct kobject *target, constchar *name)
{ int ret; struct kset *subdir; struct kobject *ko; char *name_copy, *p, *tok;
if (!dir || !target || !name || !*name) return -EINVAL;
/* clone a copy of name for parsing */
name_copy = p = kstrdup(name, GFP_KERNEL); if (!name_copy) return -ENOMEM;
/* create folders for each dirname token, then symlink for basename */ while ((tok = strsep(&p, "/")) && *tok) {
/* last (basename) token? If so, add symlink here */ if (!p || !*p) {
ret = sysfs_create_link(&dir->kobj, target, tok); break;
}
/* does the current dir contain an item named after tok ? */
ko = kset_find_obj(dir, tok); if (ko) { /* drop reference added by kset_find_obj */
kobject_put(ko);
/* ko MUST be a kset - we're about to use it as one ! */ if (ko->ktype != dir->kobj.ktype) {
ret = -EINVAL; break;
}
/* descend into already existing subdirectory */
dir = to_kset(ko);
} else { /* create new subdirectory kset */
subdir = kzalloc(sizeof(struct kset), GFP_KERNEL); if (!subdir) {
ret = -ENOMEM; break;
}
subdir->kobj.kset = dir;
subdir->kobj.ktype = dir->kobj.ktype;
ret = kobject_set_name(&subdir->kobj, "%s", tok); if (ret) {
kfree(subdir); break;
}
ret = kset_register(subdir); if (ret) {
kfree(subdir); break;
}
/* descend into newly created subdirectory */
dir = subdir;
}
}
/* we're done with cloned copy of name */
kfree(name_copy); return ret;
}
list_for_each_entry_safe(k, next, &kset->list, entry) /* all set members are ksets too, but check just in case... */ if (k->ktype == kset->kobj.ktype)
fw_cfg_kset_unregister_recursive(to_kset(k));
/* symlinks are cleanly and automatically removed with the directory */
kset_unregister(kset);
}
/* iterate over all fw_cfg directory entries, registering each one */ staticint fw_cfg_register_dir_entries(void)
{ int ret = 0;
__be32 files_count;
u32 count, i; struct fw_cfg_file *dir;
size_t dir_size;
ret = fw_cfg_read_blob(FW_CFG_FILE_DIR, &files_count,
0, sizeof(files_count)); if (ret < 0) return ret;
staticint fw_cfg_sysfs_probe(struct platform_device *pdev)
{ int err;
__le32 rev;
/* NOTE: If we supported multiple fw_cfg devices, we'd first create * a subdirectory named after e.g. pdev->id, then hang per-device * by_key (and by_name) subdirectories underneath it. However, only * one fw_cfg device exist system-wide, so if one was already found * earlier, we might as well stop here.
*/ if (fw_cfg_sel_ko) return -EBUSY;
/* create by_key and by_name subdirs of /sys/firmware/qemu_fw_cfg/ */
err = -ENOMEM;
fw_cfg_sel_ko = kobject_create_and_add("by_key", fw_cfg_top_ko); if (!fw_cfg_sel_ko) goto err_sel;
fw_cfg_fname_kset = kset_create_and_add("by_name", NULL, fw_cfg_top_ko); if (!fw_cfg_fname_kset) goto err_name;
/* initialize fw_cfg device i/o from platform data */
err = fw_cfg_do_platform_probe(pdev); if (err) goto err_probe;
/* this probably belongs in e.g. include/linux/types.h, * but right now we are the only ones doing it...
*/ #ifdef CONFIG_PHYS_ADDR_T_64BIT #define __PHYS_ADDR_PREFIX "ll" #else #define __PHYS_ADDR_PREFIX "" #endif
/* use special scanf/printf modifier for phys_addr_t, resource_size_t */ #define PH_ADDR_SCAN_FMT "@%" __PHYS_ADDR_PREFIX "i%n" \ ":%" __PHYS_ADDR_PREFIX "i" \ ":%" __PHYS_ADDR_PREFIX "i%n" \ ":%" __PHYS_ADDR_PREFIX "i%n"
#define PH_ADDR_PR_1_FMT "0x%" __PHYS_ADDR_PREFIX "x@" \ "0x%" __PHYS_ADDR_PREFIX "x"
/* only one fw_cfg device can exist system-wide, so if one * was processed on the command line already, we might as * well stop here.
*/ if (fw_cfg_cmdline_dev) { /* avoid leaking previously registered device */
platform_device_unregister(fw_cfg_cmdline_dev); return -EINVAL;
}
/* consume "<size>" portion of command line argument */
size = memparse(arg, &str);
/* sscanf() must process precisely 1, 3 or 4 chunks: * <base> is mandatory, optionally followed by <ctrl_off> * and <data_off>, and <dma_off>; * there must be no extra characters after the last chunk, * so str[consumed] must be '\0'.
*/ if (str[consumed] ||
(processed != 1 && processed != 3 && processed != 4)) return -EINVAL;
/* "processed" happens to nicely match the number of resources * we need to pass in to this platform device.
*/
fw_cfg_cmdline_dev = platform_device_register_simple("fw_cfg",
PLATFORM_DEVID_NONE, res, processed);
return PTR_ERR_OR_ZERO(fw_cfg_cmdline_dev);
}
staticint fw_cfg_cmdline_get(char *buf, conststruct kernel_param *kp)
{ /* stay silent if device was not configured via the command * line, or if the parameter name (ioport/mmio) doesn't match * the device setting
*/ if (!fw_cfg_cmdline_dev ||
(!strcmp(kp->name, "mmio") ^
(fw_cfg_cmdline_dev->resource[0].flags == IORESOURCE_MEM))) return 0;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.