1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-17 10:26:15 +00:00

Add cache handling code for sparc64.

This commit is contained in:
Thomas Moestl 2001-11-09 19:12:06 +00:00
parent 8838ea170e
commit 3c78eb652c
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=86221
2 changed files with 531 additions and 0 deletions

126
sys/sparc64/include/cache.h Normal file
View File

@ -0,0 +1,126 @@
/*
* Copyright (c) 1996
* The President and Fellows of Harvard College. All rights reserved.
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This software was developed by the Computer Systems Engineering group
* at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
* contributed to Berkeley.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Aaron Brown and
* Harvard University.
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* from: @(#)cache.h 8.1 (Berkeley) 6/11/93
* from: NetBSD: cache.h,v 1.3 2000/08/01 00:28:02 eeh Exp
*
* $FreeBSD$
*/
#ifndef _MACHINE_CACHE_H_
#define _MACHINE_CACHE_H_
#include <vm/vm.h>
#include <vm/pmap.h>
#include <dev/ofw/openfirm.h>
/*
* Cache diagnostic access definitions.
*/
/* ASI offsets for I$ diagnostic access */
#define ICDA_SET_SHIFT 13
#define ICDA_SET_MASK (1UL << 13)
#define ICDA_SET(a) (((a) << ICDA_SET_SHIFT) & ICDA_SET_MASK)
/* I$ tag/valid format */
#define ICDT_TAG_SHIFT 8
#define ICDT_TAG_BITS 28
#define ICDT_TAG_MASK (((1UL << ICDT_TAG_BITS) - 1) << ICDT_TAG_SHIFT)
#define ICDT_TAG(x) (((x) & ICDT_TAG_MASK) >> ICDT_TAG_SHIFT)
#define ICDT_VALID (1UL << 36)
/* D$ tag/valid format */
#define DCDT_TAG_SHIFT 2
#define DCDT_TAG_BITS 28
#define DCDT_TAG_MASK (((1UL << DCDT_TAG_BITS) - 1) << DCDT_TAG_SHIFT)
#define DCDT_TAG(x) (((x) & DCDT_TAG_MASK) >> DCDT_TAG_SHIFT)
#define DCDT_VALID_BITS 2
#define DCDT_VALID_MASK ((1UL << DCDT_VALID_BITS) - 1)
/* E$ ASI_ECACHE_W/ASI_ECACHE_R address flags */
#define ECDA_DATA (1UL << 39)
#define ECDA_TAG (1UL << 40)
/* E$ tag/state/parity format */
#define ECDT_TAG_BITS 13
#define ECDT_TAG_SIZE (1UL << ECDT_TAG_BITS)
#define ECDT_TAG_MASK (ECDT_TAG_SIZE - 1)
/*
* Do two virtual addresses (at which the same page is mapped) form and illegal
* alias in D$? XXX: should use cache.dc_size here.
*/
#define DCACHE_BOUNDARY 0x4000
#define DCACHE_BMASK (DCACHE_BOUNDARY - 1)
#define CACHE_BADALIAS(v1, v2) \
(((v1) & DCACHE_BMASK) != ((v2) & DCACHE_BMASK))
/*
* Routines for dealing with the cache.
*/
void cache_init __P((phandle_t)); /* turn it on */
void icache_flush __P((vm_offset_t, vm_offset_t));
void icache_inval_phys __P((vm_offset_t, vm_offset_t));
void dcache_flush __P((vm_offset_t, vm_offset_t));
void dcache_inval __P((pmap_t, vm_offset_t, vm_offset_t));
void dcache_blast __P((void));
void ecache_flush __P((vm_offset_t, vm_offset_t));
#if 0
void ecache_inval_phys __P((vm_offset_t, vm_offset_t));
#endif
/*
* Cache control information.
*/
struct cacheinfo {
int c_enabled; /* true => cache is enabled */
int ic_size; /* instruction cache */
int ic_l2set;
int ic_assoc;
int ic_linesize;
int dc_size; /* data cache */
int dc_l2size;
int dc_assoc;
int dc_linesize;
int ec_size; /* external cache info */
int ec_assoc;
int ec_l2set;
int ec_linesize;
int ec_l2linesize;
};
#endif /* !_MACHINE_CACHE_H_ */

405
sys/sparc64/sparc64/cache.c Normal file
View File

@ -0,0 +1,405 @@
/*
* Copyright (c) 1996
* The President and Fellows of Harvard College. All rights reserved.
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This software was developed by the Computer Systems Engineering group
* at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
* contributed to Berkeley.
*
* All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Harvard University.
* This product includes software developed by the University of
* California, Lawrence Berkeley Laboratory.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Aaron Brown and
* Harvard University.
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*-
* Copyright (c) 2001 by Thomas Moestl <tmm@FreeBSD.org>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* from: @(#)cache.c 8.2 (Berkeley) 10/30/93
* from: NetBSD: cache.c,v 1.5 2000/12/06 01:47:50 mrg Exp
*
* $FreeBSD$
*/
/*
* Cache routines.
*
* UltraSPARCs have an virtually indexed, physically tagged (VIPT) level 1 data
* cache (D$) and physically indexed, physically tagged (PIPT) level 1
* instruction (I$) and Level 2 (E$) caches.
* D$ is directly mapped, I$ is pseudo 2-way associative. The Level 2 cache (E$)
* is documented to be directly mapped on the UltraSPARC IIi, but there are
* apparently models (using the IIe version) that have a 4-way associative E$.
*
* D$ uses a write-through model, while E$ uses write-back and is
* write-allocating. The lines present in D$ are forced to be a subset of those
* in E$.
* This means that lines that are present in D$ always have an identical
* corresponding (sub-) line in E$.
*
* The term "main memory" is used in the following to refer to the non-cache
* memory as well as to memory-mapped device i/o space.
*
* There are 3 documented ways to flush the D$ and E$ caches:
* - displacement flushing (a sequence of loads of addresses that alias to
* to-be-flushed ones in the caches). This only works for directly mapped
* caches, and is recommended to flush D$ and E$ in the IIi manual. It is not
* used to flush E$ because of the aforementioned models that have a
* multiple-associative E$. Displacement flushing invalidates the cache
* entries and writes modified lines back to main memory.
* - diagnostic acceses can be used to invalidate cache pages. All lines
* are discarded, which means that changes in D$/E$ that have not been
* committed to main memory are lost.
* - block-commit stores. Those use the block transfer ASIs to load a
* 64-byte block to a set of FP registers and store them back using a
* special ASI that will cause the data to be immediately committed to main
* memory. This method has the same properties as the first method, but
* (hopefully) works regardless of the associativity of the caches. It is
* expected to be slow.
*
* I$ can be handled using the flush instruction.
*
* Some usage guidelines:
*
* The inval functions are variants of the flush ones that discard modified
* cache lines.
* PCI DMA transactions are cache-coherent and do not require flushing
* before DMA reads or after DMA writes. It is unclear from the manual
* how far this applies to UPA transactions.
*
* icache_flush(): needed before code that has been written to memory is
* executed, because I$ is not necessarily consistent with D$, E$, or
* main memory. An exception is that I$ snoops DMA transfers, so no
* flush is required after to-be-executed data has been fetched this way.
* icache_inval_phys(): has roughly same effect as icache_flush() since there
* are no writes to I$.
*
* dcache_flush(): required when a page mapping is changed from cacheable to
* noncacheable, or to resolve illegal aliases. Both cases should happen
* seldom. Mapping address changes do not require this, since D$ is VIPT.
* dcache_inval(): has roughly same effect as dcache_flush() since D$ is
* write-through.
* dcache_blast(): discards all lines in D$.
*
* ecache_flush(): needed to commit modified lines to main memory, and to make
* sure that no stale data is used when the main memory has changed without
* the cache controller noticing. This is e.g. needed for device i/o space.
* It is usually better to use a non-cacheable mapping in this case.
* ecache_flush() is guaranteed to also flush the relevant addresses out of
* D$.
* ecache_inval_phys(): like ecache_flush(), but invalidates a physical range
* in the cache. This function is usually dangerous and should not be used.
*
* All operations have a line size granularity!
*
* All flush methods tend to be expensive, so unnecessary flushes should be
* avoided.
*/
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/systm.h>
#include <vm/vm.h>
#include <vm/pmap.h>
#include <machine/cache.h>
#include <machine/cpufunc.h>
#include <machine/fp.h>
#include <machine/pcb.h>
#include <machine/pmap.h>
#include <machine/tte.h>
#include <machine/vmparam.h>
static struct cacheinfo cache;
#define CDIAG_CLR(asi, addr) \
__asm __volatile("stxa %%g0, [%0] %1" : : "r" (addr), "I" (asi))
/* Read to %g0, needed for E$ access. */
#define CDIAG_RDG0(asi, addr) \
__asm __volatile("ldxa [%0] %1, %%g0" : : "r" (addr), "I" (asi))
#define CDIAG_RD(asi, addr, r) \
__asm __volatile("ldxa [%1] %2, %0" : "=r" (r) : "r" (addr), "I" (asi))
/* Sigh. I$ diagnostic registers want ldda. */
#define ICDIAG_RD(asi, addr, r) \
__asm __volatile("ldda [%1] %2, %%o4; mov %%o5, %0" : "=r" (r) : \
"r" (addr), "I" (asi) : "%o4", "%o5");
#define OF_GET(h, n, v) OF_getprop((h), (n), &(v), sizeof(v))
/*
* Fill in the cache parameters using the cpu node.
*/
void
cache_init(phandle_t node)
{
u_long set;
if (OF_GET(node, "icache-size", cache.ic_size) == -1 ||
OF_GET(node, "icache-line-size", cache.ic_linesize) == -1 ||
OF_GET(node, "icache-associativity", cache.ic_assoc) == -1 ||
OF_GET(node, "dcache-size", cache.dc_size) == -1 ||
OF_GET(node, "dcache-line-size", cache.dc_linesize) == -1 ||
OF_GET(node, "dcache-associativity", cache.dc_assoc) == -1 ||
OF_GET(node, "ecache-size", cache.ec_size) == -1 ||
OF_GET(node, "ecache-line-size", cache.ec_linesize) == -1 ||
OF_GET(node, "ecache-associativity", cache.ec_assoc) == -1)
panic("cache_init: could not retrieve cache parameters");
set = cache.ic_size / cache.ic_assoc;
cache.ic_l2set = ffs(set) - 1;
if ((set & ~(1UL << cache.ic_l2set)) != 0)
panic("cache_init: I$ set size not a power of 2");
cache.dc_l2size = ffs(cache.dc_size) - 1;
if ((cache.dc_size & ~(1UL << cache.dc_l2size)) != 0)
panic("cache_init: D$ size not a power of 2");
if (cache.dc_assoc != 1)
panic("cache_init: D$ is not directly mapped!");
set = cache.ec_size / cache.ec_assoc;
cache.ec_l2set = ffs(set) - 1;
if ((set & ~(1UL << cache.ec_l2set)) != 0)
panic("cache_init: E$ set size not a power of 2");
cache.c_enabled = 1; /* enable cache flushing */
}
/* Flush a range of addresses from I$ using the flush instruction. */
void
icache_flush(vm_offset_t start, vm_offset_t end)
{
char *p, *ep;
int ls;
if (!cache.c_enabled)
return;
ls = cache.ic_linesize;
ep = (char *)ulmin(end, start + cache.ic_size);
for (p = (char *)start; p < ep; p += ls)
flush(p);
}
/*
* Blast a I$ physical range using diagnostic accesses.
* NOTE: there is a race between checking the tag and invalidating it. It
* cannot be closed by disabling interrupts, since the fetch for the next
* instruction may be in that line, so we don't even bother.
* Since blasting a line does not discard data, this has no ill effect except
* a minor slowdown.
*/
void
icache_inval_phys(vm_offset_t start, vm_offset_t end)
{
vm_offset_t addr, ica;
u_long tag;
u_long j;
if (!cache.c_enabled)
return;
for (addr = start & ~(cache.ic_linesize - 1); addr <= end;
addr += cache.ic_linesize) {
for (j = 0; j < 2; j++) {
ica = (addr & (cache.ic_size - 1)) | ICDA_SET(j);
ICDIAG_RD(ASI_ICACHE_TAG, ica, tag);
if (ICDT_TAG(tag) != addr >> cache.ic_l2set)
continue;
CDIAG_CLR(ASI_ICACHE_TAG, ica);
}
}
}
/*
* Flush a range of addresses from D$ using displacement flushes. This does
* not necessarily flush E$, because we do not take care of flushing the
* correct physical colors and E$ may not be directly mapped.
*/
void
dcache_flush(vm_offset_t start, vm_offset_t end)
{
int j;
vm_offset_t baseoff;
u_long i, mask;
char *kp;
if (!cache.c_enabled)
return;
mask = cache.dc_size - 1;
/* No need to flush lines more than once. */
baseoff = start & mask;
/*
* Use a locked page for flushing. D$ should be smaller than 4M, which
* is somewhat likely...
*/
kp = (char *)KERNBASE;
j = 0;
for (i = 0; i <= ulmin((end - start), cache.dc_size);
i += cache.dc_linesize)
j += kp[(baseoff + i) & mask];
}
/*
* Blast a D$ range using diagnostic accesses.
* This has the same (harmless) races as icache_blast().
* Assumes a page in the kernel map.
*/
void
dcache_inval(pmap_t pmap, vm_offset_t start, vm_offset_t end)
{
vm_offset_t va, pa, offs, dca;
u_long tag;
if (!cache.c_enabled)
return;
for (va = start & ~(cache.dc_linesize - 1); va <= end;
va = (va + PAGE_SIZE_MIN) & ~PAGE_MASK_MIN) {
if ((pa = pmap_extract(pmap, va)) == 0)
continue;
for (offs = start & PAGE_MASK_MIN;
offs < ulmin(PAGE_SIZE_MIN, end - va + 1);
offs += cache.dc_linesize) {
dca = (va + offs) & (cache.dc_size - 1);
CDIAG_RD(ASI_DCACHE_TAG, dca, tag);
if (DCDT_TAG(tag) != (pa + offs) >> PAGE_SHIFT_MIN)
continue;
CDIAG_CLR(ASI_DCACHE_TAG, dca);
}
}
}
/* Discard all lines in D$. */
void
dcache_blast()
{
vm_offset_t dca;
if (!cache.c_enabled)
return;
for (dca = 0; dca < cache.dc_size; dca += cache.dc_linesize)
CDIAG_CLR(ASI_DCACHE_TAG, dca);
}
/* Flush a E$ physical range using block commit stores. */
void
ecache_flush(vm_offset_t start, vm_offset_t end)
{
vm_offset_t addr;
if (!cache.c_enabled)
return;
/* XXX: not needed in all cases, provide a wrapper in fp.c */
savefpctx(&curthread->td_pcb->pcb_fpstate);
wr(fprs, 0, FPRS_FEF);
for (addr = start & ~(cache.ec_linesize - 1); addr <= end;
addr += cache.ec_linesize) {
__asm __volatile("ldda [%0] %1, %%f0; membar #Sync; "
"stda %%f0, [%0] %2" : : "r" (addr), "I" (ASI_BLK_S),
"I" (ASI_BLK_COMMIT_S));
}
membar(Sync);
restorefpctx(&curthread->td_pcb->pcb_fpstate);
}
#if 0
/*
* Blast a E$ range using diagnostic accesses.
* This is disabled: it suffers from the same races as dcache_blast() and
* icache_blast_phys(), but they may be fatal here because blasting an E$ line
* can discard modified data.
* There is no really use for this anyway.
*/
void
ecache_inval_phys(vm_offset_t start, vm_offset_t end)
{
vm_offset_t addr, eca;
critical_t c;
u_long tag, j;
if (!cache.c_enabled)
return;
for (addr = start & ~(cache.ec_linesize - 1); addr <= end;
addr += cache.ec_linesize) {
for (j = 0; j < cache.ec_assoc; j++) {
/* XXX: guesswork... */
eca = (addr & (cache.ec_size - 1)) |
(j << (cache.ec_l2set));
c = critical_enter();
/*
* Retrieve the tag:
* A read from the appropriate VA in ASI_ECACHE_R
* will transfer the tag from the tag RAM to the
* data register (ASI_ECACHE_TAG_DATA, VA 0).
*/
CDIAG_RDG0(ASI_ECACHE_R, ECDA_TAG | eca);
CDIAG_RD(ASI_ECACHE_TAG_DATA, 0, tag);
if ((addr & ~cache.ec_size) >> cache.ec_l2set ==
(tag & ECDT_TAG_MASK)) {
/*
* Clear. Works like retrieving the tag, but
* the other way round.
*/
CDIAG_CLR(ASI_ECACHE_TAG_DATA, 0);
CDIAG_CLR(ASI_ECACHE_W, ECDA_TAG | eca);
}
}
}
}
#endif