xref: /qemu/hw/core/sysbus-fdt.c (revision 15606965400b8f3038d6e85cfe5956d5a6ac33a1)
1 /*
2  * ARM Platform Bus device tree generation helpers
3  *
4  * Copyright (c) 2014 Linaro Limited
5  *
6  * Authors:
7  *  Alex Graf <agraf@suse.de>
8  *  Eric Auger <eric.auger@linaro.org>
9  *
10  * This program is free software; you can redistribute it and/or modify it
11  * under the terms and conditions of the GNU General Public License,
12  * version 2 or later, as published by the Free Software Foundation.
13  *
14  * This program is distributed in the hope it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
17  * more details.
18  *
19  * You should have received a copy of the GNU General Public License along with
20  * this program.  If not, see <http://www.gnu.org/licenses/>.
21  *
22  */
23 
24 #include "qemu/osdep.h"
25 #include "qapi/error.h"
26 #include <libfdt.h>
27 #ifdef CONFIG_LINUX
28 #include <linux/vfio.h>
29 #endif
30 #include "hw/core/sysbus-fdt.h"
31 #include "qemu/error-report.h"
32 #include "system/device_tree.h"
33 #include "system/tpm.h"
34 #include "hw/platform-bus.h"
35 #include "hw/vfio/vfio-platform.h"
36 #include "hw/vfio/vfio-calxeda-xgmac.h"
37 #include "hw/vfio/vfio-amd-xgbe.h"
38 #include "hw/display/ramfb.h"
39 #include "hw/uefi/var-service-api.h"
40 #include "hw/arm/fdt.h"
41 
42 /*
43  * internal struct that contains the information to create dynamic
44  * sysbus device node
45  */
46 typedef struct PlatformBusFDTData {
47     void *fdt; /* device tree handle */
48     int irq_start; /* index of the first IRQ usable by platform bus devices */
49     const char *pbus_node_name; /* name of the platform bus node */
50     PlatformBusDevice *pbus;
51 } PlatformBusFDTData;
52 
53 /* struct that allows to match a device and create its FDT node */
54 typedef struct BindingEntry {
55     const char *typename;
56     const char *compat;
57     int  (*add_fn)(SysBusDevice *sbdev, void *opaque);
58     bool (*match_fn)(SysBusDevice *sbdev, const struct BindingEntry *combo);
59 } BindingEntry;
60 
61 /* helpers */
62 
63 typedef struct HostProperty {
64     const char *name;
65     bool optional;
66 } HostProperty;
67 
68 #ifdef CONFIG_LINUX
69 
70 /**
71  * copy_properties_from_host
72  *
73  * copies properties listed in an array from host device tree to
74  * guest device tree. If a non optional property is not found, the
75  * function asserts. An optional property is ignored if not found
76  * in the host device tree.
77  * @props: array of HostProperty to copy
78  * @nb_props: number of properties in the array
79  * @host_dt: host device tree blob
80  * @guest_dt: guest device tree blob
81  * @node_path: host dt node path where the property is supposed to be
82               found
83  * @nodename: guest node name the properties should be added to
84  */
85 static void copy_properties_from_host(HostProperty *props, int nb_props,
86                                       void *host_fdt, void *guest_fdt,
87                                       char *node_path, char *nodename)
88 {
89     int i, prop_len;
90     const void *r;
91     Error *err = NULL;
92 
93     for (i = 0; i < nb_props; i++) {
94         r = qemu_fdt_getprop(host_fdt, node_path,
95                              props[i].name,
96                              &prop_len,
97                              &err);
98         if (r) {
99             qemu_fdt_setprop(guest_fdt, nodename,
100                              props[i].name, r, prop_len);
101         } else {
102             if (props[i].optional && prop_len == -FDT_ERR_NOTFOUND) {
103                 /* optional property does not exist */
104                 error_free(err);
105             } else {
106                 error_report_err(err);
107             }
108             if (!props[i].optional) {
109                 /* mandatory property not found: bail out */
110                 exit(1);
111             }
112             err = NULL;
113         }
114     }
115 }
116 
117 /* clock properties whose values are copied/pasted from host */
118 static HostProperty clock_copied_properties[] = {
119     {"compatible", false},
120     {"#clock-cells", false},
121     {"clock-frequency", true},
122     {"clock-output-names", true},
123 };
124 
125 /**
126  * fdt_build_clock_node
127  *
128  * Build a guest clock node, used as a dependency from a passthrough'ed
129  * device. Most information are retrieved from the host clock node.
130  * Also check the host clock is a fixed one.
131  *
132  * @host_fdt: host device tree blob from which info are retrieved
133  * @guest_fdt: guest device tree blob where the clock node is added
134  * @host_phandle: phandle of the clock in host device tree
135  * @guest_phandle: phandle to assign to the guest node
136  */
137 static void fdt_build_clock_node(void *host_fdt, void *guest_fdt,
138                                 uint32_t host_phandle,
139                                 uint32_t guest_phandle)
140 {
141     char *node_path = NULL;
142     char *nodename;
143     const void *r;
144     int ret, node_offset, prop_len, path_len = 16;
145 
146     node_offset = fdt_node_offset_by_phandle(host_fdt, host_phandle);
147     if (node_offset <= 0) {
148         error_report("not able to locate clock handle %d in host device tree",
149                      host_phandle);
150         exit(1);
151     }
152     node_path = g_malloc(path_len);
153     while ((ret = fdt_get_path(host_fdt, node_offset, node_path, path_len))
154             == -FDT_ERR_NOSPACE) {
155         path_len += 16;
156         node_path = g_realloc(node_path, path_len);
157     }
158     if (ret < 0) {
159         error_report("not able to retrieve node path for clock handle %d",
160                      host_phandle);
161         exit(1);
162     }
163 
164     r = qemu_fdt_getprop(host_fdt, node_path, "compatible", &prop_len,
165                          &error_fatal);
166     if (strcmp(r, "fixed-clock")) {
167         error_report("clock handle %d is not a fixed clock", host_phandle);
168         exit(1);
169     }
170 
171     nodename = strrchr(node_path, '/');
172     qemu_fdt_add_subnode(guest_fdt, nodename);
173 
174     copy_properties_from_host(clock_copied_properties,
175                               ARRAY_SIZE(clock_copied_properties),
176                               host_fdt, guest_fdt,
177                               node_path, nodename);
178 
179     qemu_fdt_setprop_cell(guest_fdt, nodename, "phandle", guest_phandle);
180 
181     g_free(node_path);
182 }
183 
184 /**
185  * sysfs_to_dt_name: convert the name found in sysfs into the node name
186  * for instance e0900000.xgmac is converted into xgmac@e0900000
187  * @sysfs_name: directory name in sysfs
188  *
189  * returns the device tree name upon success or NULL in case the sysfs name
190  * does not match the expected format
191  */
192 static char *sysfs_to_dt_name(const char *sysfs_name)
193 {
194     gchar **substrings =  g_strsplit(sysfs_name, ".", 2);
195     char *dt_name = NULL;
196 
197     if (!substrings || !substrings[0] || !substrings[1]) {
198         goto out;
199     }
200     dt_name = g_strdup_printf("%s@%s", substrings[1], substrings[0]);
201 out:
202     g_strfreev(substrings);
203     return dt_name;
204 }
205 
206 /* Device Specific Code */
207 
208 /**
209  * add_calxeda_midway_xgmac_fdt_node
210  *
211  * Generates a simple node with following properties:
212  * compatible string, regs, interrupts, dma-coherent
213  */
214 static int add_calxeda_midway_xgmac_fdt_node(SysBusDevice *sbdev, void *opaque)
215 {
216     PlatformBusFDTData *data = opaque;
217     PlatformBusDevice *pbus = data->pbus;
218     void *fdt = data->fdt;
219     const char *parent_node = data->pbus_node_name;
220     int compat_str_len, i;
221     char *nodename;
222     uint32_t *irq_attr, *reg_attr;
223     uint64_t mmio_base, irq_number;
224     VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev);
225     VFIODevice *vbasedev = &vdev->vbasedev;
226 
227     mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, 0);
228     nodename = g_strdup_printf("%s/%s@%" PRIx64, parent_node,
229                                vbasedev->name, mmio_base);
230     qemu_fdt_add_subnode(fdt, nodename);
231 
232     compat_str_len = strlen(vdev->compat) + 1;
233     qemu_fdt_setprop(fdt, nodename, "compatible",
234                           vdev->compat, compat_str_len);
235 
236     qemu_fdt_setprop(fdt, nodename, "dma-coherent", "", 0);
237 
238     reg_attr = g_new(uint32_t, vbasedev->num_regions * 2);
239     for (i = 0; i < vbasedev->num_regions; i++) {
240         mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, i);
241         reg_attr[2 * i] = cpu_to_be32(mmio_base);
242         reg_attr[2 * i + 1] = cpu_to_be32(
243                                 memory_region_size(vdev->regions[i]->mem));
244     }
245     qemu_fdt_setprop(fdt, nodename, "reg", reg_attr,
246                      vbasedev->num_regions * 2 * sizeof(uint32_t));
247 
248     irq_attr = g_new(uint32_t, vbasedev->num_irqs * 3);
249     for (i = 0; i < vbasedev->num_irqs; i++) {
250         irq_number = platform_bus_get_irqn(pbus, sbdev , i)
251                          + data->irq_start;
252         irq_attr[3 * i] = cpu_to_be32(GIC_FDT_IRQ_TYPE_SPI);
253         irq_attr[3 * i + 1] = cpu_to_be32(irq_number);
254         irq_attr[3 * i + 2] = cpu_to_be32(GIC_FDT_IRQ_FLAGS_LEVEL_HI);
255     }
256     qemu_fdt_setprop(fdt, nodename, "interrupts",
257                      irq_attr, vbasedev->num_irqs * 3 * sizeof(uint32_t));
258     g_free(irq_attr);
259     g_free(reg_attr);
260     g_free(nodename);
261     return 0;
262 }
263 
264 /* AMD xgbe properties whose values are copied/pasted from host */
265 static HostProperty amd_xgbe_copied_properties[] = {
266     {"compatible", false},
267     {"dma-coherent", true},
268     {"amd,per-channel-interrupt", true},
269     {"phy-mode", false},
270     {"mac-address", true},
271     {"amd,speed-set", false},
272     {"amd,serdes-blwc", true},
273     {"amd,serdes-cdr-rate", true},
274     {"amd,serdes-pq-skew", true},
275     {"amd,serdes-tx-amp", true},
276     {"amd,serdes-dfe-tap-config", true},
277     {"amd,serdes-dfe-tap-enable", true},
278     {"clock-names", false},
279 };
280 
281 /**
282  * add_amd_xgbe_fdt_node
283  *
284  * Generates the combined xgbe/phy node following kernel >=4.2
285  * binding documentation:
286  * Documentation/devicetree/bindings/net/amd-xgbe.txt:
287  * Also 2 clock nodes are created (dma and ptp)
288  *
289  * Asserts in case of error
290  */
291 static int add_amd_xgbe_fdt_node(SysBusDevice *sbdev, void *opaque)
292 {
293     PlatformBusFDTData *data = opaque;
294     PlatformBusDevice *pbus = data->pbus;
295     VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev);
296     VFIODevice *vbasedev = &vdev->vbasedev;
297     VFIOINTp *intp;
298     const char *parent_node = data->pbus_node_name;
299     char **node_path, *nodename, *dt_name;
300     void *guest_fdt = data->fdt, *host_fdt;
301     const void *r;
302     int i, prop_len;
303     uint32_t *irq_attr, *reg_attr;
304     const uint32_t *host_clock_phandles;
305     uint64_t mmio_base, irq_number;
306     uint32_t guest_clock_phandles[2];
307 
308     host_fdt = load_device_tree_from_sysfs();
309 
310     dt_name = sysfs_to_dt_name(vbasedev->name);
311     if (!dt_name) {
312         error_report("%s incorrect sysfs device name %s",
313                      __func__, vbasedev->name);
314         exit(1);
315     }
316     node_path = qemu_fdt_node_path(host_fdt, dt_name, vdev->compat,
317                                    &error_fatal);
318     if (!node_path || !node_path[0]) {
319         error_report("%s unable to retrieve node path for %s/%s",
320                      __func__, dt_name, vdev->compat);
321         exit(1);
322     }
323 
324     if (node_path[1]) {
325         error_report("%s more than one node matching %s/%s!",
326                      __func__, dt_name, vdev->compat);
327         exit(1);
328     }
329 
330     g_free(dt_name);
331 
332     if (vbasedev->num_regions != 5) {
333         error_report("%s Does the host dt node combine XGBE/PHY?", __func__);
334         exit(1);
335     }
336 
337     /* generate nodes for DMA_CLK and PTP_CLK */
338     r = qemu_fdt_getprop(host_fdt, node_path[0], "clocks",
339                          &prop_len, &error_fatal);
340     if (prop_len != 8) {
341         error_report("%s clocks property should contain 2 handles", __func__);
342         exit(1);
343     }
344     host_clock_phandles = r;
345     guest_clock_phandles[0] = qemu_fdt_alloc_phandle(guest_fdt);
346     guest_clock_phandles[1] = qemu_fdt_alloc_phandle(guest_fdt);
347 
348     /**
349      * clock handles fetched from host dt are in be32 layout whereas
350      * rest of the code uses cpu layout. Also guest clock handles are
351      * in cpu layout.
352      */
353     fdt_build_clock_node(host_fdt, guest_fdt,
354                          be32_to_cpu(host_clock_phandles[0]),
355                          guest_clock_phandles[0]);
356 
357     fdt_build_clock_node(host_fdt, guest_fdt,
358                          be32_to_cpu(host_clock_phandles[1]),
359                          guest_clock_phandles[1]);
360 
361     /* combined XGBE/PHY node */
362     mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, 0);
363     nodename = g_strdup_printf("%s/%s@%" PRIx64, parent_node,
364                                vbasedev->name, mmio_base);
365     qemu_fdt_add_subnode(guest_fdt, nodename);
366 
367     copy_properties_from_host(amd_xgbe_copied_properties,
368                        ARRAY_SIZE(amd_xgbe_copied_properties),
369                        host_fdt, guest_fdt,
370                        node_path[0], nodename);
371 
372     qemu_fdt_setprop_cells(guest_fdt, nodename, "clocks",
373                            guest_clock_phandles[0],
374                            guest_clock_phandles[1]);
375 
376     reg_attr = g_new(uint32_t, vbasedev->num_regions * 2);
377     for (i = 0; i < vbasedev->num_regions; i++) {
378         mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, i);
379         reg_attr[2 * i] = cpu_to_be32(mmio_base);
380         reg_attr[2 * i + 1] = cpu_to_be32(
381                                 memory_region_size(vdev->regions[i]->mem));
382     }
383     qemu_fdt_setprop(guest_fdt, nodename, "reg", reg_attr,
384                      vbasedev->num_regions * 2 * sizeof(uint32_t));
385 
386     irq_attr = g_new(uint32_t, vbasedev->num_irqs * 3);
387     for (i = 0; i < vbasedev->num_irqs; i++) {
388         irq_number = platform_bus_get_irqn(pbus, sbdev , i)
389                          + data->irq_start;
390         irq_attr[3 * i] = cpu_to_be32(GIC_FDT_IRQ_TYPE_SPI);
391         irq_attr[3 * i + 1] = cpu_to_be32(irq_number);
392         /*
393           * General device interrupt and PCS auto-negotiation interrupts are
394           * level-sensitive while the 4 per-channel interrupts are edge
395           * sensitive
396           */
397         QLIST_FOREACH(intp, &vdev->intp_list, next) {
398             if (intp->pin == i) {
399                 break;
400             }
401         }
402         if (intp->flags & VFIO_IRQ_INFO_AUTOMASKED) {
403             irq_attr[3 * i + 2] = cpu_to_be32(GIC_FDT_IRQ_FLAGS_LEVEL_HI);
404         } else {
405             irq_attr[3 * i + 2] = cpu_to_be32(GIC_FDT_IRQ_FLAGS_EDGE_LO_HI);
406         }
407     }
408     qemu_fdt_setprop(guest_fdt, nodename, "interrupts",
409                      irq_attr, vbasedev->num_irqs * 3 * sizeof(uint32_t));
410 
411     g_free(host_fdt);
412     g_strfreev(node_path);
413     g_free(irq_attr);
414     g_free(reg_attr);
415     g_free(nodename);
416     return 0;
417 }
418 
419 /* DT compatible matching */
420 static bool vfio_platform_match(SysBusDevice *sbdev,
421                                 const BindingEntry *entry)
422 {
423     VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev);
424     const char *compat;
425     unsigned int n;
426 
427     for (n = vdev->num_compat, compat = vdev->compat; n > 0;
428          n--, compat += strlen(compat) + 1) {
429         if (!strcmp(entry->compat, compat)) {
430             return true;
431         }
432     }
433 
434     return false;
435 }
436 
437 #define VFIO_PLATFORM_BINDING(compat, add_fn) \
438     {TYPE_VFIO_PLATFORM, (compat), (add_fn), vfio_platform_match}
439 
440 #endif /* CONFIG_LINUX */
441 
442 #ifdef CONFIG_TPM
443 /*
444  * add_tpm_tis_fdt_node: Create a DT node for TPM TIS
445  *
446  * See kernel documentation:
447  * Documentation/devicetree/bindings/security/tpm/tpm_tis_mmio.txt
448  * Optional interrupt for command completion is not exposed
449  */
450 static int add_tpm_tis_fdt_node(SysBusDevice *sbdev, void *opaque)
451 {
452     PlatformBusFDTData *data = opaque;
453     PlatformBusDevice *pbus = data->pbus;
454     void *fdt = data->fdt;
455     const char *parent_node = data->pbus_node_name;
456     char *nodename;
457     uint32_t reg_attr[2];
458     uint64_t mmio_base;
459 
460     mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, 0);
461     nodename = g_strdup_printf("%s/tpm_tis@%" PRIx64, parent_node, mmio_base);
462     qemu_fdt_add_subnode(fdt, nodename);
463 
464     qemu_fdt_setprop_string(fdt, nodename, "compatible", "tcg,tpm-tis-mmio");
465 
466     reg_attr[0] = cpu_to_be32(mmio_base);
467     reg_attr[1] = cpu_to_be32(0x5000);
468     qemu_fdt_setprop(fdt, nodename, "reg", reg_attr, 2 * sizeof(uint32_t));
469 
470     g_free(nodename);
471     return 0;
472 }
473 #endif
474 
475 static int add_uefi_vars_node(SysBusDevice *sbdev, void *opaque)
476 {
477     PlatformBusFDTData *data = opaque;
478     PlatformBusDevice *pbus = data->pbus;
479     const char *parent_node = data->pbus_node_name;
480     void *fdt = data->fdt;
481     uint64_t mmio_base;
482     char *nodename;
483 
484     mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, 0);
485     nodename = g_strdup_printf("%s/%s@%" PRIx64, parent_node,
486                                UEFI_VARS_FDT_NODE, mmio_base);
487     qemu_fdt_add_subnode(fdt, nodename);
488     qemu_fdt_setprop_string(fdt, nodename,
489                             "compatible", UEFI_VARS_FDT_COMPAT);
490     qemu_fdt_setprop_sized_cells(fdt, nodename, "reg",
491                                  1, mmio_base,
492                                  1, UEFI_VARS_REGS_SIZE);
493     g_free(nodename);
494     return 0;
495 }
496 
497 static int no_fdt_node(SysBusDevice *sbdev, void *opaque)
498 {
499     return 0;
500 }
501 
502 /* Device type based matching */
503 static bool type_match(SysBusDevice *sbdev, const BindingEntry *entry)
504 {
505     return !strcmp(object_get_typename(OBJECT(sbdev)), entry->typename);
506 }
507 
508 #define TYPE_BINDING(type, add_fn) {(type), NULL, (add_fn), NULL}
509 
510 /* list of supported dynamic sysbus bindings */
511 static const BindingEntry bindings[] = {
512 #ifdef CONFIG_LINUX
513     TYPE_BINDING(TYPE_VFIO_CALXEDA_XGMAC, add_calxeda_midway_xgmac_fdt_node),
514     TYPE_BINDING(TYPE_VFIO_AMD_XGBE, add_amd_xgbe_fdt_node),
515     VFIO_PLATFORM_BINDING("amd,xgbe-seattle-v1a", add_amd_xgbe_fdt_node),
516 #endif
517 #ifdef CONFIG_TPM
518     TYPE_BINDING(TYPE_TPM_TIS_SYSBUS, add_tpm_tis_fdt_node),
519 #endif
520     TYPE_BINDING(TYPE_RAMFB_DEVICE, no_fdt_node),
521     TYPE_BINDING(TYPE_UEFI_VARS_SYSBUS, add_uefi_vars_node),
522     TYPE_BINDING("", NULL), /* last element */
523 };
524 
525 /* Generic Code */
526 
527 /**
528  * add_fdt_node - add the device tree node of a dynamic sysbus device
529  *
530  * @sbdev: handle to the sysbus device
531  * @opaque: handle to the PlatformBusFDTData
532  *
533  * Checks the sysbus type belongs to the list of device types that
534  * are dynamically instantiable and if so call the node creation
535  * function.
536  */
537 static void add_fdt_node(SysBusDevice *sbdev, void *opaque)
538 {
539     int i, ret;
540 
541     for (i = 0; i < ARRAY_SIZE(bindings); i++) {
542         const BindingEntry *iter = &bindings[i];
543 
544         if (type_match(sbdev, iter)) {
545             if (!iter->match_fn || iter->match_fn(sbdev, iter)) {
546                 ret = iter->add_fn(sbdev, opaque);
547                 assert(!ret);
548                 return;
549             }
550         }
551     }
552     error_report("Device %s can not be dynamically instantiated",
553                      qdev_fw_name(DEVICE(sbdev)));
554     exit(1);
555 }
556 
557 void platform_bus_add_all_fdt_nodes(void *fdt, const char *intc, hwaddr addr,
558                                     hwaddr bus_size, int irq_start)
559 {
560     const char platcomp[] = "qemu,platform\0simple-bus";
561     PlatformBusDevice *pbus;
562     DeviceState *dev;
563     gchar *node;
564 
565     assert(fdt);
566 
567     node = g_strdup_printf("/platform-bus@%"PRIx64, addr);
568 
569     /* Create a /platform node that we can put all devices into */
570     qemu_fdt_add_subnode(fdt, node);
571     qemu_fdt_setprop(fdt, node, "compatible", platcomp, sizeof(platcomp));
572 
573     /* Our platform bus region is less than 32bits, so 1 cell is enough for
574      * address and size
575      */
576     qemu_fdt_setprop_cells(fdt, node, "#size-cells", 1);
577     qemu_fdt_setprop_cells(fdt, node, "#address-cells", 1);
578     qemu_fdt_setprop_cells(fdt, node, "ranges", 0, addr >> 32, addr, bus_size);
579 
580     qemu_fdt_setprop_phandle(fdt, node, "interrupt-parent", intc);
581 
582     dev = qdev_find_recursive(sysbus_get_default(), TYPE_PLATFORM_BUS_DEVICE);
583     pbus = PLATFORM_BUS_DEVICE(dev);
584 
585     PlatformBusFDTData data = {
586         .fdt = fdt,
587         .irq_start = irq_start,
588         .pbus_node_name = node,
589         .pbus = pbus,
590     };
591 
592     /* Loop through all dynamic sysbus devices and create their node */
593     foreach_dynamic_sysbus_device(add_fdt_node, &data);
594 
595     g_free(node);
596 }
597