minix/servers/rs/memory.c
Cristiano Giuffrida c5b309ff07 Merge of Wu's GSOC 09 branch (src.20090525.r4372.wu)
Main changes:
- COW optimization for safecopy.
- safemap, a grant-based interface for sharing memory regions between processes.
- Integration with safemap and complete rework of DS, supporting new data types
  natively (labels, memory ranges, memory mapped ranges).
- For further information:
  http://wiki.minix3.org/en/SummerOfCode2009/MemoryGrants

Additional changes not included in the original Wu's branch:
- Fixed unhandled case in VM when using COW optimization for safecopy in case
  of a block that has already been shared as SMAP.
- Better interface and naming scheme for sys_saferevmap and ds_retrieve_map
  calls.
- Better input checking in syslib: check for page alignment when creating
  memory mapping grants.
- DS notifies subscribers when an entry is deleted.
- Documented the behavior of indirect grants in case of memory mapping.
- Test suite in /usr/src/test/safeperf|safecopy|safemap|ds/* reworked
  and extended.
- Minor fixes and general cleanup.
- TO-DO: Grant ids should be generated and managed the way endpoints are to make
sure grant slots are never misreused.
2010-01-14 15:24:16 +00:00

213 lines
6.6 KiB
C

/* This file contains memory management routines for RS.
*
* Changes:
* Nov 22, 2009: Created (Cristiano Giuffrida)
*/
#include "inc.h"
#include "../../kernel/const.h"
#include "../../kernel/type.h"
#include "../../kernel/proc.h"
EXTERN char *_brksize;
PRIVATE char * _rs_startbrksize = NULL;
PRIVATE char * _rs_endbrksize = NULL;
#define munmap _munmap
#define munmap_text _munmap_text
#include <sys/mman.h>
#undef munmap
#undef munmap_text
PUBLIC int unmap_ok = 0;
/*===========================================================================*
* check_mem_available *
*===========================================================================*/
PRIVATE int check_mem_available(char *new_brksize)
{
/* Check if enough memory is available to grow break size. */
register struct mem_map *mem_sp, *mem_dp;
vir_clicks sp_click, gap_base, sp_lower;
int s;
long base_of_stack, sp_delta; /* longs avoid certain problems */
vir_bytes sp;
struct proc proc;
vir_clicks data_clicks;
/* Get stack pointer and pointers to data/stack segment maps. */
if ((s=sys_getproc(&proc, SELF)) != OK) {
return(s);
}
sp = proc.p_reg.sp; /* stack pointer */
mem_dp = &proc.p_memmap[D]; /* pointer to data segment map */
mem_sp = &proc.p_memmap[S]; /* pointer to stack segment map */
/* Compute how many clicks the data segment is to become. */
data_clicks = (vir_clicks) (CLICK_CEIL(new_brksize) >> CLICK_SHIFT)
- mem_dp->mem_vir;
/* See if stack size has gone negative (i.e., sp too close to 0xFFFF...) */
base_of_stack = (long) mem_sp->mem_vir + (long) mem_sp->mem_len;
sp_click = sp >> CLICK_SHIFT; /* click containing sp */
if (sp_click >= base_of_stack)
{
return(ENOMEM); /* sp too high */
}
/* Compute size of gap between stack and data segments. */
sp_delta = (long) mem_sp->mem_vir - (long) sp_click;
sp_lower = (sp_delta > 0 ? sp_click : mem_sp->mem_vir);
/* Add a safety margin for future stack growth. Impossible to do right. */
#define SAFETY_BYTES (384 * sizeof(char *))
#define SAFETY_CLICKS ((vir_clicks) (CLICK_CEIL(SAFETY_BYTES) >> CLICK_SHIFT))
gap_base = mem_dp->mem_vir + data_clicks + SAFETY_CLICKS;
if (sp_lower < gap_base)
{
return(ENOMEM); /* data and stack collided */
}
return(OK);
}
/*===========================================================================*
* rs_startup_sbrk *
*===========================================================================*/
PUBLIC void* rs_startup_sbrk(size)
size_t size; /* the size to grow */
{
/* RS's own sbrk() used at startup. */
void* addr;
char* new_brksize;
/* Check input for non-positive size or size overflows. */
new_brksize = _brksize + size;
if (size <= 0 || new_brksize < _brksize) {
return( (char *) -1);
}
/* Check if enough memory is available. */
if(check_mem_available(new_brksize) != OK) {
return( (char *) -1);
}
/* Save initial break size. */
if(_rs_startbrksize == NULL) {
_rs_startbrksize = _brksize;
}
/* Set address and adjust break size. */
addr = _brksize;
_brksize = new_brksize;
_rs_endbrksize = _brksize;
return addr;
}
/*===========================================================================*
* rs_startup_sbrk_synch *
*===========================================================================*/
PUBLIC void* rs_startup_sbrk_synch(size)
size_t size; /* the size to grow */
{
/* Synchronize RS's own sbrk() with the rest of the system right after
* startup. We use the original sbrk() here.
*/
void* addr;
/* Restore original break size. */
_brksize = _rs_startbrksize;
/* Call original sbrk() and see if we observe the same effect. */
addr = (void*)sbrk(size);
if(_rs_startbrksize != addr) {
printf("Unable to synch rs_startup_sbrk() and sbrk(): addr 0x%x!=0x%x\n",
(int) _rs_startbrksize, (int) addr);
return( (char *) -1);
}
if(_rs_endbrksize != _brksize) {
printf("Unable to synch rs_startup_sbrk() and sbrk(): size 0x%x!=0x%x\n",
(int) _rs_endbrksize, (int) _brksize);
return( (char *) -1);
}
return addr;
}
/*===========================================================================*
* rs_startup_segcopy *
*===========================================================================*/
PUBLIC int rs_startup_segcopy(src_proc, src_seg, dst_seg, dst_vir, bytes)
endpoint_t src_proc; /* source process */
int src_seg; /* source memory segment */
int dst_seg; /* destination memory segment */
vir_bytes dst_vir; /* destination virtual address */
phys_bytes bytes; /* how many bytes */
{
/* Copy a process's T, D, S segment to RS's address space. Used at startup. */
struct proc src_p, dst_p;
phys_bytes src_phys, dst_phys;
int s;
/* Check input. */
if((src_seg != T && src_seg != D && src_seg != S) || bytes <= 0) {
return EINVAL;
}
/* We don't override normal behavior when not copying to our data segment. */
if(dst_seg != D) {
s = sys_vircopy(src_proc, src_seg, 0, SELF, dst_seg, dst_vir, bytes);
return(s);
}
/* Get kernel process slot for both source and destination. */
if ((s=sys_getproc(&src_p, src_proc)) != OK) {
return(s);
}
if ((s=sys_getproc(&dst_p, SELF)) != OK) {
return(s);
}
/* Map source address to physical address. */
src_phys = (phys_bytes) src_p.p_memmap[src_seg].mem_phys << CLICK_SHIFT;
/* Check if destination address is out of bounds or overflows. */
if(dst_vir+bytes > (vir_bytes)_rs_endbrksize
|| dst_vir < (vir_bytes)_rs_startbrksize || dst_vir+bytes < dst_vir) {
return EFAULT;
}
/* Map destination address to physical address. */
dst_phys = (phys_bytes) dst_p.p_memmap[D].mem_phys << CLICK_SHIFT;
dst_phys += dst_vir - (dst_p.p_memmap[D].mem_vir << CLICK_SHIFT);
/* Make a physical copy for the requested data. */
s = sys_abscopy(src_phys, dst_phys, bytes);
return(s);
}
/*===========================================================================*
* munmap *
*===========================================================================*/
PUBLIC int munmap(void *addrstart, vir_bytes len)
{
if(!unmap_ok)
return ENOSYS;
return _munmap(addrstart, len);
}
/*===========================================================================*
* munmap_text *
*===========================================================================*/
PUBLIC int munmap_text(void *addrstart, vir_bytes len)
{
if(!unmap_ok)
return ENOSYS;
return _munmap_text(addrstart, len);
}