/* * Copyright 1991,1992,1993,1994 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; UNUSED(proc); 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 = (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]; #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; iexception_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); } #else UNUSED(proc); UNUSED(bigone); #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); }