linux/drivers/firmware/qemu_fw_cfg.c

/*
 * 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)
 */

#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/acpi.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <uapi/linux/qemu_fw_cfg.h>
#include <linux/delay.h>
#include <linux/crash_dump.h>
#include <linux/vmcore_info.h>

MODULE_AUTHOR();
MODULE_DESCRIPTION();
MODULE_LICENSE();

/* fw_cfg revision attribute, in /sys/firmware/qemu_fw_cfg top-level dir. */
static u32 fw_cfg_rev;

/* fw_cfg device i/o register addresses */
static bool fw_cfg_is_mmio;
static phys_addr_t fw_cfg_p_base;
static resource_size_t fw_cfg_p_size;
static void __iomem *fw_cfg_dev_base;
static void __iomem *fw_cfg_reg_ctrl;
static void __iomem *fw_cfg_reg_data;
static void __iomem *fw_cfg_reg_dma;

/* atomic access to fw_cfg device (potentially slow i/o, so using mutex) */
static DEFINE_MUTEX(fw_cfg_dev_lock);

/* pick appropriate endianness for selector key */
static void fw_cfg_sel_endianness(u16 key)
{}

#ifdef CONFIG_VMCORE_INFO
static inline bool fw_cfg_dma_enabled(void)
{}

/* qemu fw_cfg device is sync today, but spec says it may become async */
static void fw_cfg_wait_for_control(struct fw_cfg_dma_access *d)
{}

static ssize_t fw_cfg_dma_transfer(void *address, u32 length, u32 control)
{}
#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)
{}

#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)
{}
#endif /* CONFIG_VMCORE_INFO */

/* clean up fw_cfg device i/o */
static void fw_cfg_io_cleanup(void)
{}

/* arch-specific ctrl & data register offsets are not available in ACPI, DT */
#if !(defined(FW_CFG_CTRL_OFF) && defined(FW_CFG_DATA_OFF))
# if (defined(CONFIG_ARM) || defined(CONFIG_ARM64) || defined(CONFIG_RISCV))
#define FW_CFG_CTRL_OFF
#define FW_CFG_DATA_OFF
#define FW_CFG_DMA_OFF
# elif defined(CONFIG_PARISC)	/* parisc */
#define FW_CFG_CTRL_OFF
#define FW_CFG_DATA_OFF
# elif (defined(CONFIG_PPC_PMAC) || defined(CONFIG_SPARC32)) /* ppc/mac,sun4m */
#define FW_CFG_CTRL_OFF
#define FW_CFG_DATA_OFF
# elif (defined(CONFIG_X86) || defined(CONFIG_SPARC64)) /* x86, sun4u */
#define FW_CFG_CTRL_OFF
#define FW_CFG_DATA_OFF
#define FW_CFG_DMA_OFF
# else
#  error "QEMU FW_CFG not available on this architecture!"
# endif
#endif

/* initialize fw_cfg device i/o from platform data */
static int fw_cfg_do_platform_probe(struct platform_device *pdev)
{}

static ssize_t fw_cfg_showrev(struct kobject *k, struct kobj_attribute *a,
			      char *buf)
{}

static const struct kobj_attribute fw_cfg_rev_attr =;

/* fw_cfg_sysfs_entry type */
struct fw_cfg_sysfs_entry {};

#ifdef CONFIG_VMCORE_INFO
static ssize_t fw_cfg_write_vmcoreinfo(const struct fw_cfg_file *f)
{}
#endif /* CONFIG_VMCORE_INFO */

/* get fw_cfg_sysfs_entry from kobject member */
static inline struct fw_cfg_sysfs_entry *to_entry(struct kobject *kobj)
{}

/* fw_cfg_sysfs_attribute type */
struct fw_cfg_sysfs_attribute {};

/* get fw_cfg_sysfs_attribute from attribute member */
static inline struct fw_cfg_sysfs_attribute *to_attr(struct attribute *attr)
{}

/* global cache of fw_cfg_sysfs_entry objects */
static LIST_HEAD(fw_cfg_entry_cache);

/* kobjects removed lazily by kernel, mutual exclusion needed */
static DEFINE_SPINLOCK(fw_cfg_cache_lock);

static inline void fw_cfg_sysfs_cache_enlist(struct fw_cfg_sysfs_entry *entry)
{}

static inline void fw_cfg_sysfs_cache_delist(struct fw_cfg_sysfs_entry *entry)
{}

static void fw_cfg_sysfs_cache_cleanup(void)
{}

/* per-entry attributes and show methods */

#define FW_CFG_SYSFS_ATTR(_attr)

static ssize_t fw_cfg_sysfs_show_size(struct fw_cfg_sysfs_entry *e, char *buf)
{}

static ssize_t fw_cfg_sysfs_show_key(struct fw_cfg_sysfs_entry *e, char *buf)
{}

static ssize_t fw_cfg_sysfs_show_name(struct fw_cfg_sysfs_entry *e, char *buf)
{}

static FW_CFG_SYSFS_ATTR(size);
static FW_CFG_SYSFS_ATTR(key);
static FW_CFG_SYSFS_ATTR(name);

static struct attribute *fw_cfg_sysfs_entry_attrs[] =;
ATTRIBUTE_GROUPS();

/* sysfs_ops: find fw_cfg_[entry, attribute] and call appropriate show method */
static ssize_t fw_cfg_sysfs_attr_show(struct kobject *kobj, struct attribute *a,
				      char *buf)
{}

static const struct sysfs_ops fw_cfg_sysfs_attr_ops =;

/* release: destructor, to be called via kobject_put() */
static void fw_cfg_sysfs_release_entry(struct kobject *kobj)
{}

/* kobj_type: ties together all properties required to register an entry */
static struct kobj_type fw_cfg_sysfs_entry_ktype =;

/* raw-read method and attribute */
static ssize_t fw_cfg_sysfs_read_raw(struct file *filp, struct kobject *kobj,
				     struct bin_attribute *bin_attr,
				     char *buf, loff_t pos, size_t count)
{}

static struct bin_attribute fw_cfg_sysfs_attr_raw =;

/*
 * 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 :)
 */
static int fw_cfg_build_symlink(struct kset *dir,
				struct kobject *target, const char *name)
{}

/* recursively unregister fw_cfg/by_name/ kset directory tree */
static void fw_cfg_kset_unregister_recursive(struct kset *kset)
{}

/* kobjects & kset representing top-level, by_key, and by_name folders */
static struct kobject *fw_cfg_top_ko;
static struct kobject *fw_cfg_sel_ko;
static struct kset *fw_cfg_fname_kset;

/* register an individual fw_cfg file */
static int fw_cfg_register_file(const struct fw_cfg_file *f)
{}

/* iterate over all fw_cfg directory entries, registering each one */
static int fw_cfg_register_dir_entries(void)
{}

/* unregister top-level or by_key folder */
static inline void fw_cfg_kobj_cleanup(struct kobject *kobj)
{}

static int fw_cfg_sysfs_probe(struct platform_device *pdev)
{}

static void fw_cfg_sysfs_remove(struct platform_device *pdev)
{}

static const struct of_device_id fw_cfg_sysfs_mmio_match[] =;
MODULE_DEVICE_TABLE(of, fw_cfg_sysfs_mmio_match);

#ifdef CONFIG_ACPI
static const struct acpi_device_id fw_cfg_sysfs_acpi_match[] =;
MODULE_DEVICE_TABLE(acpi, fw_cfg_sysfs_acpi_match);
#endif

static struct platform_driver fw_cfg_sysfs_driver =;

#ifdef CONFIG_FW_CFG_SYSFS_CMDLINE

static struct platform_device *fw_cfg_cmdline_dev;

/* 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
#else
#define __PHYS_ADDR_PREFIX
#endif

/* use special scanf/printf modifier for phys_addr_t, resource_size_t */
#define PH_ADDR_SCAN_FMT

#define PH_ADDR_PR_1_FMT

#define PH_ADDR_PR_3_FMT

#define PH_ADDR_PR_4_FMT

static int fw_cfg_cmdline_set(const char *arg, const struct kernel_param *kp)
{}

static int fw_cfg_cmdline_get(char *buf, const struct kernel_param *kp)
{}

static const struct kernel_param_ops fw_cfg_cmdline_param_ops =;

device_param_cb();
device_param_cb();

#endif /* CONFIG_FW_CFG_SYSFS_CMDLINE */

static int __init fw_cfg_sysfs_init(void)
{}

static void __exit fw_cfg_sysfs_exit(void)
{}

module_init();
module_exit(fw_cfg_sysfs_exit);