HelenOS sources
This source file includes following definitions.
- tlb_arch_init
- tlb_refill
- tlb_invalid
- tlb_modified
- tlb_prepare_entry_lo
- tlb_prepare_entry_hi
- tlb_print
- tlb_invalidate_all
- tlb_invalidate_asid
- tlb_invalidate_pages
#include <arch/mm/tlb.h>
#include <mm/asid.h>
#include <mm/tlb.h>
#include <mm/page.h>
#include <mm/as.h>
#include <arch/cp0.h>
#include <panic.h>
#include <arch.h>
#include <synch/mutex.h>
#include <stdio.h>
#include <log.h>
#include <assert.h>
#include <align.h>
#include <interrupt.h>
#include <symtab.h>
#define PFN_SHIFT 12
#define VPN_SHIFT 12
#define ADDR2HI_VPN(a) ((a) >> VPN_SHIFT)
#define ADDR2HI_VPN2(a) (ADDR2HI_VPN((a)) >> 1)
#define HI_VPN2ADDR(vpn) ((vpn) << VPN_SHIFT)
#define HI_VPN22ADDR(vpn2) (HI_VPN2ADDR(vpn2) << 1)
#define LO_PFN2ADDR(pfn) ((pfn) << PFN_SHIFT)
#define BANK_SELECT_BIT(a) (((a) >> PAGE_WIDTH) & 1)
void tlb_arch_init(void)
{
int i;
cp0_pagemask_write(TLB_PAGE_MASK_16K);
cp0_entry_hi_write(0);
cp0_entry_lo0_write(0);
cp0_entry_lo1_write(0);
for (i = 0; i < TLB_ENTRY_COUNT; i++) {
cp0_index_write(i);
tlbwi();
}
cp0_wired_write(TLB_WIRED);
}
void tlb_refill(istate_t *istate)
{
entry_lo_t lo;
uintptr_t badvaddr;
pte_t pte;
badvaddr = cp0_badvaddr_read();
bool found = page_mapping_find(AS, badvaddr, true, &pte);
if (found && pte.p) {
pte.a = 1;
tlb_prepare_entry_lo(&lo, pte.g, pte.p, pte.d,
pte.cacheable, pte.pfn);
page_mapping_update(AS, badvaddr, true, &pte);
if (BANK_SELECT_BIT(badvaddr) == 0) {
cp0_entry_lo0_write(lo.value);
cp0_entry_lo1_write(0);
} else {
cp0_entry_lo0_write(0);
cp0_entry_lo1_write(lo.value);
}
cp0_pagemask_write(TLB_PAGE_MASK_16K);
tlbwr();
return;
}
(void) as_page_fault(badvaddr, PF_ACCESS_READ, istate);
}
void tlb_invalid(istate_t *istate)
{
entry_lo_t lo;
tlb_index_t index;
uintptr_t badvaddr;
pte_t pte;
tlbp();
index.value = cp0_index_read();
#if defined(PROCESSOR_4Kc)
if (index.p) {
tlb_refill(istate);
return;
}
#endif
assert(!index.p);
badvaddr = cp0_badvaddr_read();
bool found = page_mapping_find(AS, badvaddr, true, &pte);
if (found && pte.p) {
tlbr();
pte.a = 1;
tlb_prepare_entry_lo(&lo, pte.g, pte.p, pte.d,
pte.cacheable, pte.pfn);
page_mapping_update(AS, badvaddr, true, &pte);
if (BANK_SELECT_BIT(badvaddr) == 0)
cp0_entry_lo0_write(lo.value);
else
cp0_entry_lo1_write(lo.value);
tlbwi();
return;
}
(void) as_page_fault(badvaddr, PF_ACCESS_READ, istate);
}
void tlb_modified(istate_t *istate)
{
entry_lo_t lo;
tlb_index_t index;
uintptr_t badvaddr;
pte_t pte;
badvaddr = cp0_badvaddr_read();
tlbp();
index.value = cp0_index_read();
if (index.p) {
log(LF_ARCH, LVL_WARN, "%s: TLBP failed in exception handler (badvaddr=%#"
PRIxn ", ASID=%d).\n", __func__, badvaddr,
AS ? AS->asid : -1);
return;
}
bool found = page_mapping_find(AS, badvaddr, true, &pte);
if (found && pte.p && pte.w) {
tlbr();
pte.a = 1;
pte.d = 1;
tlb_prepare_entry_lo(&lo, pte.g, pte.p, pte.w,
pte.cacheable, pte.pfn);
page_mapping_update(AS, badvaddr, true, &pte);
if (BANK_SELECT_BIT(badvaddr) == 0)
cp0_entry_lo0_write(lo.value);
else
cp0_entry_lo1_write(lo.value);
tlbwi();
return;
}
(void) as_page_fault(badvaddr, PF_ACCESS_WRITE, istate);
}
void
tlb_prepare_entry_lo(entry_lo_t *lo, bool g, bool v, bool d, bool cacheable,
uintptr_t pfn)
{
lo->value = 0;
lo->g = g;
lo->v = v;
lo->d = d;
lo->c = cacheable ? PAGE_CACHEABLE_EXC_WRITE : PAGE_UNCACHED;
lo->pfn = pfn;
}
void tlb_prepare_entry_hi(entry_hi_t *hi, asid_t asid, uintptr_t addr)
{
hi->value = 0;
hi->vpn2 = ADDR2HI_VPN2(ALIGN_DOWN(addr, PAGE_SIZE));
hi->asid = asid;
}
void tlb_print(void)
{
page_mask_t mask, mask_save;
entry_lo_t lo0, lo0_save, lo1, lo1_save;
entry_hi_t hi, hi_save;
unsigned int i;
hi_save.value = cp0_entry_hi_read();
lo0_save.value = cp0_entry_lo0_read();
lo1_save.value = cp0_entry_lo1_read();
mask_save.value = cp0_pagemask_read();
printf("[nr] [asid] [vpn2 ] [mask] [gvdc] [pfn ]\n");
for (i = 0; i < TLB_ENTRY_COUNT; i++) {
cp0_index_write(i);
tlbr();
mask.value = cp0_pagemask_read();
hi.value = cp0_entry_hi_read();
lo0.value = cp0_entry_lo0_read();
lo1.value = cp0_entry_lo1_read();
printf("%-4u %-6u %0#10x %-#6x %1u%1u%1u%1u %0#10x\n",
i, hi.asid, HI_VPN22ADDR(hi.vpn2), mask.mask,
lo0.g, lo0.v, lo0.d, lo0.c, LO_PFN2ADDR(lo0.pfn));
printf(" %1u%1u%1u%1u %0#10x\n",
lo1.g, lo1.v, lo1.d, lo1.c, LO_PFN2ADDR(lo1.pfn));
}
cp0_entry_hi_write(hi_save.value);
cp0_entry_lo0_write(lo0_save.value);
cp0_entry_lo1_write(lo1_save.value);
cp0_pagemask_write(mask_save.value);
}
void tlb_invalidate_all(void)
{
entry_lo_t lo0, lo1;
entry_hi_t hi_save;
int i;
assert(interrupts_disabled());
hi_save.value = cp0_entry_hi_read();
for (i = TLB_WIRED; i < TLB_ENTRY_COUNT; i++) {
cp0_index_write(i);
tlbr();
lo0.value = cp0_entry_lo0_read();
lo1.value = cp0_entry_lo1_read();
lo0.v = 0;
lo1.v = 0;
cp0_entry_lo0_write(lo0.value);
cp0_entry_lo1_write(lo1.value);
tlbwi();
}
cp0_entry_hi_write(hi_save.value);
}
void tlb_invalidate_asid(asid_t asid)
{
entry_lo_t lo0, lo1;
entry_hi_t hi, hi_save;
int i;
assert(interrupts_disabled());
assert(asid != ASID_INVALID);
hi_save.value = cp0_entry_hi_read();
for (i = 0; i < TLB_ENTRY_COUNT; i++) {
cp0_index_write(i);
tlbr();
hi.value = cp0_entry_hi_read();
if (hi.asid == asid) {
lo0.value = cp0_entry_lo0_read();
lo1.value = cp0_entry_lo1_read();
lo0.v = 0;
lo1.v = 0;
cp0_entry_lo0_write(lo0.value);
cp0_entry_lo1_write(lo1.value);
tlbwi();
}
}
cp0_entry_hi_write(hi_save.value);
}
void tlb_invalidate_pages(asid_t asid, uintptr_t page, size_t cnt)
{
unsigned int i;
entry_lo_t lo0, lo1;
entry_hi_t hi, hi_save;
tlb_index_t index;
assert(interrupts_disabled());
if (asid == ASID_INVALID)
return;
hi_save.value = cp0_entry_hi_read();
for (i = 0; i < cnt + 1; i += 2) {
tlb_prepare_entry_hi(&hi, asid, page + i * PAGE_SIZE);
cp0_entry_hi_write(hi.value);
tlbp();
index.value = cp0_index_read();
if (!index.p) {
tlbr();
lo0.value = cp0_entry_lo0_read();
lo1.value = cp0_entry_lo1_read();
lo0.v = 0;
lo1.v = 0;
cp0_entry_lo0_write(lo0.value);
cp0_entry_lo1_write(lo1.value);
tlbwi();
}
}
cp0_entry_hi_write(hi_save.value);
}
HelenOS homepage, sources at GitHub