* Added <sys/mman.h> header. It declares only mmap() and munmap() yet

and defines the macros needed by them.
* Renamed syscall sys_vm_map_file() to _kern_map_file() and changed the
  path to an FD parameter. Changed vm_map_file() accordingly and
  adjusted the kernel ELF loader and the runtime loader.
* Added syscall _kern_unmap_memory().
* Added bool unmapAddressRange parameter to vm_create_anonymous_area()
  and map_backing_store(). If true and the address specification is
  B_EXACT_ADDRESS, all areas in the specified address range will be
  deleted (unless an area is covered only partially).
* Introduced B_SHARED_AREA flag, which is set on areas that have been
  created by {vm,_user}_map_file() with REGION_NO_PRIVATE_MAP. When
  fork()ing those areas won't be copied CoW, but rather be cloned. This
  is needed for mmap() MAP_SHARED.
* {vm,_user}_map_file() also accept an FD argument < 0, in which case an
  anonymous area is created.
* Implemented mmap() and munmap(). Currently there's the restriction
  that we can't partially unmap areas. Otherwise the functions should be
  rather compliant. We also support the non-POSIX extension
  MAP_ANONYMOUS.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@24964 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2008-04-13 22:52:11 +00:00
parent 34cbfd39d3
commit 3cf7ecd1e4
8 changed files with 289 additions and 61 deletions
+42
View File
@@ -0,0 +1,42 @@
/*
* Copyright 2008, Haiku Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _SYS_MMAN_H
#define _SYS_MMAN_H
#include <sys/types.h>
// memory protection for mmap() and others
#define PROT_READ 0x01
#define PROT_WRITE 0x02
#define PROT_EXEC 0x04
#define PROT_NONE 0x00
// mmap() flags
#define MAP_SHARED 0x01 /* changes are seen by others */
#define MAP_PRIVATE 0x02 /* changes are only seen by caller */
#define MAP_FIXED 0x04 /* require mapping to specified addr */
#define MAP_ANONYMOUS 0x08 /* no underlying object */
#define MAP_ANON MAP_ANONYMOUS
// mmap() error return code
#define MAP_FAILED ((void*)-1)
#ifdef __cplusplus
extern "C" {
#endif
extern void* mmap(void* address, size_t length, int protection, int flags,
int fd, off_t offset);
extern int munmap(void* address, size_t length);
#ifdef __cplusplus
}
#endif
#endif // _SYS_MMAN_H
+4 -2
View File
@@ -296,8 +296,10 @@ extern area_id _kern_clone_area(const char *name, void **_address, uint32 addre
extern status_t _kern_reserve_heap_address_range(addr_t* _address, uint32 addressSpec,
addr_t size);
area_id sys_vm_map_file(const char *name, void **address, int addr_type,
addr_t size, int lock, int mapping, const char *path, off_t offset);
extern area_id _kern_map_file(const char *name, void **address,
int addressSpec, addr_t size, int protection,
int mapping, int fd, off_t offset);
extern status_t _kern_unmap_memory(void *address, addr_t size);
/* kernel port functions */
extern port_id _kern_create_port(int32 queue_length, const char *name);
+11 -6
View File
@@ -45,14 +45,17 @@ struct vnode;
| B_KERNEL_STACK_AREA)
#define B_OVERCOMMITTING_AREA 0x1000
// ToDo: this is not really a protection flag, but since the "protection"
#define B_SHARED_AREA 0x2000
// TODO: These aren't really a protection flags, but since the "protection"
// field is the only flag field, we currently use it for this.
// A cleaner approach would be appreciated - maybe just an official generic
// flags region in the protection field.
#define B_USER_AREA_FLAGS (B_USER_PROTECTION)
#define B_KERNEL_AREA_FLAGS \
(B_KERNEL_PROTECTION | B_USER_CLONEABLE_AREA | B_OVERCOMMITTING_AREA)
(B_KERNEL_PROTECTION | B_USER_CLONEABLE_AREA | B_OVERCOMMITTING_AREA \
| B_SHARED_AREA)
// flags for vm_get_physical_page()
enum {
@@ -106,12 +109,13 @@ status_t vm_unreserve_address_range(team_id team, void *address, addr_t size);
status_t vm_reserve_address_range(team_id team, void **_address,
uint32 addressSpec, addr_t size, uint32 flags);
area_id vm_create_anonymous_area(team_id team, const char *name, void **address,
uint32 addressSpec, addr_t size, uint32 wiring, uint32 protection);
uint32 addressSpec, addr_t size, uint32 wiring, uint32 protection,
bool unmapAddressRange);
area_id vm_map_physical_memory(team_id team, const char *name, void **address,
uint32 addressSpec, addr_t size, uint32 protection, addr_t phys_addr);
area_id vm_map_file(team_id aid, const char *name, void **address,
uint32 addressSpec, addr_t size, uint32 protection, uint32 mapping,
const char *path, off_t offset);
int fd, off_t offset);
struct vm_cache *vm_area_get_locked_cache(struct vm_area *area);
void vm_area_put_locked_cache(struct vm_cache *cache);
area_id vm_create_null_area(team_id team, const char *name, void **address,
@@ -144,8 +148,9 @@ off_t vm_available_memory(void);
area_id _user_create_area(const char *name, void **address, uint32 addressSpec,
size_t size, uint32 lock, uint32 protection);
status_t _user_delete_area(area_id area);
area_id _user_vm_map_file(const char *uname, void **uaddress, int addr_type,
addr_t size, int lock, int mapping, const char *upath, off_t offset);
area_id _user_map_file(const char *uname, void **uaddress, int addressSpec,
addr_t size, int protection, int mapping, int fd, off_t offset);
status_t _user_unmap_memory(void *address, addr_t size);
area_id _user_area_for(void *address);
area_id _user_find_area(const char *name);
status_t _user_get_area_info(area_id area, area_info *info);
+2 -2
View File
@@ -1406,7 +1406,7 @@ elf_load_user_image(const char *path, struct team *team, int flags, addr_t *entr
B_EXACT_ADDRESS,
fileUpperBound,
B_READ_AREA | B_WRITE_AREA, REGION_PRIVATE_MAP,
path, ROUNDOWN(programHeaders[i].p_offset, B_PAGE_SIZE));
fd, ROUNDOWN(programHeaders[i].p_offset, B_PAGE_SIZE));
if (id < B_OK) {
dprintf("error mapping file data: %s!\n", strerror(id));
status = B_NOT_AN_EXECUTABLE;
@@ -1453,7 +1453,7 @@ elf_load_user_image(const char *path, struct team *team, int flags, addr_t *entr
B_EXACT_ADDRESS,
ROUNDUP(programHeaders[i].p_memsz + (programHeaders[i].p_vaddr % B_PAGE_SIZE), B_PAGE_SIZE),
B_READ_AREA | B_EXECUTE_AREA, REGION_PRIVATE_MAP,
path, ROUNDOWN(programHeaders[i].p_offset, B_PAGE_SIZE));
fd, ROUNDOWN(programHeaders[i].p_offset, B_PAGE_SIZE));
if (id < B_OK) {
dprintf("error mapping file text: %s!\n", strerror(id));
status = B_NOT_AN_EXECUTABLE;
+151 -48
View File
@@ -19,6 +19,7 @@
#include <AutoDeleter.h>
#include <fs/fd.h>
#include <vm_address_space.h>
#include <vm_priv.h>
#include <vm_page.h>
@@ -1185,6 +1186,43 @@ insert_area(vm_address_space *addressSpace, void **_address,
}
/*! Deletes all areas in the given address range.
The address space must be write-locked.
NOTE: At the moment deleting only complete areas is supported.
*/
static status_t
unmap_address_range(vm_address_space *addressSpace, addr_t address, addr_t size)
{
// TODO: Support deleting partial areas!
addr_t lastAddress = address + (size - 1);
// check whether any areas are only partially covered
vm_area* area = vm_area_lookup(addressSpace, address);
if (area != NULL && area->base < address)
return B_UNSUPPORTED;
area = vm_area_lookup(addressSpace, lastAddress);
if (area != NULL && lastAddress - area->base < area->size - 1)
return B_UNSUPPORTED;
// all areas (if any) are fully covered; let's delete them
area = addressSpace->areas;
while (area != NULL) {
vm_area* nextArea = area->address_space_next;
if (area->id != RESERVED_AREA_ID) {
if (area->base >= address && area->base < lastAddress)
delete_area(addressSpace, area);
}
area = nextArea;
}
return B_OK;
}
/*! You need to hold the lock of the cache and the write lock of the address
space when calling this function.
Note, that in case of error your cache will be temporarily unlocked.
@@ -1193,7 +1231,7 @@ static status_t
map_backing_store(vm_address_space *addressSpace, vm_cache *cache,
void **_virtualAddress, off_t offset, addr_t size, uint32 addressSpec,
int wiring, int protection, int mapping, vm_area **_area,
const char *areaName)
const char *areaName, bool unmapAddressRange)
{
TRACE(("map_backing_store: aspace %p, cache %p, *vaddr %p, offset 0x%Lx, size %lu, addressSpec %ld, wiring %d, protection %d, _area %p, area_name '%s'\n",
addressSpace, cache, *_virtualAddress, offset, size, addressSpec,
@@ -1254,6 +1292,13 @@ map_backing_store(vm_address_space *addressSpace, vm_cache *cache,
goto err2;
}
if (addressSpec == B_EXACT_ADDRESS && unmapAddressRange) {
status = unmap_address_range(addressSpace, (addr_t)*_virtualAddress,
size);
if (status != B_OK)
goto err2;
}
status = insert_area(addressSpace, _virtualAddress, addressSpec, size, area);
if (status < B_OK)
goto err2;
@@ -1379,7 +1424,8 @@ vm_reserve_address_range(team_id team, void **_address, uint32 addressSpec,
area_id
vm_create_anonymous_area(team_id team, const char *name, void **address,
uint32 addressSpec, addr_t size, uint32 wiring, uint32 protection)
uint32 addressSpec, addr_t size, uint32 wiring, uint32 protection,
bool unmapAddressRange)
{
vm_area *area;
vm_cache *cache;
@@ -1482,7 +1528,8 @@ vm_create_anonymous_area(team_id team, const char *name, void **address,
mutex_lock(&cache->lock);
status = map_backing_store(addressSpace, cache, address, 0, size,
addressSpec, wiring, protection, REGION_NO_PRIVATE_MAP, &area, name);
addressSpec, wiring, protection, REGION_NO_PRIVATE_MAP, &area, name,
unmapAddressRange);
mutex_unlock(&cache->lock);
@@ -1696,7 +1743,7 @@ vm_map_physical_memory(team_id team, const char *name, void **_address,
status_t status = map_backing_store(locker.AddressSpace(), cache, _address,
0, size, addressSpec & ~B_MTR_MASK, B_FULL_LOCK, protection,
REGION_NO_PRIVATE_MAP, &area, name);
REGION_NO_PRIVATE_MAP, &area, name, false);
mutex_unlock(&cache->lock);
@@ -1777,7 +1824,8 @@ vm_create_null_area(team_id team, const char *name, void **address,
mutex_lock(&cache->lock);
status = map_backing_store(locker.AddressSpace(), cache, address, 0, size,
addressSpec, 0, B_KERNEL_READ_AREA, REGION_NO_PRIVATE_MAP, &area, name);
addressSpec, 0, B_KERNEL_READ_AREA, REGION_NO_PRIVATE_MAP, &area, name,
false);
mutex_unlock(&cache->lock);
@@ -1821,18 +1869,16 @@ err1:
}
/*! Will map the file at the path specified by \a name to an area in memory.
The file will be mirrored beginning at the specified \a offset. The \a offset
and \a size arguments have to be page aligned.
/*! Will map the file specified by \a fd to an area in memory.
The file will be mirrored beginning at the specified \a offset. The
\a offset and \a size arguments have to be page aligned.
*/
static area_id
_vm_map_file(team_id team, const char *name, void **_address, uint32 addressSpec,
size_t size, uint32 protection, uint32 mapping, const char *path,
off_t offset, bool kernel)
size_t size, uint32 protection, uint32 mapping, int fd, off_t offset,
bool kernel)
{
// ToDo: maybe attach to an FD, not a path (or both, like VFS calls)
// ToDo: check file access permissions (would be already done if the above were true)
// ToDo: for binary files, we want to make sure that they get the
// TODO: for binary files, we want to make sure that they get the
// copy of a file at a given time, ie. later changes should not
// make it into the mapped copy -- this will need quite some changes
// to be done in a nice way
@@ -1842,38 +1888,56 @@ _vm_map_file(team_id team, const char *name, void **_address, uint32 addressSpec
offset = ROUNDOWN(offset, B_PAGE_SIZE);
size = PAGE_ALIGN(size);
if (mapping == REGION_NO_PRIVATE_MAP)
protection |= B_SHARED_AREA;
if (fd < 0) {
return vm_create_anonymous_area(team, name, _address, addressSpec, size,
B_NO_LOCK, protection, addressSpec == B_EXACT_ADDRESS);
}
// get the open flags of the FD
file_descriptor *descriptor = get_fd(get_current_io_context(kernel), fd);
if (descriptor == NULL)
return EBADF;
int32 openMode = descriptor->open_mode;
put_fd(descriptor);
// The FD must open for reading at any rate. For shared mapping with write
// access, additionally the FD must be open for writing.
if ((openMode & O_ACCMODE) == O_WRONLY
|| mapping == REGION_NO_PRIVATE_MAP
&& (protection & (B_WRITE_AREA | B_KERNEL_WRITE_AREA)) != 0
&& (openMode & O_ACCMODE) == O_RDONLY) {
return EACCES;
}
// get the vnode for the object, this also grabs a ref to it
struct vnode *vnode;
status_t status = vfs_get_vnode_from_path(path, kernel, &vnode);
struct vnode *vnode = NULL;
status_t status = vfs_get_vnode_from_fd(fd, kernel, &vnode);
if (status < B_OK)
return status;
CObjectDeleter<struct vnode> vnodePutter(vnode, vfs_put_vnode);
AddressSpaceWriteLocker locker(team);
if (!locker.IsLocked()) {
vfs_put_vnode(vnode);
if (!locker.IsLocked())
return B_BAD_TEAM_ID;
}
// ToDo: this only works for file systems that use the file cache
// TODO: this only works for file systems that use the file cache
vm_cache *cache;
status = vfs_get_vnode_cache(vnode, &cache, false);
if (status < B_OK) {
vfs_put_vnode(vnode);
if (status < B_OK)
return status;
}
mutex_lock(&cache->lock);
vm_area *area;
status = map_backing_store(locker.AddressSpace(), cache, _address,
offset, size, addressSpec, 0, protection, mapping, &area, name);
offset, size, addressSpec, 0, protection, mapping, &area, name,
addressSpec == B_EXACT_ADDRESS);
mutex_unlock(&cache->lock);
vfs_put_vnode(vnode);
// we don't need this vnode anymore - if the above call was
// successful, the store already has a ref to it
if (status < B_OK || mapping == REGION_PRIVATE_MAP) {
// map_backing_store() cannot know we no longer need the ref
vm_cache_release_ref(cache);
@@ -1888,14 +1952,13 @@ _vm_map_file(team_id team, const char *name, void **_address, uint32 addressSpec
area_id
vm_map_file(team_id aid, const char *name, void **address, uint32 addressSpec,
addr_t size, uint32 protection, uint32 mapping, const char *path,
off_t offset)
addr_t size, uint32 protection, uint32 mapping, int fd, off_t offset)
{
if (!arch_vm_supports_protection(protection))
return B_NOT_SUPPORTED;
return _vm_map_file(aid, name, address, addressSpec, size, protection,
mapping, path, offset, true);
mapping, fd, offset, true);
}
@@ -1973,7 +2036,7 @@ vm_clone_area(team_id team, const char *name, void **address,
else {
status = map_backing_store(targetAddressSpace, cache, address,
sourceArea->cache_offset, sourceArea->size, addressSpec,
sourceArea->wiring, protection, mapping, &newArea, name);
sourceArea->wiring, protection, mapping, &newArea, name, false);
}
if (status == B_OK && mapping != REGION_PRIVATE_MAP) {
// If the mapping is REGION_PRIVATE_MAP, map_backing_store() needed
@@ -2227,22 +2290,33 @@ vm_copy_area(team_id team, const char *name, void **_address,
*_address = (void *)source->base;
}
// First, create a cache on top of the source area
bool sharedArea = (source->protection & B_SHARED_AREA) != 0;
// First, create a cache on top of the source area, respectively use the
// existing one, if this is a shared area.
vm_area *target;
status = map_backing_store(targetAddressSpace, cache, _address,
source->cache_offset, source->size, addressSpec, source->wiring,
protection, REGION_PRIVATE_MAP, &target, name);
protection, sharedArea ? REGION_NO_PRIVATE_MAP : REGION_PRIVATE_MAP,
&target, name, false);
if (status < B_OK)
return status;
if (sharedArea) {
// The new area uses the old area's cache, but map_backing_store()
// hasn't acquired a ref. So we have to do that now.
vm_cache_acquire_ref(cache);
}
// If the source area is writable, we need to move it one layer up as well
if ((source->protection & (B_KERNEL_WRITE_AREA | B_WRITE_AREA)) != 0) {
// ToDo: do something more useful if this fails!
if (vm_copy_on_write_area(cache) < B_OK)
panic("vm_copy_on_write_area() failed!\n");
if (!sharedArea) {
if ((source->protection & (B_KERNEL_WRITE_AREA | B_WRITE_AREA)) != 0) {
// TODO: do something more useful if this fails!
if (vm_copy_on_write_area(cache) < B_OK)
panic("vm_copy_on_write_area() failed!\n");
}
}
// we return the ID of the newly created area
@@ -5075,7 +5149,7 @@ create_area_etc(struct team *team, const char *name, void **address, uint32 addr
fix_protection(&protection);
return vm_create_anonymous_area(team->id, (char *)name, address,
addressSpec, size, lock, protection);
addressSpec, size, lock, protection, false);
}
@@ -5086,7 +5160,7 @@ create_area(const char *name, void **_address, uint32 addressSpec, size_t size,
fix_protection(&protection);
return vm_create_anonymous_area(vm_kernel_address_space_id(), (char *)name, _address,
addressSpec, size, lock, protection);
addressSpec, size, lock, protection, false);
}
@@ -5319,7 +5393,7 @@ _user_create_area(const char *userName, void **userAddress, uint32 addressSpec,
fix_protection(&protection);
area_id area = vm_create_anonymous_area(vm_current_user_address_space_id(),
(char *)name, &address, addressSpec, size, lock, protection);
(char *)name, &address, addressSpec, size, lock, protection, false);
if (area >= B_OK && user_memcpy(userAddress, &address, sizeof(address)) < B_OK) {
delete_area(area);
@@ -5344,26 +5418,33 @@ _user_delete_area(area_id area)
// ToDo: create a BeOS style call for this!
area_id
_user_vm_map_file(const char *userName, void **userAddress, int addressSpec,
addr_t size, int protection, int mapping, const char *userPath, off_t offset)
_user_map_file(const char *userName, void **userAddress, int addressSpec,
addr_t size, int protection, int mapping, int fd, off_t offset)
{
char name[B_OS_NAME_LENGTH];
char path[B_PATH_NAME_LENGTH];
void *address;
area_id area;
if (!IS_USER_ADDRESS(userName) || !IS_USER_ADDRESS(userAddress)
|| !IS_USER_ADDRESS(userPath)
|| user_strlcpy(name, userName, B_OS_NAME_LENGTH) < B_OK
|| user_strlcpy(path, userPath, B_PATH_NAME_LENGTH) < B_OK
|| user_memcpy(&address, userAddress, sizeof(address)) < B_OK)
return B_BAD_ADDRESS;
if (addressSpec == B_EXACT_ADDRESS) {
if ((addr_t)address + size < (addr_t)address)
return B_BAD_VALUE;
if (!IS_USER_ADDRESS(address)
|| !IS_USER_ADDRESS((addr_t)address + size)) {
return B_BAD_ADDRESS;
}
}
// userland created areas can always be accessed by the kernel
protection |= B_KERNEL_READ_AREA | (protection & B_WRITE_AREA ? B_KERNEL_WRITE_AREA : 0);
protection |= B_KERNEL_READ_AREA
| (protection & B_WRITE_AREA ? B_KERNEL_WRITE_AREA : 0);
area = _vm_map_file(vm_current_user_address_space_id(), name, &address,
addressSpec, size, protection, mapping, path, offset, false);
addressSpec, size, protection, mapping, fd, offset, false);
if (area < B_OK)
return area;
@@ -5373,3 +5454,25 @@ _user_vm_map_file(const char *userName, void **userAddress, int addressSpec,
return area;
}
status_t
_user_unmap_memory(void *_address, addr_t size)
{
addr_t address = (addr_t)_address;
// check params
if (size == 0 || (addr_t)address + size < (addr_t)address)
return B_BAD_VALUE;
if (!IS_USER_ADDRESS(address) || !IS_USER_ADDRESS((addr_t)address + size))
return B_BAD_ADDRESS;
// write lock the address space
AddressSpaceWriteLocker locker;
status_t status = locker.SetTo(team_get_current_team_id());
if (status != B_OK)
return status;
// unmap
return unmap_address_range(locker.AddressSpace(), address, size);
}
+2
View File
@@ -1,5 +1,6 @@
SubDir HAIKU_TOP src system libroot posix sys ;
UsePrivateHeaders shared ;
UseHeaders $(TARGET_PRIVATE_KERNEL_HEADERS) : true ;
MergeObject posix_sys.o :
@@ -11,6 +12,7 @@ MergeObject posix_sys.o :
itimer.c
mkdir.c
mkfifo.c
mman.cpp
rlimit.c
select.c
stat.c
+73
View File
@@ -0,0 +1,73 @@
/*
* Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include <sys/mman.h>
#include <errno.h>
#include <OS.h>
#include <syscall_utils.h>
#include <syscalls.h>
#include <vm.h>
void*
mmap(void* address, size_t length, int protection, int flags, int fd,
off_t offset)
{
// offset and length must be page-aligned
if (length == 0 || (offset | length) % B_PAGE_SIZE != 0) {
errno = B_BAD_VALUE;
return MAP_FAILED;
}
// check anonymous mapping
if ((flags & MAP_ANONYMOUS) != 0) {
fd = -1;
} else if (fd < 0) {
errno = EBADF;
return MAP_FAILED;
}
// either MAP_SHARED or MAP_PRIVATE must be specified
if (((flags & MAP_SHARED) != 0) == ((flags & MAP_PRIVATE) != 0)) {
errno = B_BAD_VALUE;
return MAP_FAILED;
}
// translate mapping, address specification, and protection
int mapping = (flags & MAP_SHARED) != 0
? REGION_NO_PRIVATE_MAP : REGION_PRIVATE_MAP;
uint32 addressSpec = B_ANY_ADDRESS;
if ((flags & MAP_FIXED) != 0)
addressSpec = B_EXACT_ADDRESS;
uint32 areaProtection = 0;
if ((protection & PROT_READ) != 0)
areaProtection |= B_READ_AREA;
if ((protection & PROT_WRITE) != 0)
areaProtection |= B_WRITE_AREA;
if ((protection & PROT_EXEC) != 0)
areaProtection |= B_EXECUTE_AREA;
// ask the kernel to map
area_id area = _kern_map_file("mmap area", &address, addressSpec,
length, areaProtection, mapping, fd, offset);
if (area < 0) {
errno = area;
return MAP_FAILED;
}
return address;
}
int
munmap(void* address, size_t length)
{
RETURN_AND_SET_ERRNO(_kern_unmap_memory(address, length));
}
+4 -3
View File
@@ -794,9 +794,10 @@ map_image(int fd, char const *path, image_t *image, bool fixed)
image->regions[i].delta = loadAddress - image->regions[i].vmstart;
image->regions[i].vmstart = loadAddress;
} else {
image->regions[i].id = sys_vm_map_file(regionName, (void **)&loadAddress,
addressSpecifier, image->regions[i].vmsize, B_READ_AREA | B_WRITE_AREA,
REGION_PRIVATE_MAP, path, PAGE_BASE(image->regions[i].fdstart));
image->regions[i].id = _kern_map_file(regionName,
(void **)&loadAddress, addressSpecifier,
image->regions[i].vmsize, B_READ_AREA | B_WRITE_AREA,
REGION_PRIVATE_MAP, fd, PAGE_BASE(image->regions[i].fdstart));
if (image->regions[i].id < 0) {
status = image->regions[i].id;