1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <xen/bootfdt.h>
4 #include <xen/device_tree.h>
5 #include <xen/event.h>
6 #include <xen/static-evtchn.h>
7 
8 #define STATIC_EVTCHN_NODE_SIZE_CELLS 2
9 
get_evtchn_dt_property(const struct dt_device_node * np,uint32_t * port,uint32_t * phandle)10 static int __init get_evtchn_dt_property(const struct dt_device_node *np,
11                                          uint32_t *port, uint32_t *phandle)
12 {
13     const __be32 *prop = NULL;
14     uint32_t len;
15 
16     prop = dt_get_property(np, "xen,evtchn", &len);
17     if ( !prop )
18     {
19         printk(XENLOG_ERR "xen,evtchn property should not be empty.\n");
20         return -EINVAL;
21     }
22 
23     if ( !len || len < dt_cells_to_size(STATIC_EVTCHN_NODE_SIZE_CELLS) )
24     {
25         printk(XENLOG_ERR "xen,evtchn property value is not valid.\n");
26         return -EINVAL;
27     }
28 
29     *port = dt_next_cell(1, &prop);
30     *phandle = dt_next_cell(1, &prop);
31 
32     return 0;
33 }
34 
alloc_domain_evtchn(struct dt_device_node * node)35 static int __init alloc_domain_evtchn(struct dt_device_node *node)
36 {
37     int rc;
38     uint32_t domU1_port, domU2_port, remote_phandle;
39     struct dt_device_node *remote_node;
40     const struct dt_device_node *p1_node, *p2_node;
41     struct evtchn_alloc_unbound alloc_unbound;
42     struct evtchn_bind_interdomain bind_interdomain;
43     struct domain *d1 = NULL, *d2 = NULL;
44 
45     if ( !dt_device_is_compatible(node, "xen,evtchn-v1") )
46         return 0;
47 
48     /*
49      * Event channel is already created while parsing the other side of
50      * evtchn node.
51      */
52     if ( dt_device_static_evtchn_created(node) )
53         return 0;
54 
55     rc = get_evtchn_dt_property(node, &domU1_port, &remote_phandle);
56     if ( rc )
57         return rc;
58 
59     remote_node = dt_find_node_by_phandle(remote_phandle);
60     if ( !remote_node )
61     {
62         printk(XENLOG_ERR
63                 "evtchn: could not find remote evtchn phandle\n");
64         return -EINVAL;
65     }
66 
67     rc = get_evtchn_dt_property(remote_node, &domU2_port, &remote_phandle);
68     if ( rc )
69         return rc;
70 
71     if ( node->phandle != remote_phandle )
72     {
73         printk(XENLOG_ERR "xen,evtchn property is not setup correctly.\n");
74         return -EINVAL;
75     }
76 
77     p1_node = dt_get_parent(node);
78     if ( !p1_node )
79     {
80         printk(XENLOG_ERR "evtchn: evtchn parent node is NULL\n" );
81         return -EINVAL;
82     }
83 
84     p2_node = dt_get_parent(remote_node);
85     if ( !p2_node )
86     {
87         printk(XENLOG_ERR "evtchn: remote parent node is NULL\n" );
88         return -EINVAL;
89     }
90 
91     d1 = get_domain_by_id(p1_node->used_by);
92     d2 = get_domain_by_id(p2_node->used_by);
93 
94     if ( !d1 || !d2 )
95     {
96         printk(XENLOG_ERR "evtchn: could not find domains\n" );
97         return -EINVAL;
98     }
99 
100     alloc_unbound.dom = d1->domain_id;
101     alloc_unbound.remote_dom = d2->domain_id;
102 
103     rc = evtchn_alloc_unbound(&alloc_unbound, domU1_port);
104     if ( rc < 0 )
105     {
106         printk(XENLOG_ERR
107                 "evtchn_alloc_unbound() failure (Error %d) \n", rc);
108         return rc;
109     }
110 
111     bind_interdomain.remote_dom  = d1->domain_id;
112     bind_interdomain.remote_port = domU1_port;
113 
114     rc = evtchn_bind_interdomain(&bind_interdomain, d2, domU2_port);
115     if ( rc < 0 )
116     {
117         printk(XENLOG_ERR
118                 "evtchn_bind_interdomain() failure (Error %d) \n", rc);
119         return rc;
120     }
121 
122     dt_device_set_static_evtchn_created(node);
123     dt_device_set_static_evtchn_created(remote_node);
124 
125     return 0;
126 }
127 
alloc_static_evtchn(void)128 void __init alloc_static_evtchn(void)
129 {
130     struct dt_device_node *node, *evtchn_node;
131     struct dt_device_node *chosen = dt_find_node_by_path("/chosen");
132 
133     BUG_ON(chosen == NULL);
134 
135     if ( hardware_domain )
136         dt_device_set_used_by(chosen, hardware_domain->domain_id);
137 
138     dt_for_each_child_node(chosen, node)
139     {
140         if ( hardware_domain )
141         {
142             if ( alloc_domain_evtchn(node) != 0 )
143                 panic("Could not set up domains evtchn\n");
144         }
145 
146         dt_for_each_child_node(node, evtchn_node)
147         {
148             if ( alloc_domain_evtchn(evtchn_node) != 0 )
149                 panic("Could not set up domains evtchn\n");
150         }
151     }
152 }
153 
154 /*
155  * Local variables:
156  * mode: C
157  * c-file-style: "BSD"
158  * c-basic-offset: 4
159  * tab-width: 4
160  * indent-tabs-mode: nil
161  * End:
162  */
163