mirror of https://github.com/dzavalishin/oskit/
381 lines
8.4 KiB
C
Executable File
381 lines
8.4 KiB
C
Executable File
/*
|
|
* Copyright (c) 1997-1998 University of Utah and the Flux Group.
|
|
* All rights reserved.
|
|
*
|
|
* This file is part of the Flux OSKit. The OSKit is free software, also known
|
|
* as "open source;" you can redistribute it and/or modify it under the terms
|
|
* of the GNU General Public License (GPL), version 2, as published by the Free
|
|
* Software Foundation (FSF). To explore alternate licensing terms, contact
|
|
* the University of Utah at csl-dist@cs.utah.edu or +1-801-585-3271.
|
|
*
|
|
* The OSKit is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
* FOR A PARTICULAR PURPOSE. See the GPL for more details. You should have
|
|
* received a copy of the GPL along with the OSKit; see the file COPYING. If
|
|
* not, write to the FSF, 59 Temple Place #330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
/*
|
|
* Simple UNIX-style address map routines
|
|
*/
|
|
#include <sys/types.h>
|
|
#include <sys/mman.h>
|
|
#include <errno.h>
|
|
#include "amap.h"
|
|
|
|
static struct amap *themap;
|
|
extern int errno;
|
|
|
|
#ifndef PAGE_SIZE
|
|
#define PAGE_SIZE 4096
|
|
#endif
|
|
|
|
/*
|
|
* XXX for illustration purposes, we maintain a fixed list of map entries
|
|
* so we can use our own alloc/free/split/join routines.
|
|
*/
|
|
#define NUMME 100
|
|
static struct amap_entry *mapfree;
|
|
|
|
static struct amap_entry *
|
|
alloc_map_entry(struct amap *map)
|
|
{
|
|
struct amap_entry *me;
|
|
|
|
#ifdef DEBUG
|
|
printf("\t0x%x: map_alloc", map);
|
|
#endif
|
|
|
|
if (mapfree == 0) {
|
|
#ifdef DEBUG
|
|
printf("failed\n");
|
|
#endif
|
|
return 0;
|
|
}
|
|
me = mapfree;
|
|
mapfree = mapfree->next;
|
|
me->map = map;
|
|
me->next = 0;
|
|
|
|
#ifdef DEBUG
|
|
printf("->0x%x\n", me);
|
|
#endif
|
|
return me;
|
|
}
|
|
|
|
static void
|
|
free_map_entry(struct amap_entry *me)
|
|
{
|
|
#ifdef DEBUG
|
|
printf("\t0x%x: map_free 0x%x\n", me->map, me);
|
|
#endif
|
|
me->map = 0;
|
|
me->next = mapfree;
|
|
mapfree = me;
|
|
}
|
|
|
|
static amm_entry_t *
|
|
alloc_amm_entry(amm_t *amm, oskit_addr_t addr, oskit_size_t size, int flags)
|
|
{
|
|
amm_entry_t *entry;
|
|
|
|
#ifdef DEBUG
|
|
printf("0x%x: alloc: addr=0x%x, size=0x%x, flags=0x%x: ",
|
|
amm, addr, size, flags);
|
|
#endif
|
|
/*
|
|
* Don't ue full-sized entries for free or reserved memory
|
|
*/
|
|
if (flags == AMAP_FREE || flags == AMAP_RESERVED) {
|
|
entry = (amm_entry_t *)smalloc(sizeof(amm_entry_t));
|
|
#ifdef DEBUG
|
|
printf("smalloc->0x%x\n", entry);
|
|
#endif
|
|
} else
|
|
entry = (amm_entry_t *)alloc_map_entry((struct amap *)amm);
|
|
|
|
return entry;
|
|
}
|
|
|
|
static void
|
|
free_amm_entry(amm_t *amm, amm_entry_t *entry)
|
|
{
|
|
if (amm_entry_flags(entry) == AMAP_FREE ||
|
|
amm_entry_flags(entry) == AMAP_RESERVED) {
|
|
#ifdef DEBUG
|
|
printf("0x%x: free: 0x%x\n", amm, entry);
|
|
#endif
|
|
sfree(entry, sizeof *entry);
|
|
} else
|
|
free_map_entry((struct amap_entry *)entry);
|
|
}
|
|
|
|
static int
|
|
split_amm_entry(amm_t *amm, amm_entry_t *entry, oskit_addr_t addr,
|
|
amm_entry_t **head, amm_entry_t **tail)
|
|
{
|
|
struct amap_entry *me = (struct amap_entry *)entry;
|
|
amm_entry_t *nentry;
|
|
|
|
#ifdef DEBUG
|
|
printf("0x%x: split: entry=0x%x, addr=0x%x, flags=0x%x",
|
|
amm, entry, addr, amm_entry_flags(entry));
|
|
#endif
|
|
|
|
nentry = alloc_amm_entry(amm, addr, amm_entry_end(entry) - addr,
|
|
amm_entry_flags(entry));
|
|
if (nentry == 0) {
|
|
#ifdef DEBUG
|
|
printf("failed\n");
|
|
#endif
|
|
return ENOMEM;
|
|
}
|
|
|
|
*head = entry;
|
|
*tail = nentry;
|
|
|
|
#ifdef DEBUG
|
|
printf("->head=0x%x, tail=0x%x\n", *head, *tail);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
join_amm_entry(amm_t *amm, amm_entry_t *head, amm_entry_t *tail,
|
|
amm_entry_t **new)
|
|
{
|
|
struct amap_entry *hme = (struct amap_entry *)head;
|
|
struct amap_entry *tme = (struct amap_entry *)tail;
|
|
|
|
assert(hme->map == tme->map);
|
|
|
|
#ifdef DEBUG
|
|
printf("0x%x: join: head=0x%x, tail=0x%x", amm, head, tail);
|
|
#endif
|
|
|
|
*new = alloc_amm_entry(amm, amm_entry_start(head),
|
|
amm_entry_end(tail) - amm_entry_start(head),
|
|
amm_entry_flags(head));
|
|
if (*new == 0) {
|
|
#ifdef DEBUG
|
|
printf("failed\n");
|
|
#endif
|
|
return ENOMEM;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
printf("->new=0x%x\n", *new);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
minit(struct amap *map)
|
|
{
|
|
struct amap_entry *me;
|
|
int i;
|
|
|
|
me = (struct amap_entry *)smalloc(sizeof(struct amap) * NUMME);
|
|
mapfree = me;
|
|
for (i = 0; i < NUMME-1; i++) {
|
|
me->next = me + 1;
|
|
me++;
|
|
}
|
|
me->next = 0;
|
|
amm_init_gen(&map->amm, AMAP_FREE, 0,
|
|
alloc_amm_entry, free_amm_entry,
|
|
split_amm_entry, join_amm_entry);
|
|
|
|
/*
|
|
* First page and upper half of the AS is unusable
|
|
*/
|
|
i = amm_modify(&map->amm, AMM_MINADDR, PAGE_SIZE - AMM_MINADDR,
|
|
AMAP_RESERVED, 0);
|
|
if (i)
|
|
panic("minit: cannot set lo range");
|
|
i = amm_modify(&map->amm, 0x80000000, AMM_MAXADDR - 0x80000000,
|
|
AMAP_RESERVED, 0);
|
|
if (i)
|
|
panic("minit: cannot set hi range");
|
|
|
|
themap = map;
|
|
}
|
|
|
|
void
|
|
mdestroy(struct amap *map)
|
|
{
|
|
amm_destroy(&map->amm);
|
|
}
|
|
|
|
char *
|
|
mmap(char *addr, size_t len, int prot, int flags, int fd, off_t offset)
|
|
{
|
|
int attr = AMM_FORWARD|AMM_FIRSTFIT;
|
|
int aflags;
|
|
oskit_addr_t aaddr;
|
|
struct amap *map = themap; /* XXX */
|
|
amm_entry_t *entry;
|
|
int rc;
|
|
|
|
if (fd != -1 || offset != 0) {
|
|
errno = EINVAL;
|
|
return (char *)-1;
|
|
}
|
|
/* XXX check page alignment, etc. */
|
|
prot &= AMAP_PROTMASK;
|
|
|
|
if (flags & MAP_FIXED)
|
|
attr |= AMM_EXACT_ADDR;
|
|
|
|
aaddr = (oskit_addr_t)addr;
|
|
entry = amm_find_gen(&map->amm, &aaddr, (oskit_size_t)len,
|
|
AMAP_FREE, -1, 0, 0, attr);
|
|
if (entry == 0) {
|
|
errno = ENOMEM;
|
|
return (char *)-1;
|
|
}
|
|
|
|
errno = amm_modify(&map->amm, aaddr, (oskit_size_t)len,
|
|
AMAP_ALLOCATED|prot,
|
|
(amm_entry_t *)alloc_map_entry(map));
|
|
if (errno)
|
|
return (char *)-1;
|
|
|
|
return (char *)aaddr;
|
|
}
|
|
|
|
int
|
|
munmap(char *addr, size_t len)
|
|
{
|
|
struct amap *map = themap; /* XXX */
|
|
|
|
return amap_prot_range(map, (oskit_addr_t)addr, (oskit_size_t)len,
|
|
AMAP_FREE, -1);
|
|
}
|
|
|
|
int
|
|
mprotect(char *addr, size_t len, int prot)
|
|
{
|
|
struct amap *map = themap; /* XXX */
|
|
|
|
prot &= AMAP_PROTMASK;
|
|
/* XXX should fail if some portion of the AS is not allocated */
|
|
return amap_prot_range(map, (oskit_addr_t)addr, (oskit_size_t)len,
|
|
prot, AMAP_PROTMASK);
|
|
}
|
|
|
|
int
|
|
mlock(char *addr, size_t len)
|
|
{
|
|
struct amap *map = themap; /* XXX */
|
|
|
|
/* XXX should fail if some portion of the AS is not allocated */
|
|
return amap_prot_range(map, (oskit_addr_t)addr, (oskit_size_t)len,
|
|
AMAP_WIRED, AMAP_WIRED);
|
|
}
|
|
|
|
int
|
|
munlock(char *addr, size_t len)
|
|
{
|
|
struct amap *map = themap; /* XXX */
|
|
|
|
/* XXX should fail if some portion of the AS is not allocated/locked */
|
|
return amap_prot_range(map, (oskit_addr_t)addr, (oskit_size_t)len,
|
|
0, AMAP_WIRED);
|
|
}
|
|
|
|
/*
|
|
* Modify the attributes of all allocated entries in the given range
|
|
*/
|
|
int
|
|
amap_prot_range(struct amap *map, oskit_addr_t addr, oskit_size_t size,
|
|
int prot, int protmask)
|
|
{
|
|
oskit_addr_t saddr, eaddr;
|
|
amm_entry_t *entry;
|
|
int rc;
|
|
|
|
/*
|
|
* Loop over individual entries, skipping free and reserved ones
|
|
*/
|
|
saddr = addr;
|
|
eaddr = saddr + size;
|
|
while (saddr < eaddr) {
|
|
entry = amm_find_gen(&map->amm, &saddr, PAGE_SIZE,
|
|
AMAP_ALLOCATED, AMAP_ALLOCATED, 0, 0, 0);
|
|
/*
|
|
* No more allocated memory in the range, all done.
|
|
*/
|
|
if (entry == 0)
|
|
break;
|
|
/*
|
|
* Protection is already correct, nothing to do.
|
|
*/
|
|
if ((entry->flags & protmask) == prot) {
|
|
saddr = amm_entry_end(entry);
|
|
continue;
|
|
}
|
|
/*
|
|
* Set the new protection
|
|
*/
|
|
prot = (entry->flags & ~protmask) | prot;
|
|
|
|
/*
|
|
* Entry is completely contained in the range,
|
|
* just modify the existing entry.
|
|
*/
|
|
assert(saddr >= amm_entry_start(entry));
|
|
if (amm_entry_start(entry) == saddr &&
|
|
amm_entry_end(entry) <= eaddr) {
|
|
rc = amm_modify(&map->amm,
|
|
saddr, amm_entry_size(entry), prot,
|
|
prot == AMAP_FREE ? 0 : entry);
|
|
if (rc)
|
|
return EINVAL;
|
|
saddr = amm_entry_end(entry);
|
|
continue;
|
|
}
|
|
/*
|
|
* Otherwise allocate an entry for the new range
|
|
*/
|
|
rc = amm_modify(&map->amm, saddr, eaddr - saddr, prot,
|
|
prot == AMAP_FREE ? 0 :
|
|
(amm_entry_t *)alloc_map_entry(map));
|
|
if (rc)
|
|
return EINVAL;
|
|
saddr = eaddr;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
mdumpfunc(amm_t *amm, amm_entry_t *entry, void *arg)
|
|
{
|
|
struct amap_entry *me;
|
|
oskit_addr_t saddr, eaddr;
|
|
int flags;
|
|
|
|
flags = amm_entry_flags(entry);
|
|
if (flags == AMAP_RESERVED || flags == AMAP_FREE)
|
|
return 0;
|
|
|
|
saddr = amm_entry_start(entry);
|
|
eaddr = amm_entry_end(entry);
|
|
me = (struct amap_entry *)entry;
|
|
|
|
printf("\t0x%x: [0x%x-0x%x]: %c%c%c,%c, map=0x%x\n",
|
|
me, saddr, eaddr,
|
|
(flags & 4) ? 'r' : '-',
|
|
(flags & 2) ? 'w' : '-',
|
|
(flags & 1) ? 'x' : '-',
|
|
(flags & AMAP_WIRED) ? 'W' : '-',
|
|
me->map);
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
mdump(struct amap *map)
|
|
{
|
|
printf("MAP 0x%x:\n", map);
|
|
(void)amm_iterate(&map->amm, mdumpfunc, 0);
|
|
}
|