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