sysbus-fdt.c 19 KB

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