/* 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 *deviceaccessbythefirmware,e.g.viaAMLmethods:
*/
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 *deviceaccessbythefirmware,e.g.viaAMLmethods:
*/
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'tknowwhatelsetoformat-hostmayignoreours
*/
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,
};
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 *asubdirectorynamedaftere.g.pdev->id,thenhangper-device *by_key(andby_name)subdirectoriesunderneathit.However,only *onefw_cfgdeviceexistsystem-wide,soifonewasalreadyfound *earlier,wemightaswellstophere.
*/ 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;
/* only one fw_cfg device can exist system-wide, so if one *wasprocessedonthecommandlinealready,wemightas *wellstophere.
*/ 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);
/* "processed" happens to nicely match the number of resources *weneedtopassintothisplatformdevice.
*/
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,oriftheparametername(ioport/mmio)doesn'tmatch *thedevicesetting
*/ if (!fw_cfg_cmdline_dev ||
(!strcmp(kp->name, "mmio") ^
(fw_cfg_cmdline_dev->resource[0].flags == IORESOURCE_MEM))) return0;
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.