generic.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:4k
源码类别:

嵌入式Linux

开发平台:

Unix_Linux

  1. /* $Id: generic.c,v 1.17 2001/04/09 04:08:06 davem Exp $
  2.  * generic.c: Generic Sparc mm routines that are not dependent upon
  3.  *            MMU type but are Sparc specific.
  4.  *
  5.  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
  6.  */
  7. #include <linux/kernel.h>
  8. #include <linux/mm.h>
  9. #include <linux/swap.h>
  10. #include <linux/pagemap.h>
  11. #include <asm/pgalloc.h>
  12. #include <asm/pgtable.h>
  13. #include <asm/page.h>
  14. static inline void forget_pte(pte_t page)
  15. {
  16. if (pte_none(page))
  17. return;
  18. if (pte_present(page)) {
  19. struct page *ptpage = pte_page(page);
  20. if ((!VALID_PAGE(ptpage)) || PageReserved(ptpage))
  21. return;
  22. page_cache_release(ptpage);
  23. return;
  24. }
  25. swap_free(pte_to_swp_entry(page));
  26. }
  27. /* Remap IO memory, the same way as remap_page_range(), but use
  28.  * the obio memory space.
  29.  *
  30.  * They use a pgprot that sets PAGE_IO and does not check the
  31.  * mem_map table as this is independent of normal memory.
  32.  *
  33.  * As a special hack if the lowest bit of offset is set the
  34.  * side-effect bit will be turned off.  This is used as a
  35.  * performance improvement on FFB/AFB. -DaveM
  36.  */
  37. static inline void io_remap_pte_range(pte_t * pte, unsigned long address, unsigned long size,
  38. unsigned long offset, pgprot_t prot, int space)
  39. {
  40. unsigned long end;
  41. address &= ~PMD_MASK;
  42. end = address + size;
  43. if (end > PMD_SIZE)
  44. end = PMD_SIZE;
  45. do {
  46. pte_t oldpage;
  47. pte_t entry;
  48. unsigned long curend = address + PAGE_SIZE;
  49. entry = mk_pte_io((offset & ~(0x1UL)), prot, space);
  50. if (!(address & 0xffff)) {
  51. if (!(address & 0x3fffff) && !(offset & 0x3ffffe) && end >= address + 0x400000) {
  52. entry = mk_pte_io((offset & ~(0x1UL)),
  53.   __pgprot(pgprot_val (prot) | _PAGE_SZ4MB),
  54.   space);
  55. curend = address + 0x400000;
  56. offset += 0x400000;
  57. } else if (!(address & 0x7ffff) && !(offset & 0x7fffe) && end >= address + 0x80000) {
  58. entry = mk_pte_io((offset & ~(0x1UL)),
  59.   __pgprot(pgprot_val (prot) | _PAGE_SZ512K),
  60.   space);
  61. curend = address + 0x80000;
  62. offset += 0x80000;
  63. } else if (!(offset & 0xfffe) && end >= address + 0x10000) {
  64. entry = mk_pte_io((offset & ~(0x1UL)),
  65.   __pgprot(pgprot_val (prot) | _PAGE_SZ64K),
  66.   space);
  67. curend = address + 0x10000;
  68. offset += 0x10000;
  69. } else
  70. offset += PAGE_SIZE;
  71. } else
  72. offset += PAGE_SIZE;
  73. if (offset & 0x1UL)
  74. pte_val(entry) &= ~(_PAGE_E);
  75. do {
  76. oldpage = *pte;
  77. pte_clear(pte);
  78. set_pte(pte, entry);
  79. forget_pte(oldpage);
  80. address += PAGE_SIZE;
  81. pte++;
  82. } while (address < curend);
  83. } while (address < end);
  84. }
  85. static inline int io_remap_pmd_range(pmd_t * pmd, unsigned long address, unsigned long size,
  86. unsigned long offset, pgprot_t prot, int space)
  87. {
  88. unsigned long end;
  89. address &= ~PGDIR_MASK;
  90. end = address + size;
  91. if (end > PGDIR_SIZE)
  92. end = PGDIR_SIZE;
  93. offset -= address;
  94. do {
  95. pte_t * pte = pte_alloc(current->mm, pmd, address);
  96. if (!pte)
  97. return -ENOMEM;
  98. io_remap_pte_range(pte, address, end - address, address + offset, prot, space);
  99. address = (address + PMD_SIZE) & PMD_MASK;
  100. pmd++;
  101. } while (address < end);
  102. return 0;
  103. }
  104. int io_remap_page_range(unsigned long from, unsigned long offset, unsigned long size, pgprot_t prot, int space)
  105. {
  106. int error = 0;
  107. pgd_t * dir;
  108. unsigned long beg = from;
  109. unsigned long end = from + size;
  110. struct mm_struct *mm = current->mm;
  111. prot = __pgprot(pg_iobits);
  112. offset -= from;
  113. dir = pgd_offset(mm, from);
  114. flush_cache_range(mm, beg, end);
  115. spin_lock(&mm->page_table_lock);
  116. while (from < end) {
  117. pmd_t *pmd = pmd_alloc(current->mm, dir, from);
  118. error = -ENOMEM;
  119. if (!pmd)
  120. break;
  121. error = io_remap_pmd_range(pmd, from, end - from, offset + from, prot, space);
  122. if (error)
  123. break;
  124. from = (from + PGDIR_SIZE) & PGDIR_MASK;
  125. dir++;
  126. }
  127. spin_unlock(&mm->page_table_lock);
  128. flush_tlb_range(current->mm, beg, end);
  129. return error;
  130. }