tlb-r4k.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:9k
源码类别:

Linux/Unix编程

开发平台:

Unix_Linux

  1. /*
  2.  * This file is subject to the terms and conditions of the GNU General Public
  3.  * License.  See the file "COPYING" in the main directory of this archive
  4.  * for more details.
  5.  *
  6.  * r4xx0.c: R4000 processor variant specific MMU/Cache routines.
  7.  *
  8.  * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
  9.  * Copyright (C) 1997, 1998, 1999, 2000 Ralf Baechle ralf@gnu.org
  10.  *
  11.  * To do:
  12.  *
  13.  *  - this code is a overbloated pig
  14.  *  - many of the bug workarounds are not efficient at all, but at
  15.  *    least they are functional ...
  16.  */
  17. #include <linux/init.h>
  18. #include <linux/sched.h>
  19. #include <linux/mm.h>
  20. #include <asm/cpu.h>
  21. #include <asm/bootinfo.h>
  22. #include <asm/mmu_context.h>
  23. #include <asm/pgtable.h>
  24. #include <asm/system.h>
  25. #undef DEBUG_TLB
  26. #undef DEBUG_TLBUPDATE
  27. extern char except_vec0_nevada, except_vec0_r4000, except_vec0_r4600;
  28. /* CP0 hazard avoidance. */
  29. #define BARRIER __asm__ __volatile__(".set noreordernt" 
  30.      "nop; nop; nop; nop; nop; nop;nt" 
  31.      ".set reordernt")
  32. void local_flush_tlb_all(void)
  33. {
  34. unsigned long flags;
  35. unsigned long old_ctx;
  36. int entry;
  37. #ifdef DEBUG_TLB
  38. printk("[tlball]");
  39. #endif
  40. __save_and_cli(flags);
  41. /* Save old context and create impossible VPN2 value */
  42. old_ctx = (get_entryhi() & 0xff);
  43. set_entrylo0(0);
  44. set_entrylo1(0);
  45. BARRIER;
  46. entry = get_wired();
  47. /* Blast 'em all away. */
  48. while (entry < mips_cpu.tlbsize) {
  49. /*
  50.  * Make sure all entries differ.  If they're not different
  51.  * MIPS32 will take revenge ...
  52.  */
  53. set_entryhi(KSEG0 + entry*0x2000);
  54. set_index(entry);
  55. BARRIER;
  56. tlb_write_indexed();
  57. BARRIER;
  58. entry++;
  59. }
  60. BARRIER;
  61. set_entryhi(old_ctx);
  62. __restore_flags(flags);
  63. }
  64. void local_flush_tlb_mm(struct mm_struct *mm)
  65. {
  66. if (mm->context != 0) {
  67. unsigned long flags;
  68. #ifdef DEBUG_TLB
  69. printk("[tlbmm<%d>]", mm->context);
  70. #endif
  71. __save_and_cli(flags);
  72. get_new_mmu_context(mm, smp_processor_id());
  73. if (mm == current->active_mm)
  74. set_entryhi(mm->context & 0xff);
  75. __restore_flags(flags);
  76. }
  77. }
  78. void local_flush_tlb_range(struct mm_struct *mm, unsigned long start,
  79. unsigned long end)
  80. {
  81. if (mm->context != 0) {
  82. unsigned long flags;
  83. int size;
  84. #ifdef DEBUG_TLB
  85. printk("[tlbrange<%02x,%08lx,%08lx>]", (mm->context & 0xff),
  86.        start, end);
  87. #endif
  88. __save_and_cli(flags);
  89. size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
  90. size = (size + 1) >> 1;
  91. if (size <= mips_cpu.tlbsize/2) {
  92. int oldpid = (get_entryhi() & 0xff);
  93. int newpid = (mm->context & 0xff);
  94. start &= (PAGE_MASK << 1);
  95. end += ((PAGE_SIZE << 1) - 1);
  96. end &= (PAGE_MASK << 1);
  97. while (start < end) {
  98. int idx;
  99. set_entryhi(start | newpid);
  100. start += (PAGE_SIZE << 1);
  101. BARRIER;
  102. tlb_probe();
  103. BARRIER;
  104. idx = get_index();
  105. set_entrylo0(0);
  106. set_entrylo1(0);
  107. if (idx < 0)
  108. continue;
  109. /* Make sure all entries differ. */
  110. set_entryhi(KSEG0 + idx*0x2000);
  111. BARRIER;
  112. tlb_write_indexed();
  113. BARRIER;
  114. }
  115. set_entryhi(oldpid);
  116. } else {
  117. get_new_mmu_context(mm, smp_processor_id());
  118. if (mm == current->active_mm)
  119. set_entryhi(mm->context & 0xff);
  120. }
  121. __restore_flags(flags);
  122. }
  123. }
  124. void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
  125. {
  126. if (!vma || vma->vm_mm->context != 0) {
  127. unsigned long flags;
  128. int oldpid, newpid, idx;
  129. #ifdef DEBUG_TLB
  130. printk("[tlbpage<%d,%08lx>]", vma->vm_mm->context, page);
  131. #endif
  132. newpid = (vma->vm_mm->context & 0xff);
  133. page &= (PAGE_MASK << 1);
  134. __save_and_cli(flags);
  135. oldpid = (get_entryhi() & 0xff);
  136. set_entryhi(page | newpid);
  137. BARRIER;
  138. tlb_probe();
  139. BARRIER;
  140. idx = get_index();
  141. set_entrylo0(0);
  142. set_entrylo1(0);
  143. if(idx < 0)
  144. goto finish;
  145. /* Make sure all entries differ. */
  146. set_entryhi(KSEG0+idx*0x2000);
  147. BARRIER;
  148. tlb_write_indexed();
  149. finish:
  150. BARRIER;
  151. set_entryhi(oldpid);
  152. __restore_flags(flags);
  153. }
  154. }
  155. /* We will need multiple versions of update_mmu_cache(), one that just
  156.  * updates the TLB with the new pte(s), and another which also checks
  157.  * for the R4k "end of page" hardware bug and does the needy.
  158.  */
  159. void update_mmu_cache(struct vm_area_struct * vma, unsigned long address,
  160.       pte_t pte)
  161. {
  162. unsigned long flags;
  163. pgd_t *pgdp;
  164. pmd_t *pmdp;
  165. pte_t *ptep;
  166. int idx, pid;
  167. /*
  168.  * Handle debugger faulting in for debugee.
  169.  */
  170. if (current->active_mm != vma->vm_mm)
  171. return;
  172. pid = get_entryhi() & 0xff;
  173. #ifdef DEBUG_TLB
  174. if((pid != (vma->vm_mm->context & 0xff)) || (vma->vm_mm->context == 0)) {
  175. printk("update_mmu_cache: Wheee, bogus tlbpid mmpid=%d tlbpid=%dn",
  176.        (int) (vma->vm_mm->context & 0xff), pid);
  177. }
  178. #endif
  179. __save_and_cli(flags);
  180. address &= (PAGE_MASK << 1);
  181. set_entryhi(address | (pid));
  182. pgdp = pgd_offset(vma->vm_mm, address);
  183. BARRIER;
  184. tlb_probe();
  185. BARRIER;
  186. pmdp = pmd_offset(pgdp, address);
  187. idx = get_index();
  188. ptep = pte_offset(pmdp, address);
  189. BARRIER;
  190. set_entrylo0(pte_val(*ptep++) >> 6);
  191. set_entrylo1(pte_val(*ptep) >> 6);
  192. set_entryhi(address | (pid));
  193. BARRIER;
  194. if (idx < 0) {
  195. tlb_write_random();
  196. } else {
  197. tlb_write_indexed();
  198. }
  199. BARRIER;
  200. set_entryhi(pid);
  201. BARRIER;
  202. __restore_flags(flags);
  203. }
  204. #if 0
  205. static void r4k_update_mmu_cache_hwbug(struct vm_area_struct * vma,
  206.        unsigned long address, pte_t pte)
  207. {
  208. unsigned long flags;
  209. pgd_t *pgdp;
  210. pmd_t *pmdp;
  211. pte_t *ptep;
  212. int idx;
  213. __save_and_cli(flags);
  214. address &= (PAGE_MASK << 1);
  215. set_entryhi(address | (get_entryhi() & 0xff));
  216. pgdp = pgd_offset(vma->vm_mm, address);
  217. tlb_probe();
  218. pmdp = pmd_offset(pgdp, address);
  219. idx = get_index();
  220. ptep = pte_offset(pmdp, address);
  221. set_entrylo0(pte_val(*ptep++) >> 6);
  222. set_entrylo1(pte_val(*ptep) >> 6);
  223. BARRIER;
  224. if (idx < 0)
  225. tlb_write_random();
  226. else
  227. tlb_write_indexed();
  228. BARRIER;
  229. __restore_flags(flags);
  230. }
  231. #endif
  232. void __init add_wired_entry(unsigned long entrylo0, unsigned long entrylo1,
  233.      unsigned long entryhi, unsigned long pagemask)
  234. {
  235. unsigned long flags;
  236. unsigned long wired;
  237. unsigned long old_pagemask;
  238. unsigned long old_ctx;
  239. __save_and_cli(flags);
  240. /* Save old context and create impossible VPN2 value */
  241. old_ctx = get_entryhi() & 0xff;
  242. old_pagemask = get_pagemask();
  243. wired = get_wired();
  244. set_wired(wired + 1);
  245. set_index(wired);
  246. BARRIER;
  247. set_pagemask(pagemask);
  248. set_entryhi(entryhi);
  249. set_entrylo0(entrylo0);
  250. set_entrylo1(entrylo1);
  251. BARRIER;
  252. tlb_write_indexed();
  253. BARRIER;
  254. set_entryhi(old_ctx);
  255. BARRIER;
  256. set_pagemask(old_pagemask);
  257. local_flush_tlb_all();
  258. __restore_flags(flags);
  259. }
  260. /*
  261.  * Used for loading TLB entries before trap_init() has started, when we
  262.  * don't actually want to add a wired entry which remains throughout the
  263.  * lifetime of the system
  264.  */
  265. static int temp_tlb_entry __initdata;
  266. __init int add_temporary_entry(unsigned long entrylo0, unsigned long entrylo1,
  267.        unsigned long entryhi, unsigned long pagemask)
  268. {
  269. int ret = 0;
  270. unsigned long flags;
  271. unsigned long wired;
  272. unsigned long old_pagemask;
  273. unsigned long old_ctx;
  274. __save_and_cli(flags);
  275. /* Save old context and create impossible VPN2 value */
  276. old_ctx = get_entryhi() & 0xff;
  277. old_pagemask = get_pagemask();
  278. wired = get_wired();
  279. if (--temp_tlb_entry < wired) {
  280. printk(KERN_WARNING "No TLB space left for add_temporary_entryn");
  281. ret = -ENOSPC;
  282. goto out;
  283. }
  284. set_index(temp_tlb_entry);
  285. BARRIER;
  286. set_pagemask(pagemask);
  287. set_entryhi(entryhi);
  288. set_entrylo0(entrylo0);
  289. set_entrylo1(entrylo1);
  290. BARRIER;
  291. tlb_write_indexed();
  292. BARRIER;
  293. set_entryhi(old_ctx);
  294. BARRIER;
  295. set_pagemask(old_pagemask);
  296. out:
  297. __restore_flags(flags);
  298. return ret;
  299. }
  300. static void __init probe_tlb(unsigned long config)
  301. {
  302. unsigned int prid, config1;
  303. prid = read_32bit_cp0_register(CP0_PRID) & 0xff00;
  304. if (prid == PRID_IMP_RM7000 || !(config & (1 << 31)))
  305. /*
  306.  * Not a MIPS32 complianant CPU.  Config 1 register not
  307.  * supported, we assume R4k style.  Cpu probing already figured
  308.  * out the number of tlb entries.
  309.  */
  310. return;
  311. config1 = read_mips32_cp0_config1();
  312. if (!((config >> 7) & 3))
  313. panic("No MMU present");
  314. else
  315. mips_cpu.tlbsize = ((config1 >> 25) & 0x3f) + 1;
  316. }
  317. void __init r4k_tlb_init(void)
  318. {
  319. u32 config = read_32bit_cp0_register(CP0_CONFIG);
  320. /*
  321.  * You should never change this register:
  322.  *   - On R4600 1.7 the tlbp never hits for pages smaller than
  323.  *     the value in the c0_pagemask register.
  324.  *   - The entire mm handling assumes the c0_pagemask register to
  325.  *     be set for 4kb pages.
  326.  */
  327. probe_tlb(config);
  328. set_pagemask(PM_4K);
  329. write_32bit_cp0_register(CP0_WIRED, 0);
  330. temp_tlb_entry = mips_cpu.tlbsize - 1;
  331. local_flush_tlb_all();
  332. if ((mips_cpu.options & MIPS_CPU_4KEX)
  333.     && (mips_cpu.options & MIPS_CPU_4KTLB)) {
  334. if (mips_cpu.cputype == CPU_NEVADA)
  335. memcpy((void *)KSEG0, &except_vec0_nevada, 0x80);
  336. else if (mips_cpu.cputype == CPU_R4600)
  337. memcpy((void *)KSEG0, &except_vec0_r4600, 0x80);
  338. else
  339. memcpy((void *)KSEG0, &except_vec0_r4000, 0x80);
  340. flush_icache_range(KSEG0, KSEG0 + 0x80);
  341. }
  342. }