175f3e8e4SGabriel Somlo /* 275f3e8e4SGabriel Somlo * drivers/firmware/qemu_fw_cfg.c 375f3e8e4SGabriel Somlo * 475f3e8e4SGabriel Somlo * Copyright 2015 Carnegie Mellon University 575f3e8e4SGabriel Somlo * 675f3e8e4SGabriel Somlo * Expose entries from QEMU's firmware configuration (fw_cfg) device in 775f3e8e4SGabriel Somlo * sysfs (read-only, under "/sys/firmware/qemu_fw_cfg/..."). 875f3e8e4SGabriel Somlo * 975f3e8e4SGabriel Somlo * The fw_cfg device may be instantiated via either an ACPI node (on x86 1075f3e8e4SGabriel Somlo * and select subsets of aarch64), a Device Tree node (on arm), or using 1175f3e8e4SGabriel Somlo * a kernel module (or command line) parameter with the following syntax: 1275f3e8e4SGabriel Somlo * 1314d1824cSMarc-André Lureau * [qemu_fw_cfg.]ioport=<size>@<base>[:<ctrl_off>:<data_off>[:<dma_off>]] 1475f3e8e4SGabriel Somlo * or 1514d1824cSMarc-André Lureau * [qemu_fw_cfg.]mmio=<size>@<base>[:<ctrl_off>:<data_off>[:<dma_off>]] 1675f3e8e4SGabriel Somlo * 1775f3e8e4SGabriel Somlo * where: 1875f3e8e4SGabriel Somlo * <size> := size of ioport or mmio range 1975f3e8e4SGabriel Somlo * <base> := physical base address of ioport or mmio range 2075f3e8e4SGabriel Somlo * <ctrl_off> := (optional) offset of control register 2175f3e8e4SGabriel Somlo * <data_off> := (optional) offset of data register 2214d1824cSMarc-André Lureau * <dma_off> := (optional) offset of dma register 2375f3e8e4SGabriel Somlo * 2475f3e8e4SGabriel Somlo * e.g.: 2514d1824cSMarc-André Lureau * qemu_fw_cfg.ioport=12@0x510:0:1:4 (the default on x86) 2675f3e8e4SGabriel Somlo * or 2714d1824cSMarc-André Lureau * qemu_fw_cfg.mmio=16@0x9020000:8:0:16 (the default on arm) 2875f3e8e4SGabriel Somlo */ 2975f3e8e4SGabriel Somlo 3075f3e8e4SGabriel Somlo #include <linux/module.h> 31*1b7369acSArnd Bergmann #include <linux/mod_devicetable.h> 3275f3e8e4SGabriel Somlo #include <linux/platform_device.h> 3375f3e8e4SGabriel Somlo #include <linux/acpi.h> 3475f3e8e4SGabriel Somlo #include <linux/slab.h> 3575f3e8e4SGabriel Somlo #include <linux/io.h> 3675f3e8e4SGabriel Somlo #include <linux/ioport.h> 371f57bc12SMarc-André Lureau #include <uapi/linux/qemu_fw_cfg.h> 382d6d60a3SMarc-André Lureau #include <linux/delay.h> 392d6d60a3SMarc-André Lureau #include <linux/crash_dump.h> 402d6d60a3SMarc-André Lureau #include <linux/crash_core.h> 4175f3e8e4SGabriel Somlo 4275f3e8e4SGabriel Somlo MODULE_AUTHOR("Gabriel L. Somlo <somlo@cmu.edu>"); 4375f3e8e4SGabriel Somlo MODULE_DESCRIPTION("QEMU fw_cfg sysfs support"); 4475f3e8e4SGabriel Somlo MODULE_LICENSE("GPL"); 4575f3e8e4SGabriel Somlo 462d6d60a3SMarc-André Lureau /* fw_cfg revision attribute, in /sys/firmware/qemu_fw_cfg top-level dir. */ 472d6d60a3SMarc-André Lureau static u32 fw_cfg_rev; 482d6d60a3SMarc-André Lureau 4975f3e8e4SGabriel Somlo /* fw_cfg device i/o register addresses */ 5075f3e8e4SGabriel Somlo static bool fw_cfg_is_mmio; 5175f3e8e4SGabriel Somlo static phys_addr_t fw_cfg_p_base; 5275f3e8e4SGabriel Somlo static resource_size_t fw_cfg_p_size; 5375f3e8e4SGabriel Somlo static void __iomem *fw_cfg_dev_base; 5475f3e8e4SGabriel Somlo static void __iomem *fw_cfg_reg_ctrl; 5575f3e8e4SGabriel Somlo static void __iomem *fw_cfg_reg_data; 5614d1824cSMarc-André Lureau static void __iomem *fw_cfg_reg_dma; 5775f3e8e4SGabriel Somlo 5875f3e8e4SGabriel Somlo /* atomic access to fw_cfg device (potentially slow i/o, so using mutex) */ 5975f3e8e4SGabriel Somlo static DEFINE_MUTEX(fw_cfg_dev_lock); 6075f3e8e4SGabriel Somlo 6175f3e8e4SGabriel Somlo /* pick appropriate endianness for selector key */ 628d59d5bdSMarc-André Lureau static void fw_cfg_sel_endianness(u16 key) 6375f3e8e4SGabriel Somlo { 648d59d5bdSMarc-André Lureau if (fw_cfg_is_mmio) 658d59d5bdSMarc-André Lureau iowrite16be(key, fw_cfg_reg_ctrl); 668d59d5bdSMarc-André Lureau else 678d59d5bdSMarc-André Lureau iowrite16(key, fw_cfg_reg_ctrl); 6875f3e8e4SGabriel Somlo } 6975f3e8e4SGabriel Somlo 702d6d60a3SMarc-André Lureau #ifdef CONFIG_CRASH_CORE 712d6d60a3SMarc-André Lureau static inline bool fw_cfg_dma_enabled(void) 722d6d60a3SMarc-André Lureau { 732d6d60a3SMarc-André Lureau return (fw_cfg_rev & FW_CFG_VERSION_DMA) && fw_cfg_reg_dma; 742d6d60a3SMarc-André Lureau } 752d6d60a3SMarc-André Lureau 762d6d60a3SMarc-André Lureau /* qemu fw_cfg device is sync today, but spec says it may become async */ 772d6d60a3SMarc-André Lureau static void fw_cfg_wait_for_control(struct fw_cfg_dma_access *d) 782d6d60a3SMarc-André Lureau { 792d6d60a3SMarc-André Lureau for (;;) { 802d6d60a3SMarc-André Lureau u32 ctrl = be32_to_cpu(READ_ONCE(d->control)); 812d6d60a3SMarc-André Lureau 822d6d60a3SMarc-André Lureau /* do not reorder the read to d->control */ 832d6d60a3SMarc-André Lureau rmb(); 842d6d60a3SMarc-André Lureau if ((ctrl & ~FW_CFG_DMA_CTL_ERROR) == 0) 852d6d60a3SMarc-André Lureau return; 862d6d60a3SMarc-André Lureau 872d6d60a3SMarc-André Lureau cpu_relax(); 882d6d60a3SMarc-André Lureau } 892d6d60a3SMarc-André Lureau } 902d6d60a3SMarc-André Lureau 912d6d60a3SMarc-André Lureau static ssize_t fw_cfg_dma_transfer(void *address, u32 length, u32 control) 922d6d60a3SMarc-André Lureau { 932d6d60a3SMarc-André Lureau phys_addr_t dma; 942d6d60a3SMarc-André Lureau struct fw_cfg_dma_access *d = NULL; 952d6d60a3SMarc-André Lureau ssize_t ret = length; 962d6d60a3SMarc-André Lureau 972d6d60a3SMarc-André Lureau d = kmalloc(sizeof(*d), GFP_KERNEL); 982d6d60a3SMarc-André Lureau if (!d) { 992d6d60a3SMarc-André Lureau ret = -ENOMEM; 1002d6d60a3SMarc-André Lureau goto end; 1012d6d60a3SMarc-André Lureau } 1022d6d60a3SMarc-André Lureau 1032d6d60a3SMarc-André Lureau /* fw_cfg device does not need IOMMU protection, so use physical addresses */ 1042d6d60a3SMarc-André Lureau *d = (struct fw_cfg_dma_access) { 1052d6d60a3SMarc-André Lureau .address = cpu_to_be64(address ? virt_to_phys(address) : 0), 1062d6d60a3SMarc-André Lureau .length = cpu_to_be32(length), 1072d6d60a3SMarc-André Lureau .control = cpu_to_be32(control) 1082d6d60a3SMarc-André Lureau }; 1092d6d60a3SMarc-André Lureau 1102d6d60a3SMarc-André Lureau dma = virt_to_phys(d); 1112d6d60a3SMarc-André Lureau 1122d6d60a3SMarc-André Lureau iowrite32be((u64)dma >> 32, fw_cfg_reg_dma); 1132d6d60a3SMarc-André Lureau /* force memory to sync before notifying device via MMIO */ 1142d6d60a3SMarc-André Lureau wmb(); 1152d6d60a3SMarc-André Lureau iowrite32be(dma, fw_cfg_reg_dma + 4); 1162d6d60a3SMarc-André Lureau 1172d6d60a3SMarc-André Lureau fw_cfg_wait_for_control(d); 1182d6d60a3SMarc-André Lureau 1192d6d60a3SMarc-André Lureau if (be32_to_cpu(READ_ONCE(d->control)) & FW_CFG_DMA_CTL_ERROR) { 1202d6d60a3SMarc-André Lureau ret = -EIO; 1212d6d60a3SMarc-André Lureau } 1222d6d60a3SMarc-André Lureau 1232d6d60a3SMarc-André Lureau end: 1242d6d60a3SMarc-André Lureau kfree(d); 1252d6d60a3SMarc-André Lureau 1262d6d60a3SMarc-André Lureau return ret; 1272d6d60a3SMarc-André Lureau } 1282d6d60a3SMarc-André Lureau #endif 1292d6d60a3SMarc-André Lureau 13075f3e8e4SGabriel Somlo /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ 131b1cc4097SMarc-André Lureau static ssize_t fw_cfg_read_blob(u16 key, 13275f3e8e4SGabriel Somlo void *buf, loff_t pos, size_t count) 13375f3e8e4SGabriel Somlo { 134d4f6e272SDan Carpenter u32 glk = -1U; 135def7ac80SGabriel Somlo acpi_status status; 136def7ac80SGabriel Somlo 137def7ac80SGabriel Somlo /* If we have ACPI, ensure mutual exclusion against any potential 138def7ac80SGabriel Somlo * device access by the firmware, e.g. via AML methods: 139def7ac80SGabriel Somlo */ 140def7ac80SGabriel Somlo status = acpi_acquire_global_lock(ACPI_WAIT_FOREVER, &glk); 141def7ac80SGabriel Somlo if (ACPI_FAILURE(status) && status != AE_NOT_CONFIGURED) { 142def7ac80SGabriel Somlo /* Should never get here */ 143def7ac80SGabriel Somlo WARN(1, "fw_cfg_read_blob: Failed to lock ACPI!\n"); 144def7ac80SGabriel Somlo memset(buf, 0, count); 145b1cc4097SMarc-André Lureau return -EINVAL; 146def7ac80SGabriel Somlo } 147def7ac80SGabriel Somlo 14875f3e8e4SGabriel Somlo mutex_lock(&fw_cfg_dev_lock); 1498d59d5bdSMarc-André Lureau fw_cfg_sel_endianness(key); 15075f3e8e4SGabriel Somlo while (pos-- > 0) 15175f3e8e4SGabriel Somlo ioread8(fw_cfg_reg_data); 15275f3e8e4SGabriel Somlo ioread8_rep(fw_cfg_reg_data, buf, count); 15375f3e8e4SGabriel Somlo mutex_unlock(&fw_cfg_dev_lock); 154def7ac80SGabriel Somlo 155def7ac80SGabriel Somlo acpi_release_global_lock(glk); 156b1cc4097SMarc-André Lureau return count; 15775f3e8e4SGabriel Somlo } 15875f3e8e4SGabriel Somlo 1592d6d60a3SMarc-André Lureau #ifdef CONFIG_CRASH_CORE 1602d6d60a3SMarc-André Lureau /* write chunk of given fw_cfg blob (caller responsible for sanity-check) */ 1612d6d60a3SMarc-André Lureau static ssize_t fw_cfg_write_blob(u16 key, 1622d6d60a3SMarc-André Lureau void *buf, loff_t pos, size_t count) 1632d6d60a3SMarc-André Lureau { 1642d6d60a3SMarc-André Lureau u32 glk = -1U; 1652d6d60a3SMarc-André Lureau acpi_status status; 1662d6d60a3SMarc-André Lureau ssize_t ret = count; 1672d6d60a3SMarc-André Lureau 1682d6d60a3SMarc-André Lureau /* If we have ACPI, ensure mutual exclusion against any potential 1692d6d60a3SMarc-André Lureau * device access by the firmware, e.g. via AML methods: 1702d6d60a3SMarc-André Lureau */ 1712d6d60a3SMarc-André Lureau status = acpi_acquire_global_lock(ACPI_WAIT_FOREVER, &glk); 1722d6d60a3SMarc-André Lureau if (ACPI_FAILURE(status) && status != AE_NOT_CONFIGURED) { 1732d6d60a3SMarc-André Lureau /* Should never get here */ 1742d6d60a3SMarc-André Lureau WARN(1, "%s: Failed to lock ACPI!\n", __func__); 1752d6d60a3SMarc-André Lureau return -EINVAL; 1762d6d60a3SMarc-André Lureau } 1772d6d60a3SMarc-André Lureau 1782d6d60a3SMarc-André Lureau mutex_lock(&fw_cfg_dev_lock); 1792d6d60a3SMarc-André Lureau if (pos == 0) { 1802d6d60a3SMarc-André Lureau ret = fw_cfg_dma_transfer(buf, count, key << 16 1812d6d60a3SMarc-André Lureau | FW_CFG_DMA_CTL_SELECT 1822d6d60a3SMarc-André Lureau | FW_CFG_DMA_CTL_WRITE); 1832d6d60a3SMarc-André Lureau } else { 1842d6d60a3SMarc-André Lureau fw_cfg_sel_endianness(key); 1852d6d60a3SMarc-André Lureau ret = fw_cfg_dma_transfer(NULL, pos, FW_CFG_DMA_CTL_SKIP); 1862d6d60a3SMarc-André Lureau if (ret < 0) 1872d6d60a3SMarc-André Lureau goto end; 1882d6d60a3SMarc-André Lureau ret = fw_cfg_dma_transfer(buf, count, FW_CFG_DMA_CTL_WRITE); 1892d6d60a3SMarc-André Lureau } 1902d6d60a3SMarc-André Lureau 1912d6d60a3SMarc-André Lureau end: 1922d6d60a3SMarc-André Lureau mutex_unlock(&fw_cfg_dev_lock); 1932d6d60a3SMarc-André Lureau 1942d6d60a3SMarc-André Lureau acpi_release_global_lock(glk); 1952d6d60a3SMarc-André Lureau 1962d6d60a3SMarc-André Lureau return ret; 1972d6d60a3SMarc-André Lureau } 1982d6d60a3SMarc-André Lureau #endif /* CONFIG_CRASH_CORE */ 1992d6d60a3SMarc-André Lureau 20075f3e8e4SGabriel Somlo /* clean up fw_cfg device i/o */ 20175f3e8e4SGabriel Somlo static void fw_cfg_io_cleanup(void) 20275f3e8e4SGabriel Somlo { 20375f3e8e4SGabriel Somlo if (fw_cfg_is_mmio) { 20475f3e8e4SGabriel Somlo iounmap(fw_cfg_dev_base); 20575f3e8e4SGabriel Somlo release_mem_region(fw_cfg_p_base, fw_cfg_p_size); 20675f3e8e4SGabriel Somlo } else { 20775f3e8e4SGabriel Somlo ioport_unmap(fw_cfg_dev_base); 20875f3e8e4SGabriel Somlo release_region(fw_cfg_p_base, fw_cfg_p_size); 20975f3e8e4SGabriel Somlo } 21075f3e8e4SGabriel Somlo } 21175f3e8e4SGabriel Somlo 21275f3e8e4SGabriel Somlo /* arch-specific ctrl & data register offsets are not available in ACPI, DT */ 2139b3ec23aSValentin Rothberg #if !(defined(FW_CFG_CTRL_OFF) && defined(FW_CFG_DATA_OFF)) 21475f3e8e4SGabriel Somlo # if (defined(CONFIG_ARM) || defined(CONFIG_ARM64)) 21575f3e8e4SGabriel Somlo # define FW_CFG_CTRL_OFF 0x08 21675f3e8e4SGabriel Somlo # define FW_CFG_DATA_OFF 0x00 21714d1824cSMarc-André Lureau # define FW_CFG_DMA_OFF 0x10 21875f3e8e4SGabriel Somlo # elif (defined(CONFIG_PPC_PMAC) || defined(CONFIG_SPARC32)) /* ppc/mac,sun4m */ 21975f3e8e4SGabriel Somlo # define FW_CFG_CTRL_OFF 0x00 22075f3e8e4SGabriel Somlo # define FW_CFG_DATA_OFF 0x02 22175f3e8e4SGabriel Somlo # elif (defined(CONFIG_X86) || defined(CONFIG_SPARC64)) /* x86, sun4u */ 22275f3e8e4SGabriel Somlo # define FW_CFG_CTRL_OFF 0x00 22375f3e8e4SGabriel Somlo # define FW_CFG_DATA_OFF 0x01 22414d1824cSMarc-André Lureau # define FW_CFG_DMA_OFF 0x04 22575f3e8e4SGabriel Somlo # else 22600411b7bSGabriel Somlo # error "QEMU FW_CFG not available on this architecture!" 22775f3e8e4SGabriel Somlo # endif 22875f3e8e4SGabriel Somlo #endif 22975f3e8e4SGabriel Somlo 23075f3e8e4SGabriel Somlo /* initialize fw_cfg device i/o from platform data */ 23175f3e8e4SGabriel Somlo static int fw_cfg_do_platform_probe(struct platform_device *pdev) 23275f3e8e4SGabriel Somlo { 23375f3e8e4SGabriel Somlo char sig[FW_CFG_SIG_SIZE]; 23414d1824cSMarc-André Lureau struct resource *range, *ctrl, *data, *dma; 23575f3e8e4SGabriel Somlo 23675f3e8e4SGabriel Somlo /* acquire i/o range details */ 23775f3e8e4SGabriel Somlo fw_cfg_is_mmio = false; 23875f3e8e4SGabriel Somlo range = platform_get_resource(pdev, IORESOURCE_IO, 0); 23975f3e8e4SGabriel Somlo if (!range) { 24075f3e8e4SGabriel Somlo fw_cfg_is_mmio = true; 24175f3e8e4SGabriel Somlo range = platform_get_resource(pdev, IORESOURCE_MEM, 0); 24275f3e8e4SGabriel Somlo if (!range) 24375f3e8e4SGabriel Somlo return -EINVAL; 24475f3e8e4SGabriel Somlo } 24575f3e8e4SGabriel Somlo fw_cfg_p_base = range->start; 24675f3e8e4SGabriel Somlo fw_cfg_p_size = resource_size(range); 24775f3e8e4SGabriel Somlo 24875f3e8e4SGabriel Somlo if (fw_cfg_is_mmio) { 24975f3e8e4SGabriel Somlo if (!request_mem_region(fw_cfg_p_base, 25075f3e8e4SGabriel Somlo fw_cfg_p_size, "fw_cfg_mem")) 25175f3e8e4SGabriel Somlo return -EBUSY; 25275f3e8e4SGabriel Somlo fw_cfg_dev_base = ioremap(fw_cfg_p_base, fw_cfg_p_size); 25375f3e8e4SGabriel Somlo if (!fw_cfg_dev_base) { 25475f3e8e4SGabriel Somlo release_mem_region(fw_cfg_p_base, fw_cfg_p_size); 25575f3e8e4SGabriel Somlo return -EFAULT; 25675f3e8e4SGabriel Somlo } 25775f3e8e4SGabriel Somlo } else { 25875f3e8e4SGabriel Somlo if (!request_region(fw_cfg_p_base, 25975f3e8e4SGabriel Somlo fw_cfg_p_size, "fw_cfg_io")) 26075f3e8e4SGabriel Somlo return -EBUSY; 26175f3e8e4SGabriel Somlo fw_cfg_dev_base = ioport_map(fw_cfg_p_base, fw_cfg_p_size); 26275f3e8e4SGabriel Somlo if (!fw_cfg_dev_base) { 26375f3e8e4SGabriel Somlo release_region(fw_cfg_p_base, fw_cfg_p_size); 26475f3e8e4SGabriel Somlo return -EFAULT; 26575f3e8e4SGabriel Somlo } 26675f3e8e4SGabriel Somlo } 26775f3e8e4SGabriel Somlo 26875f3e8e4SGabriel Somlo /* were custom register offsets provided (e.g. on the command line)? */ 26975f3e8e4SGabriel Somlo ctrl = platform_get_resource_byname(pdev, IORESOURCE_REG, "ctrl"); 27075f3e8e4SGabriel Somlo data = platform_get_resource_byname(pdev, IORESOURCE_REG, "data"); 27114d1824cSMarc-André Lureau dma = platform_get_resource_byname(pdev, IORESOURCE_REG, "dma"); 27275f3e8e4SGabriel Somlo if (ctrl && data) { 27375f3e8e4SGabriel Somlo fw_cfg_reg_ctrl = fw_cfg_dev_base + ctrl->start; 27475f3e8e4SGabriel Somlo fw_cfg_reg_data = fw_cfg_dev_base + data->start; 27575f3e8e4SGabriel Somlo } else { 27675f3e8e4SGabriel Somlo /* use architecture-specific offsets */ 27775f3e8e4SGabriel Somlo fw_cfg_reg_ctrl = fw_cfg_dev_base + FW_CFG_CTRL_OFF; 27875f3e8e4SGabriel Somlo fw_cfg_reg_data = fw_cfg_dev_base + FW_CFG_DATA_OFF; 27975f3e8e4SGabriel Somlo } 28075f3e8e4SGabriel Somlo 28114d1824cSMarc-André Lureau if (dma) 28214d1824cSMarc-André Lureau fw_cfg_reg_dma = fw_cfg_dev_base + dma->start; 28314d1824cSMarc-André Lureau #ifdef FW_CFG_DMA_OFF 28414d1824cSMarc-André Lureau else 28514d1824cSMarc-André Lureau fw_cfg_reg_dma = fw_cfg_dev_base + FW_CFG_DMA_OFF; 28614d1824cSMarc-André Lureau #endif 28714d1824cSMarc-André Lureau 28875f3e8e4SGabriel Somlo /* verify fw_cfg device signature */ 289b1cc4097SMarc-André Lureau if (fw_cfg_read_blob(FW_CFG_SIGNATURE, sig, 290b1cc4097SMarc-André Lureau 0, FW_CFG_SIG_SIZE) < 0 || 291b1cc4097SMarc-André Lureau memcmp(sig, "QEMU", FW_CFG_SIG_SIZE) != 0) { 29275f3e8e4SGabriel Somlo fw_cfg_io_cleanup(); 29375f3e8e4SGabriel Somlo return -ENODEV; 29475f3e8e4SGabriel Somlo } 29575f3e8e4SGabriel Somlo 29675f3e8e4SGabriel Somlo return 0; 29775f3e8e4SGabriel Somlo } 29875f3e8e4SGabriel Somlo 29975f3e8e4SGabriel Somlo static ssize_t fw_cfg_showrev(struct kobject *k, struct attribute *a, char *buf) 30075f3e8e4SGabriel Somlo { 30175f3e8e4SGabriel Somlo return sprintf(buf, "%u\n", fw_cfg_rev); 30275f3e8e4SGabriel Somlo } 30375f3e8e4SGabriel Somlo 30475f3e8e4SGabriel Somlo static const struct { 30575f3e8e4SGabriel Somlo struct attribute attr; 30675f3e8e4SGabriel Somlo ssize_t (*show)(struct kobject *k, struct attribute *a, char *buf); 30775f3e8e4SGabriel Somlo } fw_cfg_rev_attr = { 30875f3e8e4SGabriel Somlo .attr = { .name = "rev", .mode = S_IRUSR }, 30975f3e8e4SGabriel Somlo .show = fw_cfg_showrev, 31075f3e8e4SGabriel Somlo }; 31175f3e8e4SGabriel Somlo 31275f3e8e4SGabriel Somlo /* fw_cfg_sysfs_entry type */ 31375f3e8e4SGabriel Somlo struct fw_cfg_sysfs_entry { 31475f3e8e4SGabriel Somlo struct kobject kobj; 315d174ea7dSMarc-André Lureau u32 size; 316d174ea7dSMarc-André Lureau u16 select; 317d174ea7dSMarc-André Lureau char name[FW_CFG_MAX_FILE_PATH]; 31875f3e8e4SGabriel Somlo struct list_head list; 31975f3e8e4SGabriel Somlo }; 32075f3e8e4SGabriel Somlo 3212d6d60a3SMarc-André Lureau #ifdef CONFIG_CRASH_CORE 3222d6d60a3SMarc-André Lureau static ssize_t fw_cfg_write_vmcoreinfo(const struct fw_cfg_file *f) 3232d6d60a3SMarc-André Lureau { 3242d6d60a3SMarc-André Lureau static struct fw_cfg_vmcoreinfo *data; 3252d6d60a3SMarc-André Lureau ssize_t ret; 3262d6d60a3SMarc-André Lureau 3272d6d60a3SMarc-André Lureau data = kmalloc(sizeof(struct fw_cfg_vmcoreinfo), GFP_KERNEL); 3282d6d60a3SMarc-André Lureau if (!data) 3292d6d60a3SMarc-André Lureau return -ENOMEM; 3302d6d60a3SMarc-André Lureau 3312d6d60a3SMarc-André Lureau *data = (struct fw_cfg_vmcoreinfo) { 3322d6d60a3SMarc-André Lureau .guest_format = cpu_to_le16(FW_CFG_VMCOREINFO_FORMAT_ELF), 3332d6d60a3SMarc-André Lureau .size = cpu_to_le32(VMCOREINFO_NOTE_SIZE), 3342d6d60a3SMarc-André Lureau .paddr = cpu_to_le64(paddr_vmcoreinfo_note()) 3352d6d60a3SMarc-André Lureau }; 3362d6d60a3SMarc-André Lureau /* spare ourself reading host format support for now since we 3372d6d60a3SMarc-André Lureau * don't know what else to format - host may ignore ours 3382d6d60a3SMarc-André Lureau */ 3392d6d60a3SMarc-André Lureau ret = fw_cfg_write_blob(be16_to_cpu(f->select), data, 3402d6d60a3SMarc-André Lureau 0, sizeof(struct fw_cfg_vmcoreinfo)); 3412d6d60a3SMarc-André Lureau 3422d6d60a3SMarc-André Lureau kfree(data); 3432d6d60a3SMarc-André Lureau return ret; 3442d6d60a3SMarc-André Lureau } 3452d6d60a3SMarc-André Lureau #endif /* CONFIG_CRASH_CORE */ 3462d6d60a3SMarc-André Lureau 34775f3e8e4SGabriel Somlo /* get fw_cfg_sysfs_entry from kobject member */ 34875f3e8e4SGabriel Somlo static inline struct fw_cfg_sysfs_entry *to_entry(struct kobject *kobj) 34975f3e8e4SGabriel Somlo { 35075f3e8e4SGabriel Somlo return container_of(kobj, struct fw_cfg_sysfs_entry, kobj); 35175f3e8e4SGabriel Somlo } 35275f3e8e4SGabriel Somlo 35375f3e8e4SGabriel Somlo /* fw_cfg_sysfs_attribute type */ 35475f3e8e4SGabriel Somlo struct fw_cfg_sysfs_attribute { 35575f3e8e4SGabriel Somlo struct attribute attr; 35675f3e8e4SGabriel Somlo ssize_t (*show)(struct fw_cfg_sysfs_entry *entry, char *buf); 35775f3e8e4SGabriel Somlo }; 35875f3e8e4SGabriel Somlo 35975f3e8e4SGabriel Somlo /* get fw_cfg_sysfs_attribute from attribute member */ 36075f3e8e4SGabriel Somlo static inline struct fw_cfg_sysfs_attribute *to_attr(struct attribute *attr) 36175f3e8e4SGabriel Somlo { 36275f3e8e4SGabriel Somlo return container_of(attr, struct fw_cfg_sysfs_attribute, attr); 36375f3e8e4SGabriel Somlo } 36475f3e8e4SGabriel Somlo 36575f3e8e4SGabriel Somlo /* global cache of fw_cfg_sysfs_entry objects */ 36675f3e8e4SGabriel Somlo static LIST_HEAD(fw_cfg_entry_cache); 36775f3e8e4SGabriel Somlo 36875f3e8e4SGabriel Somlo /* kobjects removed lazily by kernel, mutual exclusion needed */ 36975f3e8e4SGabriel Somlo static DEFINE_SPINLOCK(fw_cfg_cache_lock); 37075f3e8e4SGabriel Somlo 37175f3e8e4SGabriel Somlo static inline void fw_cfg_sysfs_cache_enlist(struct fw_cfg_sysfs_entry *entry) 37275f3e8e4SGabriel Somlo { 37375f3e8e4SGabriel Somlo spin_lock(&fw_cfg_cache_lock); 37475f3e8e4SGabriel Somlo list_add_tail(&entry->list, &fw_cfg_entry_cache); 37575f3e8e4SGabriel Somlo spin_unlock(&fw_cfg_cache_lock); 37675f3e8e4SGabriel Somlo } 37775f3e8e4SGabriel Somlo 37875f3e8e4SGabriel Somlo static inline void fw_cfg_sysfs_cache_delist(struct fw_cfg_sysfs_entry *entry) 37975f3e8e4SGabriel Somlo { 38075f3e8e4SGabriel Somlo spin_lock(&fw_cfg_cache_lock); 38175f3e8e4SGabriel Somlo list_del(&entry->list); 38275f3e8e4SGabriel Somlo spin_unlock(&fw_cfg_cache_lock); 38375f3e8e4SGabriel Somlo } 38475f3e8e4SGabriel Somlo 38575f3e8e4SGabriel Somlo static void fw_cfg_sysfs_cache_cleanup(void) 38675f3e8e4SGabriel Somlo { 38775f3e8e4SGabriel Somlo struct fw_cfg_sysfs_entry *entry, *next; 38875f3e8e4SGabriel Somlo 38975f3e8e4SGabriel Somlo list_for_each_entry_safe(entry, next, &fw_cfg_entry_cache, list) { 39075f3e8e4SGabriel Somlo /* will end up invoking fw_cfg_sysfs_cache_delist() 39175f3e8e4SGabriel Somlo * via each object's release() method (i.e. destructor) 39275f3e8e4SGabriel Somlo */ 39375f3e8e4SGabriel Somlo kobject_put(&entry->kobj); 39475f3e8e4SGabriel Somlo } 39575f3e8e4SGabriel Somlo } 39675f3e8e4SGabriel Somlo 39775f3e8e4SGabriel Somlo /* default_attrs: per-entry attributes and show methods */ 39875f3e8e4SGabriel Somlo 39975f3e8e4SGabriel Somlo #define FW_CFG_SYSFS_ATTR(_attr) \ 40075f3e8e4SGabriel Somlo struct fw_cfg_sysfs_attribute fw_cfg_sysfs_attr_##_attr = { \ 40175f3e8e4SGabriel Somlo .attr = { .name = __stringify(_attr), .mode = S_IRUSR }, \ 40275f3e8e4SGabriel Somlo .show = fw_cfg_sysfs_show_##_attr, \ 40375f3e8e4SGabriel Somlo } 40475f3e8e4SGabriel Somlo 40575f3e8e4SGabriel Somlo static ssize_t fw_cfg_sysfs_show_size(struct fw_cfg_sysfs_entry *e, char *buf) 40675f3e8e4SGabriel Somlo { 407d174ea7dSMarc-André Lureau return sprintf(buf, "%u\n", e->size); 40875f3e8e4SGabriel Somlo } 40975f3e8e4SGabriel Somlo 41075f3e8e4SGabriel Somlo static ssize_t fw_cfg_sysfs_show_key(struct fw_cfg_sysfs_entry *e, char *buf) 41175f3e8e4SGabriel Somlo { 412d174ea7dSMarc-André Lureau return sprintf(buf, "%u\n", e->select); 41375f3e8e4SGabriel Somlo } 41475f3e8e4SGabriel Somlo 41575f3e8e4SGabriel Somlo static ssize_t fw_cfg_sysfs_show_name(struct fw_cfg_sysfs_entry *e, char *buf) 41675f3e8e4SGabriel Somlo { 417d174ea7dSMarc-André Lureau return sprintf(buf, "%s\n", e->name); 41875f3e8e4SGabriel Somlo } 41975f3e8e4SGabriel Somlo 42075f3e8e4SGabriel Somlo static FW_CFG_SYSFS_ATTR(size); 42175f3e8e4SGabriel Somlo static FW_CFG_SYSFS_ATTR(key); 42275f3e8e4SGabriel Somlo static FW_CFG_SYSFS_ATTR(name); 42375f3e8e4SGabriel Somlo 42475f3e8e4SGabriel Somlo static struct attribute *fw_cfg_sysfs_entry_attrs[] = { 42575f3e8e4SGabriel Somlo &fw_cfg_sysfs_attr_size.attr, 42675f3e8e4SGabriel Somlo &fw_cfg_sysfs_attr_key.attr, 42775f3e8e4SGabriel Somlo &fw_cfg_sysfs_attr_name.attr, 42875f3e8e4SGabriel Somlo NULL, 42975f3e8e4SGabriel Somlo }; 43075f3e8e4SGabriel Somlo 43175f3e8e4SGabriel Somlo /* sysfs_ops: find fw_cfg_[entry, attribute] and call appropriate show method */ 43275f3e8e4SGabriel Somlo static ssize_t fw_cfg_sysfs_attr_show(struct kobject *kobj, struct attribute *a, 43375f3e8e4SGabriel Somlo char *buf) 43475f3e8e4SGabriel Somlo { 43575f3e8e4SGabriel Somlo struct fw_cfg_sysfs_entry *entry = to_entry(kobj); 43675f3e8e4SGabriel Somlo struct fw_cfg_sysfs_attribute *attr = to_attr(a); 43775f3e8e4SGabriel Somlo 43875f3e8e4SGabriel Somlo return attr->show(entry, buf); 43975f3e8e4SGabriel Somlo } 44075f3e8e4SGabriel Somlo 44175f3e8e4SGabriel Somlo static const struct sysfs_ops fw_cfg_sysfs_attr_ops = { 44275f3e8e4SGabriel Somlo .show = fw_cfg_sysfs_attr_show, 44375f3e8e4SGabriel Somlo }; 44475f3e8e4SGabriel Somlo 44575f3e8e4SGabriel Somlo /* release: destructor, to be called via kobject_put() */ 44675f3e8e4SGabriel Somlo static void fw_cfg_sysfs_release_entry(struct kobject *kobj) 44775f3e8e4SGabriel Somlo { 44875f3e8e4SGabriel Somlo struct fw_cfg_sysfs_entry *entry = to_entry(kobj); 44975f3e8e4SGabriel Somlo 45075f3e8e4SGabriel Somlo fw_cfg_sysfs_cache_delist(entry); 45175f3e8e4SGabriel Somlo kfree(entry); 45275f3e8e4SGabriel Somlo } 45375f3e8e4SGabriel Somlo 45475f3e8e4SGabriel Somlo /* kobj_type: ties together all properties required to register an entry */ 45575f3e8e4SGabriel Somlo static struct kobj_type fw_cfg_sysfs_entry_ktype = { 45675f3e8e4SGabriel Somlo .default_attrs = fw_cfg_sysfs_entry_attrs, 45775f3e8e4SGabriel Somlo .sysfs_ops = &fw_cfg_sysfs_attr_ops, 45875f3e8e4SGabriel Somlo .release = fw_cfg_sysfs_release_entry, 45975f3e8e4SGabriel Somlo }; 46075f3e8e4SGabriel Somlo 46175f3e8e4SGabriel Somlo /* raw-read method and attribute */ 46275f3e8e4SGabriel Somlo static ssize_t fw_cfg_sysfs_read_raw(struct file *filp, struct kobject *kobj, 46375f3e8e4SGabriel Somlo struct bin_attribute *bin_attr, 46475f3e8e4SGabriel Somlo char *buf, loff_t pos, size_t count) 46575f3e8e4SGabriel Somlo { 46675f3e8e4SGabriel Somlo struct fw_cfg_sysfs_entry *entry = to_entry(kobj); 46775f3e8e4SGabriel Somlo 468d174ea7dSMarc-André Lureau if (pos > entry->size) 46975f3e8e4SGabriel Somlo return -EINVAL; 47075f3e8e4SGabriel Somlo 471d174ea7dSMarc-André Lureau if (count > entry->size - pos) 472d174ea7dSMarc-André Lureau count = entry->size - pos; 47375f3e8e4SGabriel Somlo 474b1cc4097SMarc-André Lureau return fw_cfg_read_blob(entry->select, buf, pos, count); 47575f3e8e4SGabriel Somlo } 47675f3e8e4SGabriel Somlo 47775f3e8e4SGabriel Somlo static struct bin_attribute fw_cfg_sysfs_attr_raw = { 47875f3e8e4SGabriel Somlo .attr = { .name = "raw", .mode = S_IRUSR }, 47975f3e8e4SGabriel Somlo .read = fw_cfg_sysfs_read_raw, 48075f3e8e4SGabriel Somlo }; 48175f3e8e4SGabriel Somlo 482246c46ebSGabriel Somlo /* 483246c46ebSGabriel Somlo * Create a kset subdirectory matching each '/' delimited dirname token 484246c46ebSGabriel Somlo * in 'name', starting with sysfs kset/folder 'dir'; At the end, create 485246c46ebSGabriel Somlo * a symlink directed at the given 'target'. 486246c46ebSGabriel Somlo * NOTE: We do this on a best-effort basis, since 'name' is not guaranteed 487246c46ebSGabriel Somlo * to be a well-behaved path name. Whenever a symlink vs. kset directory 488246c46ebSGabriel Somlo * name collision occurs, the kernel will issue big scary warnings while 489246c46ebSGabriel Somlo * refusing to add the offending link or directory. We follow up with our 490246c46ebSGabriel Somlo * own, slightly less scary error messages explaining the situation :) 491246c46ebSGabriel Somlo */ 492246c46ebSGabriel Somlo static int fw_cfg_build_symlink(struct kset *dir, 493246c46ebSGabriel Somlo struct kobject *target, const char *name) 494246c46ebSGabriel Somlo { 495246c46ebSGabriel Somlo int ret; 496246c46ebSGabriel Somlo struct kset *subdir; 497246c46ebSGabriel Somlo struct kobject *ko; 498246c46ebSGabriel Somlo char *name_copy, *p, *tok; 499246c46ebSGabriel Somlo 500246c46ebSGabriel Somlo if (!dir || !target || !name || !*name) 501246c46ebSGabriel Somlo return -EINVAL; 502246c46ebSGabriel Somlo 503246c46ebSGabriel Somlo /* clone a copy of name for parsing */ 504246c46ebSGabriel Somlo name_copy = p = kstrdup(name, GFP_KERNEL); 505246c46ebSGabriel Somlo if (!name_copy) 506246c46ebSGabriel Somlo return -ENOMEM; 507246c46ebSGabriel Somlo 508246c46ebSGabriel Somlo /* create folders for each dirname token, then symlink for basename */ 509246c46ebSGabriel Somlo while ((tok = strsep(&p, "/")) && *tok) { 510246c46ebSGabriel Somlo 511246c46ebSGabriel Somlo /* last (basename) token? If so, add symlink here */ 512246c46ebSGabriel Somlo if (!p || !*p) { 513246c46ebSGabriel Somlo ret = sysfs_create_link(&dir->kobj, target, tok); 514246c46ebSGabriel Somlo break; 515246c46ebSGabriel Somlo } 516246c46ebSGabriel Somlo 517246c46ebSGabriel Somlo /* does the current dir contain an item named after tok ? */ 518246c46ebSGabriel Somlo ko = kset_find_obj(dir, tok); 519246c46ebSGabriel Somlo if (ko) { 520246c46ebSGabriel Somlo /* drop reference added by kset_find_obj */ 521246c46ebSGabriel Somlo kobject_put(ko); 522246c46ebSGabriel Somlo 523246c46ebSGabriel Somlo /* ko MUST be a kset - we're about to use it as one ! */ 524246c46ebSGabriel Somlo if (ko->ktype != dir->kobj.ktype) { 525246c46ebSGabriel Somlo ret = -EINVAL; 526246c46ebSGabriel Somlo break; 527246c46ebSGabriel Somlo } 528246c46ebSGabriel Somlo 529246c46ebSGabriel Somlo /* descend into already existing subdirectory */ 530246c46ebSGabriel Somlo dir = to_kset(ko); 531246c46ebSGabriel Somlo } else { 532246c46ebSGabriel Somlo /* create new subdirectory kset */ 533246c46ebSGabriel Somlo subdir = kzalloc(sizeof(struct kset), GFP_KERNEL); 534246c46ebSGabriel Somlo if (!subdir) { 535246c46ebSGabriel Somlo ret = -ENOMEM; 536246c46ebSGabriel Somlo break; 537246c46ebSGabriel Somlo } 538246c46ebSGabriel Somlo subdir->kobj.kset = dir; 539246c46ebSGabriel Somlo subdir->kobj.ktype = dir->kobj.ktype; 540246c46ebSGabriel Somlo ret = kobject_set_name(&subdir->kobj, "%s", tok); 541246c46ebSGabriel Somlo if (ret) { 542246c46ebSGabriel Somlo kfree(subdir); 543246c46ebSGabriel Somlo break; 544246c46ebSGabriel Somlo } 545246c46ebSGabriel Somlo ret = kset_register(subdir); 546246c46ebSGabriel Somlo if (ret) { 547246c46ebSGabriel Somlo kfree(subdir); 548246c46ebSGabriel Somlo break; 549246c46ebSGabriel Somlo } 550246c46ebSGabriel Somlo 551246c46ebSGabriel Somlo /* descend into newly created subdirectory */ 552246c46ebSGabriel Somlo dir = subdir; 553246c46ebSGabriel Somlo } 554246c46ebSGabriel Somlo } 555246c46ebSGabriel Somlo 556246c46ebSGabriel Somlo /* we're done with cloned copy of name */ 557246c46ebSGabriel Somlo kfree(name_copy); 558246c46ebSGabriel Somlo return ret; 559246c46ebSGabriel Somlo } 560246c46ebSGabriel Somlo 561246c46ebSGabriel Somlo /* recursively unregister fw_cfg/by_name/ kset directory tree */ 562246c46ebSGabriel Somlo static void fw_cfg_kset_unregister_recursive(struct kset *kset) 563246c46ebSGabriel Somlo { 564246c46ebSGabriel Somlo struct kobject *k, *next; 565246c46ebSGabriel Somlo 566246c46ebSGabriel Somlo list_for_each_entry_safe(k, next, &kset->list, entry) 567246c46ebSGabriel Somlo /* all set members are ksets too, but check just in case... */ 568246c46ebSGabriel Somlo if (k->ktype == kset->kobj.ktype) 569246c46ebSGabriel Somlo fw_cfg_kset_unregister_recursive(to_kset(k)); 570246c46ebSGabriel Somlo 571246c46ebSGabriel Somlo /* symlinks are cleanly and automatically removed with the directory */ 572246c46ebSGabriel Somlo kset_unregister(kset); 573246c46ebSGabriel Somlo } 574246c46ebSGabriel Somlo 575246c46ebSGabriel Somlo /* kobjects & kset representing top-level, by_key, and by_name folders */ 57675f3e8e4SGabriel Somlo static struct kobject *fw_cfg_top_ko; 57775f3e8e4SGabriel Somlo static struct kobject *fw_cfg_sel_ko; 578246c46ebSGabriel Somlo static struct kset *fw_cfg_fname_kset; 57975f3e8e4SGabriel Somlo 58075f3e8e4SGabriel Somlo /* register an individual fw_cfg file */ 58175f3e8e4SGabriel Somlo static int fw_cfg_register_file(const struct fw_cfg_file *f) 58275f3e8e4SGabriel Somlo { 58375f3e8e4SGabriel Somlo int err; 58475f3e8e4SGabriel Somlo struct fw_cfg_sysfs_entry *entry; 58575f3e8e4SGabriel Somlo 5862d6d60a3SMarc-André Lureau #ifdef CONFIG_CRASH_CORE 5872d6d60a3SMarc-André Lureau if (fw_cfg_dma_enabled() && 5882d6d60a3SMarc-André Lureau strcmp(f->name, FW_CFG_VMCOREINFO_FILENAME) == 0 && 5892d6d60a3SMarc-André Lureau !is_kdump_kernel()) { 5902d6d60a3SMarc-André Lureau if (fw_cfg_write_vmcoreinfo(f) < 0) 5912d6d60a3SMarc-André Lureau pr_warn("fw_cfg: failed to write vmcoreinfo"); 5922d6d60a3SMarc-André Lureau } 5932d6d60a3SMarc-André Lureau #endif 5942d6d60a3SMarc-André Lureau 59575f3e8e4SGabriel Somlo /* allocate new entry */ 59675f3e8e4SGabriel Somlo entry = kzalloc(sizeof(*entry), GFP_KERNEL); 59775f3e8e4SGabriel Somlo if (!entry) 59875f3e8e4SGabriel Somlo return -ENOMEM; 59975f3e8e4SGabriel Somlo 60075f3e8e4SGabriel Somlo /* set file entry information */ 601d174ea7dSMarc-André Lureau entry->size = be32_to_cpu(f->size); 602d174ea7dSMarc-André Lureau entry->select = be16_to_cpu(f->select); 603d174ea7dSMarc-André Lureau memcpy(entry->name, f->name, FW_CFG_MAX_FILE_PATH); 60475f3e8e4SGabriel Somlo 60575f3e8e4SGabriel Somlo /* register entry under "/sys/firmware/qemu_fw_cfg/by_key/" */ 60675f3e8e4SGabriel Somlo err = kobject_init_and_add(&entry->kobj, &fw_cfg_sysfs_entry_ktype, 607d174ea7dSMarc-André Lureau fw_cfg_sel_ko, "%d", entry->select); 60875f3e8e4SGabriel Somlo if (err) 60975f3e8e4SGabriel Somlo goto err_register; 61075f3e8e4SGabriel Somlo 61175f3e8e4SGabriel Somlo /* add raw binary content access */ 61275f3e8e4SGabriel Somlo err = sysfs_create_bin_file(&entry->kobj, &fw_cfg_sysfs_attr_raw); 61375f3e8e4SGabriel Somlo if (err) 61475f3e8e4SGabriel Somlo goto err_add_raw; 61575f3e8e4SGabriel Somlo 616246c46ebSGabriel Somlo /* try adding "/sys/firmware/qemu_fw_cfg/by_name/" symlink */ 617d174ea7dSMarc-André Lureau fw_cfg_build_symlink(fw_cfg_fname_kset, &entry->kobj, entry->name); 618246c46ebSGabriel Somlo 61975f3e8e4SGabriel Somlo /* success, add entry to global cache */ 62075f3e8e4SGabriel Somlo fw_cfg_sysfs_cache_enlist(entry); 62175f3e8e4SGabriel Somlo return 0; 62275f3e8e4SGabriel Somlo 62375f3e8e4SGabriel Somlo err_add_raw: 62475f3e8e4SGabriel Somlo kobject_del(&entry->kobj); 62575f3e8e4SGabriel Somlo err_register: 62675f3e8e4SGabriel Somlo kfree(entry); 62775f3e8e4SGabriel Somlo return err; 62875f3e8e4SGabriel Somlo } 62975f3e8e4SGabriel Somlo 63075f3e8e4SGabriel Somlo /* iterate over all fw_cfg directory entries, registering each one */ 63175f3e8e4SGabriel Somlo static int fw_cfg_register_dir_entries(void) 63275f3e8e4SGabriel Somlo { 63375f3e8e4SGabriel Somlo int ret = 0; 6343d47a34bSMarc-André Lureau __be32 files_count; 63575f3e8e4SGabriel Somlo u32 count, i; 63675f3e8e4SGabriel Somlo struct fw_cfg_file *dir; 63775f3e8e4SGabriel Somlo size_t dir_size; 63875f3e8e4SGabriel Somlo 639b1cc4097SMarc-André Lureau ret = fw_cfg_read_blob(FW_CFG_FILE_DIR, &files_count, 640b1cc4097SMarc-André Lureau 0, sizeof(files_count)); 641b1cc4097SMarc-André Lureau if (ret < 0) 642b1cc4097SMarc-André Lureau return ret; 643b1cc4097SMarc-André Lureau 6443d47a34bSMarc-André Lureau count = be32_to_cpu(files_count); 64575f3e8e4SGabriel Somlo dir_size = count * sizeof(struct fw_cfg_file); 64675f3e8e4SGabriel Somlo 64775f3e8e4SGabriel Somlo dir = kmalloc(dir_size, GFP_KERNEL); 64875f3e8e4SGabriel Somlo if (!dir) 64975f3e8e4SGabriel Somlo return -ENOMEM; 65075f3e8e4SGabriel Somlo 651b1cc4097SMarc-André Lureau ret = fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, 652b1cc4097SMarc-André Lureau sizeof(files_count), dir_size); 653b1cc4097SMarc-André Lureau if (ret < 0) 654b1cc4097SMarc-André Lureau goto end; 65575f3e8e4SGabriel Somlo 65675f3e8e4SGabriel Somlo for (i = 0; i < count; i++) { 65775f3e8e4SGabriel Somlo ret = fw_cfg_register_file(&dir[i]); 65875f3e8e4SGabriel Somlo if (ret) 65975f3e8e4SGabriel Somlo break; 66075f3e8e4SGabriel Somlo } 66175f3e8e4SGabriel Somlo 662b1cc4097SMarc-André Lureau end: 66375f3e8e4SGabriel Somlo kfree(dir); 66475f3e8e4SGabriel Somlo return ret; 66575f3e8e4SGabriel Somlo } 66675f3e8e4SGabriel Somlo 66775f3e8e4SGabriel Somlo /* unregister top-level or by_key folder */ 66875f3e8e4SGabriel Somlo static inline void fw_cfg_kobj_cleanup(struct kobject *kobj) 66975f3e8e4SGabriel Somlo { 67075f3e8e4SGabriel Somlo kobject_del(kobj); 67175f3e8e4SGabriel Somlo kobject_put(kobj); 67275f3e8e4SGabriel Somlo } 67375f3e8e4SGabriel Somlo 67475f3e8e4SGabriel Somlo static int fw_cfg_sysfs_probe(struct platform_device *pdev) 67575f3e8e4SGabriel Somlo { 67675f3e8e4SGabriel Somlo int err; 677f295c8dbSMarc-André Lureau __le32 rev; 67875f3e8e4SGabriel Somlo 67975f3e8e4SGabriel Somlo /* NOTE: If we supported multiple fw_cfg devices, we'd first create 68075f3e8e4SGabriel Somlo * a subdirectory named after e.g. pdev->id, then hang per-device 681246c46ebSGabriel Somlo * by_key (and by_name) subdirectories underneath it. However, only 68275f3e8e4SGabriel Somlo * one fw_cfg device exist system-wide, so if one was already found 68375f3e8e4SGabriel Somlo * earlier, we might as well stop here. 68475f3e8e4SGabriel Somlo */ 68575f3e8e4SGabriel Somlo if (fw_cfg_sel_ko) 68675f3e8e4SGabriel Somlo return -EBUSY; 68775f3e8e4SGabriel Somlo 688246c46ebSGabriel Somlo /* create by_key and by_name subdirs of /sys/firmware/qemu_fw_cfg/ */ 68975f3e8e4SGabriel Somlo err = -ENOMEM; 69075f3e8e4SGabriel Somlo fw_cfg_sel_ko = kobject_create_and_add("by_key", fw_cfg_top_ko); 69175f3e8e4SGabriel Somlo if (!fw_cfg_sel_ko) 69275f3e8e4SGabriel Somlo goto err_sel; 693246c46ebSGabriel Somlo fw_cfg_fname_kset = kset_create_and_add("by_name", NULL, fw_cfg_top_ko); 694246c46ebSGabriel Somlo if (!fw_cfg_fname_kset) 695246c46ebSGabriel Somlo goto err_name; 69675f3e8e4SGabriel Somlo 69775f3e8e4SGabriel Somlo /* initialize fw_cfg device i/o from platform data */ 69875f3e8e4SGabriel Somlo err = fw_cfg_do_platform_probe(pdev); 69975f3e8e4SGabriel Somlo if (err) 70075f3e8e4SGabriel Somlo goto err_probe; 70175f3e8e4SGabriel Somlo 70275f3e8e4SGabriel Somlo /* get revision number, add matching top-level attribute */ 703b1cc4097SMarc-André Lureau err = fw_cfg_read_blob(FW_CFG_ID, &rev, 0, sizeof(rev)); 704b1cc4097SMarc-André Lureau if (err < 0) 705b1cc4097SMarc-André Lureau goto err_probe; 706b1cc4097SMarc-André Lureau 707f295c8dbSMarc-André Lureau fw_cfg_rev = le32_to_cpu(rev); 70875f3e8e4SGabriel Somlo err = sysfs_create_file(fw_cfg_top_ko, &fw_cfg_rev_attr.attr); 70975f3e8e4SGabriel Somlo if (err) 71075f3e8e4SGabriel Somlo goto err_rev; 71175f3e8e4SGabriel Somlo 71275f3e8e4SGabriel Somlo /* process fw_cfg file directory entry, registering each file */ 71375f3e8e4SGabriel Somlo err = fw_cfg_register_dir_entries(); 71475f3e8e4SGabriel Somlo if (err) 71575f3e8e4SGabriel Somlo goto err_dir; 71675f3e8e4SGabriel Somlo 71775f3e8e4SGabriel Somlo /* success */ 71875f3e8e4SGabriel Somlo pr_debug("fw_cfg: loaded.\n"); 71975f3e8e4SGabriel Somlo return 0; 72075f3e8e4SGabriel Somlo 72175f3e8e4SGabriel Somlo err_dir: 72275f3e8e4SGabriel Somlo fw_cfg_sysfs_cache_cleanup(); 72375f3e8e4SGabriel Somlo sysfs_remove_file(fw_cfg_top_ko, &fw_cfg_rev_attr.attr); 72475f3e8e4SGabriel Somlo err_rev: 72575f3e8e4SGabriel Somlo fw_cfg_io_cleanup(); 72675f3e8e4SGabriel Somlo err_probe: 727246c46ebSGabriel Somlo fw_cfg_kset_unregister_recursive(fw_cfg_fname_kset); 728246c46ebSGabriel Somlo err_name: 72975f3e8e4SGabriel Somlo fw_cfg_kobj_cleanup(fw_cfg_sel_ko); 73075f3e8e4SGabriel Somlo err_sel: 73175f3e8e4SGabriel Somlo return err; 73275f3e8e4SGabriel Somlo } 73375f3e8e4SGabriel Somlo 73475f3e8e4SGabriel Somlo static int fw_cfg_sysfs_remove(struct platform_device *pdev) 73575f3e8e4SGabriel Somlo { 73675f3e8e4SGabriel Somlo pr_debug("fw_cfg: unloading.\n"); 73775f3e8e4SGabriel Somlo fw_cfg_sysfs_cache_cleanup(); 73823f1b8d9SMarc-André Lureau sysfs_remove_file(fw_cfg_top_ko, &fw_cfg_rev_attr.attr); 73923f1b8d9SMarc-André Lureau fw_cfg_io_cleanup(); 740246c46ebSGabriel Somlo fw_cfg_kset_unregister_recursive(fw_cfg_fname_kset); 74175f3e8e4SGabriel Somlo fw_cfg_kobj_cleanup(fw_cfg_sel_ko); 74275f3e8e4SGabriel Somlo return 0; 74375f3e8e4SGabriel Somlo } 74475f3e8e4SGabriel Somlo 74575f3e8e4SGabriel Somlo static const struct of_device_id fw_cfg_sysfs_mmio_match[] = { 74675f3e8e4SGabriel Somlo { .compatible = "qemu,fw-cfg-mmio", }, 74775f3e8e4SGabriel Somlo {}, 74875f3e8e4SGabriel Somlo }; 74975f3e8e4SGabriel Somlo MODULE_DEVICE_TABLE(of, fw_cfg_sysfs_mmio_match); 75075f3e8e4SGabriel Somlo 75175f3e8e4SGabriel Somlo #ifdef CONFIG_ACPI 75275f3e8e4SGabriel Somlo static const struct acpi_device_id fw_cfg_sysfs_acpi_match[] = { 7531f57bc12SMarc-André Lureau { FW_CFG_ACPI_DEVICE_ID, }, 75475f3e8e4SGabriel Somlo {}, 75575f3e8e4SGabriel Somlo }; 75675f3e8e4SGabriel Somlo MODULE_DEVICE_TABLE(acpi, fw_cfg_sysfs_acpi_match); 75775f3e8e4SGabriel Somlo #endif 75875f3e8e4SGabriel Somlo 75975f3e8e4SGabriel Somlo static struct platform_driver fw_cfg_sysfs_driver = { 76075f3e8e4SGabriel Somlo .probe = fw_cfg_sysfs_probe, 76175f3e8e4SGabriel Somlo .remove = fw_cfg_sysfs_remove, 76275f3e8e4SGabriel Somlo .driver = { 76375f3e8e4SGabriel Somlo .name = "fw_cfg", 76475f3e8e4SGabriel Somlo .of_match_table = fw_cfg_sysfs_mmio_match, 76575f3e8e4SGabriel Somlo .acpi_match_table = ACPI_PTR(fw_cfg_sysfs_acpi_match), 76675f3e8e4SGabriel Somlo }, 76775f3e8e4SGabriel Somlo }; 76875f3e8e4SGabriel Somlo 76975f3e8e4SGabriel Somlo #ifdef CONFIG_FW_CFG_SYSFS_CMDLINE 77075f3e8e4SGabriel Somlo 77175f3e8e4SGabriel Somlo static struct platform_device *fw_cfg_cmdline_dev; 77275f3e8e4SGabriel Somlo 77375f3e8e4SGabriel Somlo /* this probably belongs in e.g. include/linux/types.h, 77475f3e8e4SGabriel Somlo * but right now we are the only ones doing it... 77575f3e8e4SGabriel Somlo */ 77675f3e8e4SGabriel Somlo #ifdef CONFIG_PHYS_ADDR_T_64BIT 77775f3e8e4SGabriel Somlo #define __PHYS_ADDR_PREFIX "ll" 77875f3e8e4SGabriel Somlo #else 77975f3e8e4SGabriel Somlo #define __PHYS_ADDR_PREFIX "" 78075f3e8e4SGabriel Somlo #endif 78175f3e8e4SGabriel Somlo 78275f3e8e4SGabriel Somlo /* use special scanf/printf modifier for phys_addr_t, resource_size_t */ 78375f3e8e4SGabriel Somlo #define PH_ADDR_SCAN_FMT "@%" __PHYS_ADDR_PREFIX "i%n" \ 78475f3e8e4SGabriel Somlo ":%" __PHYS_ADDR_PREFIX "i" \ 78514d1824cSMarc-André Lureau ":%" __PHYS_ADDR_PREFIX "i%n" \ 78675f3e8e4SGabriel Somlo ":%" __PHYS_ADDR_PREFIX "i%n" 78775f3e8e4SGabriel Somlo 78875f3e8e4SGabriel Somlo #define PH_ADDR_PR_1_FMT "0x%" __PHYS_ADDR_PREFIX "x@" \ 78975f3e8e4SGabriel Somlo "0x%" __PHYS_ADDR_PREFIX "x" 79075f3e8e4SGabriel Somlo 79175f3e8e4SGabriel Somlo #define PH_ADDR_PR_3_FMT PH_ADDR_PR_1_FMT \ 79275f3e8e4SGabriel Somlo ":%" __PHYS_ADDR_PREFIX "u" \ 79375f3e8e4SGabriel Somlo ":%" __PHYS_ADDR_PREFIX "u" 79475f3e8e4SGabriel Somlo 79514d1824cSMarc-André Lureau #define PH_ADDR_PR_4_FMT PH_ADDR_PR_3_FMT \ 79614d1824cSMarc-André Lureau ":%" __PHYS_ADDR_PREFIX "u" 79714d1824cSMarc-André Lureau 79875f3e8e4SGabriel Somlo static int fw_cfg_cmdline_set(const char *arg, const struct kernel_param *kp) 79975f3e8e4SGabriel Somlo { 80014d1824cSMarc-André Lureau struct resource res[4] = {}; 80175f3e8e4SGabriel Somlo char *str; 80275f3e8e4SGabriel Somlo phys_addr_t base; 80314d1824cSMarc-André Lureau resource_size_t size, ctrl_off, data_off, dma_off; 80475f3e8e4SGabriel Somlo int processed, consumed = 0; 80575f3e8e4SGabriel Somlo 80675f3e8e4SGabriel Somlo /* only one fw_cfg device can exist system-wide, so if one 80775f3e8e4SGabriel Somlo * was processed on the command line already, we might as 80875f3e8e4SGabriel Somlo * well stop here. 80975f3e8e4SGabriel Somlo */ 81075f3e8e4SGabriel Somlo if (fw_cfg_cmdline_dev) { 81175f3e8e4SGabriel Somlo /* avoid leaking previously registered device */ 81275f3e8e4SGabriel Somlo platform_device_unregister(fw_cfg_cmdline_dev); 81375f3e8e4SGabriel Somlo return -EINVAL; 81475f3e8e4SGabriel Somlo } 81575f3e8e4SGabriel Somlo 81675f3e8e4SGabriel Somlo /* consume "<size>" portion of command line argument */ 81775f3e8e4SGabriel Somlo size = memparse(arg, &str); 81875f3e8e4SGabriel Somlo 81914d1824cSMarc-André Lureau /* get "@<base>[:<ctrl_off>:<data_off>[:<dma_off>]]" chunks */ 82075f3e8e4SGabriel Somlo processed = sscanf(str, PH_ADDR_SCAN_FMT, 82175f3e8e4SGabriel Somlo &base, &consumed, 82214d1824cSMarc-André Lureau &ctrl_off, &data_off, &consumed, 82314d1824cSMarc-André Lureau &dma_off, &consumed); 82475f3e8e4SGabriel Somlo 82514d1824cSMarc-André Lureau /* sscanf() must process precisely 1, 3 or 4 chunks: 82675f3e8e4SGabriel Somlo * <base> is mandatory, optionally followed by <ctrl_off> 82714d1824cSMarc-André Lureau * and <data_off>, and <dma_off>; 82875f3e8e4SGabriel Somlo * there must be no extra characters after the last chunk, 82975f3e8e4SGabriel Somlo * so str[consumed] must be '\0'. 83075f3e8e4SGabriel Somlo */ 83175f3e8e4SGabriel Somlo if (str[consumed] || 83214d1824cSMarc-André Lureau (processed != 1 && processed != 3 && processed != 4)) 83375f3e8e4SGabriel Somlo return -EINVAL; 83475f3e8e4SGabriel Somlo 83575f3e8e4SGabriel Somlo res[0].start = base; 83675f3e8e4SGabriel Somlo res[0].end = base + size - 1; 83775f3e8e4SGabriel Somlo res[0].flags = !strcmp(kp->name, "mmio") ? IORESOURCE_MEM : 83875f3e8e4SGabriel Somlo IORESOURCE_IO; 83975f3e8e4SGabriel Somlo 84075f3e8e4SGabriel Somlo /* insert register offsets, if provided */ 84175f3e8e4SGabriel Somlo if (processed > 1) { 84275f3e8e4SGabriel Somlo res[1].name = "ctrl"; 84375f3e8e4SGabriel Somlo res[1].start = ctrl_off; 84475f3e8e4SGabriel Somlo res[1].flags = IORESOURCE_REG; 84575f3e8e4SGabriel Somlo res[2].name = "data"; 84675f3e8e4SGabriel Somlo res[2].start = data_off; 84775f3e8e4SGabriel Somlo res[2].flags = IORESOURCE_REG; 84875f3e8e4SGabriel Somlo } 84914d1824cSMarc-André Lureau if (processed > 3) { 85014d1824cSMarc-André Lureau res[3].name = "dma"; 85114d1824cSMarc-André Lureau res[3].start = dma_off; 85214d1824cSMarc-André Lureau res[3].flags = IORESOURCE_REG; 85314d1824cSMarc-André Lureau } 85475f3e8e4SGabriel Somlo 85575f3e8e4SGabriel Somlo /* "processed" happens to nicely match the number of resources 85675f3e8e4SGabriel Somlo * we need to pass in to this platform device. 85775f3e8e4SGabriel Somlo */ 85875f3e8e4SGabriel Somlo fw_cfg_cmdline_dev = platform_device_register_simple("fw_cfg", 85975f3e8e4SGabriel Somlo PLATFORM_DEVID_NONE, res, processed); 86075f3e8e4SGabriel Somlo 8610a9e63aaSVasyl Gomonovych return PTR_ERR_OR_ZERO(fw_cfg_cmdline_dev); 86275f3e8e4SGabriel Somlo } 86375f3e8e4SGabriel Somlo 86475f3e8e4SGabriel Somlo static int fw_cfg_cmdline_get(char *buf, const struct kernel_param *kp) 86575f3e8e4SGabriel Somlo { 86675f3e8e4SGabriel Somlo /* stay silent if device was not configured via the command 86775f3e8e4SGabriel Somlo * line, or if the parameter name (ioport/mmio) doesn't match 86875f3e8e4SGabriel Somlo * the device setting 86975f3e8e4SGabriel Somlo */ 87075f3e8e4SGabriel Somlo if (!fw_cfg_cmdline_dev || 87175f3e8e4SGabriel Somlo (!strcmp(kp->name, "mmio") ^ 87275f3e8e4SGabriel Somlo (fw_cfg_cmdline_dev->resource[0].flags == IORESOURCE_MEM))) 87375f3e8e4SGabriel Somlo return 0; 87475f3e8e4SGabriel Somlo 87575f3e8e4SGabriel Somlo switch (fw_cfg_cmdline_dev->num_resources) { 87675f3e8e4SGabriel Somlo case 1: 87775f3e8e4SGabriel Somlo return snprintf(buf, PAGE_SIZE, PH_ADDR_PR_1_FMT, 87875f3e8e4SGabriel Somlo resource_size(&fw_cfg_cmdline_dev->resource[0]), 87975f3e8e4SGabriel Somlo fw_cfg_cmdline_dev->resource[0].start); 88075f3e8e4SGabriel Somlo case 3: 88175f3e8e4SGabriel Somlo return snprintf(buf, PAGE_SIZE, PH_ADDR_PR_3_FMT, 88275f3e8e4SGabriel Somlo resource_size(&fw_cfg_cmdline_dev->resource[0]), 88375f3e8e4SGabriel Somlo fw_cfg_cmdline_dev->resource[0].start, 88475f3e8e4SGabriel Somlo fw_cfg_cmdline_dev->resource[1].start, 88575f3e8e4SGabriel Somlo fw_cfg_cmdline_dev->resource[2].start); 88614d1824cSMarc-André Lureau case 4: 88714d1824cSMarc-André Lureau return snprintf(buf, PAGE_SIZE, PH_ADDR_PR_4_FMT, 88814d1824cSMarc-André Lureau resource_size(&fw_cfg_cmdline_dev->resource[0]), 88914d1824cSMarc-André Lureau fw_cfg_cmdline_dev->resource[0].start, 89014d1824cSMarc-André Lureau fw_cfg_cmdline_dev->resource[1].start, 89114d1824cSMarc-André Lureau fw_cfg_cmdline_dev->resource[2].start, 89214d1824cSMarc-André Lureau fw_cfg_cmdline_dev->resource[3].start); 89375f3e8e4SGabriel Somlo } 89475f3e8e4SGabriel Somlo 89575f3e8e4SGabriel Somlo /* Should never get here */ 89675f3e8e4SGabriel Somlo WARN(1, "Unexpected number of resources: %d\n", 89775f3e8e4SGabriel Somlo fw_cfg_cmdline_dev->num_resources); 89875f3e8e4SGabriel Somlo return 0; 89975f3e8e4SGabriel Somlo } 90075f3e8e4SGabriel Somlo 90175f3e8e4SGabriel Somlo static const struct kernel_param_ops fw_cfg_cmdline_param_ops = { 90275f3e8e4SGabriel Somlo .set = fw_cfg_cmdline_set, 90375f3e8e4SGabriel Somlo .get = fw_cfg_cmdline_get, 90475f3e8e4SGabriel Somlo }; 90575f3e8e4SGabriel Somlo 90675f3e8e4SGabriel Somlo device_param_cb(ioport, &fw_cfg_cmdline_param_ops, NULL, S_IRUSR); 90775f3e8e4SGabriel Somlo device_param_cb(mmio, &fw_cfg_cmdline_param_ops, NULL, S_IRUSR); 90875f3e8e4SGabriel Somlo 90975f3e8e4SGabriel Somlo #endif /* CONFIG_FW_CFG_SYSFS_CMDLINE */ 91075f3e8e4SGabriel Somlo 91175f3e8e4SGabriel Somlo static int __init fw_cfg_sysfs_init(void) 91275f3e8e4SGabriel Somlo { 913e8aabc64SMichael S. Tsirkin int ret; 914e8aabc64SMichael S. Tsirkin 91575f3e8e4SGabriel Somlo /* create /sys/firmware/qemu_fw_cfg/ top level directory */ 91675f3e8e4SGabriel Somlo fw_cfg_top_ko = kobject_create_and_add("qemu_fw_cfg", firmware_kobj); 91775f3e8e4SGabriel Somlo if (!fw_cfg_top_ko) 91875f3e8e4SGabriel Somlo return -ENOMEM; 91975f3e8e4SGabriel Somlo 920e8aabc64SMichael S. Tsirkin ret = platform_driver_register(&fw_cfg_sysfs_driver); 921e8aabc64SMichael S. Tsirkin if (ret) 922e8aabc64SMichael S. Tsirkin fw_cfg_kobj_cleanup(fw_cfg_top_ko); 923e8aabc64SMichael S. Tsirkin 924e8aabc64SMichael S. Tsirkin return ret; 92575f3e8e4SGabriel Somlo } 92675f3e8e4SGabriel Somlo 92775f3e8e4SGabriel Somlo static void __exit fw_cfg_sysfs_exit(void) 92875f3e8e4SGabriel Somlo { 92975f3e8e4SGabriel Somlo platform_driver_unregister(&fw_cfg_sysfs_driver); 93075f3e8e4SGabriel Somlo 93175f3e8e4SGabriel Somlo #ifdef CONFIG_FW_CFG_SYSFS_CMDLINE 93275f3e8e4SGabriel Somlo platform_device_unregister(fw_cfg_cmdline_dev); 93375f3e8e4SGabriel Somlo #endif 93475f3e8e4SGabriel Somlo 93575f3e8e4SGabriel Somlo /* clean up /sys/firmware/qemu_fw_cfg/ */ 93675f3e8e4SGabriel Somlo fw_cfg_kobj_cleanup(fw_cfg_top_ko); 93775f3e8e4SGabriel Somlo } 93875f3e8e4SGabriel Somlo 93975f3e8e4SGabriel Somlo module_init(fw_cfg_sysfs_init); 94075f3e8e4SGabriel Somlo module_exit(fw_cfg_sysfs_exit); 941