Subversion Repositories HelenOS

Compare Revisions

Ignore whitespace Rev 2927 → Rev 3403

/branches/dynload/kernel/arch/mips32/src/mm/tlb.c
47,18 → 47,14
#include <align.h>
#include <interrupt.h>
 
static void tlb_refill_fail(istate_t *istate);
static void tlb_invalid_fail(istate_t *istate);
static void tlb_modified_fail(istate_t *istate);
static void tlb_refill_fail(istate_t *);
static void tlb_invalid_fail(istate_t *);
static void tlb_modified_fail(istate_t *);
 
static pte_t *find_mapping_and_check(uintptr_t badvaddr, int access, istate_t *istate, int *pfrc);
static pte_t *find_mapping_and_check(uintptr_t, int, istate_t *, int *);
 
static void prepare_entry_lo(entry_lo_t *lo, bool g, bool v, bool d, bool cacheable, uintptr_t pfn);
static void prepare_entry_hi(entry_hi_t *hi, asid_t asid, uintptr_t addr);
 
/** Initialize TLB
/** Initialize TLB.
*
* Initialize TLB.
* Invalidate all entries and mark wired entries.
*/
void tlb_arch_init(void)
76,7 → 72,6
cp0_index_write(i);
tlbwi();
}
 
/*
* The kernel is going to make use of some wired
85,11 → 80,9
cp0_wired_write(TLB_WIRED);
}
 
/** Process TLB Refill Exception
/** Process TLB Refill Exception.
*
* Process TLB Refill Exception.
*
* @param istate Interrupted register context.
* @param istate Interrupted register context.
*/
void tlb_refill(istate_t *istate)
{
131,14 → 124,15
*/
pte->a = 1;
 
prepare_entry_hi(&hi, asid, badvaddr);
prepare_entry_lo(&lo, pte->g, pte->p, pte->d, pte->cacheable, pte->pfn);
tlb_prepare_entry_hi(&hi, asid, badvaddr);
tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->d, pte->cacheable,
pte->pfn);
 
/*
* New entry is to be inserted into TLB
*/
cp0_entry_hi_write(hi.value);
if ((badvaddr/PAGE_SIZE) % 2 == 0) {
if ((badvaddr / PAGE_SIZE) % 2 == 0) {
cp0_entry_lo0_write(lo.value);
cp0_entry_lo1_write(0);
}
157,11 → 151,9
tlb_refill_fail(istate);
}
 
/** Process TLB Invalid Exception
/** Process TLB Invalid Exception.
*
* Process TLB Invalid Exception.
*
* @param istate Interrupted register context.
* @param istate Interrupted register context.
*/
void tlb_invalid(istate_t *istate)
{
178,7 → 170,7
* Locate the faulting entry in TLB.
*/
hi.value = cp0_entry_hi_read();
prepare_entry_hi(&hi, hi.asid, badvaddr);
tlb_prepare_entry_hi(&hi, hi.asid, badvaddr);
cp0_entry_hi_write(hi.value);
tlbp();
index.value = cp0_index_read();
221,12 → 213,13
*/
pte->a = 1;
 
prepare_entry_lo(&lo, pte->g, pte->p, pte->d, pte->cacheable, pte->pfn);
tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->d, pte->cacheable,
pte->pfn);
 
/*
* The entry is to be updated in TLB.
*/
if ((badvaddr/PAGE_SIZE) % 2 == 0)
if ((badvaddr / PAGE_SIZE) % 2 == 0)
cp0_entry_lo0_write(lo.value);
else
cp0_entry_lo1_write(lo.value);
241,11 → 234,9
tlb_invalid_fail(istate);
}
 
/** Process TLB Modified Exception
/** Process TLB Modified Exception.
*
* Process TLB Modified Exception.
*
* @param istate Interrupted register context.
* @param istate Interrupted register context.
*/
void tlb_modified(istate_t *istate)
{
262,7 → 253,7
* Locate the faulting entry in TLB.
*/
hi.value = cp0_entry_hi_read();
prepare_entry_hi(&hi, hi.asid, badvaddr);
tlb_prepare_entry_hi(&hi, hi.asid, badvaddr);
cp0_entry_hi_write(hi.value);
tlbp();
index.value = cp0_index_read();
296,12 → 287,6
}
 
/*
* Fail if the page is not writable.
*/
if (!pte->w)
goto fail;
 
/*
* Read the faulting TLB entry.
*/
tlbr();
312,12 → 297,13
pte->a = 1;
pte->d = 1;
 
prepare_entry_lo(&lo, pte->g, pte->p, pte->w, pte->cacheable, pte->pfn);
tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->w, pte->cacheable,
pte->pfn);
 
/*
* The entry is to be updated in TLB.
*/
if ((badvaddr/PAGE_SIZE) % 2 == 0)
if ((badvaddr / PAGE_SIZE) % 2 == 0)
cp0_entry_lo0_write(lo.value);
else
cp0_entry_lo1_write(lo.value);
344,8 → 330,10
if (s)
sym2 = s;
 
fault_if_from_uspace(istate, "TLB Refill Exception on %p", cp0_badvaddr_read());
panic("%x: TLB Refill Exception at %x(%s<-%s)\n", cp0_badvaddr_read(), istate->epc, symbol, sym2);
fault_if_from_uspace(istate, "TLB Refill Exception on %p",
cp0_badvaddr_read());
panic("%x: TLB Refill Exception at %x(%s<-%s)\n", cp0_badvaddr_read(),
istate->epc, symbol, sym2);
}
 
 
356,8 → 344,10
char *s = get_symtab_entry(istate->epc);
if (s)
symbol = s;
fault_if_from_uspace(istate, "TLB Invalid Exception on %p", cp0_badvaddr_read());
panic("%x: TLB Invalid Exception at %x(%s)\n", cp0_badvaddr_read(), istate->epc, symbol);
fault_if_from_uspace(istate, "TLB Invalid Exception on %p",
cp0_badvaddr_read());
panic("%x: TLB Invalid Exception at %x(%s)\n", cp0_badvaddr_read(),
istate->epc, symbol);
}
 
void tlb_modified_fail(istate_t *istate)
367,23 → 357,27
char *s = get_symtab_entry(istate->epc);
if (s)
symbol = s;
fault_if_from_uspace(istate, "TLB Modified Exception on %p", cp0_badvaddr_read());
panic("%x: TLB Modified Exception at %x(%s)\n", cp0_badvaddr_read(), istate->epc, symbol);
fault_if_from_uspace(istate, "TLB Modified Exception on %p",
cp0_badvaddr_read());
panic("%x: TLB Modified Exception at %x(%s)\n", cp0_badvaddr_read(),
istate->epc, symbol);
}
 
/** Try to find PTE for faulting address
/** Try to find PTE for faulting address.
*
* Try to find PTE for faulting address.
* The AS->lock must be held on entry to this function.
*
* @param badvaddr Faulting virtual address.
* @param access Access mode that caused the fault.
* @param istate Pointer to interrupted state.
* @param pfrc Pointer to variable where as_page_fault() return code will be stored.
* @param badvaddr Faulting virtual address.
* @param access Access mode that caused the fault.
* @param istate Pointer to interrupted state.
* @param pfrc Pointer to variable where as_page_fault() return code
* will be stored.
*
* @return PTE on success, NULL otherwise.
* @return PTE on success, NULL otherwise.
*/
pte_t *find_mapping_and_check(uintptr_t badvaddr, int access, istate_t *istate, int *pfrc)
pte_t *
find_mapping_and_check(uintptr_t badvaddr, int access, istate_t *istate,
int *pfrc)
{
entry_hi_t hi;
pte_t *pte;
402,7 → 396,7
* Check if the mapping exists in page tables.
*/
pte = page_mapping_find(AS, badvaddr);
if (pte && pte->p) {
if (pte && pte->p && (pte->w || access != PF_ACCESS_WRITE)) {
/*
* Mapping found in page tables.
* Immediately succeed.
425,6 → 419,7
page_table_lock(AS, true);
pte = page_mapping_find(AS, badvaddr);
ASSERT(pte && pte->p);
ASSERT(pte->w || access != PF_ACCESS_WRITE);
return pte;
break;
case AS_PF_DEFER:
445,7 → 440,9
}
}
 
void prepare_entry_lo(entry_lo_t *lo, bool g, bool v, bool d, bool cacheable, uintptr_t pfn)
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;
455,7 → 452,7
lo->pfn = pfn;
}
 
void prepare_entry_hi(entry_hi_t *hi, asid_t asid, uintptr_t addr)
void tlb_prepare_entry_hi(entry_hi_t *hi, asid_t asid, uintptr_t addr)
{
hi->value = ALIGN_DOWN(addr, PAGE_SIZE * 2);
hi->asid = asid;
484,10 → 481,10
lo1.value = cp0_entry_lo1_read();
printf("%-2u %-4u %#6x %#4x %1u %1u %1u %1u %#6x\n",
i, hi.asid, hi.vpn2, mask.mask,
lo0.g, lo0.v, lo0.d, lo0.c, lo0.pfn);
i, hi.asid, hi.vpn2, mask.mask,
lo0.g, lo0.v, lo0.d, lo0.c, lo0.pfn);
printf(" %1u %1u %1u %1u %#6x\n",
lo1.g, lo1.v, lo1.d, lo1.c, lo1.pfn);
lo1.g, lo1.v, lo1.d, lo1.c, lo1.pfn);
}
cp0_entry_hi_write(hi_save.value);
564,11 → 561,12
cp0_entry_hi_write(hi_save.value);
}
 
/** Invalidate TLB entries for specified page range belonging to specified address space.
/** Invalidate TLB entries for specified page range belonging to specified
* address space.
*
* @param asid Address space identifier.
* @param page First page whose TLB entry is to be invalidated.
* @param cnt Number of entries to invalidate.
* @param asid Address space identifier.
* @param page First page whose TLB entry is to be invalidated.
* @param cnt Number of entries to invalidate.
*/
void tlb_invalidate_pages(asid_t asid, uintptr_t page, count_t cnt)
{
585,7 → 583,7
 
for (i = 0; i < cnt + 1; i += 2) {
hi.value = 0;
prepare_entry_hi(&hi, asid, page + i * PAGE_SIZE);
tlb_prepare_entry_hi(&hi, asid, page + i * PAGE_SIZE);
cp0_entry_hi_write(hi.value);
 
tlbp();
592,7 → 590,10
index.value = cp0_index_read();
 
if (!index.p) {
/* Entry was found, index register contains valid index. */
/*
* Entry was found, index register contains valid
* index.
*/
tlbr();
 
lo0.value = cp0_entry_lo0_read();
/branches/dynload/kernel/arch/mips32/src/mm/as.c
44,7 → 44,7
/** Architecture dependent address space init. */
void as_arch_init(void)
{
as_operations = &as_pt_operations;
as_operations = &as_pt_operations;
asid_fifo_init();
}
 
/branches/dynload/kernel/arch/mips32/src/mm/frame.c
32,27 → 32,238
/** @file
*/
 
#include <macros.h>
#include <arch/mm/frame.h>
#include <arch/mm/tlb.h>
#include <interrupt.h>
#include <mm/frame.h>
#include <mm/asid.h>
#include <config.h>
#include <arch/drivers/arc.h>
#include <arch/drivers/msim.h>
#include <arch/drivers/serial.h>
#include <print.h>
 
#define ZERO_PAGE_MASK TLB_PAGE_MASK_256K
#define ZERO_FRAMES 2048
#define ZERO_PAGE_WIDTH 18 /* 256K */
#define ZERO_PAGE_SIZE (1 << ZERO_PAGE_WIDTH)
#define ZERO_PAGE_ASID ASID_INVALID
#define ZERO_PAGE_TLBI 0
#define ZERO_PAGE_ADDR 0
#define ZERO_PAGE_OFFSET (ZERO_PAGE_SIZE / sizeof(uint32_t) - 1)
#define ZERO_PAGE_VALUE (((volatile uint32_t *) ZERO_PAGE_ADDR)[ZERO_PAGE_OFFSET])
 
#define ZERO_PAGE_VALUE_KSEG1(frame) (((volatile uint32_t *) (0xa0000000 + (frame << ZERO_PAGE_WIDTH)))[ZERO_PAGE_OFFSET])
 
#define MAX_REGIONS 32
 
typedef struct {
pfn_t start;
pfn_t count;
} phys_region_t;
 
static count_t phys_regions_count = 0;
static phys_region_t phys_regions[MAX_REGIONS];
 
 
/** Check whether frame is available
*
* Returns true if given frame is generally available for use.
* Returns false if given frame is used for physical memory
* mapped devices and cannot be used.
*
*/
static bool frame_available(pfn_t frame)
{
#if MACHINE == msim
/* MSIM device (dprinter) */
if (frame == (KA2PA(MSIM_VIDEORAM) >> ZERO_PAGE_WIDTH))
return false;
/* MSIM device (dkeyboard) */
if (frame == (KA2PA(MSIM_KBD_ADDRESS) >> ZERO_PAGE_WIDTH))
return false;
#endif
 
#if MACHINE == simics
/* Simics device (serial line) */
if (frame == (KA2PA(SERIAL_ADDRESS) >> ZERO_PAGE_WIDTH))
return false;
#endif
 
#if (MACHINE == lgxemul) || (MACHINE == bgxemul)
/* gxemul devices */
if (overlaps(frame << ZERO_PAGE_WIDTH, ZERO_PAGE_SIZE,
0x10000000, MB2SIZE(256)))
return false;
#endif
return true;
}
 
 
/** Check whether frame is safe to write
*
* Returns true if given frame is safe for read/write test.
* Returns false if given frame should not be touched.
*
*/
static bool frame_safe(pfn_t frame)
{
/* Kernel structures */
if ((frame << ZERO_PAGE_WIDTH) < KA2PA(config.base))
return false;
/* Kernel */
if (overlaps(frame << ZERO_PAGE_WIDTH, ZERO_PAGE_SIZE,
KA2PA(config.base), config.kernel_size))
return false;
/* Kernel stack */
if (overlaps(frame << ZERO_PAGE_WIDTH, ZERO_PAGE_SIZE,
KA2PA(config.stack_base), config.stack_size))
return false;
/* Init tasks */
bool safe = true;
count_t i;
for (i = 0; i < init.cnt; i++)
if (overlaps(frame << ZERO_PAGE_WIDTH, ZERO_PAGE_SIZE,
KA2PA(init.tasks[i].addr), init.tasks[i].size)) {
safe = false;
break;
}
return safe;
}
 
static void frame_add_region(pfn_t start_frame, pfn_t end_frame)
{
if (end_frame > start_frame) {
/* Convert 1M frames to 16K frames */
pfn_t first = ADDR2PFN(start_frame << ZERO_PAGE_WIDTH);
pfn_t count = ADDR2PFN((end_frame - start_frame) << ZERO_PAGE_WIDTH);
/* Interrupt vector frame is blacklisted */
pfn_t conf_frame;
if (first == 0)
conf_frame = 1;
else
conf_frame = first;
zone_create(first, count, conf_frame, 0);
if (phys_regions_count < MAX_REGIONS) {
phys_regions[phys_regions_count].start = first;
phys_regions[phys_regions_count].count = count;
phys_regions_count++;
}
}
}
 
 
/** Create memory zones
*
* If ARC is known, read information from ARC, otherwise
* assume some defaults.
* - blacklist first FRAME because there is an exception vector
* Walk through available 256 KB chunks of physical
* memory and create zones.
*
* Note: It is assumed that the TLB is not yet being
* used in any way, thus there is no interference.
*
*/
void frame_arch_init(void)
{
if (!arc_frame_init()) {
zone_create(0, ADDR2PFN(CONFIG_MEMORY_SIZE), 1, 0);
/*
* Blacklist interrupt vector
*/
frame_mark_unavailable(0, 1);
ipl_t ipl = interrupts_disable();
/* Clear and initialize TLB */
cp0_pagemask_write(ZERO_PAGE_MASK);
cp0_entry_lo0_write(0);
cp0_entry_lo1_write(0);
cp0_entry_hi_write(0);
 
count_t i;
for (i = 0; i < TLB_ENTRY_COUNT; i++) {
cp0_index_write(i);
tlbwi();
}
pfn_t start_frame = 0;
pfn_t frame;
bool avail = true;
/* Walk through all 1 MB frames */
for (frame = 0; frame < ZERO_FRAMES; frame++) {
if (!frame_available(frame))
avail = false;
else {
if (frame_safe(frame)) {
entry_lo_t lo0;
entry_lo_t lo1;
entry_hi_t hi;
tlb_prepare_entry_lo(&lo0, false, true, true, false, frame << (ZERO_PAGE_WIDTH - 12));
tlb_prepare_entry_lo(&lo1, false, false, false, false, 0);
tlb_prepare_entry_hi(&hi, ZERO_PAGE_ASID, ZERO_PAGE_ADDR);
cp0_pagemask_write(ZERO_PAGE_MASK);
cp0_entry_lo0_write(lo0.value);
cp0_entry_lo1_write(lo1.value);
cp0_entry_hi_write(hi.value);
cp0_index_write(ZERO_PAGE_TLBI);
tlbwi();
ZERO_PAGE_VALUE = 0;
if (ZERO_PAGE_VALUE != 0)
avail = false;
else {
ZERO_PAGE_VALUE = 0xdeadbeef;
if (ZERO_PAGE_VALUE != 0xdeadbeef)
avail = false;
#if (MACHINE == lgxemul) || (MACHINE == bgxemul)
else {
ZERO_PAGE_VALUE_KSEG1(frame) = 0xaabbccdd;
if (ZERO_PAGE_VALUE_KSEG1(frame) != 0xaabbccdd)
avail = false;
}
#endif
}
}
}
if (!avail) {
frame_add_region(start_frame, frame);
start_frame = frame + 1;
avail = true;
}
}
frame_add_region(start_frame, frame);
/* Blacklist interrupt vector frame */
frame_mark_unavailable(0, 1);
/* Cleanup */
cp0_pagemask_write(ZERO_PAGE_MASK);
cp0_entry_lo0_write(0);
cp0_entry_lo1_write(0);
cp0_entry_hi_write(0);
cp0_index_write(ZERO_PAGE_TLBI);
tlbwi();
interrupts_restore(ipl);
}
 
 
void physmem_print(void)
{
printf("Base Size\n");
printf("---------- ----------\n");
count_t i;
for (i = 0; i < phys_regions_count; i++) {
printf("%#010x %10u\n",
PFN2ADDR(phys_regions[i].start), PFN2ADDR(phys_regions[i].count));
}
}
 
/** @}
*/