File:  [Atari MiNT] / MiNT / src / memprot.c
Revision 1.1.1.4 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 17:58:58 2018 UTC (8 years, 1 month ago) by root
Branches: mint, MAIN
CVS tags: mint112, HEAD
MiNT 1.12

/*

 * 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; 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);

    }

#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);

}


unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.