1 /*
2  * xen/arch/arm/io.c
3  *
4  * ARM I/O handlers
5  *
6  * Copyright (c) 2011 Citrix Systems.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  */
18 
19 #include <xen/lib.h>
20 #include <xen/spinlock.h>
21 #include <xen/sched.h>
22 #include <xen/sort.h>
23 #include <asm/current.h>
24 #include <asm/mmio.h>
25 
handle_read(const struct mmio_handler * handler,struct vcpu * v,mmio_info_t * info)26 static int handle_read(const struct mmio_handler *handler, struct vcpu *v,
27                        mmio_info_t *info)
28 {
29     const struct hsr_dabt dabt = info->dabt;
30     struct cpu_user_regs *regs = guest_cpu_user_regs();
31     /*
32      * Initialize to zero to avoid leaking data if there is an
33      * implementation error in the emulation (such as not correctly
34      * setting r).
35      */
36     register_t r = 0;
37     uint8_t size = (1 << dabt.size) * 8;
38 
39     if ( !handler->ops->read(v, info, &r, handler->priv) )
40         return 0;
41 
42     /*
43      * Sign extend if required.
44      * Note that we expect the read handler to have zeroed the bits
45      * outside the requested access size.
46      */
47     if ( dabt.sign && (r & (1UL << (size - 1))) )
48     {
49         /*
50          * We are relying on register_t using the same as
51          * an unsigned long in order to keep the 32-bit assembly
52          * code smaller.
53          */
54         BUILD_BUG_ON(sizeof(register_t) != sizeof(unsigned long));
55         r |= (~0UL) << size;
56     }
57 
58     set_user_reg(regs, dabt.reg, r);
59 
60     return 1;
61 }
62 
handle_write(const struct mmio_handler * handler,struct vcpu * v,mmio_info_t * info)63 static int handle_write(const struct mmio_handler *handler, struct vcpu *v,
64                         mmio_info_t *info)
65 {
66     const struct hsr_dabt dabt = info->dabt;
67     struct cpu_user_regs *regs = guest_cpu_user_regs();
68 
69     return handler->ops->write(v, info, get_user_reg(regs, dabt.reg),
70                                handler->priv);
71 }
72 
73 /* This function assumes that mmio regions are not overlapped */
cmp_mmio_handler(const void * key,const void * elem)74 static int cmp_mmio_handler(const void *key, const void *elem)
75 {
76     const struct mmio_handler *handler0 = key;
77     const struct mmio_handler *handler1 = elem;
78 
79     if ( handler0->addr < handler1->addr )
80         return -1;
81 
82     if ( handler0->addr >= (handler1->addr + handler1->size) )
83         return 1;
84 
85     return 0;
86 }
87 
find_mmio_handler(struct domain * d,paddr_t gpa)88 static const struct mmio_handler *find_mmio_handler(struct domain *d,
89                                                     paddr_t gpa)
90 {
91     struct vmmio *vmmio = &d->arch.vmmio;
92     struct mmio_handler key = {.addr = gpa};
93     const struct mmio_handler *handler;
94 
95     read_lock(&vmmio->lock);
96     handler = bsearch(&key, vmmio->handlers, vmmio->num_entries,
97                       sizeof(*handler), cmp_mmio_handler);
98     read_unlock(&vmmio->lock);
99 
100     return handler;
101 }
102 
handle_mmio(mmio_info_t * info)103 int handle_mmio(mmio_info_t *info)
104 {
105     struct vcpu *v = current;
106     const struct mmio_handler *handler = NULL;
107 
108     handler = find_mmio_handler(v->domain, info->gpa);
109     if ( !handler )
110         return 0;
111 
112     if ( info->dabt.write )
113         return handle_write(handler, v, info);
114     else
115         return handle_read(handler, v, info);
116 }
117 
register_mmio_handler(struct domain * d,const struct mmio_handler_ops * ops,paddr_t addr,paddr_t size,void * priv)118 void register_mmio_handler(struct domain *d,
119                            const struct mmio_handler_ops *ops,
120                            paddr_t addr, paddr_t size, void *priv)
121 {
122     struct vmmio *vmmio = &d->arch.vmmio;
123     struct mmio_handler *handler;
124 
125     BUG_ON(vmmio->num_entries >= vmmio->max_num_entries);
126 
127     write_lock(&vmmio->lock);
128 
129     handler = &vmmio->handlers[vmmio->num_entries];
130 
131     handler->ops = ops;
132     handler->addr = addr;
133     handler->size = size;
134     handler->priv = priv;
135 
136     vmmio->num_entries++;
137 
138     /* Sort mmio handlers in ascending order based on base address */
139     sort(vmmio->handlers, vmmio->num_entries, sizeof(struct mmio_handler),
140          cmp_mmio_handler, NULL);
141 
142     write_unlock(&vmmio->lock);
143 }
144 
domain_io_init(struct domain * d,int max_count)145 int domain_io_init(struct domain *d, int max_count)
146 {
147     rwlock_init(&d->arch.vmmio.lock);
148     d->arch.vmmio.num_entries = 0;
149     d->arch.vmmio.max_num_entries = max_count;
150     d->arch.vmmio.handlers = xzalloc_array(struct mmio_handler, max_count);
151     if ( !d->arch.vmmio.handlers )
152         return -ENOMEM;
153 
154     return 0;
155 }
156 
domain_io_free(struct domain * d)157 void domain_io_free(struct domain *d)
158 {
159     xfree(d->arch.vmmio.handlers);
160 }
161 
162 /*
163  * Local variables:
164  * mode: C
165  * c-file-style: "BSD"
166  * c-basic-offset: 4
167  * indent-tabs-mode: nil
168  * End:
169  */
170