Subversion Repositories pentevo

Rev

Blame | Last modification | View Log | Download | RSS feed | ?url?

  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. }
  414.