Monday, August 2, 2010

Another TLB handler problem

I realized the reason of this problem: tlb_flush needs patch, what the reason?

On OCTEON tlbwi, it checks the entry is duplicated or not, if so it'll cause MCHECK exception.
R10k and Loongson doesn't check it I suppose.
On FreeBSD/mips, their code make different invalid address each TLB entries when invalidating TLB:
mips_wr_entryhi(TLBHI_ENTRY(MIPS_KSEG0_START + (2 * i * PAGE_SIZE), 0));
i is entry index.

So I just followed that implementation on OpenBSD/OCTEON, and it works.


LEAF(tlb_flush, 0)
mfc0 v1, COP_0_STATUS_REG # Save the status register.
ori v0, v1, SR_INT_ENAB
xori v0, v0, SR_INT_ENAB
mtc0 v0, COP_0_STATUS_REG # Disable interrupts
ITLBNOPFIX
mfc0 ta1, COP_0_TLB_WIRED




LA v0, CKSEG0_BASE # invalid address
dmfc0 ta0, COP_0_TLB_HI # Save the PID


#ifdef CPU_OCTEON
mul ta2, ta1, 2 * PAGE_SIZE
addu v0, v0, ta2
#else
dmtc0 v0, COP_0_TLB_HI # Mark entry high as invalid
#endif
dmtc0 zero, COP_0_TLB_LO0 # Zero out low entry0.
dmtc0 zero, COP_0_TLB_LO1 # Zero out low entry1.
mtc0 zero, COP_0_TLB_PG_MASK # Zero out mask entry.


/*
* Align the starting value (ta1) and the upper bound (a0).
*/
1:
mtc0 ta1, COP_0_TLB_INDEX # Set the index register.
#ifdef CPU_OCTEON
dmtc0 v0, COP_0_TLB_HI # Mark entry high as invalid
addu v0, v0, 2 * PAGE_SIZE
#endif
addu ta1, ta1, 1 # Increment index.
nop
nop
nop
tlbwi # Write the TLB entry.
nop
nop
bne ta1, a0, 1b
nop


#ifdef CPU_LOONGSON2
li v0, COP_0_DIAG_ITLB_CLEAR | COP_0_DIAG_BTB_CLEAR | COP_0_DIAG_RAS_DISABLE
dmtc0 v0, COP_0_DIAG
#endif


dmtc0 ta0, COP_0_TLB_HI # Restore the PID
li a0, TLB_PAGE_MASK
mtc0 a0, COP_0_TLB_PG_MASK # Restore default mask value.
mtc0 v1, COP_0_STATUS_REG # Restore the status register
ITLBNOPFIX
j ra
nop
END(tlb_flush)


LEAF(tlb_flush_addr, 0)
mfc0 v1, COP_0_STATUS_REG # Save the status register.
ori v0, v1, SR_INT_ENAB
xori v0, v0, SR_INT_ENAB
mtc0 v0, COP_0_STATUS_REG # Disable interrupts
ITLBNOPFIX
dli v0, (PG_HVPN | PG_ASID)
and a0, a0, v0 # Make sure valid hi value.
dmfc0 ta0, COP_0_TLB_HI # Get current PID
dmtc0 a0, COP_0_TLB_HI # look for addr & PID
nop
nop
nop
nop
tlbp # Probe for the entry.
nop
nop # Delay for effect
nop
LA ta1, CKSEG0_BASE # Load invalid entry.
mfc0 v0, COP_0_TLB_INDEX # See what we got
bltz v0, 1f # index < 0 => !found
nop


#ifdef CPU_OCTEON
mul ta2, v0, 2 * PAGE_SIZE
addu ta1, ta1, ta2
#endif


dmtc0 ta1, COP_0_TLB_HI # Mark entry high as invalid


dmtc0 zero, COP_0_TLB_LO0 # Zero out low entry.
dmtc0 zero, COP_0_TLB_LO1 # Zero out low entry.
nop
nop
nop
nop
tlbwi
nop
nop
nop
nop


#ifdef CPU_LOONGSON2
li v0, COP_0_DIAG_ITLB_CLEAR | COP_0_DIAG_BTB_CLEAR | COP_0_DIAG_RAS_DISABLE
dmtc0 v0, COP_0_DIAG
#endif


1:
dmtc0 ta0, COP_0_TLB_HI # restore PID
mtc0 v1, COP_0_STATUS_REG # Restore the status register
ITLBNOPFIX
j ra
nop
END(tlb_flush_addr)

No comments:

Post a Comment