]>
Commit | Line | Data |
---|---|---|
b20b7b7a MR |
1 | /* |
2 | * QEMU SPAPR Architecture Option Vector Helper Functions | |
3 | * | |
4 | * Copyright IBM Corp. 2016 | |
5 | * | |
6 | * Authors: | |
7 | * Bharata B Rao <bharata@linux.vnet.ibm.com> | |
8 | * Michael Roth <mdroth@linux.vnet.ibm.com> | |
9 | * | |
10 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
11 | * See the COPYING file in the top-level directory. | |
12 | */ | |
13 | ||
14 | #include "qemu/osdep.h" | |
15 | #include "hw/ppc/spapr_ovec.h" | |
d6454270 | 16 | #include "migration/vmstate.h" |
b20b7b7a MR |
17 | #include "qemu/bitmap.h" |
18 | #include "exec/address-spaces.h" | |
19 | #include "qemu/error-report.h" | |
5b929608 | 20 | #include "trace.h" |
b20b7b7a MR |
21 | #include <libfdt.h> |
22 | ||
b20b7b7a MR |
23 | #define OV_MAXBYTES 256 /* not including length byte */ |
24 | #define OV_MAXBITS (OV_MAXBYTES * BITS_PER_BYTE) | |
25 | ||
26 | /* we *could* work with bitmaps directly, but handling the bitmap privately | |
27 | * allows us to more safely make assumptions about the bitmap size and | |
28 | * simplify the calling code somewhat | |
29 | */ | |
ce2918cb | 30 | struct SpaprOptionVector { |
b20b7b7a | 31 | unsigned long *bitmap; |
62ef3760 MR |
32 | int32_t bitmap_size; /* only used for migration */ |
33 | }; | |
34 | ||
35 | const VMStateDescription vmstate_spapr_ovec = { | |
36 | .name = "spapr_option_vector", | |
37 | .version_id = 1, | |
38 | .minimum_version_id = 1, | |
39 | .fields = (VMStateField[]) { | |
ce2918cb | 40 | VMSTATE_BITMAP(bitmap, SpaprOptionVector, 1, bitmap_size), |
62ef3760 MR |
41 | VMSTATE_END_OF_LIST() |
42 | } | |
b20b7b7a MR |
43 | }; |
44 | ||
ce2918cb | 45 | SpaprOptionVector *spapr_ovec_new(void) |
b20b7b7a | 46 | { |
ce2918cb | 47 | SpaprOptionVector *ov; |
b20b7b7a | 48 | |
ce2918cb | 49 | ov = g_new0(SpaprOptionVector, 1); |
b20b7b7a | 50 | ov->bitmap = bitmap_new(OV_MAXBITS); |
62ef3760 | 51 | ov->bitmap_size = OV_MAXBITS; |
b20b7b7a MR |
52 | |
53 | return ov; | |
54 | } | |
55 | ||
ce2918cb | 56 | SpaprOptionVector *spapr_ovec_clone(SpaprOptionVector *ov_orig) |
b20b7b7a | 57 | { |
ce2918cb | 58 | SpaprOptionVector *ov; |
b20b7b7a MR |
59 | |
60 | g_assert(ov_orig); | |
61 | ||
62 | ov = spapr_ovec_new(); | |
63 | bitmap_copy(ov->bitmap, ov_orig->bitmap, OV_MAXBITS); | |
64 | ||
65 | return ov; | |
66 | } | |
67 | ||
ce2918cb DG |
68 | void spapr_ovec_intersect(SpaprOptionVector *ov, |
69 | SpaprOptionVector *ov1, | |
70 | SpaprOptionVector *ov2) | |
b20b7b7a MR |
71 | { |
72 | g_assert(ov); | |
73 | g_assert(ov1); | |
74 | g_assert(ov2); | |
75 | ||
76 | bitmap_and(ov->bitmap, ov1->bitmap, ov2->bitmap, OV_MAXBITS); | |
77 | } | |
78 | ||
d1d32d62 DG |
79 | /* returns true if ov1 has a subset of bits in ov2 */ |
80 | bool spapr_ovec_subset(SpaprOptionVector *ov1, SpaprOptionVector *ov2) | |
b20b7b7a | 81 | { |
d1d32d62 DG |
82 | unsigned long *tmp = bitmap_new(OV_MAXBITS); |
83 | bool result; | |
b20b7b7a | 84 | |
d1d32d62 DG |
85 | g_assert(ov1); |
86 | g_assert(ov2); | |
b20b7b7a | 87 | |
d1d32d62 DG |
88 | bitmap_andnot(tmp, ov1->bitmap, ov2->bitmap, OV_MAXBITS); |
89 | result = bitmap_empty(tmp, OV_MAXBITS); | |
b20b7b7a | 90 | |
d1d32d62 | 91 | g_free(tmp); |
b20b7b7a | 92 | |
d1d32d62 | 93 | return result; |
b20b7b7a MR |
94 | } |
95 | ||
ce2918cb | 96 | void spapr_ovec_cleanup(SpaprOptionVector *ov) |
b20b7b7a MR |
97 | { |
98 | if (ov) { | |
99 | g_free(ov->bitmap); | |
100 | g_free(ov); | |
101 | } | |
102 | } | |
103 | ||
ce2918cb | 104 | void spapr_ovec_set(SpaprOptionVector *ov, long bitnr) |
b20b7b7a MR |
105 | { |
106 | g_assert(ov); | |
719a3077 | 107 | g_assert(bitnr < OV_MAXBITS); |
b20b7b7a MR |
108 | |
109 | set_bit(bitnr, ov->bitmap); | |
110 | } | |
111 | ||
ce2918cb | 112 | void spapr_ovec_clear(SpaprOptionVector *ov, long bitnr) |
b20b7b7a MR |
113 | { |
114 | g_assert(ov); | |
719a3077 | 115 | g_assert(bitnr < OV_MAXBITS); |
b20b7b7a MR |
116 | |
117 | clear_bit(bitnr, ov->bitmap); | |
118 | } | |
119 | ||
ce2918cb | 120 | bool spapr_ovec_test(SpaprOptionVector *ov, long bitnr) |
b20b7b7a MR |
121 | { |
122 | g_assert(ov); | |
719a3077 | 123 | g_assert(bitnr < OV_MAXBITS); |
b20b7b7a MR |
124 | |
125 | return test_bit(bitnr, ov->bitmap) ? true : false; | |
126 | } | |
127 | ||
128 | static void guest_byte_to_bitmap(uint8_t entry, unsigned long *bitmap, | |
129 | long bitmap_offset) | |
130 | { | |
131 | int i; | |
132 | ||
133 | for (i = 0; i < BITS_PER_BYTE; i++) { | |
134 | if (entry & (1 << (BITS_PER_BYTE - 1 - i))) { | |
135 | bitmap_set(bitmap, bitmap_offset + i, 1); | |
136 | } | |
137 | } | |
138 | } | |
139 | ||
140 | static uint8_t guest_byte_from_bitmap(unsigned long *bitmap, long bitmap_offset) | |
141 | { | |
142 | uint8_t entry = 0; | |
143 | int i; | |
144 | ||
145 | for (i = 0; i < BITS_PER_BYTE; i++) { | |
146 | if (test_bit(bitmap_offset + i, bitmap)) { | |
147 | entry |= (1 << (BITS_PER_BYTE - 1 - i)); | |
148 | } | |
149 | } | |
150 | ||
151 | return entry; | |
152 | } | |
153 | ||
154 | static target_ulong vector_addr(target_ulong table_addr, int vector) | |
155 | { | |
156 | uint16_t vector_count, vector_len; | |
157 | int i; | |
158 | ||
159 | vector_count = ldub_phys(&address_space_memory, table_addr) + 1; | |
160 | if (vector > vector_count) { | |
161 | return 0; | |
162 | } | |
163 | table_addr++; /* skip nr option vectors */ | |
164 | ||
165 | for (i = 0; i < vector - 1; i++) { | |
166 | vector_len = ldub_phys(&address_space_memory, table_addr) + 1; | |
167 | table_addr += vector_len + 1; /* bit-vector + length byte */ | |
168 | } | |
169 | return table_addr; | |
170 | } | |
171 | ||
ce2918cb | 172 | SpaprOptionVector *spapr_ovec_parse_vector(target_ulong table_addr, int vector) |
b20b7b7a | 173 | { |
ce2918cb | 174 | SpaprOptionVector *ov; |
b20b7b7a MR |
175 | target_ulong addr; |
176 | uint16_t vector_len; | |
177 | int i; | |
178 | ||
179 | g_assert(table_addr); | |
719a3077 | 180 | g_assert(vector >= 1); /* vector numbering starts at 1 */ |
b20b7b7a MR |
181 | |
182 | addr = vector_addr(table_addr, vector); | |
183 | if (!addr) { | |
184 | /* specified vector isn't present */ | |
185 | return NULL; | |
186 | } | |
187 | ||
188 | vector_len = ldub_phys(&address_space_memory, addr++) + 1; | |
719a3077 | 189 | g_assert(vector_len <= OV_MAXBYTES); |
b20b7b7a MR |
190 | ov = spapr_ovec_new(); |
191 | ||
192 | for (i = 0; i < vector_len; i++) { | |
193 | uint8_t entry = ldub_phys(&address_space_memory, addr + i); | |
194 | if (entry) { | |
5b929608 | 195 | trace_spapr_ovec_parse_vector(vector, i + 1, vector_len, entry); |
b20b7b7a MR |
196 | guest_byte_to_bitmap(entry, ov->bitmap, i * BITS_PER_BYTE); |
197 | } | |
198 | } | |
199 | ||
200 | return ov; | |
201 | } | |
202 | ||
91335a5e DG |
203 | int spapr_dt_ovec(void *fdt, int fdt_offset, |
204 | SpaprOptionVector *ov, const char *name) | |
b20b7b7a MR |
205 | { |
206 | uint8_t vec[OV_MAXBYTES + 1]; | |
207 | uint16_t vec_len; | |
208 | unsigned long lastbit; | |
209 | int i; | |
210 | ||
211 | g_assert(ov); | |
212 | ||
213 | lastbit = find_last_bit(ov->bitmap, OV_MAXBITS); | |
214 | /* if no bits are set, include at least 1 byte of the vector so we can | |
215 | * still encoded this in the device tree while abiding by the same | |
216 | * encoding/sizing expected in ibm,client-architecture-support | |
217 | */ | |
218 | vec_len = (lastbit == OV_MAXBITS) ? 1 : lastbit / BITS_PER_BYTE + 1; | |
719a3077 | 219 | g_assert(vec_len <= OV_MAXBYTES); |
b20b7b7a MR |
220 | /* guest expects vector len encoded as vec_len - 1, since the length byte |
221 | * is assumed and not included, and the first byte of the vector | |
222 | * is assumed as well | |
223 | */ | |
224 | vec[0] = vec_len - 1; | |
225 | ||
226 | for (i = 1; i < vec_len + 1; i++) { | |
227 | vec[i] = guest_byte_from_bitmap(ov->bitmap, (i - 1) * BITS_PER_BYTE); | |
228 | if (vec[i]) { | |
5b929608 | 229 | trace_spapr_ovec_populate_dt(i, vec_len, vec[i]); |
b20b7b7a MR |
230 | } |
231 | } | |
232 | ||
fe93e3e6 | 233 | return fdt_setprop(fdt, fdt_offset, name, vec, vec_len + 1); |
b20b7b7a | 234 | } |