Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1186 | savelij | 1 | /* cpu2phys.c */ |
2 | /*****************************************************************************/ |
||
3 | /* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ |
||
4 | /* */ |
||
5 | /* AS */ |
||
6 | /* */ |
||
7 | /* CPU->Physical Address Translation */ |
||
8 | /* */ |
||
9 | /*****************************************************************************/ |
||
10 | |||
11 | #include "stdinc.h" |
||
12 | #include <stddef.h> |
||
13 | #include <assert.h> |
||
14 | #include <string.h> |
||
15 | #include "strutil.h" |
||
16 | #include "tempresult.h" |
||
17 | #include "asmdef.h" |
||
18 | #include "cpu2phys.h" |
||
19 | |||
20 | typedef struct |
||
21 | { |
||
22 | LargeWord cpu_start, cpu_end; |
||
23 | LargeWord phys_start, phys_end; |
||
24 | } cpu_2_phys_area_t; |
||
25 | |||
26 | typedef struct |
||
27 | { |
||
28 | cpu_2_phys_area_t *areas; |
||
29 | size_t capacity; |
||
30 | size_t cnt; |
||
31 | LargeWord end; |
||
32 | } cpu_2_phys_area_list_t; |
||
33 | |||
34 | static cpu_2_phys_area_list_t area_lists[SegCount]; |
||
35 | |||
36 | /*!------------------------------------------------------------------------ |
||
37 | * \fn cpu_2_phys_area_t *insert_area(cpu_2_phys_area_list_t *p_list, unsigned index) |
||
38 | * \brief make space for area at given index, implicitly increasing array size |
||
39 | * \param p_list list to operate on |
||
40 | * \param index place to insert |
||
41 | * \return * to freed and cleared area |
||
42 | * ------------------------------------------------------------------------ */ |
||
43 | |||
44 | static cpu_2_phys_area_t *insert_area(cpu_2_phys_area_list_t *p_list, unsigned index) |
||
45 | { |
||
46 | cpu_2_phys_area_t *p_ret; |
||
47 | |||
48 | assert(index <= p_list->cnt); |
||
49 | |||
50 | if (p_list->cnt >= p_list->capacity) |
||
51 | { |
||
52 | size_t new_capacity = p_list->cnt + 1; |
||
53 | size_t new_alloc = new_capacity * sizeof(*p_list->areas); |
||
54 | p_list->areas = p_list->capacity |
||
55 | ? (cpu_2_phys_area_t*) realloc(p_list->areas, new_alloc) |
||
56 | : (cpu_2_phys_area_t*) malloc(new_alloc); |
||
57 | p_list->capacity = new_capacity; |
||
58 | } |
||
59 | p_ret = &p_list->areas[index]; |
||
60 | p_list->cnt++; |
||
61 | memmove(p_ret + 1, p_ret, sizeof(*p_ret) * (p_list->cnt - 1 - index)); |
||
62 | memset(p_ret, 0, sizeof(*p_ret)); |
||
63 | return p_ret; |
||
64 | } |
||
65 | |||
66 | /*!------------------------------------------------------------------------ |
||
67 | * \fn delete_area(cpu_2_phys_area_list_t *p_list, unsigned index) |
||
68 | * \brief delete entry from table |
||
69 | * \param p_list list to operate on |
||
70 | * \param index place to delete |
||
71 | * ------------------------------------------------------------------------ */ |
||
72 | |||
73 | static void delete_area(cpu_2_phys_area_list_t *p_list, unsigned index) |
||
74 | { |
||
75 | cpu_2_phys_area_t *p_ret; |
||
76 | |||
77 | assert(index < p_list->cnt); |
||
78 | p_ret = &p_list->areas[index]; |
||
79 | memmove(p_ret, p_ret + 1, sizeof(*p_ret) * (p_list->cnt - 1 - index)); |
||
80 | p_list->cnt--; |
||
81 | } |
||
82 | |||
83 | /*!------------------------------------------------------------------------ |
||
84 | * \fn get_list(as_addrspace_t addr_space) |
||
85 | * \brief retrieve list per address space |
||
86 | * \param addr_space address space |
||
87 | * \return * to list |
||
88 | * ------------------------------------------------------------------------ */ |
||
89 | |||
90 | static cpu_2_phys_area_list_t *get_list(as_addrspace_t addr_space) |
||
91 | { |
||
92 | assert(addr_space < SegCount); |
||
93 | return &area_lists[addr_space]; |
||
94 | } |
||
95 | |||
96 | /*!------------------------------------------------------------------------ |
||
97 | * \fn cpu_2_phys_area_clear(as_addrspace_t addr_space) |
||
98 | * \brief clear tables before adding new ones |
||
99 | * \param addr_space address space to operate on |
||
100 | * ------------------------------------------------------------------------ */ |
||
101 | |||
102 | void cpu_2_phys_area_clear(as_addrspace_t addr_space) |
||
103 | { |
||
104 | assert(addr_space < SegCount); |
||
105 | area_lists[addr_space].cnt = 0; |
||
106 | area_lists[addr_space].end = 0; |
||
107 | } |
||
108 | |||
109 | /*!------------------------------------------------------------------------ |
||
110 | * \fn cpu_2_phys_area_add(as_addrspace_t addr_space, LargeWord new_cpu_start, LargeWord new_phys_start, LargeWord new_len) |
||
111 | * \brief add another area |
||
112 | * \param addr_space address space to operate on |
||
113 | * \param new_cpu_start start address in CPU address space |
||
114 | * \param new_phys_start start address in physical address space |
||
115 | * \param new_len area size in <gran> |
||
116 | * ------------------------------------------------------------------------ */ |
||
117 | |||
118 | void cpu_2_phys_area_add(as_addrspace_t addr_space, LargeWord new_cpu_start, LargeWord new_phys_start, LargeWord new_len) |
||
119 | { |
||
120 | size_t z, z2; |
||
121 | cpu_2_phys_area_list_t *p_list = get_list(addr_space); |
||
122 | cpu_2_phys_area_t *p_area; |
||
123 | LargeWord new_cpu_end = new_cpu_start + (new_len - 1); |
||
124 | LargeWord overlap_start, overlap_end, delta; |
||
125 | Boolean change; |
||
126 | |||
127 | /* Do not add zero-length areas */ |
||
128 | |||
129 | if (!new_len) |
||
130 | return; |
||
131 | |||
132 | /* sort in according to CPU start address */ |
||
133 | |||
134 | for (z = 0; z < p_list->cnt; z++) |
||
135 | if (p_list->areas[z].cpu_start >= new_cpu_start) |
||
136 | break; |
||
137 | |||
138 | p_area = insert_area(p_list, z); |
||
139 | p_area->cpu_start = new_cpu_start; |
||
140 | p_area->cpu_end = new_cpu_end; |
||
141 | p_area->phys_start = new_phys_start; |
||
142 | p_area->phys_end = new_phys_start + (new_len - 1); |
||
143 | |||
144 | /* shrink/delete (partially) overlapping areas: */ |
||
145 | |||
146 | do |
||
147 | { |
||
148 | change = False; |
||
149 | for (z2 = 0; z2 < p_list->cnt; z2++) |
||
150 | { |
||
151 | /* do not test overlap with new entry itself */ |
||
152 | |||
153 | if (z2 == z) |
||
154 | continue; |
||
155 | |||
156 | /* deduce overlapping area */ |
||
157 | |||
158 | overlap_start = max(p_list->areas[z].cpu_start, p_list->areas[z2].cpu_start); |
||
159 | overlap_end = min(p_list->areas[z].cpu_end, p_list->areas[z2].cpu_end); |
||
160 | if (overlap_start >= overlap_end) |
||
161 | continue; |
||
162 | |||
163 | /* Delete old area entirely? -> possibly correct index of new entry and restart */ |
||
164 | |||
165 | if ((overlap_start == p_list->areas[z2].cpu_start) |
||
166 | && (overlap_end == p_list->areas[z2].cpu_end)) |
||
167 | { |
||
168 | delete_area(p_list, z2); |
||
169 | if (z2 < z) |
||
170 | z--; |
||
171 | change = True; |
||
172 | break; |
||
173 | } |
||
174 | |||
175 | /* shorten old area at beginning? */ |
||
176 | |||
177 | else if (overlap_start == p_list->areas[z2].cpu_start) |
||
178 | { |
||
179 | delta = overlap_end - overlap_start + 1; |
||
180 | p_list->areas[z2].cpu_start += delta; |
||
181 | p_list->areas[z2].phys_start += delta; |
||
182 | } |
||
183 | |||
184 | /* shorten old area at end? */ |
||
185 | |||
186 | else if (overlap_end == p_list->areas[z2].cpu_end) |
||
187 | { |
||
188 | delta = overlap_end - overlap_start + 1; |
||
189 | p_list->areas[z2].cpu_end -= delta; |
||
190 | p_list->areas[z2].phys_end -= delta; |
||
191 | } |
||
192 | |||
193 | /* Overlap cuts out in the mid, split into parts. Assuming the addresses were sorted, |
||
194 | z2 equals z - 1 and the old area's part surround the new one: */ |
||
195 | |||
196 | else |
||
197 | { |
||
198 | cpu_2_phys_area_t save = p_list->areas[z2]; |
||
199 | |||
200 | delta = save.cpu_end - overlap_start + 1; |
||
201 | p_list->areas[z2].cpu_end -= delta; |
||
202 | p_list->areas[z2].phys_end -= delta; |
||
203 | p_area = insert_area(p_list, z + 1); |
||
204 | *p_area = save; |
||
205 | delta = overlap_end - save.cpu_start + 1; |
||
206 | p_area->cpu_start += delta; |
||
207 | p_area->phys_start += delta; |
||
208 | change = True; |
||
209 | break; |
||
210 | } |
||
211 | } |
||
212 | } |
||
213 | while (change); |
||
214 | } |
||
215 | |||
216 | /*!------------------------------------------------------------------------ |
||
217 | * \fn cpu_2_phys_area_set_cpu_end(as_addrspace_t addr_space, LargeWord cpu_end) |
||
218 | * \brief set the end of the CPU's address space |
||
219 | * \param addr_space address space to operate on |
||
220 | * \param cpu_end end of CPU address space |
||
221 | * ------------------------------------------------------------------------ */ |
||
222 | |||
223 | void cpu_2_phys_area_set_cpu_end(as_addrspace_t addr_space, LargeWord cpu_end) |
||
224 | { |
||
225 | cpu_2_phys_area_list_t *p_list = get_list(addr_space); |
||
226 | |||
227 | p_list->end = cpu_end; |
||
228 | } |
||
229 | |||
230 | /*!------------------------------------------------------------------------ |
||
231 | * \fn cpu_2_phys_area_fill(as_addrspace_t addr_space, LargeWord cpu_start, LargeWord cpu_end) |
||
232 | * \brief fill gaps in the CPU-side address space with 1:1 mappings |
||
233 | * \param addr_space address space to operate on |
||
234 | * \param cpu_start start of CPU address space |
||
235 | * \param cpu_end end of CPU address space |
||
236 | * ------------------------------------------------------------------------ */ |
||
237 | |||
238 | void cpu_2_phys_area_fill(as_addrspace_t addr_space, LargeWord cpu_start, LargeWord cpu_end) |
||
239 | { |
||
240 | LargeWord expected_start; |
||
241 | size_t z; |
||
242 | cpu_2_phys_area_list_t *p_list = get_list(addr_space); |
||
243 | cpu_2_phys_area_t *p_area; |
||
244 | |||
245 | /* For each mapping, check whether there is no gap between its start address |
||
246 | and the predecessor's end address. If so, insert a 1:1 mapped area to |
||
247 | fill the gap: */ |
||
248 | |||
249 | z = 0; |
||
250 | while (z < p_list->cnt) |
||
251 | { |
||
252 | expected_start = z ? p_list->areas[z - 1].cpu_end + 1 : cpu_start; |
||
253 | |||
254 | /* no gap -> just continue with the next entry */ |
||
255 | |||
256 | if ((z < p_list->cnt) && (p_list->areas[z].cpu_start <= expected_start)) |
||
257 | { |
||
258 | z++; |
||
259 | continue; |
||
260 | } |
||
261 | p_area = insert_area(p_list, z); |
||
262 | p_area->cpu_start = |
||
263 | p_area->phys_start = expected_start; |
||
264 | p_area->cpu_end = |
||
265 | p_area->phys_end = (z + 1 < p_list->cnt) ? p_list->areas[z + 1].cpu_start - 1 : cpu_end; |
||
266 | |||
267 | /* We know the entries at z (freshly inserted) and z+1 (moved up one index) are |
||
268 | continuous, so we may increase the counter by two: */ |
||
269 | |||
270 | z += 2; |
||
271 | } |
||
272 | |||
273 | /* Do the same test once again at the very end of the array, to be sure it covers everything |
||
274 | up to the given cpu_end: */ |
||
275 | |||
276 | if (!p_list->cnt || p_list->areas[p_list->cnt - 1].cpu_end < cpu_end) |
||
277 | { |
||
278 | expected_start = p_list->cnt ? p_list->areas[p_list->cnt - 1].cpu_end + 1 : cpu_start; |
||
279 | p_area = insert_area(p_list, p_list->cnt); |
||
280 | p_area->cpu_start = |
||
281 | p_area->phys_start = expected_start; |
||
282 | p_area->cpu_end = |
||
283 | p_area->phys_end = cpu_end; |
||
284 | } |
||
285 | |||
286 | /* Save the cpu_end for usage in phys_2_cpu(): */ |
||
287 | |||
288 | cpu_2_phys_area_set_cpu_end(addr_space, cpu_end); |
||
289 | } |
||
290 | |||
291 | /*!------------------------------------------------------------------------ |
||
292 | * \fn cpu_2_phys_area_dump(as_addrspace_t addr_space, FILE *p_file) |
||
293 | * \brief output current mapping |
||
294 | * \param addr_space address space to operate on |
||
295 | * \param p_file where to dump |
||
296 | * ------------------------------------------------------------------------ */ |
||
297 | |||
298 | void cpu_2_phys_area_dump(as_addrspace_t addr_space, FILE *p_file) |
||
299 | { |
||
300 | cpu_2_phys_area_list_t *p_list = get_list(addr_space); |
||
301 | char str[100]; |
||
302 | size_t z; |
||
303 | int cpu_max_len = 0, phys_max_len = 0; |
||
304 | |||
305 | for (z = 0; z < p_list->cnt; z++) |
||
306 | { |
||
307 | int this_len; |
||
308 | |||
309 | this_len = as_snprintf(str, sizeof str, "%lllx", p_list->areas[z].cpu_end); |
||
310 | if (this_len > cpu_max_len) |
||
311 | cpu_max_len = this_len; |
||
312 | this_len = as_snprintf(str, sizeof str, "%lllx", p_list->areas[z].phys_end); |
||
313 | if (this_len > phys_max_len) |
||
314 | phys_max_len = this_len; |
||
315 | } |
||
316 | for (z = 0; z < p_list->cnt; z++) |
||
317 | { |
||
318 | as_snprintf(str, sizeof str, "[%2u] %0*lllx...%0*lllx -> %0*lllx...%0*lllx\n", z, |
||
319 | cpu_max_len, p_list->areas[z].cpu_start, |
||
320 | cpu_max_len, p_list->areas[z].cpu_end, |
||
321 | phys_max_len, p_list->areas[z].phys_start, |
||
322 | phys_max_len, p_list->areas[z].phys_end); |
||
323 | fputs(str, p_file); |
||
324 | } |
||
325 | } |
||
326 | |||
327 | /*!------------------------------------------------------------------------ |
||
328 | * \fn def_phys_2_cpu(as_addrspace_t addr_space, LargeWord *p_address) |
||
329 | * \brief physical -> CPU address translation |
||
330 | * \param addr_space address space to operate on |
||
331 | * \param p_address address to translate |
||
332 | * \return True if translation succeeded |
||
333 | * ------------------------------------------------------------------------ */ |
||
334 | |||
335 | Boolean def_phys_2_cpu(as_addrspace_t addr_space, LargeWord *p_address) |
||
336 | { |
||
337 | cpu_2_phys_area_list_t *p_list = get_list(addr_space); |
||
338 | size_t win; |
||
339 | |||
340 | for (win = 0; win < p_list->cnt; win++) |
||
341 | if ((*p_address >= p_list->areas[win].phys_start) && (*p_address <= p_list->areas[win].phys_end)) |
||
342 | { |
||
343 | *p_address = (*p_address - p_list->areas[win].phys_start) + p_list->areas[win].cpu_start; |
||
344 | return True; |
||
345 | } |
||
346 | return False; |
||
347 | } |
||
348 | |||
349 | /*!------------------------------------------------------------------------ |
||
350 | * \fn def_cpu_2_phys(as_addrspace_t addr_space, LargeWord *p_address) |
||
351 | * \brief CPU -> physical address translation |
||
352 | * \param addr_space address space to operate on |
||
353 | * \param p_address address to translate |
||
354 | * \return True if translation succeeded |
||
355 | * ------------------------------------------------------------------------ */ |
||
356 | |||
357 | Boolean def_cpu_2_phys(as_addrspace_t addr_space, LargeWord *p_address) |
||
358 | { |
||
359 | cpu_2_phys_area_list_t *p_list = get_list(addr_space); |
||
360 | size_t win; |
||
361 | |||
362 | for (win = 0; win < p_list->cnt; win++) |
||
363 | if ((*p_address >= p_list->areas[win].cpu_start) && (*p_address <= p_list->areas[win].cpu_end)) |
||
364 | { |
||
365 | *p_address = (*p_address - p_list->areas[win].cpu_start) + p_list->areas[win].phys_start; |
||
366 | return True; |
||
367 | } |
||
368 | return False; |
||
369 | } |
||
370 | |||
371 | /*!------------------------------------------------------------------------ |
||
372 | * \fn fnc_phys_2_cpu(TempResult *p_ret, const TempResult *p_args, unsigned arg_cnt) |
||
373 | * \brief built-in function for physical -> CPU address translation |
||
374 | * \param p_ret returns CPU address |
||
375 | * \param p_args physical address argument |
||
376 | * \return True if translation possible at all |
||
377 | * ------------------------------------------------------------------------ */ |
||
378 | |||
379 | Boolean fnc_phys_2_cpu(TempResult *p_ret, const TempResult *p_args, unsigned arg_cnt) |
||
380 | { |
||
381 | LargeWord address; |
||
382 | |||
383 | UNUSED(arg_cnt); |
||
384 | |||
385 | if (!area_lists[ActPC].cnt) |
||
386 | return False; |
||
387 | |||
388 | address = p_args[0].Contents.Int; |
||
389 | as_tempres_set_int(p_ret, def_phys_2_cpu(ActPC, &address) ? address : area_lists[ActPC].end + 1); |
||
390 | return True; |
||
391 | } |
||
392 | |||
393 | /*!------------------------------------------------------------------------ |
||
394 | * \fn fnc_cpu_2_phys(TempResult *p_ret, const TempResult *p_args, unsigned arg_cnt) |
||
395 | * \brief built-in function for CPU -> physical address translation |
||
396 | * \param p_ret returns physical address |
||
397 | * \param p_args CPU address argument |
||
398 | * \return True if translation possible at all |
||
399 | * ------------------------------------------------------------------------ */ |
||
400 | |||
401 | Boolean fnc_cpu_2_phys(TempResult *p_ret, const TempResult *p_args, unsigned arg_cnt) |
||
402 | { |
||
403 | LargeWord address; |
||
404 | |||
405 | UNUSED(arg_cnt); |
||
406 | |||
407 | if (!area_lists[ActPC].cnt) |
||
408 | return False; |
||
409 | |||
410 | address = p_args[0].Contents.Int; |
||
411 | as_tempres_set_int(p_ret, def_cpu_2_phys(ActPC, &address) ? address : SegLimits[ActPC] + 1); |
||
412 | return True; |
||
413 | } |