Michael suggested on IRC we write a TLB miss handler that somewhat resembles real life, to illustrate what the kernel needs. Here's a first try.
/* * For the general structure of Linux page tables, see * http://lwn.net/Articles/106177/ * In the case of LM32, we only need three levels, hence to PMD. * * Also note that I simplified the types. So there's a lot of uint32_t, * which in the kernel would be pmd_t, pte_t, etc. Also, in the kernel, * the many operations are encapsulated in macros or inline functions. */ #define DTLBMA (*(volatile uint32_t *) 0x....) /* miss VA */ #define TLBCTRL (*(volatile uint32_t *) 0x....) /* TLB command */ #define TLBVADDR (*(volatile uint32_t *) 0x....) /* VA operand */ #define TLBPADDR (*(volatile uint32_t *) 0x....) /* PA operand */ #define TLB_CMD_UPDATE (2 << 1) void handle_tlb_miss(void) /* 0 */ { uint32_t addr = DTLBMA; /* virtual address of miss */ uint32_t **pgd = current_task->pgd; /* 1st level page table */ uint32_t *pmd = pgd[addr >> 22]; /* 2nd level page table */ if (unlikely(!pmd)) /* 1 */ goto no_page_table_fault; uint32_t pte = pmd[(addr >> 12) & 1023]; if (unlikely(!pte_present(pte))) /* 2 */ goto no_page_fault; TLBVADDR = addr; /* 3, 4 */ TLBPADDR = pte; TLBCTRL = TLB_CMD_UPDATE; /* 5 */ return; /* from exception */ no_page_table_fault: /* page fault (table miss) exception */ no_page_fault: /* 6 */ /* page fault (page miss) exception */ /* 7 */ } Notes: 0) or handle_itlb_miss / handle_dtlb_miss, depending on whether we want separate handlers. 1) assumes NULL indicates a page table is absent (i.e., the virtual address isn't mapped.) 2) the page table entry may contain non-zero bits also if the page is absent. Therefore, we use pte_present, which does whatever test is necessary. (E.g., on x86, it would be something similar to static inline int pte_present(pte_t pte) { return pte & PAGE_PRESENT_BIT; } 3) I lost track of how the TLB is currently selected. So add TLB selection bits as needed :-) Ideally, the TLB selection would be implicit, e.g., by encoding it in the lower 10 bits of DTLBMA/TLBVADDR, or by remembering what kind of TLB miss exception we're handling at the moment. 4) ideally, DTLBMA == TLBVADDR so that we don't have to copy the address. 5) if a TLB entry update is the only operation that uses TLBPADDR, then we could make a write to TLBPADDR trigger an implicit TLB update, and eliminate the CSR write to TLBCTRL. 6) no_page_table_fault and no_page_fault may use the same code path, i.e., a general "map this page" operation. Note that handling a page fault can be a very complex operation, with "disk" I/O and other processes getting scheduled (possibly incurring their own misses and faults). 7) the page fault code path may or may not update the TLB. If it doesn't, then we'd return from the TLB miss exception and just get the same exception again, but this time with the page present. This architecture assumes that the hardware will perform a permission check when retrying the faulting operation. Thus, a write to a read-only page (e.g., an executable page), could cause this sequence: DTLB miss exception (1) -> page table is not present -> create page table, return DTLB miss exception (2) -> page is not present -> create/read page, return without updating the TLB (see 7) DTLB miss exception (3) -> page is not mapped in TLB -> map page DTLB permission exception -> figure out what to do, e.g., segfault If we're after a fork() and the write is to a data page that has been cloned but not accessed yet, the sequence would end like this: ... DTLB miss exception (3) -> page is not mapped in TLB -> map page /* until now it's the same */ DTLB permission exception -> oh, it's a copy-on-write (COW) page -> new_page = allocate_page(); -> memcpy(new_page, old_page, PAGE_SIZE); -> invalidate TLB entry (*) DTLB miss exception (4) -> page is not mapped in TLB -> map page write succeeds now (*) if, instead of invalidating the TLB entry, we update it, then we'd avoid the 4th DTLB miss. - Werner _______________________________________________ http://lists.milkymist.org/listinfo.cgi/devel-milkymist.org IRC: #milkymist@Freenode