1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /*
3  * Device tree overlay support in Xen.
4  *
5  * Copyright (C) 2023, Advanced Micro Devices, Inc. All Rights Reserved.
6  * Written by Vikram Garhwal <vikram.garhwal@amd.com>
7  *
8  */
9 #include <xen/dt-overlay.h>
10 #include <xen/fdt-kernel.h>
11 #include <xen/guest_access.h>
12 #include <xen/iocap.h>
13 #include <xen/libfdt/libfdt.h>
14 #include <xen/xmalloc.h>
15 
16 #include <asm/setup.h>
17 
18 #define DT_OVERLAY_MAX_SIZE KB(500)
19 
20 static LIST_HEAD(overlay_tracker);
21 static DEFINE_SPINLOCK(overlay_lock);
22 
23 /* Find last descendants of the device_node. */
24 static struct dt_device_node *
find_last_descendants_node(const struct dt_device_node * device_node)25 find_last_descendants_node(const struct dt_device_node *device_node)
26 {
27     struct dt_device_node *child_node;
28 
29     for ( child_node = device_node->child; child_node->sibling != NULL;
30           child_node = child_node->sibling );
31 
32     /* If last child_node also have children. */
33     if ( child_node->child )
34         child_node = find_last_descendants_node(child_node);
35 
36     return child_node;
37 }
38 
39 /*
40  * Returns next node to the input node. If node has children then return
41  * last descendant's next node.
42 */
43 static struct dt_device_node *
dt_find_next_node(struct dt_device_node * dt,const struct dt_device_node * node)44 dt_find_next_node(struct dt_device_node *dt, const struct dt_device_node *node)
45 {
46     struct dt_device_node *np;
47 
48     dt_for_each_device_node(dt, np)
49         if ( np == node )
50             break;
51 
52     if ( np->child )
53         np = find_last_descendants_node(np);
54 
55     return np->allnext;
56 }
57 
dt_overlay_remove_node(struct dt_device_node * device_node)58 static int dt_overlay_remove_node(struct dt_device_node *device_node)
59 {
60     struct dt_device_node *np;
61     struct dt_device_node *parent_node;
62     struct dt_device_node *last_descendant = device_node->child;
63 
64     parent_node = device_node->parent;
65 
66     /* Check if we are trying to remove "/" i.e. root node. */
67     if ( parent_node == NULL )
68     {
69         dt_dprintk("%s's parent node not found\n", device_node->name);
70         return -EFAULT;
71     }
72 
73     /* Sanity check for linking between parent and child node. */
74     np = parent_node->child;
75     if ( np == NULL )
76     {
77         dt_dprintk("parent node %s's not found\n", parent_node->name);
78         return -EFAULT;
79     }
80 
81     /* If node to be removed is only child node or first child. */
82     if ( !dt_node_cmp(np->full_name, device_node->full_name) )
83     {
84         parent_node->child = np->sibling;
85 
86         /*
87          * Iterate over all child nodes of device_node. Given that we are
88          * removing a node, we need to remove all it's descendants too.
89          * Reason behind finding last_descendant:
90          * If device_node has multiple children, device_node->allnext will point
91          * to first_child and first_child->allnext will be a sibling. When the
92          * device_node and it's all children are removed, parent_node->allnext
93          * should point to node next to last children.
94          */
95         if ( last_descendant )
96         {
97             last_descendant = find_last_descendants_node(device_node);
98             parent_node->allnext = last_descendant->allnext;
99         }
100         else
101             parent_node->allnext = np->allnext;
102 
103         return 0;
104     }
105 
106     for ( np = parent_node->child; np->sibling != NULL; np = np->sibling )
107     {
108         if ( !dt_node_cmp(np->sibling->full_name, device_node->full_name) )
109         {
110             /* Found the node. Now we remove it. */
111             np->sibling = np->sibling->sibling;
112 
113             if ( np->child )
114                 np = find_last_descendants_node(np);
115 
116             /*
117              * Iterate over all child nodes of device_node. Given that we are
118              * removing parent node, we need to remove all it's descendants too.
119              */
120             if ( last_descendant )
121                 last_descendant = find_last_descendants_node(device_node);
122 
123             if ( last_descendant )
124                 np->allnext = last_descendant->allnext;
125             else
126                 np->allnext = np->allnext->allnext;
127 
128             break;
129         }
130     }
131 
132     return 0;
133 }
134 
dt_overlay_add_node(struct dt_device_node * device_node,const char * parent_node_path)135 static int dt_overlay_add_node(struct dt_device_node *device_node,
136                                const char *parent_node_path)
137 {
138     struct dt_device_node *parent_node;
139     struct dt_device_node *next_node;
140 
141     parent_node = dt_find_node_by_path(parent_node_path);
142 
143     if ( parent_node == NULL )
144     {
145         dt_dprintk("Parent node %s not found. Overlay node will not be added\n",
146                    parent_node_path);
147         return -EINVAL;
148     }
149 
150     /* If parent has no child. */
151     if ( parent_node->child == NULL )
152     {
153         next_node = parent_node->allnext;
154         device_node->parent = parent_node;
155         parent_node->allnext = device_node;
156         parent_node->child = device_node;
157     }
158     else
159     {
160         struct dt_device_node *np;
161         /*
162          * If parent has at least one child node.
163          * Iterate to the last child node of parent.
164          */
165         for ( np = parent_node->child; np->sibling != NULL; np = np->sibling );
166 
167         /* Iterate over all child nodes of np node. */
168         if ( np->child )
169         {
170             struct dt_device_node *np_last_descendant;
171 
172             np_last_descendant = find_last_descendants_node(np);
173 
174             next_node = np_last_descendant->allnext;
175             np_last_descendant->allnext = device_node;
176         }
177         else
178         {
179             next_node = np->allnext;
180             np->allnext = device_node;
181         }
182 
183         device_node->parent = parent_node;
184         np->sibling = device_node;
185         np->sibling->sibling = NULL;
186     }
187 
188     /* Iterate over all child nodes of device_node to add children too. */
189     if ( device_node->child )
190     {
191         struct dt_device_node *device_node_last_descendant;
192 
193         device_node_last_descendant = find_last_descendants_node(device_node);
194 
195         /* Plug next_node at the end of last children of device_node. */
196         device_node_last_descendant->allnext = next_node;
197     }
198     else
199     {
200         /* Now plug next_node at the end of device_node. */
201         device_node->allnext = next_node;
202     }
203 
204     return 0;
205 }
206 
207 /* Basic sanity check for the dtbo tool stack provided to Xen. */
check_overlay_fdt(const void * overlay_fdt,uint32_t overlay_fdt_size)208 static int check_overlay_fdt(const void *overlay_fdt, uint32_t overlay_fdt_size)
209 {
210     if ( (fdt_totalsize(overlay_fdt) != overlay_fdt_size) ||
211           fdt_check_header(overlay_fdt) )
212     {
213         printk(XENLOG_ERR "The overlay FDT is not a valid Flat Device Tree\n");
214         return -EINVAL;
215     }
216 
217     return 0;
218 }
219 
irq_remove_cb(unsigned long s,unsigned long e,void * dom,unsigned long * c)220 static int irq_remove_cb(unsigned long s, unsigned long e, void *dom,
221                          unsigned long *c)
222 {
223     int rc;
224     struct domain *d = dom;
225 
226     /*
227      * TODO: We don't handle shared IRQs for now. So, it is assumed that
228      * the IRQs was not shared with another devices.
229      * TODO: Undo the IRQ routing.
230      */
231     rc = irq_deny_access(d, s);
232     if ( rc )
233     {
234         printk(XENLOG_ERR "unable to revoke access for irq %lu\n", s);
235     }
236     else
237         *c += e - s + 1;
238 
239     return rc;
240 
241 }
242 
iomem_remove_cb(unsigned long s,unsigned long e,void * dom,unsigned long * c)243 static int iomem_remove_cb(unsigned long s, unsigned long e, void *dom,
244                            unsigned long *c)
245 {
246     int rc;
247     struct domain *d = dom;
248 
249     /*
250     * Remove mmio access.
251     * TODO: Support for remove/add the mapping in P2M.
252     */
253     rc = iomem_deny_access(d, s, e);
254     if ( rc )
255     {
256         printk(XENLOG_ERR "Unable to remove %pd access to %#lx - %#lx\n",
257                d, s, e);
258     }
259     else
260         *c += e - s + 1;
261 
262     return rc;
263 }
264 
265 /* Count number of nodes till one level of __overlay__ tag. */
overlay_node_count(const void * overlay_fdt)266 static unsigned int overlay_node_count(const void *overlay_fdt)
267 {
268     unsigned int num_overlay_nodes = 0;
269     int fragment;
270 
271     fdt_for_each_subnode(fragment, overlay_fdt, 0)
272     {
273         int subnode;
274         int overlay;
275 
276         overlay = fdt_subnode_offset(overlay_fdt, fragment, "__overlay__");
277         if ( overlay < 0 )
278             continue;
279 
280         fdt_for_each_subnode(subnode, overlay_fdt, overlay)
281         {
282             num_overlay_nodes++;
283         }
284     }
285 
286     return num_overlay_nodes;
287 }
288 
289 /*
290  * overlay_get_nodes_info gets full name with path for all the nodes which
291  * are in one level of __overlay__ tag. This is useful when checking node for
292  * duplication i.e. dtbo tries to add nodes which already exists in device tree.
293  */
overlay_get_nodes_info(const void * fdto,char ** nodes_full_path)294 static int overlay_get_nodes_info(const void *fdto, char **nodes_full_path)
295 {
296     int fragment;
297     unsigned int node_num = 0;
298 
299     fdt_for_each_subnode(fragment, fdto, 0)
300     {
301         int target;
302         int overlay;
303         int subnode;
304         const char *target_path;
305 
306         overlay = fdt_subnode_offset(fdto, fragment, "__overlay__");
307         if ( overlay < 0 )
308             continue;
309 
310         target = fdt_overlay_target_offset(device_tree_flattened, fdto,
311                                            fragment, &target_path);
312         if ( target < 0 )
313             return target;
314 
315         if ( target_path == NULL )
316             return -EINVAL;
317 
318         fdt_for_each_subnode(subnode, fdto, overlay)
319         {
320             const char *node_name = NULL;
321             int node_name_len;
322             unsigned int target_path_len = strlen(target_path);
323             unsigned int node_full_name_len;
324             unsigned int extra_len;
325 
326             node_name = fdt_get_name(fdto, subnode, &node_name_len);
327 
328             if ( node_name == NULL )
329                 return node_name_len;
330 
331             /*
332              * Extra length is for adding '/' and '\0' unless the target path is
333              * root in which case we don't add the '/' at the beginning. This is
334              * done to keep the node_full_path in the correct full node name
335              * format.
336              */
337             extra_len = (target_path_len > 1) ? 2 : 1;
338             node_full_name_len = target_path_len + node_name_len + extra_len;
339 
340             nodes_full_path[node_num] = xmalloc_bytes(node_full_name_len);
341 
342             if ( nodes_full_path[node_num] == NULL )
343                 return -ENOMEM;
344 
345             memcpy(nodes_full_path[node_num], target_path, target_path_len);
346 
347             /* Target is not root - add separator */
348             if ( target_path_len > 1 )
349                 nodes_full_path[node_num][target_path_len++] = '/';
350 
351             memcpy(nodes_full_path[node_num] + target_path_len,
352                     node_name, node_name_len);
353 
354             nodes_full_path[node_num][node_full_name_len - 1] = '\0';
355 
356             node_num++;
357         }
358     }
359 
360     return 0;
361 }
362 
363 /* This function should be called with the overlay_lock taken */
364 static struct overlay_track *
find_track_entry_from_tracker(const void * overlay_fdt,uint32_t overlay_fdt_size)365 find_track_entry_from_tracker(const void *overlay_fdt,
366                               uint32_t overlay_fdt_size)
367 {
368     struct overlay_track *entry, *temp;
369     bool found_entry = false;
370 
371     ASSERT(spin_is_locked(&overlay_lock));
372 
373     /*
374      * First check if dtbo is correct i.e. it should one of the dtbo which was
375      * used when dynamically adding the node.
376      * Limitation: Cases with same node names but different property are not
377      * supported currently. We are relying on user to provide the same dtbo
378      * as it was used when adding the nodes.
379      */
380     list_for_each_entry_safe( entry, temp, &overlay_tracker, entry )
381     {
382         if ( memcmp(entry->overlay_fdt, overlay_fdt, overlay_fdt_size) == 0 )
383         {
384             found_entry = true;
385             break;
386         }
387     }
388 
389     if ( !found_entry )
390     {
391         printk(XENLOG_ERR "Cannot find any matching tracker with input dtbo."
392                " Operation is supported only for prior added dtbo.\n");
393         return NULL;
394     }
395 
396     return entry;
397 }
398 
399 /* Check if node itself can be removed and remove node from IOMMU. */
remove_node_resources(struct dt_device_node * device_node)400 static int remove_node_resources(struct dt_device_node *device_node)
401 {
402     int rc = 0;
403     unsigned int len;
404     domid_t domid;
405 
406     domid = dt_device_used_by(device_node);
407 
408     dt_dprintk("Checking if node %s is used by any domain\n",
409                device_node->full_name);
410 
411     /* Remove the node if only it's assigned to hardware domain or domain io. */
412     if ( domid != hardware_domain->domain_id && domid != DOMID_IO )
413     {
414         printk(XENLOG_ERR "Device %s is being used by domain %u. Removing nodes failed\n",
415                device_node->full_name, domid);
416         return -EINVAL;
417     }
418 
419     /* Check if iommu property exists. */
420     if ( dt_get_property(device_node, "iommus", &len) )
421     {
422         if ( dt_device_is_protected(device_node) )
423         {
424             rc = iommu_remove_dt_device(device_node);
425             if ( rc < 0 )
426                 return rc;
427         }
428     }
429 
430     return rc;
431 }
432 
433 /* Remove all descendants from IOMMU. */
434 static int
remove_descendant_nodes_resources(const struct dt_device_node * device_node)435 remove_descendant_nodes_resources(const struct dt_device_node *device_node)
436 {
437     int rc = 0;
438     struct dt_device_node *child_node;
439 
440     for ( child_node = device_node->child; child_node != NULL;
441          child_node = child_node->sibling )
442     {
443         if ( child_node->child )
444         {
445             rc = remove_descendant_nodes_resources(child_node);
446             if ( rc )
447                 return rc;
448         }
449 
450         rc = remove_node_resources(child_node);
451         if ( rc )
452             return rc;
453     }
454 
455     return rc;
456 }
457 
458 /* Remove nodes from dt_host. */
remove_nodes(const struct overlay_track * tracker)459 static int remove_nodes(const struct overlay_track *tracker)
460 {
461     int rc = 0;
462     struct dt_device_node *overlay_node;
463     unsigned int j;
464     struct domain *d = hardware_domain;
465 
466     for ( j = 0; j < tracker->num_nodes; j++ )
467     {
468         overlay_node = (struct dt_device_node *)tracker->nodes_address[j];
469         if ( overlay_node == NULL )
470             return -EINVAL;
471 
472         write_lock(&dt_host_lock);
473 
474         rc = remove_descendant_nodes_resources(overlay_node);
475         if ( rc )
476         {
477             write_unlock(&dt_host_lock);
478             return rc;
479         }
480 
481         rc = remove_node_resources(overlay_node);
482         if ( rc )
483         {
484             write_unlock(&dt_host_lock);
485             return rc;
486         }
487 
488         dt_dprintk("Removing node: %s\n", overlay_node->full_name);
489 
490         rc = dt_overlay_remove_node(overlay_node);
491         if ( rc )
492         {
493             write_unlock(&dt_host_lock);
494             return rc;
495         }
496 
497         write_unlock(&dt_host_lock);
498     }
499 
500     /* Remove IRQ access. */
501     if ( tracker->irq_ranges )
502     {
503         rc = rangeset_consume_ranges(tracker->irq_ranges, irq_remove_cb, d);
504         if ( rc )
505             return rc;
506     }
507 
508    /* Remove mmio access. */
509     if ( tracker->iomem_ranges )
510     {
511         rc = rangeset_consume_ranges(tracker->iomem_ranges, iomem_remove_cb, d);
512         if ( rc )
513             return rc;
514     }
515 
516     return rc;
517 }
518 
519 /*
520  * First finds the device node to remove. Check if the device is being used by
521  * any dom and finally remove it from dt_host. IOMMU is already being taken care
522  * while destroying the domain.
523  */
handle_remove_overlay_nodes(const void * overlay_fdt,uint32_t overlay_fdt_size)524 static long handle_remove_overlay_nodes(const void *overlay_fdt,
525                                         uint32_t overlay_fdt_size)
526 {
527     int rc;
528     struct overlay_track *entry;
529 
530     rc = check_overlay_fdt(overlay_fdt, overlay_fdt_size);
531     if ( rc )
532         return rc;
533 
534     spin_lock(&overlay_lock);
535 
536     entry = find_track_entry_from_tracker(overlay_fdt, overlay_fdt_size);
537     if ( entry == NULL )
538     {
539         rc = -EINVAL;
540         goto out;
541 
542     }
543 
544     rc = remove_nodes(entry);
545     if ( rc )
546     {
547         printk(XENLOG_ERR "Removing node failed\n");
548         goto out;
549     }
550 
551     list_del(&entry->entry);
552 
553     xfree(entry->dt_host_new);
554     xfree(entry->fdt);
555     xfree(entry->overlay_fdt);
556 
557     xfree(entry->nodes_address);
558 
559     rangeset_destroy(entry->irq_ranges);
560     rangeset_destroy(entry->iomem_ranges);
561 
562     xfree(entry);
563 
564  out:
565     spin_unlock(&overlay_lock);
566     return rc;
567 }
568 
free_nodes_full_path(unsigned int num_nodes,char ** nodes_full_path)569 static void free_nodes_full_path(unsigned int num_nodes, char **nodes_full_path)
570 {
571     unsigned int i;
572 
573     if ( nodes_full_path == NULL )
574         return;
575 
576     for ( i = 0; i < num_nodes && nodes_full_path[i] != NULL; i++ )
577     {
578         xfree(nodes_full_path[i]);
579     }
580 
581     xfree(nodes_full_path);
582 }
583 
add_nodes(struct overlay_track * tr,char ** nodes_full_path)584 static long add_nodes(struct overlay_track *tr, char **nodes_full_path)
585 
586 {
587     int rc;
588     unsigned int j;
589     struct dt_device_node *overlay_node;
590 
591     for ( j = 0; j < tr->num_nodes; j++ )
592     {
593         struct dt_device_node *prev_node, *next_node;
594 
595         dt_dprintk("Adding node: %s\n", nodes_full_path[j]);
596 
597         /* Find the newly added node in tr->dt_host_new by it's full path. */
598         overlay_node = dt_find_node_by_path_from(tr->dt_host_new,
599                                                  nodes_full_path[j]);
600         if ( overlay_node == NULL )
601             return -EFAULT;
602 
603         /*
604          * Find previous and next node to overlay_node in dt_host_new. We will
605          * need these nodes to fix the dt_host_new mapping. When overlay_node is
606          * take out of dt_host_new tree and added to dt_host, link between
607          * previous node and next_node is broken. We will need to refresh
608          * dt_host_new with correct linking for any other overlay nodes
609          * extraction in future.
610          */
611         dt_for_each_device_node(tr->dt_host_new, prev_node)
612             if ( prev_node->allnext == overlay_node )
613                 break;
614 
615         next_node = dt_find_next_node(tr->dt_host_new, overlay_node);
616 
617         write_lock(&dt_host_lock);
618 
619         /* Add the node to dt_host. */
620         rc = dt_overlay_add_node(overlay_node, overlay_node->parent->full_name);
621         if ( rc )
622         {
623             write_unlock(&dt_host_lock);
624 
625             /* Node not added in dt_host. */
626             return rc;
627         }
628 
629         prev_node->allnext = next_node;
630 
631         overlay_node = dt_find_node_by_path(overlay_node->full_name);
632         if ( overlay_node == NULL )
633         {
634             /* Sanity check. But code will never come here. */
635             ASSERT_UNREACHABLE();
636             return -EFAULT;
637         }
638 
639         write_unlock(&dt_host_lock);
640 
641         /* Keep overlay_node address in tracker. */
642         tr->nodes_address[j] = (unsigned long)overlay_node;
643     }
644 
645     return 0;
646 }
647 /*
648  * Adds device tree nodes under target node.
649  * We use tr->dt_host_new to unflatten the updated device_tree_flattened.
650  */
handle_add_overlay_nodes(void * overlay_fdt,uint32_t overlay_fdt_size)651 static long handle_add_overlay_nodes(void *overlay_fdt,
652                                      uint32_t overlay_fdt_size)
653 {
654     int rc;
655     unsigned int j;
656     struct dt_device_node *overlay_node;
657     struct overlay_track *tr = NULL;
658     char **nodes_full_path = NULL;
659     unsigned int new_fdt_size;
660 
661     tr = xzalloc(struct overlay_track);
662     if ( tr == NULL )
663         return -ENOMEM;
664 
665     new_fdt_size = fdt_totalsize(device_tree_flattened) +
666                                  fdt_totalsize(overlay_fdt);
667 
668     tr->fdt = xzalloc_bytes(new_fdt_size);
669     if ( tr->fdt == NULL )
670     {
671         xfree(tr);
672         return -ENOMEM;
673     }
674 
675     tr->num_nodes = overlay_node_count(overlay_fdt);
676     if ( tr->num_nodes == 0 )
677     {
678         xfree(tr->fdt);
679         xfree(tr);
680         return -ENOMEM;
681     }
682 
683     tr->nodes_address = xzalloc_bytes(tr->num_nodes * sizeof(unsigned long));
684     if ( tr->nodes_address == NULL )
685     {
686         xfree(tr->fdt);
687         xfree(tr);
688         return -ENOMEM;
689     }
690 
691     rc = check_overlay_fdt(overlay_fdt, overlay_fdt_size);
692     if ( rc )
693     {
694         xfree(tr->nodes_address);
695         xfree(tr->fdt);
696         xfree(tr);
697         return rc;
698     }
699 
700     /*
701      * Keep a copy of overlay_fdt as fdt_overlay_apply will change the input
702      * overlay's content(magic) when applying overlay.
703      */
704     tr->overlay_fdt = xzalloc_bytes(overlay_fdt_size);
705     if ( tr->overlay_fdt == NULL )
706     {
707         xfree(tr->nodes_address);
708         xfree(tr->fdt);
709         xfree(tr);
710         return -ENOMEM;
711     }
712 
713     memcpy(tr->overlay_fdt, overlay_fdt, overlay_fdt_size);
714 
715     spin_lock(&overlay_lock);
716 
717     memcpy(tr->fdt, device_tree_flattened,
718            fdt_totalsize(device_tree_flattened));
719 
720     /* Open tr->fdt with more space to accommodate the overlay_fdt. */
721     rc = fdt_open_into(tr->fdt, tr->fdt, new_fdt_size);
722     if ( rc )
723     {
724         printk(XENLOG_ERR "Increasing fdt size to accommodate overlay_fdt failed with error %d\n",
725                rc);
726         goto err;
727     }
728 
729     nodes_full_path = xzalloc_bytes(tr->num_nodes * sizeof(char *));
730     if ( nodes_full_path == NULL )
731     {
732         rc = -ENOMEM;
733         goto err;
734     }
735 
736     /*
737      * overlay_get_nodes_info is called to get the node information from dtbo.
738      * This is done before fdt_overlay_apply() because the overlay apply will
739      * erase the magic of overlay_fdt.
740      */
741     rc = overlay_get_nodes_info(overlay_fdt, nodes_full_path);
742     if ( rc )
743     {
744         printk(XENLOG_ERR "Getting nodes information failed with error %d\n",
745                rc);
746         goto err;
747     }
748 
749     rc = fdt_overlay_apply(tr->fdt, overlay_fdt);
750     if ( rc )
751     {
752         printk(XENLOG_ERR "Adding overlay node failed with error %d\n", rc);
753         goto err;
754     }
755 
756     /*
757      * Check if any of the node already exists in dt_host. If node already exits
758      * we can return here as this overlay_fdt is not suitable for overlay ops.
759      */
760     for ( j = 0; j < tr->num_nodes; j++ )
761     {
762         overlay_node = dt_find_node_by_path(nodes_full_path[j]);
763         if ( overlay_node != NULL )
764         {
765             printk(XENLOG_ERR "node %s exists in device tree\n",
766                    nodes_full_path[j]);
767             rc = -EINVAL;
768             goto err;
769         }
770     }
771 
772     /*
773      * Unflatten the tr->fdt into a new dt_host.
774      * TODO: Check and add alias_scan() if it's needed for overlay in future.
775      */
776     rc = unflatten_device_tree(tr->fdt, &tr->dt_host_new);
777     if ( rc )
778     {
779         printk(XENLOG_ERR "unflatten_device_tree failed with error %d\n", rc);
780         goto err;
781     }
782 
783     rc = add_nodes(tr, nodes_full_path);
784     if ( rc )
785     {
786         printk(XENLOG_ERR "Adding nodes failed. Removing the partially added nodes.\n");
787         goto remove_node;
788     }
789 
790     INIT_LIST_HEAD(&tr->entry);
791     list_add_tail(&tr->entry, &overlay_tracker);
792 
793     spin_unlock(&overlay_lock);
794 
795     free_nodes_full_path(tr->num_nodes, nodes_full_path);
796 
797     return rc;
798 
799 /*
800  * Failure case. We need to remove the nodes, free tracker(if tr exists) and
801  * tr->dt_host_new.
802  */
803  remove_node:
804     tr->num_nodes = j;
805     rc = remove_nodes(tr);
806 
807     if ( rc )
808     {
809         /*
810          * User needs to provide right overlay. Incorrect node information
811          * example parent node doesn't exist in dt_host etc can cause memory
812          * leaks as removing_nodes() will fail and this means nodes memory is
813          * not freed from tracker. Which may cause memory leaks. Ideally, these
814          * device tree related mistakes will be caught by fdt_overlay_apply()
815          * but given that we don't manage that code keeping this warning message
816          * is better here.
817          */
818         printk(XENLOG_ERR "Removing node failed.\n");
819         spin_unlock(&overlay_lock);
820 
821         free_nodes_full_path(tr->num_nodes, nodes_full_path);
822 
823         return rc;
824     }
825 
826  err:
827     spin_unlock(&overlay_lock);
828 
829     if ( tr->dt_host_new )
830         xfree(tr->dt_host_new);
831 
832     free_nodes_full_path(tr->num_nodes, nodes_full_path);
833 
834     xfree(tr->overlay_fdt);
835     xfree(tr->nodes_address);
836     xfree(tr->fdt);
837 
838     xfree(tr);
839 
840     return rc;
841 }
842 
handle_attach_overlay_nodes(struct domain * d,const void * overlay_fdt,uint32_t overlay_fdt_size)843 static long handle_attach_overlay_nodes(struct domain *d,
844                                         const void *overlay_fdt,
845                                         uint32_t overlay_fdt_size)
846 {
847     int rc;
848     unsigned int j;
849     struct overlay_track *entry;
850 
851     rc = check_overlay_fdt(overlay_fdt, overlay_fdt_size);
852     if ( rc )
853         return rc;
854 
855     spin_lock(&overlay_lock);
856 
857     entry = find_track_entry_from_tracker(overlay_fdt, overlay_fdt_size);
858     if ( entry == NULL )
859     {
860         rc = -EINVAL;
861         goto out;
862     }
863 
864     entry->irq_ranges = rangeset_new(d, "Overlays: Interrupts", 0);
865     if (entry->irq_ranges == NULL)
866     {
867         rc = -ENOMEM;
868         printk(XENLOG_ERR "Creating IRQ rangeset failed");
869         goto out;
870     }
871 
872     entry->iomem_ranges = rangeset_new(d, "Overlay: I/O Memory",
873                                        RANGESETF_prettyprint_hex);
874     if (entry->iomem_ranges == NULL)
875     {
876         rc = -ENOMEM;
877         printk(XENLOG_ERR "Creating IOMMU rangeset failed");
878         goto out;
879     }
880 
881     for ( j = 0; j < entry->num_nodes; j++ )
882     {
883         struct dt_device_node *overlay_node;
884 
885         overlay_node = (struct dt_device_node *)entry->nodes_address[j];
886         if ( overlay_node == NULL )
887         {
888             rc = -EINVAL;
889             goto out;
890         }
891 
892         write_lock(&dt_host_lock);
893         rc = handle_device(d, overlay_node, p2m_mmio_direct_c,
894                            entry->iomem_ranges, entry->irq_ranges);
895         write_unlock(&dt_host_lock);
896         if ( rc )
897         {
898             printk(XENLOG_ERR "Adding IRQ and IOMMU failed\n");
899             goto out;
900         }
901     }
902 
903     spin_unlock(&overlay_lock);
904 
905     return 0;
906 
907  out:
908     spin_unlock(&overlay_lock);
909 
910     if ( entry )
911     {
912         rangeset_destroy(entry->irq_ranges);
913         rangeset_destroy(entry->iomem_ranges);
914     }
915 
916     return rc;
917 }
918 
dt_overlay_sysctl(struct xen_sysctl_dt_overlay * op)919 long dt_overlay_sysctl(struct xen_sysctl_dt_overlay *op)
920 {
921     long ret;
922     void *overlay_fdt;
923 
924     if ( op->overlay_op != XEN_SYSCTL_DT_OVERLAY_ADD &&
925          op->overlay_op != XEN_SYSCTL_DT_OVERLAY_REMOVE )
926         return -EOPNOTSUPP;
927 
928     if ( op->overlay_fdt_size == 0 ||
929          op->overlay_fdt_size > DT_OVERLAY_MAX_SIZE )
930         return -EINVAL;
931 
932     if ( op->pad[0] || op->pad[1] || op->pad[2] )
933         return -EINVAL;
934 
935     overlay_fdt = xmalloc_bytes(op->overlay_fdt_size);
936 
937     if ( overlay_fdt == NULL )
938         return -ENOMEM;
939 
940     ret = copy_from_guest(overlay_fdt, op->overlay_fdt, op->overlay_fdt_size);
941     if ( ret )
942     {
943         gprintk(XENLOG_ERR, "copy from guest failed\n");
944         xfree(overlay_fdt);
945 
946         return -EFAULT;
947     }
948 
949     if ( op->overlay_op == XEN_SYSCTL_DT_OVERLAY_REMOVE )
950         ret = handle_remove_overlay_nodes(overlay_fdt, op->overlay_fdt_size);
951     else
952         ret = handle_add_overlay_nodes(overlay_fdt, op->overlay_fdt_size);
953 
954     xfree(overlay_fdt);
955 
956     return ret;
957 }
958 
dt_overlay_domctl(struct domain * d,struct xen_domctl_dt_overlay * op)959 long dt_overlay_domctl(struct domain *d, struct xen_domctl_dt_overlay *op)
960 {
961     long ret;
962     void *overlay_fdt;
963 
964     if ( op->overlay_op != XEN_DOMCTL_DT_OVERLAY_ATTACH )
965         return -EOPNOTSUPP;
966 
967     if ( op->overlay_fdt_size == 0 ||
968          op->overlay_fdt_size > DT_OVERLAY_MAX_SIZE )
969         return -EINVAL;
970 
971     if ( op->pad[0] || op->pad[1] || op->pad[2] )
972         return -EINVAL;
973 
974     /* TODO: add support for non-1:1 domains using xen,reg */
975     if ( !is_domain_direct_mapped(d) )
976         return -EOPNOTSUPP;
977 
978     overlay_fdt = xmalloc_bytes(op->overlay_fdt_size);
979 
980     if ( overlay_fdt == NULL )
981         return -ENOMEM;
982 
983     ret = copy_from_guest(overlay_fdt, op->overlay_fdt, op->overlay_fdt_size);
984     if ( ret )
985     {
986         gprintk(XENLOG_ERR, "copy from guest failed\n");
987         xfree(overlay_fdt);
988 
989         return -EFAULT;
990     }
991 
992     if ( op->overlay_op == XEN_DOMCTL_DT_OVERLAY_ATTACH )
993         ret = handle_attach_overlay_nodes(d, overlay_fdt, op->overlay_fdt_size);
994     else
995         ret = -EOPNOTSUPP;
996 
997     xfree(overlay_fdt);
998 
999     return ret;
1000 }
1001 
1002 /*
1003  * Local variables:
1004  * mode: C
1005  * c-file-style: "BSD"
1006  * c-basic-offset: 4
1007  * indent-tabs-mode: nil
1008  * End:
1009  */
1010