/* pointers to the mm structs for each task in each
context. 0xffffffff is a marker for kernel context */ staticstruct mm_struct *ctx_alloc[CONTEXTS_NUM] = {
[0] = (struct mm_struct *)0xffffffff
};
/* has this context been mmdrop'd? */ staticunsignedchar ctx_avail = CONTEXTS_NUM-1;
/* array of pages to be marked off for the rom when we do mem_init later */ /* 256 pages lets the rom take up to 2mb of physical ram.. I really
hope it never wants mote than that. */ unsignedlong rom_pages[256];
/* Print a PTE value in symbolic form. For debugging. */ staticvoid print_pte(pte_t pte)
{ #if0 /* Verbose version. */ unsignedlong val = pte_val (pte);
pr_cont(" pte=%lx [addr=%lx",
val, (val & SUN3_PAGE_PGNUM_MASK) << PAGE_SHIFT); if (val & SUN3_PAGE_VALID) pr_cont(" valid"); if (val & SUN3_PAGE_WRITEABLE) pr_cont(" write"); if (val & SUN3_PAGE_SYSTEM) pr_cont(" sys"); if (val & SUN3_PAGE_NOCACHE) pr_cont(" nocache"); if (val & SUN3_PAGE_ACCESSED) pr_cont(" accessed"); if (val & SUN3_PAGE_MODIFIED) pr_cont(" modified"); switch (val & SUN3_PAGE_TYPE_MASK) { case SUN3_PAGE_TYPE_MEMORY: pr_cont(" memory"); break; case SUN3_PAGE_TYPE_IO: pr_cont(" io"); break; case SUN3_PAGE_TYPE_VME16: pr_cont(" vme16"); break; case SUN3_PAGE_TYPE_VME32: pr_cont(" vme32"); break;
}
pr_cont("]\n"); #else /* Terse version. More likely to fit on a line. */ unsignedlong val = pte_val (pte); char flags[7], *type;
switch (val & SUN3_PAGE_TYPE_MASK) { case SUN3_PAGE_TYPE_MEMORY: type = "memory"; break; case SUN3_PAGE_TYPE_IO: type = "io" ; break; case SUN3_PAGE_TYPE_VME16: type = "vme16" ; break; case SUN3_PAGE_TYPE_VME32: type = "vme32" ; break; default: type = "unknown?"; break;
}
/* Print the PTE value for a given virtual address. For debugging. */ void print_pte_vaddr (unsignedlong vaddr)
{
pr_cont(" vaddr=%lx [%02lx]", vaddr, sun3_get_segmap (vaddr));
print_pte (__pte (sun3_get_pte (vaddr)));
}
/* pmeg align the end of bootmem, adding another pmeg,
* later bootmem allocations will likely need it */
bootmem_end = (bootmem_end + (2 * SUN3_PMEG_SIZE)) & ~SUN3_PMEG_MASK;
/* mark all of the pmegs used thus far as reserved */ for (i=0; i < __pa(bootmem_end) / SUN3_PMEG_SIZE ; ++i)
pmeg_alloc[i] = 2;
/* I'm thinking that most of the top pmeg's are going to be
used for something, and we probably shouldn't risk it */ for(num = 0xf0; num <= 0xff; num++)
pmeg_alloc[num] = 2;
/* liberate all existing mappings in the rest of kernel space */ for(seg = bootmem_end; seg < 0x0f800000; seg += SUN3_PMEG_SIZE) {
i = sun3_get_segmap(seg);
j = 0; for (num=0, seg=0x0F800000; seg<0x10000000; seg+=16*PAGE_SIZE) { if (sun3_get_segmap (seg) != SUN3_INVALID_PMEG) { #ifdef DEBUG_PROM_MAPS for(i = 0; i < 16; i++) {
pr_info("mapped:");
print_pte_vaddr (seg + (i*PAGE_SIZE)); break;
} #endif // the lowest mapping here is the end of our // vmalloc region if (!m68k_vmalloc_end)
m68k_vmalloc_end = seg;
// mark the segmap alloc'd, and reserve any // of the first 0xbff pages the hardware is // already using... does any sun3 support > 24mb?
pmeg_alloc[sun3_get_segmap(seg)] = 2;
}
}
dvma_init();
/* blank everything below the kernel, and we've got the base
mapping to start all the contexts off with... */ for(seg = 0; seg < PAGE_OFFSET; seg += SUN3_PMEG_SIZE)
sun3_put_segmap(seg, SUN3_INVALID_PMEG);
set_fc(3); for(seg = 0; seg < 0x10000000; seg += SUN3_PMEG_SIZE) {
i = sun3_get_segmap(seg); for(j = 1; j < CONTEXTS_NUM; j++)
(*(romvec->pv_setctxt))(j, (void *)seg, i);
}
set_fc(USER_DATA);
}
/* erase the mappings for a dead context. Uses the pg_dir for hints asthepmegtablesprovedsomewhatunreliable,andunmappingallof
TASK_SIZE was much slower and no more stable. */ /* todo: find a better way to keep track of the pmegs used by a
context for when they're cleared */ void clear_context(unsignedlong context)
{ unsignedchar oldctx; unsignedlong i;
if (context) { if (!ctx_alloc[context])
panic("%s: context not allocated\n", __func__);
for (i = 0; i < SUN3_INVALID_PMEG; i++) { if ((pmeg_ctx[i] == context) && (pmeg_alloc[i] == 1)) {
sun3_put_segmap(pmeg_vaddr[i], SUN3_INVALID_PMEG);
pmeg_ctx[i] = 0;
pmeg_alloc[i] = 0;
pmeg_vaddr[i] = 0;
}
}
sun3_put_context(oldctx);
}
/* gets an empty context. if full, kills the next context listed to
die first */ /* This context invalidation scheme is, well, totally arbitrary, I'm sureitcouldbemuchmoreintelligent...butitgetsthejobdone
for now without much overhead in making it's decision. */ /* todo: come up with optimized scheme for flushing contexts */ unsignedlong get_free_context(struct mm_struct *mm)
{ unsignedlongnew = 1; staticunsignedchar next_to_die = 1;
if(!ctx_avail) { /* kill someone to get our context */ new = next_to_die;
clear_context(new);
next_to_die = (next_to_die + 1) & 0x7; if(!next_to_die)
next_to_die++;
} else { while(new < CONTEXTS_NUM) { if(ctx_alloc[new]) new++; else break;
} // check to make sure one was really free... if(new == CONTEXTS_NUM)
panic("%s: failed to find free context", __func__);
}
ctx_alloc[new] = mm;
ctx_avail--;
returnnew;
}
/* *Dynamicallyselecta`spare'PMEGanduseittomapvirtual`vaddr'in *`context'.MaintaininternalPMEGmanagementstructures.Thisdoesn't *actuallymapthephysicaladdress,butdoescleartheoldmappings.
*/ //todo: better allocation scheme? but is extra complexity worthwhile? //todo: only clear old entries if necessary? how to tell?
inlinevoid mmu_emu_map_pmeg (int context, int vaddr)
{ staticunsignedchar curr_pmeg = 128; int i;
/* Round address to PMEG boundary. */
vaddr &= ~SUN3_PMEG_MASK;
/* Find a spare one. */ while (pmeg_alloc[curr_pmeg] == 2)
++curr_pmeg;
/* Invalidate old mapping for the pmeg, if any */ if (pmeg_alloc[curr_pmeg] == 1) {
sun3_put_context(pmeg_ctx[curr_pmeg]);
sun3_put_segmap (pmeg_vaddr[curr_pmeg], SUN3_INVALID_PMEG);
sun3_put_context(context);
}
/* Update PMEG management structures. */ // don't take pmeg's away from the kernel... if(vaddr >= PAGE_OFFSET) { /* map kernel pmegs into all contexts */ unsignedchar i;
/* Set hardware mapping and clear the old PTE entries. */ for (i=0; i<SUN3_PMEG_SIZE; i+=SUN3_PTE_SIZE)
sun3_put_pte (vaddr + i, SUN3_PAGE_SYSTEM);
/* Consider a different one next time. */
++curr_pmeg;
}
/* *Handleapagefaultatvirtualaddress`vaddr';checkifthereshouldbea *pagethere(specifically,whetherthesoftwarepagetablesindicatethat *thereis).Thisisnecessaryduetothelimitedsizeofthesecond-level *Sun3hardwarepagetables(256groupsof16pages).Ifthereshouldbea *mappingpresent,weselecta`spare'PMEGanduseittocreateamapping. *`read_flag'isnonzeroforareadfault;zeroforawrite.Returnsnonzero *ifwesuccessfullyhandledthefault.
*/ //todo: should we bump minor pagefault counter? if so, here or in caller? //todo: possibly inline this into bus_error030 in <asm/buserror.h> ?
// kernel_fault is set when a kernel page couldn't be demand mapped, // and forces another try using the kernel page table. basically a // hack so that vmalloc would work correctly.
int mmu_emu_handle_fault (unsignedlong vaddr, int read_flag, int kernel_fault)
{ unsignedlong segment, offset; unsignedchar context;
pte_t *pte;
pgd_t * crp;
/* Make sure this is a valid page */ if (!(pte_val (*pte) & SUN3_PAGE_VALID)) return0;
/* Make sure there's a pmeg allocated for the page */ if (sun3_get_segmap (vaddr&~SUN3_PMEG_MASK) == SUN3_INVALID_PMEG)
mmu_emu_map_pmeg (context, vaddr);
/* Write the pte value to hardware MMU */
sun3_put_pte (vaddr&PAGE_MASK, pte_val (*pte));
/* Update software copy of the pte value */ // I'm not sure this is necessary. If this is required, we ought to simply // copy this out when we reuse the PMEG or at some other convenient time. // Doing it here is fairly meaningless, anyway, as we only know about the // first access to a given page. --m if (!read_flag) { if (pte_val (*pte) & SUN3_PAGE_WRITEABLE)
pte_val (*pte) |= (SUN3_PAGE_ACCESSED
| SUN3_PAGE_MODIFIED); else return0; /* Write-protect error. */
} else
pte_val (*pte) |= SUN3_PAGE_ACCESSED;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.