📄 sun4c.c
字号:
/* $Id: sun4c.c,v 1.202 2000/12/01 03:17:31 anton Exp $ * sun4c.c: Doing in software what should be done in hardware. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) * Copyright (C) 1996 Andrew Tridgell (Andrew.Tridgell@anu.edu.au) * Copyright (C) 1997-2000 Anton Blanchard (anton@linuxcare.com) * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */#define NR_TASK_BUCKETS 512#include <linux/config.h>#include <linux/kernel.h>#include <linux/mm.h>#include <linux/init.h>#include <linux/bootmem.h>#include <asm/scatterlist.h>#include <asm/page.h>#include <asm/pgalloc.h>#include <asm/pgtable.h>#include <asm/vaddrs.h>#include <asm/idprom.h>#include <asm/machines.h>#include <asm/memreg.h>#include <asm/processor.h>#include <asm/auxio.h>#include <asm/io.h>#include <asm/oplib.h>#include <asm/openprom.h>#include <asm/mmu_context.h>#include <asm/sun4paddr.h>/* Because of our dynamic kernel TLB miss strategy, and how * our DVMA mapping allocation works, you _MUST_: * * 1) Disable interrupts _and_ not touch any dynamic kernel * memory while messing with kernel MMU state. By * dynamic memory I mean any object which is not in * the kernel image itself or a task_struct (both of * which are locked into the MMU). * 2) Disable interrupts while messing with user MMU state. */extern int num_segmaps, num_contexts;extern unsigned long page_kernel;#ifdef CONFIG_SUN4#define SUN4C_VAC_SIZE sun4c_vacinfo.num_bytes#else/* That's it, we prom_halt() on sun4c if the cache size is something other than 65536. * So let's save some cycles and just use that everywhere except for that bootup * sanity check. */#define SUN4C_VAC_SIZE 65536#endif#define SUN4C_KERNEL_BUCKETS 32#ifndef MAX#define MAX(a,b) ((a)<(b)?(b):(a))#endif#ifndef MIN#define MIN(a,b) ((a)<(b)?(a):(b))#endif/* Flushing the cache. */struct sun4c_vac_props sun4c_vacinfo;unsigned long sun4c_kernel_faults;/* Invalidate every sun4c cache line tag. */void sun4c_flush_all(void){ unsigned long begin, end; if (sun4c_vacinfo.on) panic("SUN4C: AIEEE, trying to invalidate vac while" " it is on."); /* Clear 'valid' bit in all cache line tags */ begin = AC_CACHETAGS; end = (AC_CACHETAGS + SUN4C_VAC_SIZE); while (begin < end) { __asm__ __volatile__("sta %%g0, [%0] %1\n\t" : : "r" (begin), "i" (ASI_CONTROL)); begin += sun4c_vacinfo.linesize; }}static __inline__ void sun4c_flush_context_hw(void){ unsigned long end = SUN4C_VAC_SIZE; __asm__ __volatile__( "1: addcc %0, -4096, %0\n\t" " bne 1b\n\t" " sta %%g0, [%0] %2" : "=&r" (end) : "0" (end), "i" (ASI_HWFLUSHCONTEXT) : "cc");}/* Must be called minimally with IRQs disabled. */static void sun4c_flush_segment_hw(unsigned long addr){ if (sun4c_get_segmap(addr) != invalid_segment) { unsigned long vac_size = SUN4C_VAC_SIZE; __asm__ __volatile__( "1: addcc %0, -4096, %0\n\t" " bne 1b\n\t" " sta %%g0, [%2 + %0] %3" : "=&r" (vac_size) : "0" (vac_size), "r" (addr), "i" (ASI_HWFLUSHSEG) : "cc"); }}/* Must be called minimally with interrupts disabled. */static __inline__ void sun4c_flush_page_hw(unsigned long addr){ addr &= PAGE_MASK; if ((int)sun4c_get_pte(addr) < 0) __asm__ __volatile__("sta %%g0, [%0] %1" : : "r" (addr), "i" (ASI_HWFLUSHPAGE));}/* Don't inline the software version as it eats too many cache lines if expanded. */static void sun4c_flush_context_sw(void){ unsigned long nbytes = SUN4C_VAC_SIZE; unsigned long lsize = sun4c_vacinfo.linesize; __asm__ __volatile__(" add %2, %2, %%g1 add %2, %%g1, %%g2 add %2, %%g2, %%g3 add %2, %%g3, %%g4 add %2, %%g4, %%g5 add %2, %%g5, %%o4 add %2, %%o4, %%o51: subcc %0, %%o5, %0 sta %%g0, [%0] %3 sta %%g0, [%0 + %2] %3 sta %%g0, [%0 + %%g1] %3 sta %%g0, [%0 + %%g2] %3 sta %%g0, [%0 + %%g3] %3 sta %%g0, [%0 + %%g4] %3 sta %%g0, [%0 + %%g5] %3 bg 1b sta %%g0, [%1 + %%o4] %3" : "=&r" (nbytes) : "0" (nbytes), "r" (lsize), "i" (ASI_FLUSHCTX) : "g1", "g2", "g3", "g4", "g5", "o4", "o5", "cc");}/* Don't inline the software version as it eats too many cache lines if expanded. */static void sun4c_flush_segment_sw(unsigned long addr){ if (sun4c_get_segmap(addr) != invalid_segment) { unsigned long nbytes = SUN4C_VAC_SIZE; unsigned long lsize = sun4c_vacinfo.linesize; __asm__ __volatile__(" add %2, %2, %%g1 add %2, %%g1, %%g2 add %2, %%g2, %%g3 add %2, %%g3, %%g4 add %2, %%g4, %%g5 add %2, %%g5, %%o4 add %2, %%o4, %%o51: subcc %1, %%o5, %1 sta %%g0, [%0] %6 sta %%g0, [%0 + %2] %6 sta %%g0, [%0 + %%g1] %6 sta %%g0, [%0 + %%g2] %6 sta %%g0, [%0 + %%g3] %6 sta %%g0, [%0 + %%g4] %6 sta %%g0, [%0 + %%g5] %6 sta %%g0, [%0 + %%o4] %6 bg 1b add %0, %%o5, %0" : "=&r" (addr), "=&r" (nbytes), "=&r" (lsize) : "0" (addr), "1" (nbytes), "2" (lsize), "i" (ASI_FLUSHSEG) : "g1", "g2", "g3", "g4", "g5", "o4", "o5", "cc"); }}/* Bolix one page from the virtual cache. */static void sun4c_flush_page(unsigned long addr){ addr &= PAGE_MASK; if ((sun4c_get_pte(addr) & (_SUN4C_PAGE_NOCACHE | _SUN4C_PAGE_VALID)) != _SUN4C_PAGE_VALID) return; if (sun4c_vacinfo.do_hwflushes) { __asm__ __volatile__("sta %%g0, [%0] %1;nop;nop;nop;\n\t" : : "r" (addr), "i" (ASI_HWFLUSHPAGE)); } else { unsigned long left = PAGE_SIZE; unsigned long lsize = sun4c_vacinfo.linesize; __asm__ __volatile__("add %2, %2, %%g1\n\t" "add %2, %%g1, %%g2\n\t" "add %2, %%g2, %%g3\n\t" "add %2, %%g3, %%g4\n\t" "add %2, %%g4, %%g5\n\t" "add %2, %%g5, %%o4\n\t" "add %2, %%o4, %%o5\n" "1:\n\t" "subcc %1, %%o5, %1\n\t" "sta %%g0, [%0] %6\n\t" "sta %%g0, [%0 + %2] %6\n\t" "sta %%g0, [%0 + %%g1] %6\n\t" "sta %%g0, [%0 + %%g2] %6\n\t" "sta %%g0, [%0 + %%g3] %6\n\t" "sta %%g0, [%0 + %%g4] %6\n\t" "sta %%g0, [%0 + %%g5] %6\n\t" "sta %%g0, [%0 + %%o4] %6\n\t" "bg 1b\n\t" " add %0, %%o5, %0\n\t" : "=&r" (addr), "=&r" (left), "=&r" (lsize) : "0" (addr), "1" (left), "2" (lsize), "i" (ASI_FLUSHPG) : "g1", "g2", "g3", "g4", "g5", "o4", "o5", "cc"); }}/* Don't inline the software version as it eats too many cache lines if expanded. */static void sun4c_flush_page_sw(unsigned long addr){ addr &= PAGE_MASK; if ((sun4c_get_pte(addr) & (_SUN4C_PAGE_NOCACHE | _SUN4C_PAGE_VALID)) == _SUN4C_PAGE_VALID) { unsigned long left = PAGE_SIZE; unsigned long lsize = sun4c_vacinfo.linesize; __asm__ __volatile__(" add %2, %2, %%g1 add %2, %%g1, %%g2 add %2, %%g2, %%g3 add %2, %%g3, %%g4 add %2, %%g4, %%g5 add %2, %%g5, %%o4 add %2, %%o4, %%o51: subcc %1, %%o5, %1 sta %%g0, [%0] %6 sta %%g0, [%0 + %2] %6 sta %%g0, [%0 + %%g1] %6 sta %%g0, [%0 + %%g2] %6 sta %%g0, [%0 + %%g3] %6 sta %%g0, [%0 + %%g4] %6 sta %%g0, [%0 + %%g5] %6 sta %%g0, [%0 + %%o4] %6 bg 1b add %0, %%o5, %0" : "=&r" (addr), "=&r" (left), "=&r" (lsize) : "0" (addr), "1" (left), "2" (lsize), "i" (ASI_FLUSHPG) : "g1", "g2", "g3", "g4", "g5", "o4", "o5", "cc"); }}/* The sun4c's do have an on chip store buffer. And the way you * clear them out isn't so obvious. The only way I can think of * to accomplish this is to read the current context register, * store the same value there, then read an external hardware * register. */void sun4c_complete_all_stores(void){ volatile int _unused; _unused = sun4c_get_context(); sun4c_set_context(_unused);#ifdef CONFIG_SUN_AUXIO _unused = *AUXREG;#endif}/* Bootup utility functions. */static inline void sun4c_init_clean_segmap(unsigned char pseg){ unsigned long vaddr; sun4c_put_segmap(0, pseg); for (vaddr = 0; vaddr < SUN4C_REAL_PGDIR_SIZE; vaddr += PAGE_SIZE) sun4c_put_pte(vaddr, 0); sun4c_put_segmap(0, invalid_segment);}static inline void sun4c_init_clean_mmu(unsigned long kernel_end){ unsigned long vaddr; unsigned char savectx, ctx; savectx = sun4c_get_context(); kernel_end = SUN4C_REAL_PGDIR_ALIGN(kernel_end); for (ctx = 0; ctx < num_contexts; ctx++) { sun4c_set_context(ctx); for (vaddr = 0; vaddr < 0x20000000; vaddr += SUN4C_REAL_PGDIR_SIZE) sun4c_put_segmap(vaddr, invalid_segment); for (vaddr = 0xe0000000; vaddr < KERNBASE; vaddr += SUN4C_REAL_PGDIR_SIZE) sun4c_put_segmap(vaddr, invalid_segment); for (vaddr = kernel_end; vaddr < KADB_DEBUGGER_BEGVM; vaddr += SUN4C_REAL_PGDIR_SIZE) sun4c_put_segmap(vaddr, invalid_segment); for (vaddr = LINUX_OPPROM_ENDVM; vaddr; vaddr += SUN4C_REAL_PGDIR_SIZE) sun4c_put_segmap(vaddr, invalid_segment); } sun4c_set_context(savectx);}void __init sun4c_probe_vac(void){ sun4c_disable_vac(); if (ARCH_SUN4) { switch (idprom->id_machtype) { case (SM_SUN4|SM_4_110): sun4c_vacinfo.type = NONE; sun4c_vacinfo.num_bytes = 0; sun4c_vacinfo.linesize = 0; sun4c_vacinfo.do_hwflushes = 0; prom_printf("No VAC. Get some bucks and buy a real computer."); prom_halt(); break; case (SM_SUN4|SM_4_260): sun4c_vacinfo.type = WRITE_BACK; sun4c_vacinfo.num_bytes = 128 * 1024; sun4c_vacinfo.linesize = 16; sun4c_vacinfo.do_hwflushes = 0; break; case (SM_SUN4|SM_4_330): sun4c_vacinfo.type = WRITE_THROUGH; sun4c_vacinfo.num_bytes = 128 * 1024; sun4c_vacinfo.linesize = 16; sun4c_vacinfo.do_hwflushes = 0; break; case (SM_SUN4|SM_4_470): sun4c_vacinfo.type = WRITE_BACK; sun4c_vacinfo.num_bytes = 128 * 1024; sun4c_vacinfo.linesize = 32; sun4c_vacinfo.do_hwflushes = 0; break; default: prom_printf("Cannot initialize VAC - wierd sun4 model idprom->id_machtype = %d", idprom->id_machtype); prom_halt(); }; } else { sun4c_vacinfo.type = WRITE_THROUGH; if ((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) || (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) { /* PROM on SS1 lacks this info, to be super safe we * hard code it here since this arch is cast in stone. */ sun4c_vacinfo.num_bytes = 65536; sun4c_vacinfo.linesize = 16; } else { sun4c_vacinfo.num_bytes = prom_getintdefault(prom_root_node, "vac-size", 65536); sun4c_vacinfo.linesize = prom_getintdefault(prom_root_node, "vac-linesize", 16); } sun4c_vacinfo.do_hwflushes = prom_getintdefault(prom_root_node, "vac-hwflush", 0); if (sun4c_vacinfo.do_hwflushes == 0) sun4c_vacinfo.do_hwflushes = prom_getintdefault(prom_root_node, "vac_hwflush", 0); if (sun4c_vacinfo.num_bytes != 65536) { prom_printf("WEIRD Sun4C VAC cache size, tell davem"); prom_halt(); } } sun4c_vacinfo.num_lines = (sun4c_vacinfo.num_bytes / sun4c_vacinfo.linesize); switch (sun4c_vacinfo.linesize) { case 16: sun4c_vacinfo.log2lsize = 4; break; case 32: sun4c_vacinfo.log2lsize = 5; break; default: prom_printf("probe_vac: Didn't expect vac-linesize of %d, halting\n", sun4c_vacinfo.linesize); prom_halt(); }; sun4c_flush_all(); sun4c_enable_vac();}/* Patch instructions for the low level kernel fault handler. */extern unsigned long invalid_segment_patch1, invalid_segment_patch1_ff;extern unsigned long invalid_segment_patch2, invalid_segment_patch2_ff;extern unsigned long invalid_segment_patch1_1ff, invalid_segment_patch2_1ff;extern unsigned long num_context_patch1, num_context_patch1_16;extern unsigned long num_context_patch2, num_context_patch2_16;extern unsigned long vac_linesize_patch, vac_linesize_patch_32;extern unsigned long vac_hwflush_patch1, vac_hwflush_patch1_on;extern unsigned long vac_hwflush_patch2, vac_hwflush_patch2_on;#define PATCH_INSN(src, dst) do { \ daddr = &(dst); \ iaddr = &(src); \ *daddr = *iaddr; \ } while (0);static void patch_kernel_fault_handler(void){ unsigned long *iaddr, *daddr; switch (num_segmaps) { case 128: /* Default, nothing to do. */ break; case 256: PATCH_INSN(invalid_segment_patch1_ff, invalid_segment_patch1); PATCH_INSN(invalid_segment_patch2_ff, invalid_segment_patch2); break; case 512: PATCH_INSN(invalid_segment_patch1_1ff, invalid_segment_patch1); PATCH_INSN(invalid_segment_patch2_1ff, invalid_segment_patch2); break; default: prom_printf("Unhandled number of segmaps: %d\n", num_segmaps); prom_halt(); }; switch (num_contexts) { case 8: /* Default, nothing to do. */ break; case 16: PATCH_INSN(num_context_patch1_16, num_context_patch1);#if 0 PATCH_INSN(num_context_patch2_16, num_context_patch2);#endif break; default: prom_printf("Unhandled number of contexts: %d\n", num_contexts); prom_halt(); }; if (sun4c_vacinfo.do_hwflushes != 0) { PATCH_INSN(vac_hwflush_patch1_on, vac_hwflush_patch1); PATCH_INSN(vac_hwflush_patch2_on, vac_hwflush_patch2); } else { switch (sun4c_vacinfo.linesize) { case 16: /* Default, nothing to do. */ break; case 32: PATCH_INSN(vac_linesize_patch_32, vac_linesize_patch); break; default: prom_printf("Impossible VAC linesize %d, halting...\n", sun4c_vacinfo.linesize); prom_halt(); }; }}static void __init sun4c_probe_mmu(void){ if (ARCH_SUN4) { switch (idprom->id_machtype) { case (SM_SUN4|SM_4_110): prom_printf("No support for 4100 yet\n"); prom_halt(); num_segmaps = 256; num_contexts = 8; break; case (SM_SUN4|SM_4_260): /* should be 512 segmaps. when it get fixed */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -