mirror of
https://gitlab.com/qemu-project/openbios.git
synced 2024-02-13 08:34:06 +08:00
Allow context-switching within the MMU TLB miss handlers on SPARC64, and use this to implement MMU miss handlers in C rather
than assembler. In order to allow OpenSolaris to boot under OpenBIOS, it is necessary to be able to invoke Forth words from within the MMU I/D-TLB miss handlers, since Solaris 10 kernels hook into the virtual to physical address translation process via va>tte-data at boot time. Hence this patch implements two macros: SAVE_CPU_STATE and RESTORE_CPU_STATE which enable a context switch to occur from within these trap handlers. Things are more complicated from within the MMU miss handlers because we can't use flushw to flush the processor registers to stack. This is because the memory pointed to by the stack pointer may not be in the TLB either, and so we'd end up in a recursive MMU trap. Hence we solve this by creating a static stack within OpenBIOS which is guaranteed to be locked in the TLB and storing all of our state there. Once the ability to switch context has been implemented, it is possible to invoke C functions as per normal from within the MMU miss handlers. Hence as a proof of concept I've migrated the MMU miss handling code from ASM to C with a view of making the relevant changes to invoke the relevant Forth functions at a later date. I'd also like to say thank you to Blue Swirl who took the time to answer all my questions and generally point out the shortcomings in my first attempts at SPARC assembler. Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@siriusit.co.uk> git-svn-id: svn://coreboot.org/openbios/trunk/openbios-devel@874 f158a5a8-5612-0410-a976-696ce0be7e32
This commit is contained in:
committed by
Mark Cave-Ayland
parent
e9c1365d32
commit
6d590534bb
@@ -154,6 +154,18 @@ dtlb_load3(unsigned long vaddr, unsigned long tte_data,
|
||||
"r" (tte_data), "r" (tte_index << 3), "i" (ASI_DTLB_DATA_ACCESS));
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
dtlb_faultva(void)
|
||||
{
|
||||
unsigned long faultva;
|
||||
|
||||
asm("ldxa [%1] %2, %0\n"
|
||||
: "=r" (faultva)
|
||||
: "r" (48), "i" (ASI_DMMU));
|
||||
|
||||
return faultva;
|
||||
}
|
||||
|
||||
/*
|
||||
( index tte_data vaddr -- ? )
|
||||
*/
|
||||
@@ -168,6 +180,45 @@ dtlb_load(void)
|
||||
dtlb_load3(vaddr, tte_data, idx);
|
||||
}
|
||||
|
||||
/* MMU D-TLB miss handler */
|
||||
void
|
||||
dtlb_miss_handler(void)
|
||||
{
|
||||
unsigned long faultva, tte_data = 0;
|
||||
translation_t *t = *g_ofmem_translations;
|
||||
|
||||
/* Grab fault address from MMU and round to nearest 8k page */
|
||||
faultva = dtlb_faultva();
|
||||
faultva >>= 13;
|
||||
faultva <<= 13;
|
||||
|
||||
/* Search the ofmem linked list for this virtual address */
|
||||
while (t != NULL) {
|
||||
/* Find the correct range */
|
||||
if (faultva >= t->virt && faultva < (t->virt + t->size)) {
|
||||
|
||||
/* valid tte, 8k size */
|
||||
tte_data = 0x8000000000000000UL;
|
||||
|
||||
/* mix in phys address mode */
|
||||
tte_data |= t->mode;
|
||||
|
||||
/* mix in page physical address = t->phys + offset */
|
||||
tte_data |= t->phys + (faultva - t->virt);
|
||||
|
||||
/* Update MMU */
|
||||
dtlb_load2(faultva, tte_data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
t = t->next;
|
||||
}
|
||||
|
||||
/* If we got here, there was no translation so fail */
|
||||
bug();
|
||||
}
|
||||
|
||||
static void
|
||||
itlb_load2(unsigned long vaddr, unsigned long tte_data)
|
||||
{
|
||||
@@ -187,6 +238,58 @@ itlb_load3(unsigned long vaddr, unsigned long tte_data,
|
||||
"r" (tte_data), "r" (tte_index << 3), "i" (ASI_ITLB_DATA_ACCESS));
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
itlb_faultva(void)
|
||||
{
|
||||
unsigned long faultva;
|
||||
|
||||
asm("ldxa [%1] %2, %0\n"
|
||||
: "=r" (faultva)
|
||||
: "r" (48), "i" (ASI_IMMU));
|
||||
|
||||
return faultva;
|
||||
}
|
||||
|
||||
/* MMU I-TLB miss handler */
|
||||
void
|
||||
itlb_miss_handler(void)
|
||||
{
|
||||
unsigned long faultva, tte_data = 0;
|
||||
translation_t *t = *g_ofmem_translations;
|
||||
|
||||
/* Grab fault address from MMU and round to nearest 8k page */
|
||||
faultva = itlb_faultva();
|
||||
faultva >>= 13;
|
||||
faultva <<= 13;
|
||||
|
||||
/* Search the ofmem linked list for this virtual address */
|
||||
while (t != NULL) {
|
||||
/* Find the correct range */
|
||||
if (faultva >= t->virt && faultva < (t->virt + t->size)) {
|
||||
|
||||
/* valid tte, 8k size */
|
||||
tte_data = 0x8000000000000000UL;
|
||||
|
||||
/* mix in phys address mode */
|
||||
tte_data |= t->mode;
|
||||
|
||||
/* mix in page physical address = t->phys + offset */
|
||||
tte_data |= t->phys + (faultva - t->virt);
|
||||
|
||||
/* Update MMU */
|
||||
itlb_load2(faultva, tte_data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
t = t->next;
|
||||
}
|
||||
|
||||
/* If we got here, there was no translation so fail */
|
||||
bug();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
( index tte_data vaddr -- ? )
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user