// SPDX-License-Identifier: GPL-2.0 /* * srmmu.c: SRMMU specific routines for memory management. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1995,2002 Pete Zaitcev (zaitcev@yahoo.com) * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) * Copyright (C) 1999,2000 Anton Blanchard (anton@samba.org)
*/
/* * size: bytes to allocate in the nocache area. * align: bytes, number to align at. * Returns the virtual address of the allocated area.
*/ staticvoid *__srmmu_get_nocache(int size, int align)
{ int offset, minsz = 1 << SRMMU_NOCACHE_BITMAP_SHIFT; unsignedlong addr;
if (size < minsz) {
printk(KERN_ERR "Size 0x%x too small for nocache request\n",
size);
size = minsz;
} if (size & (minsz - 1)) {
printk(KERN_ERR "Size 0x%x unaligned in nocache request\n",
size);
size += minsz - 1;
}
BUG_ON(align > SRMMU_NOCACHE_ALIGN_MAX);
offset = bit_map_string_get(&srmmu_nocache_map,
size >> SRMMU_NOCACHE_BITMAP_SHIFT,
align >> SRMMU_NOCACHE_BITMAP_SHIFT); if (offset == -1) {
printk(KERN_ERR "srmmu: out of nocache %d: %d/%d\n",
size, (int) srmmu_nocache_size,
srmmu_nocache_map.used << SRMMU_NOCACHE_BITMAP_SHIFT); return NULL;
}
void *srmmu_get_nocache(int size, int align)
{ void *tmp;
tmp = __srmmu_get_nocache(size, align);
if (tmp)
memset(tmp, 0, size);
return tmp;
}
void srmmu_free_nocache(void *addr, int size)
{ unsignedlong vaddr; int offset;
vaddr = (unsignedlong)addr; if (vaddr < SRMMU_NOCACHE_VADDR) {
printk("Vaddr %lx is smaller than nocache base 0x%lx\n",
vaddr, (unsignedlong)SRMMU_NOCACHE_VADDR);
BUG();
} if (vaddr + size > srmmu_nocache_end) {
printk("Vaddr %lx is bigger than nocache end 0x%lx\n",
vaddr, srmmu_nocache_end);
BUG();
} if (!is_power_of_2(size)) {
printk("Size 0x%x is not a power of 2\n", size);
BUG();
} if (size < SRMMU_NOCACHE_BITMAP_SHIFT) {
printk("Size 0x%x is too small\n", size);
BUG();
} if (vaddr & (size - 1)) {
printk("Vaddr %lx is not aligned to size 0x%x\n", vaddr, size);
BUG();
}
/* Return how much physical memory we have. */ staticunsignedlong __init probe_memory(void)
{ unsignedlong total = 0; int i;
for (i = 0; sp_banks[i].num_bytes; i++)
total += sp_banks[i].num_bytes;
return total;
}
/* * Reserve nocache dynamically proportionally to the amount of * system RAM. -- Tomas Szepe <szepe@pinerecords.com>, June 2002
*/ staticvoid __init srmmu_nocache_calcsize(void)
{ unsignedlong sysmemavail = probe_memory() / 1024; int srmmu_nocache_npages;
/* * Hardware needs alignment to 256 only, but we align to whole page size * to reduce fragmentation problems due to the buddy principle. * XXX Provide actual fragmentation statistics in /proc. * * Alignments up to the page size are the same for physical and virtual * addresses of the nocache area.
*/
pgtable_t pte_alloc_one(struct mm_struct *mm)
{
pte_t *ptep; struct page *page;
/* I need to test whether this is consistent over all * sun4m's. The bus_type represents the upper 4 bits of * 36-bit physical address on the I/O space lines...
*/
tmp |= (bus_type << 28);
tmp |= SRMMU_PRIV;
__flush_page_to_ram(virt_addr);
set_pte(ptep, __pte(tmp));
}
/* * The following are all MBUS based SRMMU modules, and therefore could * be found in a multiprocessor configuration. On the whole, these * chips seems to be much more touchy about DVMA and page tables * with respect to cache coherency.
*/
/* * NOTE: All of this startup code assumes the low 16mb (approx.) of * kernel mappings are done with one single contiguous chunk of * ram. On small ram machines (classics mainly) we only get * around 8mb mapped for us.
*/
/* * This is much cleaner than poking around physical address space * looking at the prom's page table directly which is what most * other OS's do. Yuck... this is much better.
*/ staticvoid __init srmmu_inherit_prom_mappings(unsignedlong start, unsignedlong end)
{ unsignedlong probed; unsignedlong addr;
pgd_t *pgdp;
p4d_t *p4dp;
pud_t *pudp;
pmd_t *pmdp;
pte_t *ptep; int what; /* 0 = normal-pte, 1 = pmd-level pte, 2 = pgd-level pte */
while (start <= end) { if (start == 0) break; /* probably wrap around */ if (start == 0xfef00000)
start = KADB_DEBUGGER_BEGVM;
probed = srmmu_probe(start); if (!probed) { /* continue probing until we find an entry */
start += PAGE_SIZE; continue;
}
/* A red snapper, see what it really is. */
what = 0;
addr = start - PAGE_SIZE;
if (!(start & ~(PMD_MASK))) { if (srmmu_probe(addr + PMD_SIZE) == probed)
what = 1;
}
if (!(start & ~(PGDIR_MASK))) { if (srmmu_probe(addr + PGDIR_SIZE) == probed)
what = 2;
}
/* ctx table has to be physically aligned to its size */
srmmu_context_table = __srmmu_get_nocache(num_contexts * sizeof(ctxd_t), num_contexts * sizeof(ctxd_t));
srmmu_ctx_table_phys = (ctxd_t *)__nocache_pa(srmmu_context_table);
for (i = 0; i < num_contexts; i++)
srmmu_ctxd_set(__nocache_fix(&srmmu_context_table[i]), srmmu_swapper_pg_dir);
/* Clear any crap from the cache or else... */
swift_flush_cache_all();
/* Enable I & D caches */
mreg = srmmu_get_mmureg();
mreg |= (SWIFT_IE | SWIFT_DE); /* * The Swift branch folding logic is completely broken. At * trap time, if things are just right, if can mistakenly * think that a trap is coming from kernel mode when in fact * it is coming from user mode (it mis-executes the branch in * the trap code). So you see things like crashme completely * hosing your machine which is completely unacceptable. Turn * this shit off... nice job Fujitsu.
*/
mreg &= ~(SWIFT_BF);
srmmu_set_mmureg(mreg);
}
__asm__ __volatile__("lda [%1] %2, %0\n\t" "srl %0, 0x18, %0\n\t" : "=r" (swift_rev) : "r" (SWIFT_MASKID_ADDR), "i" (ASI_M_BYPASS));
srmmu_name = "Fujitsu Swift"; switch (swift_rev) { case 0x11: case 0x20: case 0x23: case 0x30:
srmmu_modtype = Swift_lots_o_bugs;
hwbug_bitmask |= (HWBUG_KERN_ACCBROKEN | HWBUG_KERN_CBITBROKEN); /* * Gee george, I wonder why Sun is so hush hush about * this hardware bug... really braindamage stuff going * on here. However I think we can find a way to avoid * all of the workaround overhead under Linux. Basically, * any page fault can cause kernel pages to become user * accessible (the mmu gets confused and clears some of * the ACC bits in kernel ptes). Aha, sounds pretty * horrible eh? But wait, after extensive testing it appears * that if you use pgd_t level large kernel pte's (like the * 4MB pages on the Pentium) the bug does not get tripped * at all. This avoids almost all of the major overhead. * Welcome to a world where your vendor tells you to, * "apply this kernel patch" instead of "sorry for the * broken hardware, send it back and we'll give you * properly functioning parts"
*/ break; case 0x25: case 0x31:
srmmu_modtype = Swift_bad_c;
hwbug_bitmask |= HWBUG_KERN_CBITBROKEN; /* * You see Sun allude to this hardware bug but never * admit things directly, they'll say things like, * "the Swift chip cache problems" or similar.
*/ break; default:
srmmu_modtype = Swift_ok; break;
}
/* * Are you now convinced that the Swift is one of the * biggest VLSI abortions of all time? Bravo Fujitsu! * Fujitsu, the !#?!%$'d up processor people. I bet if * you examined the microcode of the Swift you'd find * XXX's all over the place.
*/
poke_srmmu = poke_swift;
}
/* TurboSparc is copy-back, if we turn it on, but this does not work. */ staticvoid turbosparc_flush_page_to_ram(unsignedlong page)
{ #ifdef TURBOSPARC_WRITEBACK volatileunsignedlong clear;
if (srmmu_probe(page))
turbosparc_flush_page_cache(page);
clear = srmmu_get_fstatus(); #endif
}
/* Clear any crap from the cache or else... */
turbosparc_flush_cache_all(); /* Temporarily disable I & D caches */
mreg &= ~(TURBOSPARC_ICENABLE | TURBOSPARC_DCENABLE);
mreg &= ~(TURBOSPARC_PCENABLE); /* Don't check parity */
srmmu_set_mmureg(mreg);
ccreg = turbosparc_get_ccreg();
#ifdef TURBOSPARC_WRITEBACK
ccreg |= (TURBOSPARC_SNENABLE); /* Do DVMA snooping in Dcache */
ccreg &= ~(TURBOSPARC_uS2 | TURBOSPARC_WTENABLE); /* Write-back D-cache, emulate VLSI
* abortion number three, not number one */ #else /* For now let's play safe, optimize later */
ccreg |= (TURBOSPARC_SNENABLE | TURBOSPARC_WTENABLE); /* Do DVMA snooping in Dcache, Write-thru D-cache */
ccreg &= ~(TURBOSPARC_uS2); /* Emulate VLSI abortion number three, not number one */ #endif
switch (ccreg & 7) { case 0: /* No SE cache */ case 7: /* Test mode */ break; default:
ccreg |= (TURBOSPARC_SCENABLE);
}
turbosparc_set_ccreg(ccreg);
mreg |= (TURBOSPARC_ICENABLE | TURBOSPARC_DCENABLE); /* I & D caches on */
mreg |= (TURBOSPARC_ICSNOOP); /* Icache snooping on */
srmmu_set_mmureg(mreg);
}
staticvoid __init init_tsunami(void)
{ /* * Tsunami's pretty sane, Sun and TI actually got it * somewhat right this time. Fujitsu should have * taken some lessons from them.
*/
/* * We don't need memory parity checks. * XXX This is a mess, have to dig out later. ecd. viking_mxcc_turn_off_parity(&mreg, &mxcc_control);
*/
/* We do cache ptables on MXCC. */
mreg |= VIKING_TCENABLE;
} else { unsignedlong bpreg;
mreg &= ~(VIKING_TCENABLE); if (smp_catch++) { /* Must disable mixed-cmd mode here for other cpu's. */
bpreg = viking_get_bpreg();
bpreg &= ~(VIKING_ACTION_MIX);
viking_set_bpreg(bpreg);
/* Just in case PROM does something funny. */
msi_set_sync();
}
}
#ifdef CONFIG_SMP /* On sun4d the cpu broadcasts local TLB flushes, so we can just * perform the local TLB flush and all the other cpus will see it. * But, unfortunately, there is a bug in the sun4d XBUS backplane * that requires that we add some synchronization to these flushes. * * The bug is that the fifo which keeps track of all the pending TLB * broadcasts in the system is an entry or two too small, so if we * have too many going at once we'll overflow that fifo and lose a TLB * flush resulting in corruption. * * Our workaround is to take a global spinlock around the TLB flushes, * which guarentees we won't ever have too many pending. It's a big * hammer, but a semaphore like system to make sure we only have N TLB * flushes going at once will require SMP locking anyways so there's * no real value in trying any harder than this.
*/ staticstruct sparc32_cachetlb_ops viking_sun4d_smp_ops __ro_after_init = {
.cache_all = viking_flush_cache_all,
.cache_mm = viking_flush_cache_mm,
.cache_page = viking_flush_cache_page,
.cache_range = viking_flush_cache_range,
.tlb_all = sun4dsmp_flush_tlb_all,
.tlb_mm = sun4dsmp_flush_tlb_mm,
.tlb_page = sun4dsmp_flush_tlb_page,
.tlb_range = sun4dsmp_flush_tlb_range,
.page_to_ram = viking_flush_page_to_ram,
.sig_insns = viking_flush_sig_insns,
.page_for_dma = viking_flush_page_for_dma,
}; #endif
/* Ahhh, the viking. SRMMU VLSI abortion number two... */ if (mreg & VIKING_MMODE) {
srmmu_name = "TI Viking";
viking_mxcc_present = 0;
msi_set_sync();
/* * We need this to make sure old viking takes no hits * on its cache for dma snoops to workaround the * "load from non-cacheable memory" interrupt bug. * This is only necessary because of the new way in * which we use the IOMMU.
*/
viking_ops.page_for_dma = viking_flush_page; #ifdef CONFIG_SMP
viking_sun4d_smp_ops.page_for_dma = viking_flush_page; #endif
flush_page_for_dma_global = 0;
} else {
srmmu_name = "TI Viking/MXCC";
viking_mxcc_present = 1;
srmmu_cache_pagetables = 1;
}
/* First, check for sparc-leon. */ if (sparc_cpu_model == sparc_leon) {
init_leon(); return;
}
/* Second, check for HyperSparc or Cypress. */ if (mod_typ == 1) { switch (mod_rev) { case 7: /* UP or MP Hypersparc */
init_hypersparc(); break; case 0: case 2: case 10: case 11: case 12: case 13: case 14: case 15: default:
prom_printf("Sparc-Linux Cypress support does not longer exit.\n");
prom_halt(); break;
} return;
}
/* Now Fujitsu TurboSparc. It might happen that it is * in Swift emulation mode, so we will check later...
*/ if (psr_typ == 0 && psr_vers == 5) {
init_turbosparc(); return;
}
/* Next check for Fujitsu Swift. */ if (psr_typ == 0 && psr_vers == 4) {
phandle cpunode; char node_str[128];
/* Look if it is not a TurboSparc emulating Swift... */
cpunode = prom_getchild(prom_root_node); while ((cpunode = prom_getsibling(cpunode)) != 0) {
prom_getstring(cpunode, "device_type", node_str, sizeof(node_str)); if (!strcmp(node_str, "cpu")) { if (!prom_getintdefault(cpunode, "psr-implementation", 1) &&
prom_getintdefault(cpunode, "psr-version", 1) == 5) {
init_turbosparc(); return;
} break;
}
}
init_swift(); return;
}
/* Now the Viking family of srmmu. */ if (psr_typ == 4 &&
((psr_vers == 0) ||
((psr_vers == 1) && (mod_typ == 0) && (mod_rev == 0)))) {
init_viking(); return;
}
if (mm->context != NO_CONTEXT) { if (any_other_mm_cpus(mm))
xc2(local_ops->tlb_page, (unsignedlong)vma, page);
local_ops->tlb_page(vma, page);
}
}
staticvoid smp_flush_page_to_ram(unsignedlong page)
{ /* Current theory is that those who call this are the one's * who have just dirtied their cache with the pages contents * in kernel space, therefore we only run this on local cpu. * * XXX This experiment failed, research further... -DaveM
*/ #if 1
xc1(local_ops->page_to_ram, page); #endif
local_ops->page_to_ram(page);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.