home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright 1991,1992,1993 Atari Corporation.
- * All rights reserved.
- */
-
- /*
- * page-table data structures
- *
- *
- * The root pointer points to a list of pointers to top-level pointer tables.
- *
- * Each entry in a pointer table points to another pointer table or to
- * a page table, or is a page descriptor.
- *
- * Since, initially, the logical address space is the physical address space,
- * we only need to worry about 26MB plus 32K for I/O space.
- *
- * Since we want some pages to be supervisor-accessible, but we don't want
- * a whole separate table for that, we use long-format descriptors.
- *
- * Initial memory map:
- *
- * 0 - membot: S (supervisor only)
- * membot - memtop: P (protected TPA)
- * memtop - phystop: G (screen)
- * phystop - $00E00000: bus error
- * $00E00000- $00E3FFFF: G (ROM)
- * $00E40000- $00FF7FFF: bus error
- * $00FF8000- $00FFFFFF: G (mostly S: I/O space, but that's done in hardware)
- * $01000000- ramtop: P
- * ramtop - $7FFFFFFF: G (A32/D32 VME, cacheable)
- * $80000000- $FEFFFFFF: G (A32/D32 VME, non cacheable)
- * $FFxxxxxx just like $00xxxxxx.
- *
- * Here's a final choice of layouts: IS=0, PS=13 (8K), TIA=4, TIB=4, TIC=4,
- * TID=7. This lets us map out entire unused megabytes at level C, and gives
- * us an 8K page size, which is the largest the '040 can deal with.
- *
- * This code implements 4+4+4+7, as follows:
- *
- * tbl_a
- * 0 -> tbl_b0
- * 1-7 -> Cacheable direct (VME) page descriptors
- * 8-E -> Non-cacheable direct (VME) page descriptors
- * F -> tbl_bf
- *
- * tbl_b0 table: 16 entries (assumes only 16MB of TT RAM)
- * 0 -> tbl_c00 (16MB of ST RAM address space)
- * 1 -> tbl_c01 (16MB of TT RAM address space)
- * 2-F -> cacheable direct (VME) page descriptors
- *
- * tbl_bF table: 16 entries (deals with $FF mapping to $00)
- * 0-E -> Non-cacheable direct (VME) page descriptors
- * F -> tbl_c00 (16MB of ST RAM address space, repeated here as $FF)
- *
- * tbl_c00 table: ST RAM address space (example assuming 4MB ST RAM)
- * 0-3 -> RAM page tables
- * 4-D -> invalid
- * E -> direct map, cache enable (ROM)
- * F -> direct map, cache inhibit (I/O)
- *
- * For each 16MB containing any TT RAM, there's a tbl_c. Within those,
- * for each MB that actually has TT RAM, there's another table, containing
- * 128 RAM page tables. Where there isn't RAM, there are "global"
- * pages, to let the hardware bus error or not as it sees fit.
- *
- * One RAM page table is allocated per megabyte of real RAM; each table has
- * 128 entries, which is 8K per page. For a TT with 4MB ST RAM and 4MB TT RAM
- * that's 8K in page tables. You can cut this down by not allocating page
- * tables for which the entire megabyte is not accessible (i.e. it's all
- * private memory and it's not YOUR private memory).
- *
- * You have one of these per process. When somebody loads into G or S memory
- * or leaves it, you have to go through the page tables of every process
- * updating S bits (for S) and DT (for G) bits.
- *
- * The top levels are small & easy so replicating them once per process
- * doesn't really hurt us.
- *
- */
-
- #include "mint.h"
-
- #if 0
- #define MP_DEBUG(x) DEBUG(x)
- #else
- #define MP_DEBUG(x)
- #endif
-
- void *memset P_((void *s, int ucharfill, unsigned long size));
- static void _dump_tree P_((long_desc tbl, int level));
-
- extern int debug_level; /* see debug.c */
- extern long mcpu; /* in main.c */
-
- /*
- * You can turn this whole module off, and the stuff in context.s,
- * by setting no_mem_prot to 1.
- */
-
- int no_mem_prot;
- long page_table_size;
-
- /*
- * PMMU stuff
- */
-
- /*
- * This is one global TC register that is copied into every process'
- * context, even though it never changes. It's also used by the
- * functions that dump page tables.
- */
-
- tc_reg tc;
-
- /* mint_top_* get used in mem.c also */
- ulong mint_top_tt;
- ulong mint_top_st;
-
- int tt_mbytes; /* number of megabytds of TT RAM */
-
- /*
- * global_mode_table: one byte per page in the system. Initially all pages
- * are set to "global" but then the TPA pages are set to "invalid" in
- * init_mem. This has to be allocated and initialized in init_tables,
- * when you know how much memory there is. You need a byte per page,
- * from zero to the end of TT RAM, including the space between STRAM
- * and TTRAM. That is, you need 16MB/pagesize plus (tt_mbytes/pagesize)
- * bytes here.
- */
-
- unsigned char *global_mode_table;
-
- /*
- * prototype descriptors; field u1 must be all ones, other u? are zero.
- * This is just the first long of a full descriptor; the ".page_type" part
- * of the union. These are initialized by init_tables.
- *
- * The proto_page_type table yields the value to stuff into the page_type
- * field of a new process' page table. It is the "non-owner" mode for
- * a page with the corresponding value in global_mode_table.
- */
-
- page_type g_page;
- page_type g_ci_page;
- page_type s_page;
- page_type readable_page;
- page_type invalid_page;
- page_type page_ptr;
-
- page_type *const proto_page_type[] =
- { &invalid_page, &g_page, &s_page, &readable_page, &invalid_page };
- /* private global super private/read invalid */
-
- /*
- * Init_tables: called sometime in initialization. We set up some
- * constants here, but that's all. The first new_proc call will set up the
- * page table for the root process and switch it in; from then on, we're
- * always under some process' control.
- *
- * The master page-mode table is initialized here, and some constants like
- * the size needed for future page tables.
- *
- * One important constant initialized here is page_table_size, which is
- * the amount of memory required per page table. new_proc allocates
- * this much memory for each process' page table. This number will be
- * 1K/megabyte plus page table overhead. There are TBL_PAGES_OFFS
- * tables at TBL_SIZE_BYTES each before the main tables begin; then
- * there is 1024 bytes per megabyte of memory being mapped.
- */
-
- void
- init_tables()
- {
- int n_megabytes;
- long global_mode_table_size;
-
- if (no_mem_prot) return;
-
- TRACE(("init_tables"));
-
- #define phys_top_tt (*(ulong *)0x5a4L)
- if (phys_top_tt == 0x01000000L) mint_top_tt = 0;
- else mint_top_tt = phys_top_tt;
-
- #define phys_top_st (*(ulong *)0x42eL)
- mint_top_st = phys_top_st;
-
- if (mint_top_tt)
- tt_mbytes = (int) ((mint_top_tt - 0x01000000L) / ONE_MEG);
- else
- tt_mbytes = 0;
-
- n_megabytes = (int) ((mint_top_st / ONE_MEG) + tt_mbytes);
-
- /*
- * page table size: room for A table, B0 table, BF table, STRAM C
- * table, one TTRAM C table per 16MB (or fraction) of TTRAM, and 1024
- * bytes per megabyte.
- */
-
- page_table_size = (4L * TBL_SIZE_BYTES) +
- (((tt_mbytes+15L)/16L) * TBL_SIZE_BYTES) +
- (n_megabytes*1024L);
-
- global_mode_table_size = ((SIXTEEN_MEG / QUANTUM) +
- (((ulong)tt_mbytes * ONE_MEG) / QUANTUM));
-
- global_mode_table = kmalloc(global_mode_table_size);
-
- assert(global_mode_table);
-
- TRACELOW(("mint_top_st is $%lx; mint_top_tt is $%lx, n_megabytes is %d",
- mint_top_st, mint_top_tt, n_megabytes));
- TRACELOW(("page_table_size is %ld, global_mode_table_size %ld",
- page_table_size,
- global_mode_table_size));
-
- g_page.limit = 0x7fff; /* set nonzero fields: disabled limit */
- g_page.unused1 = 0x3f; /* ones in this reserved field */
- g_page.unused2 = 0;
- g_page.s = 0;
- g_page.unused3 = 0;
- g_page.ci = 0;
- g_page.unused4 = 0;
- g_page.m = 1; /* set m and u to 1 so CPU won't do writes */
- g_page.u = 1;
- g_page.wp = 0; /* not write-protected */
- g_page.dt = 1; /* descriptor type 1: page descriptor */
-
- g_ci_page = g_page;
- g_ci_page.ci = 1;
-
- readable_page = g_page; /* a page which is globally readable */
- readable_page.wp = 1; /* but write protected */
-
- s_page = g_page; /* a page which is globally accessible */
- s_page.s = 1; /* if you're supervisor */
-
- invalid_page = g_page;
- invalid_page.dt = 0;
-
- page_ptr = g_page;
- page_ptr.m = 0; /* this must be zero in page pointers */
- page_ptr.dt = 3;
-
- tc.enable = 1;
- tc.zeros = 0;
- tc.sre = 0;
- tc.fcl = 0;
- tc.is = 0;
- tc.tia = 4;
- tc.tib = 4;
- tc.tic = 4;
- tc.tid = 7; /* 0+4+4+4+7+13 == 32 */
- tc.ps = 13; /* 8K page size */
-
- /* set the whole global_mode_table to "global" */
- memset(global_mode_table,PROT_G,global_mode_table_size);
- }
-
- /*
- * mark_region: mark a region of memory as having a particular type.
- * The arguments are the memory region in question and the new type.
- * If the new type is zero then the old type is preserved. The
- * type of each page is kept in a global place for this purpose,
- * among others.
- *
- * The types are:
- * 0 private
- * 1 global
- * 2 private, but super-accessible
- * 3 private, but world readable
- * 4 invalid
- *
-
- The idea is this:
-
- for (each process) {
- if (you're an owner or you're special) {
- set up owner modes
- }
- else {
- set up non-owner modes
- }
-
- mark_pages(pagetbl,start,len,modes);
- }
-
- */
-
- /*
- invalid---v
- private/gr---v |
- super-------v | |
- global-------v | | |
- private-------v | | | |
- | | | | |
- */
- const ushort other_dt[] = { 0, 1, 1, 1, 0 };
- const ushort other_s[] = { 0, 0, 1, 0, 0 };
- const ushort other_wp[] = { 0, 0, 0, 1, 0 };
-
-
- /*
- * get_page_cookie: return a cookie representing the protection status
- * of some memory.
- *
- * Returns ((wp << 3) | (s << 2) | (dt) | 0x8000) when it wins.
- * Returns 1 if the pages are not all controlled, 0 if they're not all the same.
- */
-
- static short
- get_page_cookie(long_desc *base_tbl,ulong start,ulong len)
- {
- int b_index, c_index, d_index;
- long_desc *tbl, *tbl_b, *tbl_c;
- int dt, s, wp;
-
- if (start < mint_top_st) {
- /* start is in ST RAM; fail if not entirely in ST RAM */
- if (start+len > mint_top_st) {
- return 1;
- }
- }
- else if (start >= 0x01000000L && start < mint_top_tt) {
- /* start is in TT RAM; fail if not entirely in TT RAM */
- if (start+len > mint_top_tt) {
- return 1;
- }
- }
-
- /*
- * a_index is always zero. Only the first 256MB is mapped.
- * b_index is the 16MB number of the page.
- * c_index is the 1MB number of that page within the 16MB (0-15)
- * d_index is the 8K number within that 1MB (0-127).
- */
-
- b_index = (int)(start >> LOG2_16_MEG);
- c_index = (int)(start >> LOG2_ONE_MEG) & 0xf;
- d_index = (int)(start >> LOG2_EIGHT_K) & 0x7f;
-
- /* precompute the table addresses */
- tbl_b = &base_tbl[0].tbl_address[b_index];
- tbl_c = &tbl_b->tbl_address[c_index];
- tbl = &tbl_c->tbl_address[d_index];
-
- dt = tbl->page_type.dt;
- wp = tbl->page_type.wp;
- s = tbl->page_type.s;
-
- for (;;) {
- /* quickly loop through the 1MB-block */
- for (; len && tbl < &tbl_c->tbl_address[0x80]; tbl++)
- {
- if ((tbl->page_type.dt != dt) ||
- (tbl->page_type.s != s) ||
- (tbl->page_type.wp != wp)) {
- /* fail because it's not all the same protection */
- return 0;
- }
- len -= EIGHT_K;
- }
-
- if (len == 0L)
- break;
-
- /* step to the next d-table */
- tbl_c++;
- /* if crossing a 16MB boundary, get the next c-table */
- if (tbl_c == &tbl_b->tbl_address[0x10])
- {
- tbl_b++;
- tbl_c = tbl_b->tbl_address;
- }
- tbl = tbl_c->tbl_address;
- }
- /* we passed -- all the pages in question have the same prot. status */
- return (wp << 3) | (s << 2) | dt | 0x8000;
- }
-
- static void
- mark_pages(long_desc *base_tbl,ulong start,ulong len,
- ushort dt_val, ushort s_val, ushort wp_val, PROC *proc)
- {
- int b_index, c_index, d_index;
- long_desc *tbl, *tbl_b, *tbl_c;
- ulong oldlen;
-
- if (no_mem_prot) return;
-
- oldlen = len;
-
- /*
- * a_index is always zero. Only the first 256MB is mapped.
- * b_index is the 16MB number of the page.
- * c_index is the 1MB number of that page within the 16MB (0-15)
- * d_index is the 8K number within that 1MB (0-127).
- */
-
- b_index = (start >> LOG2_16_MEG);
- c_index = (start >> LOG2_ONE_MEG) & 0xf;
- d_index = (start >> LOG2_EIGHT_K) & 0x7f;
-
- /* precompute the table addresses */
- tbl_b = &base_tbl[0].tbl_address[b_index];
- tbl_c = &tbl_b->tbl_address[c_index];
- tbl = &tbl_c->tbl_address[d_index];
-
- #ifdef MEMPROT_SHORTCUT
- /*
- * Take a shortcut here: we're done if first page of the region is
- * already right.
- */
- /* I don't think this shortcut is a good idea, since while we
- * are doing Mshrink or Srealloc we may very well have a region
- * with mixed page types -- ERS
- */
-
- if (tbl->page_type.dt == dt_val &&
- tbl->page_type.s == s_val &&
- tbl->page_type.wp == wp_val) {
- /*
- TRACE(("mark_pages a:0 b:%d c:%d d:%d (same)",
- b_index,c_index,d_index));
- */
- return;
- }
-
- #endif /* MEMPROT_SHORTCUT */
- /*
- MP_DEBUG(("mark_pages a:0 b:%d c:%d d:%d (diff)",b_index,c_index,d_index));
- */
-
- for (;;)
- {
- /* quickly loop through the 1MB-block */
- for (; len && tbl < &tbl_c->tbl_address[0x80]; tbl++)
- {
- tbl->page_type.dt = dt_val;
- tbl->page_type.s = s_val;
- tbl->page_type.wp = wp_val;
- len -= EIGHT_K;
- }
-
- if (len == 0L)
- break;
-
- /* get the next d-table */
- tbl_c++;
- /* if crossing a 16MB boundary, get the next c-table */
- if (tbl_c == &tbl_b->tbl_address[0x10])
- {
- tbl_b++;
- tbl_c = tbl_b->tbl_address;
- }
- tbl = tbl_c->tbl_address;
- }
-
- flush_pmmu();
- if (mcpu <= 30) {
- /* On the '020 & '030 we have a logical cache, i.e. the DC & IC are on
- * the CPU side of the MMU, hence on an MMU context switch we must flush
- * them too. On the '040, by comparison, we have a physical cache, i.e.
- * the DC & IC are on the memory side of the MMU, so no DC/IC cache flush
- * is needed.
- */
- cpush((void *)start, oldlen);
- }
- }
-
- /* get_prot_mode(r): returns the type of protection region r
- * has
- */
-
- int
- get_prot_mode(r)
- MEMREGION *r;
- {
- ulong start = r->loc;
-
- if (no_mem_prot)
- return PROT_G;
- return global_mode_table[(start >> 13)];
- }
-
- void
- mark_region(region,mode)
- MEMREGION *region;
- short mode;
- {
- ulong start = region->loc;
- ulong len = region->len;
- ulong i;
- ushort dt_val, s_val, wp_val;
- PROC *proc;
- MEMREGION **mr;
-
- if (no_mem_prot) return;
-
- MP_DEBUG(("mark_region %lx len %lx mode %d",start,len,mode));
-
- #if 0 /* this should not occur any more */
- if (mode == PROT_NOCHANGE) {
- mode = global_mode_table[(start >> 13)];
- }
- #else
- assert(mode != PROT_NOCHANGE);
- #endif
-
- /* mark the global page table */
-
- memset(&global_mode_table[start >> 13],mode,(len >> 13));
-
- for (proc = proclist; proc; proc = proc->gl_next) {
- assert(proc->page_table);
- if (mode == PROT_I || mode == PROT_G) {
- /* everybody gets the same flags */
- goto notowner;
- }
- if (proc->memflags & F_OS_SPECIAL) {
- /* you're special; you get owner flags */
- MP_DEBUG(("mark_region: pid %d is an OS special!",proc->pid));
- goto owner;
- }
- if ((mr = proc->mem) != 0) {
- for (i = 0; i < proc->num_reg; i++, mr++) {
- if (*mr == region) {
- MP_DEBUG(("mark_region: pid %d is an owner",proc->pid));
- owner:
- dt_val = 1;
- s_val = 0;
- wp_val = 0;
- goto gotvals;
- }
- }
- }
-
- notowner:
-
- /* if you get here you're not an owner, or mode is G or I */
- MP_DEBUG(("mark_region: pid %d gets non-owner modes",proc->pid));
-
- dt_val = other_dt[mode];
- s_val = other_s[mode];
- wp_val = other_wp[mode];
-
- gotvals:
- mark_pages(proc->page_table,start,len,dt_val,s_val,wp_val,proc);
- }
- }
-
- /* special version of mark_region, used for attaching (mode == PROT_P)
- and detaching (mode == PROT_I) a memory region to/from a process. */
- void
- mark_proc_region(proc,region,mode)
- PROC *proc;
- MEMREGION *region;
- short mode;
- {
- ulong start = region->loc;
- ulong len = region->len;
- ushort dt_val, s_val, wp_val;
- short global_mode;
-
- if (no_mem_prot) return;
-
- MP_DEBUG(("mark_region %lx len %lx mode %d for pid %d",
- start, len, mode, proc->pid));
-
- global_mode = global_mode_table[(start >> 13)];
-
- assert(proc->page_table);
- if (global_mode == PROT_I || global_mode == PROT_G)
- mode = global_mode;
- else {
- if (proc->memflags & F_OS_SPECIAL) {
- /* you're special; you get owner flags */
- MP_DEBUG(("mark_region: pid %d is an OS special!",proc->pid));
- goto owner;
- }
- if (mode == PROT_P) {
- MP_DEBUG(("mark_region: pid %d is an owner",proc->pid));
- owner:
- dt_val = 1;
- s_val = 0;
- wp_val = 0;
- goto gotvals;
- }
- }
-
- /* if you get here you're not an owner, or mode is G or I */
- MP_DEBUG(("mark_region: pid %d gets non-owner modes",proc->pid));
-
- dt_val = other_dt[mode];
- s_val = other_s[mode];
- wp_val = other_wp[mode];
-
- gotvals:
- mark_pages(proc->page_table,start,len,dt_val,s_val,wp_val,proc);
- }
-
- /*
- * prot_temp: temporarily alter curproc's access to memory.
- * Pass in a -1 to give curproc global access; returns a cookie. Call
- * again with that cookie to return the memory to the old mode.
- * There should be no context switches or memory protection changes
- * in the meantime.
- *
- * If called with mode == -1, returns...
- * -1 if mem prot is off -- no error, no action.
- * 0 if the pages are not all the same.
- * 1 if the pages are not all controlled by the page tables.
- *
- * When mode != -1, returns...
- * 0 for success (should never fail). There is little checking.
- * Calling with mode == 0 or 1 results in zero to spoof success, but in fact
- * this is an error. Mode is only really valid if (mode & 0x8000).
- */
-
- int
- prot_temp(loc,len,mode)
- ulong loc;
- ulong len;
- int mode;
- {
- int cookie;
-
- if (no_mem_prot) return -1;
-
- /* round start down to the previous page and len up to the next one. */
- len += loc & MASKBITS;
- loc &= ~MASKBITS;
- len = ROUND(len);
-
- if (mode == 0 || mode == 1) return 0; /* do nothing */
- if (mode == -1) {
- cookie = get_page_cookie(curproc->page_table,loc,len);
-
- /* if not all controlled, return status */
- if (cookie == 0 || cookie == 1) return cookie;
-
- mark_pages(curproc->page_table,loc,len,1,0,0,curproc);
-
- return cookie;
- }
- else {
- mark_pages(curproc->page_table,loc,len,
- mode&3,(mode&4)>>2,(mode&8)>>3,curproc);
- return 0;
- }
- }
-
- /*
- * init_page_table: fill in the page table for the indicated process. The
- * master page map is consulted for the modes of all pages, and the memory
- * region data structures are consulted to see if this process is the owner
- * of any of those tables.
- *
- * This also sets crp and tc in both ctxts of the process. If this is the
- * first call, then the CPU tc is cleared, the TT0 and TT1 regs are zapped,
- * and then this proc's crp and tc are loaded into it.
- */
-
- static short mmu_is_set_up = 0;
-
- void
- init_page_table(proc)
- PROC *proc;
- {
- long_desc *tptr;
- long_desc *tbl_a; /* top-level table */
- long_desc *tbl_b0; /* second level, handles $0 nybble */
- long_desc *tbl_bf; /* handles $F nybble */
- long_desc *tbl_c; /* temp pointer to start of 16MB */
- ulong p, q, r;
- ulong i, j, k;
- int g;
- MEMREGION **mr;
-
- if (no_mem_prot) return;
-
- assert(proc && proc->page_table);
-
- if (proc->pid)
- TRACELOW(("init_page_table(proc=%lx, pid %d)",proc,proc->pid));
-
- tptr = proc->page_table;
- tbl_a = tptr;
- tptr += TBL_SIZE;
- tbl_b0 = tptr;
- tptr += TBL_SIZE;
- tbl_bf = tptr;
- tptr += TBL_SIZE;
-
- /*
- * table A indexes by the first nybble: $0 and $F refer to their tables,
- * $1-$7 are uncontrolled, cacheable; $8-$E are uncontrolled, ci.
- */
-
- tbl_a[0].page_type = page_ptr;
- tbl_a[0].tbl_address = tbl_b0;
-
- for (i=1; i<0xf; i++) {
- if (i < 8) tbl_a[i].page_type = g_page;
- else tbl_a[i].page_type = g_ci_page;
- tbl_a[i].tbl_address = (long_desc *)(i << 28);
- }
-
- /* $F entry of table A refers to table BF */
- tbl_a[0xf].page_type = page_ptr;
- tbl_a[0xf].tbl_address = tbl_bf;
-
- /*
- * table B0: entry 0 is $00, the 16MB of ST address space.
- */
-
- tbl_b0[0].page_type = page_ptr;
- tbl_b0[0].tbl_address = tptr;
- tbl_c = tptr;
- tptr += TBL_SIZE;
-
- /* for each megabyte that is RAM, allocate a table */
- for (i = 0, k = 0, p = 0; p < mint_top_st; i++, p += 0x00100000L) {
- tbl_c[i].page_type = page_ptr;
- tbl_c[i].tbl_address = tptr;
-
- /* for each page in this megabyte, write a page entry */
- for (q = p, j = 0; j < 128; j++, q += 0x2000, k++) {
- tptr->page_type = *proto_page_type[global_mode_table[k]];
- tptr->tbl_address = (long_desc *)q;
- tptr++;
- }
- }
-
- /* now for each megabyte from mint_top_st to ROM, mark global */
- for ( ; p < 0x00E00000L; i++, p += 0x00100000L) {
- tbl_c[i].page_type = g_page;
- tbl_c[i].tbl_address = (long_desc *)p;
- }
-
- /* fill in the E and F tables: 00Ex is ROM, 00Fx is I/O */
- tbl_c[i].page_type = g_page;
- tbl_c[i].tbl_address = (long_desc *)p;
- i++, p += 0x00100000L;
- tbl_c[i].page_type = g_ci_page;
- tbl_c[i].tbl_address = (long_desc *)p;
-
- /* Done with tbl_c for 0th 16MB; go on to TT RAM */
-
- /*
- structure:
-
- for (i = each 16MB that has any TT RAM in it)
- allocate a table tbl_c, point tbl_b0[i] at it
- for (j = each 1MB that is RAM)
- allocate a table, point tbl_c[j] at it
- for (k = each page in the megabyte)
- fill in tbl_c[j][k] with page entry from global_mode_table
- for (j = the rest of the 16MB)
- set tbl_c[j] to "global, cacheable"
-
- for (i = the rest of the 16MBs from here to $7F)
- set tbl_b0[i] to "global, cacheable"
-
- for (i = the rest of the 16MBs from $80 up to but not incl. $FF)
- set tbl_b0[i] to "global, not cacheable"
- */
-
- /* i counts 16MBs */
- for (i = 1, p = 0x01000000L, g = 2048;
- p < mint_top_tt;
- p += SIXTEEN_MEG, i++) {
- tbl_b0[i].page_type = page_ptr;
- tbl_b0[i].tbl_address = tptr;
- tbl_c = tptr;
- tptr += TBL_SIZE;
-
- /* j counts MBs */
- for (j = 0, q = p; j < 16 && q < mint_top_tt; q += ONE_MEG, j++) {
- tbl_c[j].page_type = page_ptr;
- tbl_c[j].tbl_address = tptr;
- /* k counts pages (8K) */
- for (r = q, k = 0; k < 128; k++, r += 0x2000, g++) {
- tptr->page_type = *proto_page_type[global_mode_table[g]];
- tptr->tbl_address = (long_desc *)r;
- tptr++;
- }
- }
- for ( ; j < 16; j++, q += ONE_MEG) {
- /* fill in the rest of this 16MB */
- tbl_c[j].page_type = g_page;
- tbl_c[j].tbl_address = (long_desc *)q;
- }
- }
-
- /* fill in the rest of $00-$0F as cacheable */
- for ( ; i < 16; i++, p += SIXTEEN_MEG) {
- tbl_b0[i].page_type = g_page;
- tbl_b0[i].tbl_address = (long_desc *)p;
- }
-
- /* done with TT RAM in table b0; do table bf */
-
- /*
- * Table BF: translates addresses starting with $F. First 15 are
- * uncontrolled, cacheable; last one translates $FF, which
- * which shadows $00 (the 16MB ST address space). The rest
- * are uncontrolled, not cacheable.
- *
- * The table address of the copy has a 1 in the low (unused) bit, which
- * is a signal to the table dumper not to dump this, as it's a copy
- * of tbl_b0[0].
- */
-
- for (i=0; i<0xf; i++) {
- tbl_bf[i].page_type = g_ci_page;
- tbl_bf[i].tbl_address = (long_desc *)((i << 24) | 0xf0000000L);
- }
- tbl_bf[0xf] = tbl_b0[0];
- *(ulong *)(&(tbl_bf[0xf].tbl_address)) |= 1;
-
- proc->ctxt[0].crp.limit = 0x7fff; /* disable limit function */
- proc->ctxt[0].crp.dt = 3; /* points to valid 8-byte entries */
- proc->ctxt[0].crp.tbl_address = tbl_a;
- proc->ctxt[1].crp = proc->ctxt[0].crp;
- proc->ctxt[0].tc = tc;
- proc->ctxt[1].tc = tc;
-
- /*
- * OK, memory tables are now there as if you're a non-owner of every
- * page. Now for each region you ARE an owner of, mark with owner
- * modes.
- */
-
- mr = proc->mem;
- for (i=0; i < proc->num_reg; i++, mr++) {
- if (*mr) {
- mark_pages(proc->page_table,(*mr)->loc,(*mr)->len,1,0,0,proc);
- }
- }
-
- if (!mmu_is_set_up) {
- set_mmu(proc->ctxt[0].crp,proc->ctxt[0].tc);
- mmu_is_set_up = 1;
- }
- }
-
- /*
- * This routine is called when procfs detects that a process wants to be an
- * OS SPECIAL. The AES, SCRENMGR, and DESKTOP do this, and so does FSMGDOS
- * and possibly some other stuff. It has to re-mark every page in that
- * process' page table based on its new special status. The "special
- * status" is "you get global access to all of memory" and "everybody
- * gets Super access to yours." It is the caller's responsibility
- * to set proc's memflags, usually to (F_OS_SPECIAL | F_PROT_S).
- */
-
- void
- mem_prot_special(proc)
- PROC *proc;
- {
- MEMREGION **mr;
- int i;
-
- if (no_mem_prot) return;
-
- TRACE(("mem_prot_special(pid %d)",proc->pid));
-
- /*
- * This marks ALL memory, allocated or not, as accessible. When memory
- * is freed even F_OS_SPECIAL processes lose access to it. So one or
- * the other of these is a bug, depending on how you want it to work.
- */
- mark_pages(proc->page_table,0,mint_top_st,1,0,0,proc);
- if (mint_top_tt) {
- mark_pages(proc->page_table,
- 0x01000000L,
- mint_top_tt - 0x01000000L,
- 1,0,0,
- proc);
- }
-
- /*
- * In addition, mark all the pages the process already owns as "super"
- * in all other processes. Thus the "special" process can access all
- * of memory, and any process can access the "special" process' memory
- * when in super mode.
- */
-
- mr = proc->mem;
-
- for (i=0; i < proc->num_reg; i++, mr++) {
- if (*mr) {
- mark_region(*mr,PROT_S);
- }
- }
- }
-
- /*----------------------------------------------------------------------------
- * DEBUGGING SECTION
- *--------------------------------------------------------------------------*/
-
- static void
- _dump_tree(tbl,level)
- long_desc tbl;
- int level;
- {
- int i, j;
- long_desc *p;
- static const char spaces[9] = " ";
-
- /* print the level and display the table descriptor */
- FORCE("\r%s s:%x wp:%x dt:%x a:%08lx",
- &spaces[8-(level*2)],
- tbl.page_type.s,
- tbl.page_type.wp,
- tbl.page_type.dt,
- tbl.tbl_address);
-
- if (tbl.page_type.dt == 3) {
- if (level == 0) {
- j = (1 << tc.tia);
- }
- else if (level == 1) {
- j = (1 << tc.tib);
- }
- else if (level == 2) {
- j = (1 << tc.tic);
- }
- else {
- j = (1 << tc.tid);
- }
-
- /* don't show table if it's the duplicate */
- if ((ulong)tbl.tbl_address & 1) return;
-
- ++level;
- p = tbl.tbl_address;
- for (i=0; i<j; i++, p++) {
- _dump_tree(*p,level);
- }
- }
- }
-
- static const char modesym[] = { 'p', 'g', 's', 'r', 'i' };
-
- void
- QUICKDUMP()
- {
- char outstr[33];
- ulong i, j, end;
-
- if (no_mem_prot) return;
-
- FORCE("STRAM global table:");
- outstr[32] = '\0';
- end = mint_top_st / QUANTUM;
- for (i = 0; i < end; i += 32) {
- for (j=0; j<32; j++) {
- outstr[j] = modesym[global_mode_table[j+i]];
- }
- FORCE("%08lx: %s",i*8192L,outstr);
- }
-
- if (mint_top_tt) {
- FORCE("TTRAM global table:");
- end = mint_top_tt / QUANTUM;
- for (i = 2048; i < end; i += 32) {
- for (j=0; j<32; j++) {
- outstr[j] = modesym[global_mode_table[j+i]];
- }
- FORCE("%08lx: %s",i*8192L,outstr);
- }
- }
- }
-
- const char *berr_msg[] = {
- /* "........." */
- "private ",
- "global ", /* turned into "hardware" for violation reports */
- "super ",
- "readable ",
- "free ",
- "hardware " /* used when the memory is not controlled by us */
- };
-
- void
- report_buserr()
- {
- const char *vmsg;
- short mode;
- ulong aa, pc;
- char alertbuf[5*32+16]; /* enough for an alert */
- char *aptr;
-
- if (no_mem_prot) return;
-
- aa = curproc->exception_addr;
- pc = curproc->exception_pc;
- if ((mint_top_tt && aa < mint_top_tt) || (aa < mint_top_st)) {
- mode = global_mode_table[(curproc->exception_addr >> 13)];
- if (mode == PROT_G) {
- /* page is global: obviously a hardware bus error */
- mode = 5;
- }
- }
- else {
- /* (addr is > mint_top_tt) set mode = 5 so we don't look for owners */
- mode = 5;
- }
- vmsg = berr_msg[mode];
-
- /* construct an AES alert box for this error:
- | PROCESS "buserrxx" KILLED: |
- | MEMORY VIOLATION. (PID 000) |
- | |
- | Type: ......... PC: pc...... |
- | Addr: ........ BP: ........ |
- */
-
- /* we play games to get around 128-char max for ksprintf */
- ksprintf(alertbuf,"[1][ PROCESS \"%s\" KILLED: |",curproc->name);
- aptr = alertbuf + strlen(alertbuf);
- ksprintf(aptr," MEMORY VIOLATION. (PID %03d) | |",curproc->pid);
- aptr = alertbuf + strlen(alertbuf);
- ksprintf(aptr," Type: %s PC: %08lx |",vmsg,pc);
- aptr = alertbuf + strlen(alertbuf);
- ksprintf(aptr," Addr: %08lx BP: %08lx ][ OK ]",aa,curproc->base);
- if (!_ALERT(alertbuf)) {
- /* this will call _alert again, but it will just fail again */
- ALERT("MEMORY VIOLATION: type=%s AA=%lx PC=%lx BP=%lx",
- vmsg,aa,pc,curproc->base);
- }
-
- if (curproc->pid == 0 || curproc->memflags & F_OS_SPECIAL) {
- /* the system is so thoroughly hosed that anything we try will
- * likely cause another bus error; so let's just hang up
- */
- FATAL("Operating system killed");
- }
- }
-
- /*
- * big_mem_dump is a biggie: for each page in the system, it
- * displays the PID of the (first) owner and the protection mode.
- * The output has three chars per page, and eight chars per line.
- * The first page of a region is marked with the mode, and the
- * rest with a space.
- *
- * Logic:
- for (mp = *core; mp; mp++) {
- for (each page of this region) {
- if (start of line) {
- output line starter;
- }
- if (start of region) {
- output mode of this page;
- determine owner;
- output owner;
- }
- else {
- output space;
- output owner;
- }
- }
- }
- */
-
- void
- BIG_MEM_DUMP(bigone,proc)
- int bigone;
- PROC *proc;
- {
- #ifdef DEBUG_INFO
- char linebuf[128];
- char *lp = linebuf;
- MEMREGION *mp, **mr, **map;
- PROC *p;
- ulong loc;
- short owner;
- short i;
- short first;
-
-
- if (no_mem_prot) return;
-
- for (map = core; map != 0; ((map == core) ? (map = alt) : (map = 0))) {
- FORCE("Annotated memory dump for %s",(map == core ? "core" : "alt"));
- first = 1;
- *linebuf = '\0';
- for (mp = *map; mp; mp = mp->next) {
- for (loc = mp->loc; loc < (mp->loc + mp->len); loc += EIGHT_K) {
- if (first || ((loc & 0x1ffff) == 0)) {
- if (*linebuf) FORCE(linebuf);
- ksprintf(linebuf,"\r%08lx: ",loc);
- lp = &linebuf[11];
- first = 0;
- }
- if (loc == mp->loc) {
- *lp++ = modesym[global_mode_table[loc / EIGHT_K]];
-
- for (p = proclist; p; p = p->gl_next) {
- if (p->mem) {
- mr = p->mem;
- for (i=0; i < p->num_reg; i++, mr++) {
- if (*mr == mp) {
- owner = p->pid;
- goto gotowner;
- }
- }
- }
- }
- owner = 000;
- gotowner:
- ksprintf(lp,"%03d",owner);
- lp += 3;
- }
- else {
- *lp++ = ' ';
- *lp++ = '-';
- *lp++ = '-';
- *lp++ = '-';
- *lp = '\0'; /* string is always null-terminated */
- }
- }
- }
- FORCE(linebuf);
- }
-
- if (bigone) {
- long_desc tbl;
-
- /* fill in tbl with the only parts used at the top level */
- tbl.page_type.dt = proc->ctxt[CURRENT].crp.dt;
- tbl.tbl_address = proc->ctxt[CURRENT].crp.tbl_address;
- _dump_tree(tbl,0);
- }
- #endif /* DEBUG_INFO */
- }
-
-
- /*
- * Can the process "p" access the "nbytes" long
- * block of memory starting at "start"?
- * If it would be a legal access, the current
- * process is given temporary access via
- * prot_temp.
- * Returns a cookie like the one prot_temp
- * returns; if the process shouldn't have
- * access to the memory, returns 1.
- *
- * BUG: should actually read p's page table to
- * determine access
- */
-
- int
- mem_access_for(p, start, nbytes)
- PROC *p;
- ulong start;
- long nbytes;
- {
- MEMREGION **mr;
- int i;
-
- if (no_mem_prot) return -1;
- if (start >= (ulong)p && start+nbytes <= (ulong)(p+1))
- return -1;
- if (p == rootproc)
- goto win_and_mark;
-
- mr = p->mem;
- if (mr) {
- for (i = 0; i < p->num_reg; i++, mr++) {
- if (*mr) {
- if (((*mr)->loc <= start) &&
- ((*mr)->loc + (*mr)->len >= start + nbytes))
- goto win_and_mark;
- }
- }
- }
-
- return 0; /* we don't own this memory */
-
- win_and_mark:
- return prot_temp(start, nbytes, -1);
- }
-