From 78ad3fa67cf5e0a5dc2964e9ae7285950a7b4ca5 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Tue, 6 Dec 2016 00:18:02 +0000 Subject: [PATCH] bank8k: first cut at an 8K banking module This needs to handle the tables differently as they don't fit in the page pointers but that also lets us address Brett's wish for video mapping which with 8K banking actually sort of makes sense on the COCO3. --- Kernel/bank8k.c | 309 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 309 insertions(+) create mode 100644 Kernel/bank8k.c diff --git a/Kernel/bank8k.c b/Kernel/bank8k.c new file mode 100644 index 00000000..aa35168d --- /dev/null +++ b/Kernel/bank8k.c @@ -0,0 +1,309 @@ +/* + * Manage a system with 8K pages, and 16bit pointers. + * + * It also supports a 'magic page' to allow graphics page mapping + * into a process and the correct behaviour on execve etc. + * + * To use this banker you must be able to set + * PAGE_INVALID a value meaning this page does not map anywhere + * PAGE_VIDEO a page for video mapping (or set to PAGE_INVALID) + * + * Invalid can either be a real 'invalid' for the MMU or can be set + * to something like a random chunk of ROM. Anything that stops + * scribbles. + * + * The banker will keep an array of 8 or 10 items per process (8 x 8K + * banks lus a reference for the video bank underlay) + * + * Set: + * CONFIG_BANK8 + * CONFIG_VIDMAP8 if you have an 8K video mapping option + * MAX_MAPS to the largest number of free pages there can be + * SWAPDEV if using swap + * PROGTOP first byte above process space + * SWAPBASE address we swap from + * + * Page numbers must not include 0 (0 is taken as swapped) + * + * FIXME: currently requires that PROGTOP aligns to an 8K boundary + */ +#include +#include +#include + +#ifdef CONFIG_BANK8 + +#ifdef CONFIG_VIDMAP8 +#define PTNUM 9 +#define PTSIZE 10 +#else +#define PTSIZE 8 +#define PTNUM 8 +#endif + +/* Bank numbers we actually use */ +#define HIBANK (((PROGTOP) + 0x1FFF) >> 13) +#define LOBANK ((SWAPBASE) >> 13) + +static uint8_t pfree[MAX_MAPS]; +static uint8_t pfptr = 0; +static uint8_t pmap[PTABSIZE][PTSIZE]; + +#ifdef CONFIG_VIDMAP8 + +int vidmap_unmap(void) +{ + uint8_t *p = (uint8_t *)udata.u_page; + if (p[8] == PAGE_INVALID) { + udata.u_error = EINVAL; + return -1; + } + p[p[9]] = p[8]; + p[8] = PAGE_INVALID; + return 0; +} + +/* Caller must validate addr */ +int vidmap_map(uint16_t addr) +{ + uint8_t *p = (uint8_t *)udata.u_page; + uint8_t s = addr >> 13; + if (p[8] != PAGE_INVALID) { + udata.u_error = EINVAL; + return -1; + } + p[8] = p[s]; + p[9] = s; + p[s] = PAGE_VIDEO; + return 0; +} + +#endif + +/* + * Map handling + */ + +/* + * Helper for platform to add pages at boot + */ +void pagemap_add(uint8_t page) +{ + if (pfptr == MAX_MAPS) + panic(PANIC_MAPOVER); + pfree[pfptr++] = page; +} + +/* + * Free the maps for this task + */ +void pagemap_free(ptptr p) +{ + uint8_t *pt = (uint8_t *)p->p_page; + uint8_t *e = pt + PTNUM; + while(pt < e) { + if (*pt != PAGE_INVALID && *pt != PAGE_VIDEO) + pfree[--pfptr] = *pt; + pt++; + } +} + +static int maps_needed(uint16_t top) +{ + /* On many platforms if you touch this or PROGTOP you must + touch tricks.s */ + uint16_t needed = top + 0xFFFF - PROGTOP; + /* Usually we have 0x1000 common - 1 for shift and inc */ + needed >>= 13; /* in banks */ + needed++; /* rounded */ + return needed; +} + +/* + * Allocate the maps for this task post fork + * We have a hackish fix for init that would be nice + * resolve. + * + * Use p-> not udata. The udata is not yet swapped in! + */ +int pagemap_alloc(ptptr p) +{ + uint8_t *ptr; + int needed = maps_needed(p->p_top); + int i; + + /* Cheapest way to keep it non zero */ + ptr = pmap[p - ptab]; + p->p_page = (uint16_t)ptr; + +#ifdef SWAPDEV + /* Throw our toys out of our pram until we have enough room */ + while (needed > pfptr) + if (swapneeded(p, 1) == NULL) + return ENOMEM; +#else + if (needed > pfptr) /* We have no swap so poof... */ + return ENOMEM; +#endif + + /* Allocate the pages upwards */ + for (i = 0; i < needed; i++) + *ptr++ = pfree[--pfptr]; + while (i < PTNUM) + *ptr++ = PAGE_INVALID; + return 0; +} + +/* + * Reallocate the maps for a process + * + * Currently only called on execve and we rely on this in the + * specific case of bank8k + */ +int pagemap_realloc(usize_t size) +{ + int8_t have = maps_needed(udata.u_top); + int8_t want = maps_needed(size); + uint8_t *ptr = (uint8_t *)udata.u_page; + +#ifdef CONFIG_VIDMAP8 + if (ptr[8] != PAGE_INVALID) + vidmap_unmap(); +#endif + + /* If we are shrinking then free pages and propogate the + common page into the freed spaces */ + if (want == have) + return 0; + + /* We assume sane vector management or a platform which is saving + and restoring the IRQ vectors not reloading them from the process + data */ + if (want < have) { + while(want < have) { + pfree[--pfptr] = ptr[want]; + ptr[want] = PAGE_INVALID; + } + return 0; + } +#ifdef SWAPDEV + /* Throw our toys out of our pram until we have enough room */ + while (want - have > pfptr) + if (swapneeded(udata.u_ptab, 1) == NULL) + return ENOMEM; +#else + if (want - have > pfptr) /* We have no swap so poof... */ + return ENOMEM; +#endif + while(have < want) + ptr[have++] = pfree[pfptr++]; + return 0; +} + +usize_t pagemap_mem_used(void) +{ + return pfptr << 3; +} + +#ifdef SWAPDEV + +/* + * Swap out the memory of a process to make room + * for something else. For bank8k do this as nine operations and + * skip invalid pages. + */ + +int swapout(ptptr p) +{ + uint16_t page = p->p_page; + uint16_t blk; + uint16_t map; + uint16_t base = SWAPBASE; + uint16_t size = (0x2000 - SWAPBASE) >> 9; + uint16_t i; + uint8_t *pt = (uint8_t *)p->p_page; +#ifdef CONFIG_VIDMAP8 + uint8_t pv = pt[8]; +#endif + + if (!page) + panic(PANIC_ALREADYSWAP); +#ifdef DEBUG + kprintf("Swapping out %x (%x)\n", p, p->p_page); +#endif + + /* Are we out of swap ? */ + map = swapmap_alloc(); + if (map == 0) + return ENOMEM; + blk = map * SWAP_SIZE; + /* Write the app (and possibly the uarea etc..) to disk */ + for (i = LOBANK; i < HIBANK; i++) { + uint8_t pg = *pt++; + if (pg != PAGE_INVALID) { +#ifdef CONFIG_VIDMAP8 + if (pg == PAGE_VIDEO) + pg = pv; +#endif + swapwrite(SWAPDEV, blk, size << 9, base, pg); + } + base += 0x2000; + base &= 0xE000; /* Snap to bank alignment */ + blk += size; + size = 0x10; + } + pagemap_free(p); + p->p_page = 0; + p->p_page2 = map; + +#ifdef DEBUG + kprintf("%x: swapout done %d\n", p, p->p_page); +#endif + return 0; +} + +/* + * Swap ourself in: must be on the swap stack when we do this + */ + +void swapin(ptptr p, uint16_t map) +{ + uint16_t blk = map * SWAP_SIZE; + uint16_t base = SWAPBASE; + uint16_t size = (0x2000 - SWAPBASE) >> 9; + uint16_t i; + uint8_t *pt = (uint8_t *)p->p_page; +#ifdef CONFIG_VIDMAP8 + uint8_t pv = pt[8]; +#endif + +#ifdef DEBUG + kprintf("Swapin %x, %x\n", p, p->p_page); +#endif + if (!p->p_page) { + kprintf("%x: nopage!\n", p); + return; + } + + for (i = LOBANK; i < HIBANK; i++) { + uint8_t pg = *pt++; + if (pg != PAGE_INVALID) { +#ifdef CONFIG_VIDMAP8 + if (pg == PAGE_VIDEO) + pg = pv; +#endif + swapread(SWAPDEV, blk, size << 9, base, *pt++); + } + base += 0x2000; + base &= 0xE000; + blk += size; + size = 0x10; /* 8K */ + } +#ifdef DEBUG + kprintf("%x: swapin done %d\n", p, p->p_page); +#endif +} + +#endif + +#endif -- 2.34.1