remap.c 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. #include <linux/vmalloc.h>
  2. #include <asm/pgalloc.h>
  3. #include <asm/cacheflush.h>
  4. /* called with the page_table_lock held */
  5. static inline void
  6. remap_area_pte(pte_t * pte, unsigned long address, unsigned long size,
  7. unsigned long phys_addr, unsigned long flags)
  8. {
  9. unsigned long end;
  10. unsigned long pfn;
  11. address &= ~PMD_MASK;
  12. end = address + size;
  13. if (end > PMD_SIZE)
  14. end = PMD_SIZE;
  15. if (address >= end)
  16. BUG();
  17. pfn = phys_addr >> PAGE_SHIFT;
  18. do {
  19. if (!pte_none(*pte)) {
  20. printk("remap_area_pte: page already exists\n");
  21. BUG();
  22. }
  23. set_pte(pte, pfn_pte(pfn,
  24. __pgprot(_PAGE_VALID | _PAGE_ASM |
  25. _PAGE_KRE | _PAGE_KWE | flags)));
  26. address += PAGE_SIZE;
  27. pfn++;
  28. pte++;
  29. } while (address && (address < end));
  30. }
  31. /* called with the page_table_lock held */
  32. static inline int
  33. remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size,
  34. unsigned long phys_addr, unsigned long flags)
  35. {
  36. unsigned long end;
  37. address &= ~PGDIR_MASK;
  38. end = address + size;
  39. if (end > PGDIR_SIZE)
  40. end = PGDIR_SIZE;
  41. phys_addr -= address;
  42. if (address >= end)
  43. BUG();
  44. do {
  45. pte_t * pte = pte_alloc_kernel(&init_mm, pmd, address);
  46. if (!pte)
  47. return -ENOMEM;
  48. remap_area_pte(pte, address, end - address,
  49. address + phys_addr, flags);
  50. address = (address + PMD_SIZE) & PMD_MASK;
  51. pmd++;
  52. } while (address && (address < end));
  53. return 0;
  54. }
  55. int
  56. __alpha_remap_area_pages(unsigned long address, unsigned long phys_addr,
  57. unsigned long size, unsigned long flags)
  58. {
  59. pgd_t * dir;
  60. int error = 0;
  61. unsigned long end = address + size;
  62. phys_addr -= address;
  63. dir = pgd_offset(&init_mm, address);
  64. flush_cache_all();
  65. if (address >= end)
  66. BUG();
  67. spin_lock(&init_mm.page_table_lock);
  68. do {
  69. pmd_t *pmd;
  70. pmd = pmd_alloc(&init_mm, dir, address);
  71. error = -ENOMEM;
  72. if (!pmd)
  73. break;
  74. if (remap_area_pmd(pmd, address, end - address,
  75. phys_addr + address, flags))
  76. break;
  77. error = 0;
  78. address = (address + PGDIR_SIZE) & PGDIR_MASK;
  79. dir++;
  80. } while (address && (address < end));
  81. spin_unlock(&init_mm.page_table_lock);
  82. return error;
  83. }