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> 311b7369acSArnd 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 2186b698713SHelge Deller # elif defined(CONFIG_PARISC) /* parisc */ 2196b698713SHelge Deller # define FW_CFG_CTRL_OFF 0x00 2206b698713SHelge Deller # define FW_CFG_DATA_OFF 0x04 22175f3e8e4SGabriel Somlo # elif (defined(CONFIG_PPC_PMAC) || defined(CONFIG_SPARC32)) /* ppc/mac,sun4m */ 22275f3e8e4SGabriel Somlo # define FW_CFG_CTRL_OFF 0x00 22375f3e8e4SGabriel Somlo # define FW_CFG_DATA_OFF 0x02 22475f3e8e4SGabriel Somlo # elif (defined(CONFIG_X86) || defined(CONFIG_SPARC64)) /* x86, sun4u */ 22575f3e8e4SGabriel Somlo # define FW_CFG_CTRL_OFF 0x00 22675f3e8e4SGabriel Somlo # define FW_CFG_DATA_OFF 0x01 22714d1824cSMarc-André Lureau # define FW_CFG_DMA_OFF 0x04 22875f3e8e4SGabriel Somlo # else 22900411b7bSGabriel Somlo # error "QEMU FW_CFG not available on this architecture!" 23075f3e8e4SGabriel Somlo # endif 23175f3e8e4SGabriel Somlo #endif 23275f3e8e4SGabriel Somlo 23375f3e8e4SGabriel Somlo /* initialize fw_cfg device i/o from platform data */ 23475f3e8e4SGabriel Somlo static int fw_cfg_do_platform_probe(struct platform_device *pdev) 23575f3e8e4SGabriel Somlo { 23675f3e8e4SGabriel Somlo char sig[FW_CFG_SIG_SIZE]; 23714d1824cSMarc-André Lureau struct resource *range, *ctrl, *data, *dma; 23875f3e8e4SGabriel Somlo 23975f3e8e4SGabriel Somlo /* acquire i/o range details */ 24075f3e8e4SGabriel Somlo fw_cfg_is_mmio = false; 24175f3e8e4SGabriel Somlo range = platform_get_resource(pdev, IORESOURCE_IO, 0); 24275f3e8e4SGabriel Somlo if (!range) { 24375f3e8e4SGabriel Somlo fw_cfg_is_mmio = true; 24475f3e8e4SGabriel Somlo range = platform_get_resource(pdev, IORESOURCE_MEM, 0); 24575f3e8e4SGabriel Somlo if (!range) 24675f3e8e4SGabriel Somlo return -EINVAL; 24775f3e8e4SGabriel Somlo } 24875f3e8e4SGabriel Somlo fw_cfg_p_base = range->start; 24975f3e8e4SGabriel Somlo fw_cfg_p_size = resource_size(range); 25075f3e8e4SGabriel Somlo 25175f3e8e4SGabriel Somlo if (fw_cfg_is_mmio) { 25275f3e8e4SGabriel Somlo if (!request_mem_region(fw_cfg_p_base, 25375f3e8e4SGabriel Somlo fw_cfg_p_size, "fw_cfg_mem")) 25475f3e8e4SGabriel Somlo return -EBUSY; 25575f3e8e4SGabriel Somlo fw_cfg_dev_base = ioremap(fw_cfg_p_base, fw_cfg_p_size); 25675f3e8e4SGabriel Somlo if (!fw_cfg_dev_base) { 25775f3e8e4SGabriel Somlo release_mem_region(fw_cfg_p_base, fw_cfg_p_size); 25875f3e8e4SGabriel Somlo return -EFAULT; 25975f3e8e4SGabriel Somlo } 26075f3e8e4SGabriel Somlo } else { 26175f3e8e4SGabriel Somlo if (!request_region(fw_cfg_p_base, 26275f3e8e4SGabriel Somlo fw_cfg_p_size, "fw_cfg_io")) 26375f3e8e4SGabriel Somlo return -EBUSY; 26475f3e8e4SGabriel Somlo fw_cfg_dev_base = ioport_map(fw_cfg_p_base, fw_cfg_p_size); 26575f3e8e4SGabriel Somlo if (!fw_cfg_dev_base) { 26675f3e8e4SGabriel Somlo release_region(fw_cfg_p_base, fw_cfg_p_size); 26775f3e8e4SGabriel Somlo return -EFAULT; 26875f3e8e4SGabriel Somlo } 26975f3e8e4SGabriel Somlo } 27075f3e8e4SGabriel Somlo 27175f3e8e4SGabriel Somlo /* were custom register offsets provided (e.g. on the command line)? */ 27275f3e8e4SGabriel Somlo ctrl = platform_get_resource_byname(pdev, IORESOURCE_REG, "ctrl"); 27375f3e8e4SGabriel Somlo data = platform_get_resource_byname(pdev, IORESOURCE_REG, "data"); 27414d1824cSMarc-André Lureau dma = platform_get_resource_byname(pdev, IORESOURCE_REG, "dma"); 27575f3e8e4SGabriel Somlo if (ctrl && data) { 27675f3e8e4SGabriel Somlo fw_cfg_reg_ctrl = fw_cfg_dev_base + ctrl->start; 27775f3e8e4SGabriel Somlo fw_cfg_reg_data = fw_cfg_dev_base + data->start; 27875f3e8e4SGabriel Somlo } else { 27975f3e8e4SGabriel Somlo /* use architecture-specific offsets */ 28075f3e8e4SGabriel Somlo fw_cfg_reg_ctrl = fw_cfg_dev_base + FW_CFG_CTRL_OFF; 28175f3e8e4SGabriel Somlo fw_cfg_reg_data = fw_cfg_dev_base + FW_CFG_DATA_OFF; 28275f3e8e4SGabriel Somlo } 28375f3e8e4SGabriel Somlo 28414d1824cSMarc-André Lureau if (dma) 28514d1824cSMarc-André Lureau fw_cfg_reg_dma = fw_cfg_dev_base + dma->start; 28614d1824cSMarc-André Lureau #ifdef FW_CFG_DMA_OFF 28714d1824cSMarc-André Lureau else 28814d1824cSMarc-André Lureau fw_cfg_reg_dma = fw_cfg_dev_base + FW_CFG_DMA_OFF; 28914d1824cSMarc-André Lureau #endif 29014d1824cSMarc-André Lureau 29175f3e8e4SGabriel Somlo /* verify fw_cfg device signature */ 292b1cc4097SMarc-André Lureau if (fw_cfg_read_blob(FW_CFG_SIGNATURE, sig, 293b1cc4097SMarc-André Lureau 0, FW_CFG_SIG_SIZE) < 0 || 294b1cc4097SMarc-André Lureau memcmp(sig, "QEMU", FW_CFG_SIG_SIZE) != 0) { 29575f3e8e4SGabriel Somlo fw_cfg_io_cleanup(); 29675f3e8e4SGabriel Somlo return -ENODEV; 29775f3e8e4SGabriel Somlo } 29875f3e8e4SGabriel Somlo 29975f3e8e4SGabriel Somlo return 0; 30075f3e8e4SGabriel Somlo } 30175f3e8e4SGabriel Somlo 302fca41af1SNathan Chancellor static ssize_t fw_cfg_showrev(struct kobject *k, struct kobj_attribute *a, 303fca41af1SNathan Chancellor char *buf) 30475f3e8e4SGabriel Somlo { 30575f3e8e4SGabriel Somlo return sprintf(buf, "%u\n", fw_cfg_rev); 30675f3e8e4SGabriel Somlo } 30775f3e8e4SGabriel Somlo 308fca41af1SNathan Chancellor static const struct kobj_attribute fw_cfg_rev_attr = { 30975f3e8e4SGabriel Somlo .attr = { .name = "rev", .mode = S_IRUSR }, 31075f3e8e4SGabriel Somlo .show = fw_cfg_showrev, 31175f3e8e4SGabriel Somlo }; 31275f3e8e4SGabriel Somlo 31375f3e8e4SGabriel Somlo /* fw_cfg_sysfs_entry type */ 31475f3e8e4SGabriel Somlo struct fw_cfg_sysfs_entry { 31575f3e8e4SGabriel Somlo struct kobject kobj; 316d174ea7dSMarc-André Lureau u32 size; 317d174ea7dSMarc-André Lureau u16 select; 318d174ea7dSMarc-André Lureau char name[FW_CFG_MAX_FILE_PATH]; 31975f3e8e4SGabriel Somlo struct list_head list; 32075f3e8e4SGabriel Somlo }; 32175f3e8e4SGabriel Somlo 3222d6d60a3SMarc-André Lureau #ifdef CONFIG_CRASH_CORE 3232d6d60a3SMarc-André Lureau static ssize_t fw_cfg_write_vmcoreinfo(const struct fw_cfg_file *f) 3242d6d60a3SMarc-André Lureau { 3252d6d60a3SMarc-André Lureau static struct fw_cfg_vmcoreinfo *data; 3262d6d60a3SMarc-André Lureau ssize_t ret; 3272d6d60a3SMarc-André Lureau 3282d6d60a3SMarc-André Lureau data = kmalloc(sizeof(struct fw_cfg_vmcoreinfo), GFP_KERNEL); 3292d6d60a3SMarc-André Lureau if (!data) 3302d6d60a3SMarc-André Lureau return -ENOMEM; 3312d6d60a3SMarc-André Lureau 3322d6d60a3SMarc-André Lureau *data = (struct fw_cfg_vmcoreinfo) { 3332d6d60a3SMarc-André Lureau .guest_format = cpu_to_le16(FW_CFG_VMCOREINFO_FORMAT_ELF), 3342d6d60a3SMarc-André Lureau .size = cpu_to_le32(VMCOREINFO_NOTE_SIZE), 3352d6d60a3SMarc-André Lureau .paddr = cpu_to_le64(paddr_vmcoreinfo_note()) 3362d6d60a3SMarc-André Lureau }; 3372d6d60a3SMarc-André Lureau /* spare ourself reading host format support for now since we 3382d6d60a3SMarc-André Lureau * don't know what else to format - host may ignore ours 3392d6d60a3SMarc-André Lureau */ 3402d6d60a3SMarc-André Lureau ret = fw_cfg_write_blob(be16_to_cpu(f->select), data, 3412d6d60a3SMarc-André Lureau 0, sizeof(struct fw_cfg_vmcoreinfo)); 3422d6d60a3SMarc-André Lureau 3432d6d60a3SMarc-André Lureau kfree(data); 3442d6d60a3SMarc-André Lureau return ret; 3452d6d60a3SMarc-André Lureau } 3462d6d60a3SMarc-André Lureau #endif /* CONFIG_CRASH_CORE */ 3472d6d60a3SMarc-André Lureau 34875f3e8e4SGabriel Somlo /* get fw_cfg_sysfs_entry from kobject member */ 34975f3e8e4SGabriel Somlo static inline struct fw_cfg_sysfs_entry *to_entry(struct kobject *kobj) 35075f3e8e4SGabriel Somlo { 35175f3e8e4SGabriel Somlo return container_of(kobj, struct fw_cfg_sysfs_entry, kobj); 35275f3e8e4SGabriel Somlo } 35375f3e8e4SGabriel Somlo 35475f3e8e4SGabriel Somlo /* fw_cfg_sysfs_attribute type */ 35575f3e8e4SGabriel Somlo struct fw_cfg_sysfs_attribute { 35675f3e8e4SGabriel Somlo struct attribute attr; 35775f3e8e4SGabriel Somlo ssize_t (*show)(struct fw_cfg_sysfs_entry *entry, char *buf); 35875f3e8e4SGabriel Somlo }; 35975f3e8e4SGabriel Somlo 36075f3e8e4SGabriel Somlo /* get fw_cfg_sysfs_attribute from attribute member */ 36175f3e8e4SGabriel Somlo static inline struct fw_cfg_sysfs_attribute *to_attr(struct attribute *attr) 36275f3e8e4SGabriel Somlo { 36375f3e8e4SGabriel Somlo return container_of(attr, struct fw_cfg_sysfs_attribute, attr); 36475f3e8e4SGabriel Somlo } 36575f3e8e4SGabriel Somlo 36675f3e8e4SGabriel Somlo /* global cache of fw_cfg_sysfs_entry objects */ 36775f3e8e4SGabriel Somlo static LIST_HEAD(fw_cfg_entry_cache); 36875f3e8e4SGabriel Somlo 36975f3e8e4SGabriel Somlo /* kobjects removed lazily by kernel, mutual exclusion needed */ 37075f3e8e4SGabriel Somlo static DEFINE_SPINLOCK(fw_cfg_cache_lock); 37175f3e8e4SGabriel Somlo 37275f3e8e4SGabriel Somlo static inline void fw_cfg_sysfs_cache_enlist(struct fw_cfg_sysfs_entry *entry) 37375f3e8e4SGabriel Somlo { 37475f3e8e4SGabriel Somlo spin_lock(&fw_cfg_cache_lock); 37575f3e8e4SGabriel Somlo list_add_tail(&entry->list, &fw_cfg_entry_cache); 37675f3e8e4SGabriel Somlo spin_unlock(&fw_cfg_cache_lock); 37775f3e8e4SGabriel Somlo } 37875f3e8e4SGabriel Somlo 37975f3e8e4SGabriel Somlo static inline void fw_cfg_sysfs_cache_delist(struct fw_cfg_sysfs_entry *entry) 38075f3e8e4SGabriel Somlo { 38175f3e8e4SGabriel Somlo spin_lock(&fw_cfg_cache_lock); 38275f3e8e4SGabriel Somlo list_del(&entry->list); 38375f3e8e4SGabriel Somlo spin_unlock(&fw_cfg_cache_lock); 38475f3e8e4SGabriel Somlo } 38575f3e8e4SGabriel Somlo 38675f3e8e4SGabriel Somlo static void fw_cfg_sysfs_cache_cleanup(void) 38775f3e8e4SGabriel Somlo { 38875f3e8e4SGabriel Somlo struct fw_cfg_sysfs_entry *entry, *next; 38975f3e8e4SGabriel Somlo 39075f3e8e4SGabriel Somlo list_for_each_entry_safe(entry, next, &fw_cfg_entry_cache, list) { 391a57ac7acSJohan Hovold fw_cfg_sysfs_cache_delist(entry); 39275f3e8e4SGabriel Somlo kobject_put(&entry->kobj); 39375f3e8e4SGabriel Somlo } 39475f3e8e4SGabriel Somlo } 39575f3e8e4SGabriel Somlo 39675f3e8e4SGabriel Somlo /* default_attrs: per-entry attributes and show methods */ 39775f3e8e4SGabriel Somlo 39875f3e8e4SGabriel Somlo #define FW_CFG_SYSFS_ATTR(_attr) \ 39975f3e8e4SGabriel Somlo struct fw_cfg_sysfs_attribute fw_cfg_sysfs_attr_##_attr = { \ 40075f3e8e4SGabriel Somlo .attr = { .name = __stringify(_attr), .mode = S_IRUSR }, \ 40175f3e8e4SGabriel Somlo .show = fw_cfg_sysfs_show_##_attr, \ 40275f3e8e4SGabriel Somlo } 40375f3e8e4SGabriel Somlo 40475f3e8e4SGabriel Somlo static ssize_t fw_cfg_sysfs_show_size(struct fw_cfg_sysfs_entry *e, char *buf) 40575f3e8e4SGabriel Somlo { 406d174ea7dSMarc-André Lureau return sprintf(buf, "%u\n", e->size); 40775f3e8e4SGabriel Somlo } 40875f3e8e4SGabriel Somlo 40975f3e8e4SGabriel Somlo static ssize_t fw_cfg_sysfs_show_key(struct fw_cfg_sysfs_entry *e, char *buf) 41075f3e8e4SGabriel Somlo { 411d174ea7dSMarc-André Lureau return sprintf(buf, "%u\n", e->select); 41275f3e8e4SGabriel Somlo } 41375f3e8e4SGabriel Somlo 41475f3e8e4SGabriel Somlo static ssize_t fw_cfg_sysfs_show_name(struct fw_cfg_sysfs_entry *e, char *buf) 41575f3e8e4SGabriel Somlo { 416d174ea7dSMarc-André Lureau return sprintf(buf, "%s\n", e->name); 41775f3e8e4SGabriel Somlo } 41875f3e8e4SGabriel Somlo 41975f3e8e4SGabriel Somlo static FW_CFG_SYSFS_ATTR(size); 42075f3e8e4SGabriel Somlo static FW_CFG_SYSFS_ATTR(key); 42175f3e8e4SGabriel Somlo static FW_CFG_SYSFS_ATTR(name); 42275f3e8e4SGabriel Somlo 42375f3e8e4SGabriel Somlo static struct attribute *fw_cfg_sysfs_entry_attrs[] = { 42475f3e8e4SGabriel Somlo &fw_cfg_sysfs_attr_size.attr, 42575f3e8e4SGabriel Somlo &fw_cfg_sysfs_attr_key.attr, 42675f3e8e4SGabriel Somlo &fw_cfg_sysfs_attr_name.attr, 42775f3e8e4SGabriel Somlo NULL, 42875f3e8e4SGabriel Somlo }; 42975f3e8e4SGabriel Somlo 43075f3e8e4SGabriel Somlo /* sysfs_ops: find fw_cfg_[entry, attribute] and call appropriate show method */ 43175f3e8e4SGabriel Somlo static ssize_t fw_cfg_sysfs_attr_show(struct kobject *kobj, struct attribute *a, 43275f3e8e4SGabriel Somlo char *buf) 43375f3e8e4SGabriel Somlo { 43475f3e8e4SGabriel Somlo struct fw_cfg_sysfs_entry *entry = to_entry(kobj); 43575f3e8e4SGabriel Somlo struct fw_cfg_sysfs_attribute *attr = to_attr(a); 43675f3e8e4SGabriel Somlo 43775f3e8e4SGabriel Somlo return attr->show(entry, buf); 43875f3e8e4SGabriel Somlo } 43975f3e8e4SGabriel Somlo 44075f3e8e4SGabriel Somlo static const struct sysfs_ops fw_cfg_sysfs_attr_ops = { 44175f3e8e4SGabriel Somlo .show = fw_cfg_sysfs_attr_show, 44275f3e8e4SGabriel Somlo }; 44375f3e8e4SGabriel Somlo 44475f3e8e4SGabriel Somlo /* release: destructor, to be called via kobject_put() */ 44575f3e8e4SGabriel Somlo static void fw_cfg_sysfs_release_entry(struct kobject *kobj) 44675f3e8e4SGabriel Somlo { 44775f3e8e4SGabriel Somlo struct fw_cfg_sysfs_entry *entry = to_entry(kobj); 44875f3e8e4SGabriel Somlo 44975f3e8e4SGabriel Somlo kfree(entry); 45075f3e8e4SGabriel Somlo } 45175f3e8e4SGabriel Somlo 45275f3e8e4SGabriel Somlo /* kobj_type: ties together all properties required to register an entry */ 45375f3e8e4SGabriel Somlo static struct kobj_type fw_cfg_sysfs_entry_ktype = { 45475f3e8e4SGabriel Somlo .default_attrs = fw_cfg_sysfs_entry_attrs, 45575f3e8e4SGabriel Somlo .sysfs_ops = &fw_cfg_sysfs_attr_ops, 45675f3e8e4SGabriel Somlo .release = fw_cfg_sysfs_release_entry, 45775f3e8e4SGabriel Somlo }; 45875f3e8e4SGabriel Somlo 45975f3e8e4SGabriel Somlo /* raw-read method and attribute */ 46075f3e8e4SGabriel Somlo static ssize_t fw_cfg_sysfs_read_raw(struct file *filp, struct kobject *kobj, 46175f3e8e4SGabriel Somlo struct bin_attribute *bin_attr, 46275f3e8e4SGabriel Somlo char *buf, loff_t pos, size_t count) 46375f3e8e4SGabriel Somlo { 46475f3e8e4SGabriel Somlo struct fw_cfg_sysfs_entry *entry = to_entry(kobj); 46575f3e8e4SGabriel Somlo 466d174ea7dSMarc-André Lureau if (pos > entry->size) 46775f3e8e4SGabriel Somlo return -EINVAL; 46875f3e8e4SGabriel Somlo 469d174ea7dSMarc-André Lureau if (count > entry->size - pos) 470d174ea7dSMarc-André Lureau count = entry->size - pos; 47175f3e8e4SGabriel Somlo 472b1cc4097SMarc-André Lureau return fw_cfg_read_blob(entry->select, buf, pos, count); 47375f3e8e4SGabriel Somlo } 47475f3e8e4SGabriel Somlo 47575f3e8e4SGabriel Somlo static struct bin_attribute fw_cfg_sysfs_attr_raw = { 47675f3e8e4SGabriel Somlo .attr = { .name = "raw", .mode = S_IRUSR }, 47775f3e8e4SGabriel Somlo .read = fw_cfg_sysfs_read_raw, 47875f3e8e4SGabriel Somlo }; 47975f3e8e4SGabriel Somlo 480246c46ebSGabriel Somlo /* 481246c46ebSGabriel Somlo * Create a kset subdirectory matching each '/' delimited dirname token 482246c46ebSGabriel Somlo * in 'name', starting with sysfs kset/folder 'dir'; At the end, create 483246c46ebSGabriel Somlo * a symlink directed at the given 'target'. 484246c46ebSGabriel Somlo * NOTE: We do this on a best-effort basis, since 'name' is not guaranteed 485246c46ebSGabriel Somlo * to be a well-behaved path name. Whenever a symlink vs. kset directory 486246c46ebSGabriel Somlo * name collision occurs, the kernel will issue big scary warnings while 487246c46ebSGabriel Somlo * refusing to add the offending link or directory. We follow up with our 488246c46ebSGabriel Somlo * own, slightly less scary error messages explaining the situation :) 489246c46ebSGabriel Somlo */ 490246c46ebSGabriel Somlo static int fw_cfg_build_symlink(struct kset *dir, 491246c46ebSGabriel Somlo struct kobject *target, const char *name) 492246c46ebSGabriel Somlo { 493246c46ebSGabriel Somlo int ret; 494246c46ebSGabriel Somlo struct kset *subdir; 495246c46ebSGabriel Somlo struct kobject *ko; 496246c46ebSGabriel Somlo char *name_copy, *p, *tok; 497246c46ebSGabriel Somlo 498246c46ebSGabriel Somlo if (!dir || !target || !name || !*name) 499246c46ebSGabriel Somlo return -EINVAL; 500246c46ebSGabriel Somlo 501246c46ebSGabriel Somlo /* clone a copy of name for parsing */ 502246c46ebSGabriel Somlo name_copy = p = kstrdup(name, GFP_KERNEL); 503246c46ebSGabriel Somlo if (!name_copy) 504246c46ebSGabriel Somlo return -ENOMEM; 505246c46ebSGabriel Somlo 506246c46ebSGabriel Somlo /* create folders for each dirname token, then symlink for basename */ 507246c46ebSGabriel Somlo while ((tok = strsep(&p, "/")) && *tok) { 508246c46ebSGabriel Somlo 509246c46ebSGabriel Somlo /* last (basename) token? If so, add symlink here */ 510246c46ebSGabriel Somlo if (!p || !*p) { 511246c46ebSGabriel Somlo ret = sysfs_create_link(&dir->kobj, target, tok); 512246c46ebSGabriel Somlo break; 513246c46ebSGabriel Somlo } 514246c46ebSGabriel Somlo 515246c46ebSGabriel Somlo /* does the current dir contain an item named after tok ? */ 516246c46ebSGabriel Somlo ko = kset_find_obj(dir, tok); 517246c46ebSGabriel Somlo if (ko) { 518246c46ebSGabriel Somlo /* drop reference added by kset_find_obj */ 519246c46ebSGabriel Somlo kobject_put(ko); 520246c46ebSGabriel Somlo 521246c46ebSGabriel Somlo /* ko MUST be a kset - we're about to use it as one ! */ 522246c46ebSGabriel Somlo if (ko->ktype != dir->kobj.ktype) { 523246c46ebSGabriel Somlo ret = -EINVAL; 524246c46ebSGabriel Somlo break; 525246c46ebSGabriel Somlo } 526246c46ebSGabriel Somlo 527246c46ebSGabriel Somlo /* descend into already existing subdirectory */ 528246c46ebSGabriel Somlo dir = to_kset(ko); 529246c46ebSGabriel Somlo } else { 530246c46ebSGabriel Somlo /* create new subdirectory kset */ 531246c46ebSGabriel Somlo subdir = kzalloc(sizeof(struct kset), GFP_KERNEL); 532246c46ebSGabriel Somlo if (!subdir) { 533246c46ebSGabriel Somlo ret = -ENOMEM; 534246c46ebSGabriel Somlo break; 535246c46ebSGabriel Somlo } 536246c46ebSGabriel Somlo subdir->kobj.kset = dir; 537246c46ebSGabriel Somlo subdir->kobj.ktype = dir->kobj.ktype; 538246c46ebSGabriel Somlo ret = kobject_set_name(&subdir->kobj, "%s", tok); 539246c46ebSGabriel Somlo if (ret) { 540246c46ebSGabriel Somlo kfree(subdir); 541246c46ebSGabriel Somlo break; 542246c46ebSGabriel Somlo } 543246c46ebSGabriel Somlo ret = kset_register(subdir); 544246c46ebSGabriel Somlo if (ret) { 545246c46ebSGabriel Somlo kfree(subdir); 546246c46ebSGabriel Somlo break; 547246c46ebSGabriel Somlo } 548246c46ebSGabriel Somlo 549246c46ebSGabriel Somlo /* descend into newly created subdirectory */ 550246c46ebSGabriel Somlo dir = subdir; 551246c46ebSGabriel Somlo } 552246c46ebSGabriel Somlo } 553246c46ebSGabriel Somlo 554246c46ebSGabriel Somlo /* we're done with cloned copy of name */ 555246c46ebSGabriel Somlo kfree(name_copy); 556246c46ebSGabriel Somlo return ret; 557246c46ebSGabriel Somlo } 558246c46ebSGabriel Somlo 559246c46ebSGabriel Somlo /* recursively unregister fw_cfg/by_name/ kset directory tree */ 560246c46ebSGabriel Somlo static void fw_cfg_kset_unregister_recursive(struct kset *kset) 561246c46ebSGabriel Somlo { 562246c46ebSGabriel Somlo struct kobject *k, *next; 563246c46ebSGabriel Somlo 564246c46ebSGabriel Somlo list_for_each_entry_safe(k, next, &kset->list, entry) 565246c46ebSGabriel Somlo /* all set members are ksets too, but check just in case... */ 566246c46ebSGabriel Somlo if (k->ktype == kset->kobj.ktype) 567246c46ebSGabriel Somlo fw_cfg_kset_unregister_recursive(to_kset(k)); 568246c46ebSGabriel Somlo 569246c46ebSGabriel Somlo /* symlinks are cleanly and automatically removed with the directory */ 570246c46ebSGabriel Somlo kset_unregister(kset); 571246c46ebSGabriel Somlo } 572246c46ebSGabriel Somlo 573246c46ebSGabriel Somlo /* kobjects & kset representing top-level, by_key, and by_name folders */ 57475f3e8e4SGabriel Somlo static struct kobject *fw_cfg_top_ko; 57575f3e8e4SGabriel Somlo static struct kobject *fw_cfg_sel_ko; 576246c46ebSGabriel Somlo static struct kset *fw_cfg_fname_kset; 57775f3e8e4SGabriel Somlo 57875f3e8e4SGabriel Somlo /* register an individual fw_cfg file */ 57975f3e8e4SGabriel Somlo static int fw_cfg_register_file(const struct fw_cfg_file *f) 58075f3e8e4SGabriel Somlo { 58175f3e8e4SGabriel Somlo int err; 58275f3e8e4SGabriel Somlo struct fw_cfg_sysfs_entry *entry; 58375f3e8e4SGabriel Somlo 5842d6d60a3SMarc-André Lureau #ifdef CONFIG_CRASH_CORE 5852d6d60a3SMarc-André Lureau if (fw_cfg_dma_enabled() && 5862d6d60a3SMarc-André Lureau strcmp(f->name, FW_CFG_VMCOREINFO_FILENAME) == 0 && 5872d6d60a3SMarc-André Lureau !is_kdump_kernel()) { 5882d6d60a3SMarc-André Lureau if (fw_cfg_write_vmcoreinfo(f) < 0) 5892d6d60a3SMarc-André Lureau pr_warn("fw_cfg: failed to write vmcoreinfo"); 5902d6d60a3SMarc-André Lureau } 5912d6d60a3SMarc-André Lureau #endif 5922d6d60a3SMarc-André Lureau 59375f3e8e4SGabriel Somlo /* allocate new entry */ 59475f3e8e4SGabriel Somlo entry = kzalloc(sizeof(*entry), GFP_KERNEL); 59575f3e8e4SGabriel Somlo if (!entry) 59675f3e8e4SGabriel Somlo return -ENOMEM; 59775f3e8e4SGabriel Somlo 59875f3e8e4SGabriel Somlo /* set file entry information */ 599d174ea7dSMarc-André Lureau entry->size = be32_to_cpu(f->size); 600d174ea7dSMarc-André Lureau entry->select = be16_to_cpu(f->select); 601d174ea7dSMarc-André Lureau memcpy(entry->name, f->name, FW_CFG_MAX_FILE_PATH); 60275f3e8e4SGabriel Somlo 60375f3e8e4SGabriel Somlo /* register entry under "/sys/firmware/qemu_fw_cfg/by_key/" */ 60475f3e8e4SGabriel Somlo err = kobject_init_and_add(&entry->kobj, &fw_cfg_sysfs_entry_ktype, 605d174ea7dSMarc-André Lureau fw_cfg_sel_ko, "%d", entry->select); 606*47a1db8eSJohan Hovold if (err) 607*47a1db8eSJohan Hovold goto err_put_entry; 60875f3e8e4SGabriel Somlo 60975f3e8e4SGabriel Somlo /* add raw binary content access */ 61075f3e8e4SGabriel Somlo err = sysfs_create_bin_file(&entry->kobj, &fw_cfg_sysfs_attr_raw); 61175f3e8e4SGabriel Somlo if (err) 612*47a1db8eSJohan Hovold goto err_del_entry; 61375f3e8e4SGabriel Somlo 614246c46ebSGabriel Somlo /* try adding "/sys/firmware/qemu_fw_cfg/by_name/" symlink */ 615d174ea7dSMarc-André Lureau fw_cfg_build_symlink(fw_cfg_fname_kset, &entry->kobj, entry->name); 616246c46ebSGabriel Somlo 61775f3e8e4SGabriel Somlo /* success, add entry to global cache */ 61875f3e8e4SGabriel Somlo fw_cfg_sysfs_cache_enlist(entry); 61975f3e8e4SGabriel Somlo return 0; 62075f3e8e4SGabriel Somlo 621*47a1db8eSJohan Hovold err_del_entry: 62275f3e8e4SGabriel Somlo kobject_del(&entry->kobj); 623*47a1db8eSJohan Hovold err_put_entry: 624*47a1db8eSJohan Hovold kobject_put(&entry->kobj); 62575f3e8e4SGabriel Somlo return err; 62675f3e8e4SGabriel Somlo } 62775f3e8e4SGabriel Somlo 62875f3e8e4SGabriel Somlo /* iterate over all fw_cfg directory entries, registering each one */ 62975f3e8e4SGabriel Somlo static int fw_cfg_register_dir_entries(void) 63075f3e8e4SGabriel Somlo { 63175f3e8e4SGabriel Somlo int ret = 0; 6323d47a34bSMarc-André Lureau __be32 files_count; 63375f3e8e4SGabriel Somlo u32 count, i; 63475f3e8e4SGabriel Somlo struct fw_cfg_file *dir; 63575f3e8e4SGabriel Somlo size_t dir_size; 63675f3e8e4SGabriel Somlo 637b1cc4097SMarc-André Lureau ret = fw_cfg_read_blob(FW_CFG_FILE_DIR, &files_count, 638b1cc4097SMarc-André Lureau 0, sizeof(files_count)); 639b1cc4097SMarc-André Lureau if (ret < 0) 640b1cc4097SMarc-André Lureau return ret; 641b1cc4097SMarc-André Lureau 6423d47a34bSMarc-André Lureau count = be32_to_cpu(files_count); 64375f3e8e4SGabriel Somlo dir_size = count * sizeof(struct fw_cfg_file); 64475f3e8e4SGabriel Somlo 64575f3e8e4SGabriel Somlo dir = kmalloc(dir_size, GFP_KERNEL); 64675f3e8e4SGabriel Somlo if (!dir) 64775f3e8e4SGabriel Somlo return -ENOMEM; 64875f3e8e4SGabriel Somlo 649b1cc4097SMarc-André Lureau ret = fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, 650b1cc4097SMarc-André Lureau sizeof(files_count), dir_size); 651b1cc4097SMarc-André Lureau if (ret < 0) 652b1cc4097SMarc-André Lureau goto end; 65375f3e8e4SGabriel Somlo 65475f3e8e4SGabriel Somlo for (i = 0; i < count; i++) { 65575f3e8e4SGabriel Somlo ret = fw_cfg_register_file(&dir[i]); 65675f3e8e4SGabriel Somlo if (ret) 65775f3e8e4SGabriel Somlo break; 65875f3e8e4SGabriel Somlo } 65975f3e8e4SGabriel Somlo 660b1cc4097SMarc-André Lureau end: 66175f3e8e4SGabriel Somlo kfree(dir); 66275f3e8e4SGabriel Somlo return ret; 66375f3e8e4SGabriel Somlo } 66475f3e8e4SGabriel Somlo 66575f3e8e4SGabriel Somlo /* unregister top-level or by_key folder */ 66675f3e8e4SGabriel Somlo static inline void fw_cfg_kobj_cleanup(struct kobject *kobj) 66775f3e8e4SGabriel Somlo { 66875f3e8e4SGabriel Somlo kobject_del(kobj); 66975f3e8e4SGabriel Somlo kobject_put(kobj); 67075f3e8e4SGabriel Somlo } 67175f3e8e4SGabriel Somlo 67275f3e8e4SGabriel Somlo static int fw_cfg_sysfs_probe(struct platform_device *pdev) 67375f3e8e4SGabriel Somlo { 67475f3e8e4SGabriel Somlo int err; 675f295c8dbSMarc-André Lureau __le32 rev; 67675f3e8e4SGabriel Somlo 67775f3e8e4SGabriel Somlo /* NOTE: If we supported multiple fw_cfg devices, we'd first create 67875f3e8e4SGabriel Somlo * a subdirectory named after e.g. pdev->id, then hang per-device 679246c46ebSGabriel Somlo * by_key (and by_name) subdirectories underneath it. However, only 68075f3e8e4SGabriel Somlo * one fw_cfg device exist system-wide, so if one was already found 68175f3e8e4SGabriel Somlo * earlier, we might as well stop here. 68275f3e8e4SGabriel Somlo */ 68375f3e8e4SGabriel Somlo if (fw_cfg_sel_ko) 68475f3e8e4SGabriel Somlo return -EBUSY; 68575f3e8e4SGabriel Somlo 686246c46ebSGabriel Somlo /* create by_key and by_name subdirs of /sys/firmware/qemu_fw_cfg/ */ 68775f3e8e4SGabriel Somlo err = -ENOMEM; 68875f3e8e4SGabriel Somlo fw_cfg_sel_ko = kobject_create_and_add("by_key", fw_cfg_top_ko); 68975f3e8e4SGabriel Somlo if (!fw_cfg_sel_ko) 69075f3e8e4SGabriel Somlo goto err_sel; 691246c46ebSGabriel Somlo fw_cfg_fname_kset = kset_create_and_add("by_name", NULL, fw_cfg_top_ko); 692246c46ebSGabriel Somlo if (!fw_cfg_fname_kset) 693246c46ebSGabriel Somlo goto err_name; 69475f3e8e4SGabriel Somlo 69575f3e8e4SGabriel Somlo /* initialize fw_cfg device i/o from platform data */ 69675f3e8e4SGabriel Somlo err = fw_cfg_do_platform_probe(pdev); 69775f3e8e4SGabriel Somlo if (err) 69875f3e8e4SGabriel Somlo goto err_probe; 69975f3e8e4SGabriel Somlo 70075f3e8e4SGabriel Somlo /* get revision number, add matching top-level attribute */ 701b1cc4097SMarc-André Lureau err = fw_cfg_read_blob(FW_CFG_ID, &rev, 0, sizeof(rev)); 702b1cc4097SMarc-André Lureau if (err < 0) 703b1cc4097SMarc-André Lureau goto err_probe; 704b1cc4097SMarc-André Lureau 705f295c8dbSMarc-André Lureau fw_cfg_rev = le32_to_cpu(rev); 70675f3e8e4SGabriel Somlo err = sysfs_create_file(fw_cfg_top_ko, &fw_cfg_rev_attr.attr); 70775f3e8e4SGabriel Somlo if (err) 70875f3e8e4SGabriel Somlo goto err_rev; 70975f3e8e4SGabriel Somlo 71075f3e8e4SGabriel Somlo /* process fw_cfg file directory entry, registering each file */ 71175f3e8e4SGabriel Somlo err = fw_cfg_register_dir_entries(); 71275f3e8e4SGabriel Somlo if (err) 71375f3e8e4SGabriel Somlo goto err_dir; 71475f3e8e4SGabriel Somlo 71575f3e8e4SGabriel Somlo /* success */ 71675f3e8e4SGabriel Somlo pr_debug("fw_cfg: loaded.\n"); 71775f3e8e4SGabriel Somlo return 0; 71875f3e8e4SGabriel Somlo 71975f3e8e4SGabriel Somlo err_dir: 72075f3e8e4SGabriel Somlo fw_cfg_sysfs_cache_cleanup(); 72175f3e8e4SGabriel Somlo sysfs_remove_file(fw_cfg_top_ko, &fw_cfg_rev_attr.attr); 72275f3e8e4SGabriel Somlo err_rev: 72375f3e8e4SGabriel Somlo fw_cfg_io_cleanup(); 72475f3e8e4SGabriel Somlo err_probe: 725246c46ebSGabriel Somlo fw_cfg_kset_unregister_recursive(fw_cfg_fname_kset); 726246c46ebSGabriel Somlo err_name: 72775f3e8e4SGabriel Somlo fw_cfg_kobj_cleanup(fw_cfg_sel_ko); 72875f3e8e4SGabriel Somlo err_sel: 72975f3e8e4SGabriel Somlo return err; 73075f3e8e4SGabriel Somlo } 73175f3e8e4SGabriel Somlo 73275f3e8e4SGabriel Somlo static int fw_cfg_sysfs_remove(struct platform_device *pdev) 73375f3e8e4SGabriel Somlo { 73475f3e8e4SGabriel Somlo pr_debug("fw_cfg: unloading.\n"); 73575f3e8e4SGabriel Somlo fw_cfg_sysfs_cache_cleanup(); 73623f1b8d9SMarc-André Lureau sysfs_remove_file(fw_cfg_top_ko, &fw_cfg_rev_attr.attr); 73723f1b8d9SMarc-André Lureau fw_cfg_io_cleanup(); 738246c46ebSGabriel Somlo fw_cfg_kset_unregister_recursive(fw_cfg_fname_kset); 73975f3e8e4SGabriel Somlo fw_cfg_kobj_cleanup(fw_cfg_sel_ko); 74075f3e8e4SGabriel Somlo return 0; 74175f3e8e4SGabriel Somlo } 74275f3e8e4SGabriel Somlo 74375f3e8e4SGabriel Somlo static const struct of_device_id fw_cfg_sysfs_mmio_match[] = { 74475f3e8e4SGabriel Somlo { .compatible = "qemu,fw-cfg-mmio", }, 74575f3e8e4SGabriel Somlo {}, 74675f3e8e4SGabriel Somlo }; 74775f3e8e4SGabriel Somlo MODULE_DEVICE_TABLE(of, fw_cfg_sysfs_mmio_match); 74875f3e8e4SGabriel Somlo 74975f3e8e4SGabriel Somlo #ifdef CONFIG_ACPI 75075f3e8e4SGabriel Somlo static const struct acpi_device_id fw_cfg_sysfs_acpi_match[] = { 7511f57bc12SMarc-André Lureau { FW_CFG_ACPI_DEVICE_ID, }, 75275f3e8e4SGabriel Somlo {}, 75375f3e8e4SGabriel Somlo }; 75475f3e8e4SGabriel Somlo MODULE_DEVICE_TABLE(acpi, fw_cfg_sysfs_acpi_match); 75575f3e8e4SGabriel Somlo #endif 75675f3e8e4SGabriel Somlo 75775f3e8e4SGabriel Somlo static struct platform_driver fw_cfg_sysfs_driver = { 75875f3e8e4SGabriel Somlo .probe = fw_cfg_sysfs_probe, 75975f3e8e4SGabriel Somlo .remove = fw_cfg_sysfs_remove, 76075f3e8e4SGabriel Somlo .driver = { 76175f3e8e4SGabriel Somlo .name = "fw_cfg", 76275f3e8e4SGabriel Somlo .of_match_table = fw_cfg_sysfs_mmio_match, 76375f3e8e4SGabriel Somlo .acpi_match_table = ACPI_PTR(fw_cfg_sysfs_acpi_match), 76475f3e8e4SGabriel Somlo }, 76575f3e8e4SGabriel Somlo }; 76675f3e8e4SGabriel Somlo 76775f3e8e4SGabriel Somlo #ifdef CONFIG_FW_CFG_SYSFS_CMDLINE 76875f3e8e4SGabriel Somlo 76975f3e8e4SGabriel Somlo static struct platform_device *fw_cfg_cmdline_dev; 77075f3e8e4SGabriel Somlo 77175f3e8e4SGabriel Somlo /* this probably belongs in e.g. include/linux/types.h, 77275f3e8e4SGabriel Somlo * but right now we are the only ones doing it... 77375f3e8e4SGabriel Somlo */ 77475f3e8e4SGabriel Somlo #ifdef CONFIG_PHYS_ADDR_T_64BIT 77575f3e8e4SGabriel Somlo #define __PHYS_ADDR_PREFIX "ll" 77675f3e8e4SGabriel Somlo #else 77775f3e8e4SGabriel Somlo #define __PHYS_ADDR_PREFIX "" 77875f3e8e4SGabriel Somlo #endif 77975f3e8e4SGabriel Somlo 78075f3e8e4SGabriel Somlo /* use special scanf/printf modifier for phys_addr_t, resource_size_t */ 78175f3e8e4SGabriel Somlo #define PH_ADDR_SCAN_FMT "@%" __PHYS_ADDR_PREFIX "i%n" \ 78275f3e8e4SGabriel Somlo ":%" __PHYS_ADDR_PREFIX "i" \ 78314d1824cSMarc-André Lureau ":%" __PHYS_ADDR_PREFIX "i%n" \ 78475f3e8e4SGabriel Somlo ":%" __PHYS_ADDR_PREFIX "i%n" 78575f3e8e4SGabriel Somlo 78675f3e8e4SGabriel Somlo #define PH_ADDR_PR_1_FMT "0x%" __PHYS_ADDR_PREFIX "x@" \ 78775f3e8e4SGabriel Somlo "0x%" __PHYS_ADDR_PREFIX "x" 78875f3e8e4SGabriel Somlo 78975f3e8e4SGabriel Somlo #define PH_ADDR_PR_3_FMT PH_ADDR_PR_1_FMT \ 79075f3e8e4SGabriel Somlo ":%" __PHYS_ADDR_PREFIX "u" \ 79175f3e8e4SGabriel Somlo ":%" __PHYS_ADDR_PREFIX "u" 79275f3e8e4SGabriel Somlo 79314d1824cSMarc-André Lureau #define PH_ADDR_PR_4_FMT PH_ADDR_PR_3_FMT \ 79414d1824cSMarc-André Lureau ":%" __PHYS_ADDR_PREFIX "u" 79514d1824cSMarc-André Lureau 79675f3e8e4SGabriel Somlo static int fw_cfg_cmdline_set(const char *arg, const struct kernel_param *kp) 79775f3e8e4SGabriel Somlo { 79814d1824cSMarc-André Lureau struct resource res[4] = {}; 79975f3e8e4SGabriel Somlo char *str; 80075f3e8e4SGabriel Somlo phys_addr_t base; 80114d1824cSMarc-André Lureau resource_size_t size, ctrl_off, data_off, dma_off; 80275f3e8e4SGabriel Somlo int processed, consumed = 0; 80375f3e8e4SGabriel Somlo 80475f3e8e4SGabriel Somlo /* only one fw_cfg device can exist system-wide, so if one 80575f3e8e4SGabriel Somlo * was processed on the command line already, we might as 80675f3e8e4SGabriel Somlo * well stop here. 80775f3e8e4SGabriel Somlo */ 80875f3e8e4SGabriel Somlo if (fw_cfg_cmdline_dev) { 80975f3e8e4SGabriel Somlo /* avoid leaking previously registered device */ 81075f3e8e4SGabriel Somlo platform_device_unregister(fw_cfg_cmdline_dev); 81175f3e8e4SGabriel Somlo return -EINVAL; 81275f3e8e4SGabriel Somlo } 81375f3e8e4SGabriel Somlo 81475f3e8e4SGabriel Somlo /* consume "<size>" portion of command line argument */ 81575f3e8e4SGabriel Somlo size = memparse(arg, &str); 81675f3e8e4SGabriel Somlo 81714d1824cSMarc-André Lureau /* get "@<base>[:<ctrl_off>:<data_off>[:<dma_off>]]" chunks */ 81875f3e8e4SGabriel Somlo processed = sscanf(str, PH_ADDR_SCAN_FMT, 81975f3e8e4SGabriel Somlo &base, &consumed, 82014d1824cSMarc-André Lureau &ctrl_off, &data_off, &consumed, 82114d1824cSMarc-André Lureau &dma_off, &consumed); 82275f3e8e4SGabriel Somlo 82314d1824cSMarc-André Lureau /* sscanf() must process precisely 1, 3 or 4 chunks: 82475f3e8e4SGabriel Somlo * <base> is mandatory, optionally followed by <ctrl_off> 82514d1824cSMarc-André Lureau * and <data_off>, and <dma_off>; 82675f3e8e4SGabriel Somlo * there must be no extra characters after the last chunk, 82775f3e8e4SGabriel Somlo * so str[consumed] must be '\0'. 82875f3e8e4SGabriel Somlo */ 82975f3e8e4SGabriel Somlo if (str[consumed] || 83014d1824cSMarc-André Lureau (processed != 1 && processed != 3 && processed != 4)) 83175f3e8e4SGabriel Somlo return -EINVAL; 83275f3e8e4SGabriel Somlo 83375f3e8e4SGabriel Somlo res[0].start = base; 83475f3e8e4SGabriel Somlo res[0].end = base + size - 1; 83575f3e8e4SGabriel Somlo res[0].flags = !strcmp(kp->name, "mmio") ? IORESOURCE_MEM : 83675f3e8e4SGabriel Somlo IORESOURCE_IO; 83775f3e8e4SGabriel Somlo 83875f3e8e4SGabriel Somlo /* insert register offsets, if provided */ 83975f3e8e4SGabriel Somlo if (processed > 1) { 84075f3e8e4SGabriel Somlo res[1].name = "ctrl"; 84175f3e8e4SGabriel Somlo res[1].start = ctrl_off; 84275f3e8e4SGabriel Somlo res[1].flags = IORESOURCE_REG; 84375f3e8e4SGabriel Somlo res[2].name = "data"; 84475f3e8e4SGabriel Somlo res[2].start = data_off; 84575f3e8e4SGabriel Somlo res[2].flags = IORESOURCE_REG; 84675f3e8e4SGabriel Somlo } 84714d1824cSMarc-André Lureau if (processed > 3) { 84814d1824cSMarc-André Lureau res[3].name = "dma"; 84914d1824cSMarc-André Lureau res[3].start = dma_off; 85014d1824cSMarc-André Lureau res[3].flags = IORESOURCE_REG; 85114d1824cSMarc-André Lureau } 85275f3e8e4SGabriel Somlo 85375f3e8e4SGabriel Somlo /* "processed" happens to nicely match the number of resources 85475f3e8e4SGabriel Somlo * we need to pass in to this platform device. 85575f3e8e4SGabriel Somlo */ 85675f3e8e4SGabriel Somlo fw_cfg_cmdline_dev = platform_device_register_simple("fw_cfg", 85775f3e8e4SGabriel Somlo PLATFORM_DEVID_NONE, res, processed); 85875f3e8e4SGabriel Somlo 8590a9e63aaSVasyl Gomonovych return PTR_ERR_OR_ZERO(fw_cfg_cmdline_dev); 86075f3e8e4SGabriel Somlo } 86175f3e8e4SGabriel Somlo 86275f3e8e4SGabriel Somlo static int fw_cfg_cmdline_get(char *buf, const struct kernel_param *kp) 86375f3e8e4SGabriel Somlo { 86475f3e8e4SGabriel Somlo /* stay silent if device was not configured via the command 86575f3e8e4SGabriel Somlo * line, or if the parameter name (ioport/mmio) doesn't match 86675f3e8e4SGabriel Somlo * the device setting 86775f3e8e4SGabriel Somlo */ 86875f3e8e4SGabriel Somlo if (!fw_cfg_cmdline_dev || 86975f3e8e4SGabriel Somlo (!strcmp(kp->name, "mmio") ^ 87075f3e8e4SGabriel Somlo (fw_cfg_cmdline_dev->resource[0].flags == IORESOURCE_MEM))) 87175f3e8e4SGabriel Somlo return 0; 87275f3e8e4SGabriel Somlo 87375f3e8e4SGabriel Somlo switch (fw_cfg_cmdline_dev->num_resources) { 87475f3e8e4SGabriel Somlo case 1: 87575f3e8e4SGabriel Somlo return snprintf(buf, PAGE_SIZE, PH_ADDR_PR_1_FMT, 87675f3e8e4SGabriel Somlo resource_size(&fw_cfg_cmdline_dev->resource[0]), 87775f3e8e4SGabriel Somlo fw_cfg_cmdline_dev->resource[0].start); 87875f3e8e4SGabriel Somlo case 3: 87975f3e8e4SGabriel Somlo return snprintf(buf, PAGE_SIZE, PH_ADDR_PR_3_FMT, 88075f3e8e4SGabriel Somlo resource_size(&fw_cfg_cmdline_dev->resource[0]), 88175f3e8e4SGabriel Somlo fw_cfg_cmdline_dev->resource[0].start, 88275f3e8e4SGabriel Somlo fw_cfg_cmdline_dev->resource[1].start, 88375f3e8e4SGabriel Somlo fw_cfg_cmdline_dev->resource[2].start); 88414d1824cSMarc-André Lureau case 4: 88514d1824cSMarc-André Lureau return snprintf(buf, PAGE_SIZE, PH_ADDR_PR_4_FMT, 88614d1824cSMarc-André Lureau resource_size(&fw_cfg_cmdline_dev->resource[0]), 88714d1824cSMarc-André Lureau fw_cfg_cmdline_dev->resource[0].start, 88814d1824cSMarc-André Lureau fw_cfg_cmdline_dev->resource[1].start, 88914d1824cSMarc-André Lureau fw_cfg_cmdline_dev->resource[2].start, 89014d1824cSMarc-André Lureau fw_cfg_cmdline_dev->resource[3].start); 89175f3e8e4SGabriel Somlo } 89275f3e8e4SGabriel Somlo 89375f3e8e4SGabriel Somlo /* Should never get here */ 89475f3e8e4SGabriel Somlo WARN(1, "Unexpected number of resources: %d\n", 89575f3e8e4SGabriel Somlo fw_cfg_cmdline_dev->num_resources); 89675f3e8e4SGabriel Somlo return 0; 89775f3e8e4SGabriel Somlo } 89875f3e8e4SGabriel Somlo 89975f3e8e4SGabriel Somlo static const struct kernel_param_ops fw_cfg_cmdline_param_ops = { 90075f3e8e4SGabriel Somlo .set = fw_cfg_cmdline_set, 90175f3e8e4SGabriel Somlo .get = fw_cfg_cmdline_get, 90275f3e8e4SGabriel Somlo }; 90375f3e8e4SGabriel Somlo 90475f3e8e4SGabriel Somlo device_param_cb(ioport, &fw_cfg_cmdline_param_ops, NULL, S_IRUSR); 90575f3e8e4SGabriel Somlo device_param_cb(mmio, &fw_cfg_cmdline_param_ops, NULL, S_IRUSR); 90675f3e8e4SGabriel Somlo 90775f3e8e4SGabriel Somlo #endif /* CONFIG_FW_CFG_SYSFS_CMDLINE */ 90875f3e8e4SGabriel Somlo 90975f3e8e4SGabriel Somlo static int __init fw_cfg_sysfs_init(void) 91075f3e8e4SGabriel Somlo { 911e8aabc64SMichael S. Tsirkin int ret; 912e8aabc64SMichael S. Tsirkin 91375f3e8e4SGabriel Somlo /* create /sys/firmware/qemu_fw_cfg/ top level directory */ 91475f3e8e4SGabriel Somlo fw_cfg_top_ko = kobject_create_and_add("qemu_fw_cfg", firmware_kobj); 91575f3e8e4SGabriel Somlo if (!fw_cfg_top_ko) 91675f3e8e4SGabriel Somlo return -ENOMEM; 91775f3e8e4SGabriel Somlo 918e8aabc64SMichael S. Tsirkin ret = platform_driver_register(&fw_cfg_sysfs_driver); 919e8aabc64SMichael S. Tsirkin if (ret) 920e8aabc64SMichael S. Tsirkin fw_cfg_kobj_cleanup(fw_cfg_top_ko); 921e8aabc64SMichael S. Tsirkin 922e8aabc64SMichael S. Tsirkin return ret; 92375f3e8e4SGabriel Somlo } 92475f3e8e4SGabriel Somlo 92575f3e8e4SGabriel Somlo static void __exit fw_cfg_sysfs_exit(void) 92675f3e8e4SGabriel Somlo { 92775f3e8e4SGabriel Somlo platform_driver_unregister(&fw_cfg_sysfs_driver); 92875f3e8e4SGabriel Somlo 92975f3e8e4SGabriel Somlo #ifdef CONFIG_FW_CFG_SYSFS_CMDLINE 93075f3e8e4SGabriel Somlo platform_device_unregister(fw_cfg_cmdline_dev); 93175f3e8e4SGabriel Somlo #endif 93275f3e8e4SGabriel Somlo 93375f3e8e4SGabriel Somlo /* clean up /sys/firmware/qemu_fw_cfg/ */ 93475f3e8e4SGabriel Somlo fw_cfg_kobj_cleanup(fw_cfg_top_ko); 93575f3e8e4SGabriel Somlo } 93675f3e8e4SGabriel Somlo 93775f3e8e4SGabriel Somlo module_init(fw_cfg_sysfs_init); 93875f3e8e4SGabriel Somlo module_exit(fw_cfg_sysfs_exit); 939