blob: df028faa2d00270cd5ce520f2ccbef4bfe26bd05 [file] [log] [blame]
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -05001/*
2 * drivers/firmware/qemu_fw_cfg.c
3 *
4 * Copyright 2015 Carnegie Mellon University
5 *
6 * Expose entries from QEMU's firmware configuration (fw_cfg) device in
7 * sysfs (read-only, under "/sys/firmware/qemu_fw_cfg/...").
8 *
9 * The fw_cfg device may be instantiated via either an ACPI node (on x86
10 * and select subsets of aarch64), a Device Tree node (on arm), or using
11 * a kernel module (or command line) parameter with the following syntax:
12 *
Marc-André Lureau14d18242018-02-28 16:06:12 +010013 * [qemu_fw_cfg.]ioport=<size>@<base>[:<ctrl_off>:<data_off>[:<dma_off>]]
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -050014 * or
Marc-André Lureau14d18242018-02-28 16:06:12 +010015 * [qemu_fw_cfg.]mmio=<size>@<base>[:<ctrl_off>:<data_off>[:<dma_off>]]
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -050016 *
17 * where:
18 * <size> := size of ioport or mmio range
19 * <base> := physical base address of ioport or mmio range
20 * <ctrl_off> := (optional) offset of control register
21 * <data_off> := (optional) offset of data register
Marc-André Lureau14d18242018-02-28 16:06:12 +010022 * <dma_off> := (optional) offset of dma register
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -050023 *
24 * e.g.:
Marc-André Lureau14d18242018-02-28 16:06:12 +010025 * qemu_fw_cfg.ioport=12@0x510:0:1:4 (the default on x86)
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -050026 * or
Marc-André Lureau14d18242018-02-28 16:06:12 +010027 * qemu_fw_cfg.mmio=16@0x9020000:8:0:16 (the default on arm)
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -050028 */
29
30#include <linux/module.h>
31#include <linux/platform_device.h>
32#include <linux/acpi.h>
33#include <linux/slab.h>
34#include <linux/io.h>
35#include <linux/ioport.h>
Marc-André Lureau1f57bc12018-02-28 16:06:11 +010036#include <uapi/linux/qemu_fw_cfg.h>
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -050037
38MODULE_AUTHOR("Gabriel L. Somlo <somlo@cmu.edu>");
39MODULE_DESCRIPTION("QEMU fw_cfg sysfs support");
40MODULE_LICENSE("GPL");
41
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -050042/* fw_cfg device i/o register addresses */
43static bool fw_cfg_is_mmio;
44static phys_addr_t fw_cfg_p_base;
45static resource_size_t fw_cfg_p_size;
46static void __iomem *fw_cfg_dev_base;
47static void __iomem *fw_cfg_reg_ctrl;
48static void __iomem *fw_cfg_reg_data;
Marc-André Lureau14d18242018-02-28 16:06:12 +010049static void __iomem *fw_cfg_reg_dma;
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -050050
51/* atomic access to fw_cfg device (potentially slow i/o, so using mutex) */
52static DEFINE_MUTEX(fw_cfg_dev_lock);
53
54/* pick appropriate endianness for selector key */
Marc-André Lureau8d59d5b2018-02-28 16:06:05 +010055static void fw_cfg_sel_endianness(u16 key)
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -050056{
Marc-André Lureau8d59d5b2018-02-28 16:06:05 +010057 if (fw_cfg_is_mmio)
58 iowrite16be(key, fw_cfg_reg_ctrl);
59 else
60 iowrite16(key, fw_cfg_reg_ctrl);
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -050061}
62
63/* read chunk of given fw_cfg blob (caller responsible for sanity-check) */
Marc-André Lureaub1cc4092018-02-28 16:06:10 +010064static ssize_t fw_cfg_read_blob(u16 key,
65 void *buf, loff_t pos, size_t count)
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -050066{
Dan Carpenterd4f6e272016-04-14 12:33:37 +030067 u32 glk = -1U;
Gabriel Somlodef7ac82016-03-08 13:30:50 -050068 acpi_status status;
69
70 /* If we have ACPI, ensure mutual exclusion against any potential
71 * device access by the firmware, e.g. via AML methods:
72 */
73 status = acpi_acquire_global_lock(ACPI_WAIT_FOREVER, &glk);
74 if (ACPI_FAILURE(status) && status != AE_NOT_CONFIGURED) {
75 /* Should never get here */
76 WARN(1, "fw_cfg_read_blob: Failed to lock ACPI!\n");
77 memset(buf, 0, count);
Marc-André Lureaub1cc4092018-02-28 16:06:10 +010078 return -EINVAL;
Gabriel Somlodef7ac82016-03-08 13:30:50 -050079 }
80
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -050081 mutex_lock(&fw_cfg_dev_lock);
Marc-André Lureau8d59d5b2018-02-28 16:06:05 +010082 fw_cfg_sel_endianness(key);
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -050083 while (pos-- > 0)
84 ioread8(fw_cfg_reg_data);
85 ioread8_rep(fw_cfg_reg_data, buf, count);
86 mutex_unlock(&fw_cfg_dev_lock);
Gabriel Somlodef7ac82016-03-08 13:30:50 -050087
88 acpi_release_global_lock(glk);
Marc-André Lureaub1cc4092018-02-28 16:06:10 +010089 return count;
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -050090}
91
92/* clean up fw_cfg device i/o */
93static void fw_cfg_io_cleanup(void)
94{
95 if (fw_cfg_is_mmio) {
96 iounmap(fw_cfg_dev_base);
97 release_mem_region(fw_cfg_p_base, fw_cfg_p_size);
98 } else {
99 ioport_unmap(fw_cfg_dev_base);
100 release_region(fw_cfg_p_base, fw_cfg_p_size);
101 }
102}
103
104/* arch-specific ctrl & data register offsets are not available in ACPI, DT */
Valentin Rothberg9b3ec232016-02-11 12:19:03 +0100105#if !(defined(FW_CFG_CTRL_OFF) && defined(FW_CFG_DATA_OFF))
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500106# if (defined(CONFIG_ARM) || defined(CONFIG_ARM64))
107# define FW_CFG_CTRL_OFF 0x08
108# define FW_CFG_DATA_OFF 0x00
Marc-André Lureau14d18242018-02-28 16:06:12 +0100109# define FW_CFG_DMA_OFF 0x10
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500110# elif (defined(CONFIG_PPC_PMAC) || defined(CONFIG_SPARC32)) /* ppc/mac,sun4m */
111# define FW_CFG_CTRL_OFF 0x00
112# define FW_CFG_DATA_OFF 0x02
113# elif (defined(CONFIG_X86) || defined(CONFIG_SPARC64)) /* x86, sun4u */
114# define FW_CFG_CTRL_OFF 0x00
115# define FW_CFG_DATA_OFF 0x01
Marc-André Lureau14d18242018-02-28 16:06:12 +0100116# define FW_CFG_DMA_OFF 0x04
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500117# else
Gabriel Somlo00411b72016-02-22 16:18:18 -0500118# error "QEMU FW_CFG not available on this architecture!"
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500119# endif
120#endif
121
122/* initialize fw_cfg device i/o from platform data */
123static int fw_cfg_do_platform_probe(struct platform_device *pdev)
124{
125 char sig[FW_CFG_SIG_SIZE];
Marc-André Lureau14d18242018-02-28 16:06:12 +0100126 struct resource *range, *ctrl, *data, *dma;
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500127
128 /* acquire i/o range details */
129 fw_cfg_is_mmio = false;
130 range = platform_get_resource(pdev, IORESOURCE_IO, 0);
131 if (!range) {
132 fw_cfg_is_mmio = true;
133 range = platform_get_resource(pdev, IORESOURCE_MEM, 0);
134 if (!range)
135 return -EINVAL;
136 }
137 fw_cfg_p_base = range->start;
138 fw_cfg_p_size = resource_size(range);
139
140 if (fw_cfg_is_mmio) {
141 if (!request_mem_region(fw_cfg_p_base,
142 fw_cfg_p_size, "fw_cfg_mem"))
143 return -EBUSY;
144 fw_cfg_dev_base = ioremap(fw_cfg_p_base, fw_cfg_p_size);
145 if (!fw_cfg_dev_base) {
146 release_mem_region(fw_cfg_p_base, fw_cfg_p_size);
147 return -EFAULT;
148 }
149 } else {
150 if (!request_region(fw_cfg_p_base,
151 fw_cfg_p_size, "fw_cfg_io"))
152 return -EBUSY;
153 fw_cfg_dev_base = ioport_map(fw_cfg_p_base, fw_cfg_p_size);
154 if (!fw_cfg_dev_base) {
155 release_region(fw_cfg_p_base, fw_cfg_p_size);
156 return -EFAULT;
157 }
158 }
159
160 /* were custom register offsets provided (e.g. on the command line)? */
161 ctrl = platform_get_resource_byname(pdev, IORESOURCE_REG, "ctrl");
162 data = platform_get_resource_byname(pdev, IORESOURCE_REG, "data");
Marc-André Lureau14d18242018-02-28 16:06:12 +0100163 dma = platform_get_resource_byname(pdev, IORESOURCE_REG, "dma");
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500164 if (ctrl && data) {
165 fw_cfg_reg_ctrl = fw_cfg_dev_base + ctrl->start;
166 fw_cfg_reg_data = fw_cfg_dev_base + data->start;
167 } else {
168 /* use architecture-specific offsets */
169 fw_cfg_reg_ctrl = fw_cfg_dev_base + FW_CFG_CTRL_OFF;
170 fw_cfg_reg_data = fw_cfg_dev_base + FW_CFG_DATA_OFF;
171 }
172
Marc-André Lureau14d18242018-02-28 16:06:12 +0100173 if (dma)
174 fw_cfg_reg_dma = fw_cfg_dev_base + dma->start;
175#ifdef FW_CFG_DMA_OFF
176 else
177 fw_cfg_reg_dma = fw_cfg_dev_base + FW_CFG_DMA_OFF;
178#endif
179
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500180 /* verify fw_cfg device signature */
Marc-André Lureaub1cc4092018-02-28 16:06:10 +0100181 if (fw_cfg_read_blob(FW_CFG_SIGNATURE, sig,
182 0, FW_CFG_SIG_SIZE) < 0 ||
183 memcmp(sig, "QEMU", FW_CFG_SIG_SIZE) != 0) {
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500184 fw_cfg_io_cleanup();
185 return -ENODEV;
186 }
187
188 return 0;
189}
190
191/* fw_cfg revision attribute, in /sys/firmware/qemu_fw_cfg top-level dir. */
192static u32 fw_cfg_rev;
193
194static ssize_t fw_cfg_showrev(struct kobject *k, struct attribute *a, char *buf)
195{
196 return sprintf(buf, "%u\n", fw_cfg_rev);
197}
198
199static const struct {
200 struct attribute attr;
201 ssize_t (*show)(struct kobject *k, struct attribute *a, char *buf);
202} fw_cfg_rev_attr = {
203 .attr = { .name = "rev", .mode = S_IRUSR },
204 .show = fw_cfg_showrev,
205};
206
207/* fw_cfg_sysfs_entry type */
208struct fw_cfg_sysfs_entry {
209 struct kobject kobj;
Marc-André Lureaud174ea7d2018-02-28 16:06:06 +0100210 u32 size;
211 u16 select;
212 char name[FW_CFG_MAX_FILE_PATH];
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500213 struct list_head list;
214};
215
216/* get fw_cfg_sysfs_entry from kobject member */
217static inline struct fw_cfg_sysfs_entry *to_entry(struct kobject *kobj)
218{
219 return container_of(kobj, struct fw_cfg_sysfs_entry, kobj);
220}
221
222/* fw_cfg_sysfs_attribute type */
223struct fw_cfg_sysfs_attribute {
224 struct attribute attr;
225 ssize_t (*show)(struct fw_cfg_sysfs_entry *entry, char *buf);
226};
227
228/* get fw_cfg_sysfs_attribute from attribute member */
229static inline struct fw_cfg_sysfs_attribute *to_attr(struct attribute *attr)
230{
231 return container_of(attr, struct fw_cfg_sysfs_attribute, attr);
232}
233
234/* global cache of fw_cfg_sysfs_entry objects */
235static LIST_HEAD(fw_cfg_entry_cache);
236
237/* kobjects removed lazily by kernel, mutual exclusion needed */
238static DEFINE_SPINLOCK(fw_cfg_cache_lock);
239
240static inline void fw_cfg_sysfs_cache_enlist(struct fw_cfg_sysfs_entry *entry)
241{
242 spin_lock(&fw_cfg_cache_lock);
243 list_add_tail(&entry->list, &fw_cfg_entry_cache);
244 spin_unlock(&fw_cfg_cache_lock);
245}
246
247static inline void fw_cfg_sysfs_cache_delist(struct fw_cfg_sysfs_entry *entry)
248{
249 spin_lock(&fw_cfg_cache_lock);
250 list_del(&entry->list);
251 spin_unlock(&fw_cfg_cache_lock);
252}
253
254static void fw_cfg_sysfs_cache_cleanup(void)
255{
256 struct fw_cfg_sysfs_entry *entry, *next;
257
258 list_for_each_entry_safe(entry, next, &fw_cfg_entry_cache, list) {
259 /* will end up invoking fw_cfg_sysfs_cache_delist()
260 * via each object's release() method (i.e. destructor)
261 */
262 kobject_put(&entry->kobj);
263 }
264}
265
266/* default_attrs: per-entry attributes and show methods */
267
268#define FW_CFG_SYSFS_ATTR(_attr) \
269struct fw_cfg_sysfs_attribute fw_cfg_sysfs_attr_##_attr = { \
270 .attr = { .name = __stringify(_attr), .mode = S_IRUSR }, \
271 .show = fw_cfg_sysfs_show_##_attr, \
272}
273
274static ssize_t fw_cfg_sysfs_show_size(struct fw_cfg_sysfs_entry *e, char *buf)
275{
Marc-André Lureaud174ea7d2018-02-28 16:06:06 +0100276 return sprintf(buf, "%u\n", e->size);
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500277}
278
279static ssize_t fw_cfg_sysfs_show_key(struct fw_cfg_sysfs_entry *e, char *buf)
280{
Marc-André Lureaud174ea7d2018-02-28 16:06:06 +0100281 return sprintf(buf, "%u\n", e->select);
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500282}
283
284static ssize_t fw_cfg_sysfs_show_name(struct fw_cfg_sysfs_entry *e, char *buf)
285{
Marc-André Lureaud174ea7d2018-02-28 16:06:06 +0100286 return sprintf(buf, "%s\n", e->name);
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500287}
288
289static FW_CFG_SYSFS_ATTR(size);
290static FW_CFG_SYSFS_ATTR(key);
291static FW_CFG_SYSFS_ATTR(name);
292
293static struct attribute *fw_cfg_sysfs_entry_attrs[] = {
294 &fw_cfg_sysfs_attr_size.attr,
295 &fw_cfg_sysfs_attr_key.attr,
296 &fw_cfg_sysfs_attr_name.attr,
297 NULL,
298};
299
300/* sysfs_ops: find fw_cfg_[entry, attribute] and call appropriate show method */
301static ssize_t fw_cfg_sysfs_attr_show(struct kobject *kobj, struct attribute *a,
302 char *buf)
303{
304 struct fw_cfg_sysfs_entry *entry = to_entry(kobj);
305 struct fw_cfg_sysfs_attribute *attr = to_attr(a);
306
307 return attr->show(entry, buf);
308}
309
310static const struct sysfs_ops fw_cfg_sysfs_attr_ops = {
311 .show = fw_cfg_sysfs_attr_show,
312};
313
314/* release: destructor, to be called via kobject_put() */
315static void fw_cfg_sysfs_release_entry(struct kobject *kobj)
316{
317 struct fw_cfg_sysfs_entry *entry = to_entry(kobj);
318
319 fw_cfg_sysfs_cache_delist(entry);
320 kfree(entry);
321}
322
323/* kobj_type: ties together all properties required to register an entry */
324static struct kobj_type fw_cfg_sysfs_entry_ktype = {
325 .default_attrs = fw_cfg_sysfs_entry_attrs,
326 .sysfs_ops = &fw_cfg_sysfs_attr_ops,
327 .release = fw_cfg_sysfs_release_entry,
328};
329
330/* raw-read method and attribute */
331static ssize_t fw_cfg_sysfs_read_raw(struct file *filp, struct kobject *kobj,
332 struct bin_attribute *bin_attr,
333 char *buf, loff_t pos, size_t count)
334{
335 struct fw_cfg_sysfs_entry *entry = to_entry(kobj);
336
Marc-André Lureaud174ea7d2018-02-28 16:06:06 +0100337 if (pos > entry->size)
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500338 return -EINVAL;
339
Marc-André Lureaud174ea7d2018-02-28 16:06:06 +0100340 if (count > entry->size - pos)
341 count = entry->size - pos;
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500342
Marc-André Lureaub1cc4092018-02-28 16:06:10 +0100343 return fw_cfg_read_blob(entry->select, buf, pos, count);
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500344}
345
346static struct bin_attribute fw_cfg_sysfs_attr_raw = {
347 .attr = { .name = "raw", .mode = S_IRUSR },
348 .read = fw_cfg_sysfs_read_raw,
349};
350
Gabriel Somlo246c46e2016-01-28 09:23:13 -0500351/*
352 * Create a kset subdirectory matching each '/' delimited dirname token
353 * in 'name', starting with sysfs kset/folder 'dir'; At the end, create
354 * a symlink directed at the given 'target'.
355 * NOTE: We do this on a best-effort basis, since 'name' is not guaranteed
356 * to be a well-behaved path name. Whenever a symlink vs. kset directory
357 * name collision occurs, the kernel will issue big scary warnings while
358 * refusing to add the offending link or directory. We follow up with our
359 * own, slightly less scary error messages explaining the situation :)
360 */
361static int fw_cfg_build_symlink(struct kset *dir,
362 struct kobject *target, const char *name)
363{
364 int ret;
365 struct kset *subdir;
366 struct kobject *ko;
367 char *name_copy, *p, *tok;
368
369 if (!dir || !target || !name || !*name)
370 return -EINVAL;
371
372 /* clone a copy of name for parsing */
373 name_copy = p = kstrdup(name, GFP_KERNEL);
374 if (!name_copy)
375 return -ENOMEM;
376
377 /* create folders for each dirname token, then symlink for basename */
378 while ((tok = strsep(&p, "/")) && *tok) {
379
380 /* last (basename) token? If so, add symlink here */
381 if (!p || !*p) {
382 ret = sysfs_create_link(&dir->kobj, target, tok);
383 break;
384 }
385
386 /* does the current dir contain an item named after tok ? */
387 ko = kset_find_obj(dir, tok);
388 if (ko) {
389 /* drop reference added by kset_find_obj */
390 kobject_put(ko);
391
392 /* ko MUST be a kset - we're about to use it as one ! */
393 if (ko->ktype != dir->kobj.ktype) {
394 ret = -EINVAL;
395 break;
396 }
397
398 /* descend into already existing subdirectory */
399 dir = to_kset(ko);
400 } else {
401 /* create new subdirectory kset */
402 subdir = kzalloc(sizeof(struct kset), GFP_KERNEL);
403 if (!subdir) {
404 ret = -ENOMEM;
405 break;
406 }
407 subdir->kobj.kset = dir;
408 subdir->kobj.ktype = dir->kobj.ktype;
409 ret = kobject_set_name(&subdir->kobj, "%s", tok);
410 if (ret) {
411 kfree(subdir);
412 break;
413 }
414 ret = kset_register(subdir);
415 if (ret) {
416 kfree(subdir);
417 break;
418 }
419
420 /* descend into newly created subdirectory */
421 dir = subdir;
422 }
423 }
424
425 /* we're done with cloned copy of name */
426 kfree(name_copy);
427 return ret;
428}
429
430/* recursively unregister fw_cfg/by_name/ kset directory tree */
431static void fw_cfg_kset_unregister_recursive(struct kset *kset)
432{
433 struct kobject *k, *next;
434
435 list_for_each_entry_safe(k, next, &kset->list, entry)
436 /* all set members are ksets too, but check just in case... */
437 if (k->ktype == kset->kobj.ktype)
438 fw_cfg_kset_unregister_recursive(to_kset(k));
439
440 /* symlinks are cleanly and automatically removed with the directory */
441 kset_unregister(kset);
442}
443
444/* kobjects & kset representing top-level, by_key, and by_name folders */
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500445static struct kobject *fw_cfg_top_ko;
446static struct kobject *fw_cfg_sel_ko;
Gabriel Somlo246c46e2016-01-28 09:23:13 -0500447static struct kset *fw_cfg_fname_kset;
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500448
449/* register an individual fw_cfg file */
450static int fw_cfg_register_file(const struct fw_cfg_file *f)
451{
452 int err;
453 struct fw_cfg_sysfs_entry *entry;
454
455 /* allocate new entry */
456 entry = kzalloc(sizeof(*entry), GFP_KERNEL);
457 if (!entry)
458 return -ENOMEM;
459
460 /* set file entry information */
Marc-André Lureaud174ea7d2018-02-28 16:06:06 +0100461 entry->size = be32_to_cpu(f->size);
462 entry->select = be16_to_cpu(f->select);
463 memcpy(entry->name, f->name, FW_CFG_MAX_FILE_PATH);
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500464
465 /* register entry under "/sys/firmware/qemu_fw_cfg/by_key/" */
466 err = kobject_init_and_add(&entry->kobj, &fw_cfg_sysfs_entry_ktype,
Marc-André Lureaud174ea7d2018-02-28 16:06:06 +0100467 fw_cfg_sel_ko, "%d", entry->select);
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500468 if (err)
469 goto err_register;
470
471 /* add raw binary content access */
472 err = sysfs_create_bin_file(&entry->kobj, &fw_cfg_sysfs_attr_raw);
473 if (err)
474 goto err_add_raw;
475
Gabriel Somlo246c46e2016-01-28 09:23:13 -0500476 /* try adding "/sys/firmware/qemu_fw_cfg/by_name/" symlink */
Marc-André Lureaud174ea7d2018-02-28 16:06:06 +0100477 fw_cfg_build_symlink(fw_cfg_fname_kset, &entry->kobj, entry->name);
Gabriel Somlo246c46e2016-01-28 09:23:13 -0500478
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500479 /* success, add entry to global cache */
480 fw_cfg_sysfs_cache_enlist(entry);
481 return 0;
482
483err_add_raw:
484 kobject_del(&entry->kobj);
485err_register:
486 kfree(entry);
487 return err;
488}
489
490/* iterate over all fw_cfg directory entries, registering each one */
491static int fw_cfg_register_dir_entries(void)
492{
493 int ret = 0;
Marc-André Lureau3d47a342018-02-28 16:06:08 +0100494 __be32 files_count;
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500495 u32 count, i;
496 struct fw_cfg_file *dir;
497 size_t dir_size;
498
Marc-André Lureaub1cc4092018-02-28 16:06:10 +0100499 ret = fw_cfg_read_blob(FW_CFG_FILE_DIR, &files_count,
500 0, sizeof(files_count));
501 if (ret < 0)
502 return ret;
503
Marc-André Lureau3d47a342018-02-28 16:06:08 +0100504 count = be32_to_cpu(files_count);
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500505 dir_size = count * sizeof(struct fw_cfg_file);
506
507 dir = kmalloc(dir_size, GFP_KERNEL);
508 if (!dir)
509 return -ENOMEM;
510
Marc-André Lureaub1cc4092018-02-28 16:06:10 +0100511 ret = fw_cfg_read_blob(FW_CFG_FILE_DIR, dir,
512 sizeof(files_count), dir_size);
513 if (ret < 0)
514 goto end;
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500515
516 for (i = 0; i < count; i++) {
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500517 ret = fw_cfg_register_file(&dir[i]);
518 if (ret)
519 break;
520 }
521
Marc-André Lureaub1cc4092018-02-28 16:06:10 +0100522end:
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500523 kfree(dir);
524 return ret;
525}
526
527/* unregister top-level or by_key folder */
528static inline void fw_cfg_kobj_cleanup(struct kobject *kobj)
529{
530 kobject_del(kobj);
531 kobject_put(kobj);
532}
533
534static int fw_cfg_sysfs_probe(struct platform_device *pdev)
535{
536 int err;
Marc-André Lureauf295c8d2018-02-28 16:06:07 +0100537 __le32 rev;
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500538
539 /* NOTE: If we supported multiple fw_cfg devices, we'd first create
540 * a subdirectory named after e.g. pdev->id, then hang per-device
Gabriel Somlo246c46e2016-01-28 09:23:13 -0500541 * by_key (and by_name) subdirectories underneath it. However, only
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500542 * one fw_cfg device exist system-wide, so if one was already found
543 * earlier, we might as well stop here.
544 */
545 if (fw_cfg_sel_ko)
546 return -EBUSY;
547
Gabriel Somlo246c46e2016-01-28 09:23:13 -0500548 /* create by_key and by_name subdirs of /sys/firmware/qemu_fw_cfg/ */
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500549 err = -ENOMEM;
550 fw_cfg_sel_ko = kobject_create_and_add("by_key", fw_cfg_top_ko);
551 if (!fw_cfg_sel_ko)
552 goto err_sel;
Gabriel Somlo246c46e2016-01-28 09:23:13 -0500553 fw_cfg_fname_kset = kset_create_and_add("by_name", NULL, fw_cfg_top_ko);
554 if (!fw_cfg_fname_kset)
555 goto err_name;
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500556
557 /* initialize fw_cfg device i/o from platform data */
558 err = fw_cfg_do_platform_probe(pdev);
559 if (err)
560 goto err_probe;
561
562 /* get revision number, add matching top-level attribute */
Marc-André Lureaub1cc4092018-02-28 16:06:10 +0100563 err = fw_cfg_read_blob(FW_CFG_ID, &rev, 0, sizeof(rev));
564 if (err < 0)
565 goto err_probe;
566
Marc-André Lureauf295c8d2018-02-28 16:06:07 +0100567 fw_cfg_rev = le32_to_cpu(rev);
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500568 err = sysfs_create_file(fw_cfg_top_ko, &fw_cfg_rev_attr.attr);
569 if (err)
570 goto err_rev;
571
572 /* process fw_cfg file directory entry, registering each file */
573 err = fw_cfg_register_dir_entries();
574 if (err)
575 goto err_dir;
576
577 /* success */
578 pr_debug("fw_cfg: loaded.\n");
579 return 0;
580
581err_dir:
582 fw_cfg_sysfs_cache_cleanup();
583 sysfs_remove_file(fw_cfg_top_ko, &fw_cfg_rev_attr.attr);
584err_rev:
585 fw_cfg_io_cleanup();
586err_probe:
Gabriel Somlo246c46e2016-01-28 09:23:13 -0500587 fw_cfg_kset_unregister_recursive(fw_cfg_fname_kset);
588err_name:
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500589 fw_cfg_kobj_cleanup(fw_cfg_sel_ko);
590err_sel:
591 return err;
592}
593
594static int fw_cfg_sysfs_remove(struct platform_device *pdev)
595{
596 pr_debug("fw_cfg: unloading.\n");
597 fw_cfg_sysfs_cache_cleanup();
Marc-André Lureau23f1b8d2017-11-20 10:55:15 +0100598 sysfs_remove_file(fw_cfg_top_ko, &fw_cfg_rev_attr.attr);
599 fw_cfg_io_cleanup();
Gabriel Somlo246c46e2016-01-28 09:23:13 -0500600 fw_cfg_kset_unregister_recursive(fw_cfg_fname_kset);
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500601 fw_cfg_kobj_cleanup(fw_cfg_sel_ko);
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500602 return 0;
603}
604
605static const struct of_device_id fw_cfg_sysfs_mmio_match[] = {
606 { .compatible = "qemu,fw-cfg-mmio", },
607 {},
608};
609MODULE_DEVICE_TABLE(of, fw_cfg_sysfs_mmio_match);
610
611#ifdef CONFIG_ACPI
612static const struct acpi_device_id fw_cfg_sysfs_acpi_match[] = {
Marc-André Lureau1f57bc12018-02-28 16:06:11 +0100613 { FW_CFG_ACPI_DEVICE_ID, },
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500614 {},
615};
616MODULE_DEVICE_TABLE(acpi, fw_cfg_sysfs_acpi_match);
617#endif
618
619static struct platform_driver fw_cfg_sysfs_driver = {
620 .probe = fw_cfg_sysfs_probe,
621 .remove = fw_cfg_sysfs_remove,
622 .driver = {
623 .name = "fw_cfg",
624 .of_match_table = fw_cfg_sysfs_mmio_match,
625 .acpi_match_table = ACPI_PTR(fw_cfg_sysfs_acpi_match),
626 },
627};
628
629#ifdef CONFIG_FW_CFG_SYSFS_CMDLINE
630
631static struct platform_device *fw_cfg_cmdline_dev;
632
633/* this probably belongs in e.g. include/linux/types.h,
634 * but right now we are the only ones doing it...
635 */
636#ifdef CONFIG_PHYS_ADDR_T_64BIT
637#define __PHYS_ADDR_PREFIX "ll"
638#else
639#define __PHYS_ADDR_PREFIX ""
640#endif
641
642/* use special scanf/printf modifier for phys_addr_t, resource_size_t */
643#define PH_ADDR_SCAN_FMT "@%" __PHYS_ADDR_PREFIX "i%n" \
644 ":%" __PHYS_ADDR_PREFIX "i" \
Marc-André Lureau14d18242018-02-28 16:06:12 +0100645 ":%" __PHYS_ADDR_PREFIX "i%n" \
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500646 ":%" __PHYS_ADDR_PREFIX "i%n"
647
648#define PH_ADDR_PR_1_FMT "0x%" __PHYS_ADDR_PREFIX "x@" \
649 "0x%" __PHYS_ADDR_PREFIX "x"
650
651#define PH_ADDR_PR_3_FMT PH_ADDR_PR_1_FMT \
652 ":%" __PHYS_ADDR_PREFIX "u" \
653 ":%" __PHYS_ADDR_PREFIX "u"
654
Marc-André Lureau14d18242018-02-28 16:06:12 +0100655#define PH_ADDR_PR_4_FMT PH_ADDR_PR_3_FMT \
656 ":%" __PHYS_ADDR_PREFIX "u"
657
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500658static int fw_cfg_cmdline_set(const char *arg, const struct kernel_param *kp)
659{
Marc-André Lureau14d18242018-02-28 16:06:12 +0100660 struct resource res[4] = {};
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500661 char *str;
662 phys_addr_t base;
Marc-André Lureau14d18242018-02-28 16:06:12 +0100663 resource_size_t size, ctrl_off, data_off, dma_off;
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500664 int processed, consumed = 0;
665
666 /* only one fw_cfg device can exist system-wide, so if one
667 * was processed on the command line already, we might as
668 * well stop here.
669 */
670 if (fw_cfg_cmdline_dev) {
671 /* avoid leaking previously registered device */
672 platform_device_unregister(fw_cfg_cmdline_dev);
673 return -EINVAL;
674 }
675
676 /* consume "<size>" portion of command line argument */
677 size = memparse(arg, &str);
678
Marc-André Lureau14d18242018-02-28 16:06:12 +0100679 /* get "@<base>[:<ctrl_off>:<data_off>[:<dma_off>]]" chunks */
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500680 processed = sscanf(str, PH_ADDR_SCAN_FMT,
681 &base, &consumed,
Marc-André Lureau14d18242018-02-28 16:06:12 +0100682 &ctrl_off, &data_off, &consumed,
683 &dma_off, &consumed);
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500684
Marc-André Lureau14d18242018-02-28 16:06:12 +0100685 /* sscanf() must process precisely 1, 3 or 4 chunks:
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500686 * <base> is mandatory, optionally followed by <ctrl_off>
Marc-André Lureau14d18242018-02-28 16:06:12 +0100687 * and <data_off>, and <dma_off>;
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500688 * there must be no extra characters after the last chunk,
689 * so str[consumed] must be '\0'.
690 */
691 if (str[consumed] ||
Marc-André Lureau14d18242018-02-28 16:06:12 +0100692 (processed != 1 && processed != 3 && processed != 4))
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500693 return -EINVAL;
694
695 res[0].start = base;
696 res[0].end = base + size - 1;
697 res[0].flags = !strcmp(kp->name, "mmio") ? IORESOURCE_MEM :
698 IORESOURCE_IO;
699
700 /* insert register offsets, if provided */
701 if (processed > 1) {
702 res[1].name = "ctrl";
703 res[1].start = ctrl_off;
704 res[1].flags = IORESOURCE_REG;
705 res[2].name = "data";
706 res[2].start = data_off;
707 res[2].flags = IORESOURCE_REG;
708 }
Marc-André Lureau14d18242018-02-28 16:06:12 +0100709 if (processed > 3) {
710 res[3].name = "dma";
711 res[3].start = dma_off;
712 res[3].flags = IORESOURCE_REG;
713 }
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500714
715 /* "processed" happens to nicely match the number of resources
716 * we need to pass in to this platform device.
717 */
718 fw_cfg_cmdline_dev = platform_device_register_simple("fw_cfg",
719 PLATFORM_DEVID_NONE, res, processed);
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500720
Vasyl Gomonovych0a9e63a2017-11-28 22:40:27 +0100721 return PTR_ERR_OR_ZERO(fw_cfg_cmdline_dev);
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500722}
723
724static int fw_cfg_cmdline_get(char *buf, const struct kernel_param *kp)
725{
726 /* stay silent if device was not configured via the command
727 * line, or if the parameter name (ioport/mmio) doesn't match
728 * the device setting
729 */
730 if (!fw_cfg_cmdline_dev ||
731 (!strcmp(kp->name, "mmio") ^
732 (fw_cfg_cmdline_dev->resource[0].flags == IORESOURCE_MEM)))
733 return 0;
734
735 switch (fw_cfg_cmdline_dev->num_resources) {
736 case 1:
737 return snprintf(buf, PAGE_SIZE, PH_ADDR_PR_1_FMT,
738 resource_size(&fw_cfg_cmdline_dev->resource[0]),
739 fw_cfg_cmdline_dev->resource[0].start);
740 case 3:
741 return snprintf(buf, PAGE_SIZE, PH_ADDR_PR_3_FMT,
742 resource_size(&fw_cfg_cmdline_dev->resource[0]),
743 fw_cfg_cmdline_dev->resource[0].start,
744 fw_cfg_cmdline_dev->resource[1].start,
745 fw_cfg_cmdline_dev->resource[2].start);
Marc-André Lureau14d18242018-02-28 16:06:12 +0100746 case 4:
747 return snprintf(buf, PAGE_SIZE, PH_ADDR_PR_4_FMT,
748 resource_size(&fw_cfg_cmdline_dev->resource[0]),
749 fw_cfg_cmdline_dev->resource[0].start,
750 fw_cfg_cmdline_dev->resource[1].start,
751 fw_cfg_cmdline_dev->resource[2].start,
752 fw_cfg_cmdline_dev->resource[3].start);
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500753 }
754
755 /* Should never get here */
756 WARN(1, "Unexpected number of resources: %d\n",
757 fw_cfg_cmdline_dev->num_resources);
758 return 0;
759}
760
761static const struct kernel_param_ops fw_cfg_cmdline_param_ops = {
762 .set = fw_cfg_cmdline_set,
763 .get = fw_cfg_cmdline_get,
764};
765
766device_param_cb(ioport, &fw_cfg_cmdline_param_ops, NULL, S_IRUSR);
767device_param_cb(mmio, &fw_cfg_cmdline_param_ops, NULL, S_IRUSR);
768
769#endif /* CONFIG_FW_CFG_SYSFS_CMDLINE */
770
771static int __init fw_cfg_sysfs_init(void)
772{
Michael S. Tsirkine8aabc62016-04-03 15:22:08 +0300773 int ret;
774
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500775 /* create /sys/firmware/qemu_fw_cfg/ top level directory */
776 fw_cfg_top_ko = kobject_create_and_add("qemu_fw_cfg", firmware_kobj);
777 if (!fw_cfg_top_ko)
778 return -ENOMEM;
779
Michael S. Tsirkine8aabc62016-04-03 15:22:08 +0300780 ret = platform_driver_register(&fw_cfg_sysfs_driver);
781 if (ret)
782 fw_cfg_kobj_cleanup(fw_cfg_top_ko);
783
784 return ret;
Gabriel Somlo75f3e8e2016-01-28 09:23:11 -0500785}
786
787static void __exit fw_cfg_sysfs_exit(void)
788{
789 platform_driver_unregister(&fw_cfg_sysfs_driver);
790
791#ifdef CONFIG_FW_CFG_SYSFS_CMDLINE
792 platform_device_unregister(fw_cfg_cmdline_dev);
793#endif
794
795 /* clean up /sys/firmware/qemu_fw_cfg/ */
796 fw_cfg_kobj_cleanup(fw_cfg_top_ko);
797}
798
799module_init(fw_cfg_sysfs_init);
800module_exit(fw_cfg_sysfs_exit);