Subversion Repositories pentevo

Rev

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
}