mirror of
https://gitlab.com/sortix/sortix.git
synced 2023-02-13 20:55:38 -05:00
Add mmap(2), mprotect(2) and munmap(2).
This commit is contained in:
parent
8e867908ab
commit
04019cab9e
7 changed files with 331 additions and 4 deletions
|
@ -304,6 +304,9 @@ stdlib/system.o \
|
||||||
sys/display/dispmsg_issue.o \
|
sys/display/dispmsg_issue.o \
|
||||||
sys/ioctl/ioctl.o \
|
sys/ioctl/ioctl.o \
|
||||||
sys/kernelinfo/kernelinfo.o \
|
sys/kernelinfo/kernelinfo.o \
|
||||||
|
sys/mman/mmap.o \
|
||||||
|
sys/mman/mprotect.o \
|
||||||
|
sys/mman/munmap.o \
|
||||||
sys/readdirents/readdirents.o \
|
sys/readdirents/readdirents.o \
|
||||||
sys/select/select.o \
|
sys/select/select.o \
|
||||||
sys/socket/accept4.o \
|
sys/socket/accept4.o \
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
|
||||||
Copyright(C) Jonas 'Sortie' Termansen 2012.
|
Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||||
|
|
||||||
This file is part of the Sortix C Library.
|
This file is part of the Sortix C Library.
|
||||||
|
|
||||||
|
@ -22,14 +22,22 @@
|
||||||
|
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
#ifndef _SYS_MMAN_H
|
#ifndef INCLUDE_SYS_MMAN_H
|
||||||
#define _SYS_MMAN_H 1
|
#define INCLUDE_SYS_MMAN_H
|
||||||
|
|
||||||
#include <features.h>
|
#include <features.h>
|
||||||
#include <sortix/mman.h>
|
#include <sortix/mman.h>
|
||||||
|
|
||||||
__BEGIN_DECLS
|
__BEGIN_DECLS
|
||||||
|
|
||||||
|
@include(mode_t.h)
|
||||||
|
@include(off_t.h)
|
||||||
|
@include(size_t.h)
|
||||||
|
|
||||||
|
void* mmap(void*, size_t, int, int, int, off_t);
|
||||||
|
int mprotect(const void*, size_t, int);
|
||||||
|
int munmap(void*, size_t);
|
||||||
|
|
||||||
__END_DECLS
|
__END_DECLS
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
56
libc/sys/mman/mmap.cpp
Normal file
56
libc/sys/mman/mmap.cpp
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||||
|
|
||||||
|
This file is part of the Sortix C Library.
|
||||||
|
|
||||||
|
The Sortix C Library is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
option) any later version.
|
||||||
|
|
||||||
|
The Sortix C Library 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 GNU Lesser General Public
|
||||||
|
License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with the Sortix C Library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
sys/mman/mmap.cpp
|
||||||
|
Maps a window of a file into memory.
|
||||||
|
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
|
||||||
|
// TODO: We use a wrapper system call here because there are too many parameters
|
||||||
|
// to the system call for some platforms. We should extend the system call
|
||||||
|
// ABI so we can do system calls with huge parameter lists and huge return
|
||||||
|
// values portably - then we'll make a new mmap system call that uses this
|
||||||
|
// mechanism if needed.
|
||||||
|
|
||||||
|
struct mmap_request /* duplicated in sortix/mmap.cpp */
|
||||||
|
{
|
||||||
|
void* addr;
|
||||||
|
size_t size;
|
||||||
|
int prot;
|
||||||
|
int flags;
|
||||||
|
int fd;
|
||||||
|
off_t offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
DEFN_SYSCALL1(void*, sys_mmap_wrapper, SYSCALL_MMAP_WRAPPER, struct mmap_request*);
|
||||||
|
|
||||||
|
static void* mmap_wrapper(struct mmap_request* request)
|
||||||
|
{
|
||||||
|
return sys_mmap_wrapper(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
void* mmap(void* addr, size_t size, int prot, int flags, int fd, off_t offset)
|
||||||
|
{
|
||||||
|
struct mmap_request request = { addr, size, prot, flags, fd, offset };
|
||||||
|
return mmap_wrapper(&request);
|
||||||
|
}
|
33
libc/sys/mman/mprotect.cpp
Normal file
33
libc/sys/mman/mprotect.cpp
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||||
|
|
||||||
|
This file is part of the Sortix C Library.
|
||||||
|
|
||||||
|
The Sortix C Library is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
option) any later version.
|
||||||
|
|
||||||
|
The Sortix C Library 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 GNU Lesser General Public
|
||||||
|
License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with the Sortix C Library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
sys/mman/mprotect.cpp
|
||||||
|
Changes the protection of a memory region.
|
||||||
|
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
|
||||||
|
DEFN_SYSCALL3(int, sys_mprotect, SYSCALL_MPROTECT, const void*, size_t, int);
|
||||||
|
|
||||||
|
extern "C" int mprotect(const void* addr, size_t size, int prot)
|
||||||
|
{
|
||||||
|
return sys_mprotect(addr, size, prot);
|
||||||
|
}
|
33
libc/sys/mman/munmap.cpp
Normal file
33
libc/sys/mman/munmap.cpp
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||||
|
|
||||||
|
This file is part of the Sortix C Library.
|
||||||
|
|
||||||
|
The Sortix C Library is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
option) any later version.
|
||||||
|
|
||||||
|
The Sortix C Library 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 GNU Lesser General Public
|
||||||
|
License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with the Sortix C Library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
sys/mman/munmap.cpp
|
||||||
|
Unmaps a memory region.
|
||||||
|
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
|
||||||
|
DEFN_SYSCALL2(int, sys_munmap, SYSCALL_MUNMAP, void*, size_t);
|
||||||
|
|
||||||
|
extern "C" int munmap(void* addr, size_t size)
|
||||||
|
{
|
||||||
|
return sys_munmap(addr, size);
|
||||||
|
}
|
|
@ -137,6 +137,9 @@
|
||||||
#define SYSCALL_SETPGID 113
|
#define SYSCALL_SETPGID 113
|
||||||
#define SYSCALL_TCGETPGRP 114
|
#define SYSCALL_TCGETPGRP 114
|
||||||
#define SYSCALL_TCSETPGRP 115
|
#define SYSCALL_TCSETPGRP 115
|
||||||
#define SYSCALL_MAX_NUM 116 /* index of highest constant + 1 */
|
#define SYSCALL_MMAP_WRAPPER 116
|
||||||
|
#define SYSCALL_MPROTECT 117
|
||||||
|
#define SYSCALL_MUNMAP 118
|
||||||
|
#define SYSCALL_MAX_NUM 119 /* index of highest constant + 1 */
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -31,7 +31,11 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <sortix/mman.h>
|
#include <sortix/mman.h>
|
||||||
|
#include <sortix/seek.h>
|
||||||
|
|
||||||
|
#include <sortix/kernel/copy.h>
|
||||||
|
#include <sortix/kernel/descriptor.h>
|
||||||
|
#include <sortix/kernel/ioctx.h>
|
||||||
#include <sortix/kernel/kernel.h>
|
#include <sortix/kernel/kernel.h>
|
||||||
#include <sortix/kernel/memorymanagement.h>
|
#include <sortix/kernel/memorymanagement.h>
|
||||||
#include <sortix/kernel/process.h>
|
#include <sortix/kernel/process.h>
|
||||||
|
@ -94,6 +98,7 @@ void UnmapMemory(Process* process, uintptr_t addr, size_t size)
|
||||||
struct segment right_segment;
|
struct segment right_segment;
|
||||||
right_segment.addr = addr + size;
|
right_segment.addr = addr + size;
|
||||||
right_segment.size = conflict->addr + conflict->size - (addr + size);
|
right_segment.size = conflict->addr + conflict->size - (addr + size);
|
||||||
|
right_segment.prot = conflict->prot;
|
||||||
conflict->size = addr - conflict->addr;
|
conflict->size = addr - conflict->addr;
|
||||||
// TODO: This shouldn't really fail as we free memory above, but
|
// TODO: This shouldn't really fail as we free memory above, but
|
||||||
// this code isn't really provably reliable.
|
// this code isn't really provably reliable.
|
||||||
|
@ -246,6 +251,189 @@ bool MapMemory(Process* process, uintptr_t addr, size_t size, int prot)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const int USER_SETTABLE_PROT = PROT_USER | PROT_HEAP;
|
||||||
|
const int UNDERSTOOD_MMAP_FLAGS = MAP_SHARED |
|
||||||
|
MAP_PRIVATE |
|
||||||
|
MAP_ANONYMOUS |
|
||||||
|
MAP_FIXED;
|
||||||
|
|
||||||
|
static
|
||||||
|
void* sys_mmap(void* addr_ptr, size_t size, int prot, int flags, int fd,
|
||||||
|
off_t offset)
|
||||||
|
{
|
||||||
|
// Verify that that the address is suitable aligned if fixed.
|
||||||
|
uintptr_t addr = (uintptr_t) addr_ptr;
|
||||||
|
if ( flags & MAP_FIXED && !Page::IsAligned(addr) )
|
||||||
|
return errno = EINVAL, MAP_FAILED;
|
||||||
|
// We don't allow zero-size mappings.
|
||||||
|
if ( size == 0 )
|
||||||
|
return errno = EINVAL, MAP_FAILED;
|
||||||
|
// Verify that the user didn't request permissions not allowed.
|
||||||
|
if ( prot & ~USER_SETTABLE_PROT )
|
||||||
|
return errno = EINVAL, MAP_FAILED;
|
||||||
|
// Verify that we understand all the flags we were passed.
|
||||||
|
if ( flags & ~UNDERSTOOD_MMAP_FLAGS )
|
||||||
|
return errno = EINVAL, MAP_FAILED;
|
||||||
|
// Verify that MAP_PRIVATE and MAP_SHARED are not both set.
|
||||||
|
if ( bool(flags & MAP_PRIVATE) == bool(flags & MAP_SHARED) )
|
||||||
|
return errno = EINVAL, MAP_FAILED;
|
||||||
|
// TODO: MAP_SHARED is not currently supported.
|
||||||
|
if ( flags & MAP_SHARED )
|
||||||
|
return errno = EINVAL, MAP_FAILED;
|
||||||
|
// Verify the fíle descriptor and the offset is suitable set if needed.
|
||||||
|
if ( !(flags & MAP_ANONYMOUS) &&
|
||||||
|
(fd < 0 || offset < 0 || (offset & (Page::Size()-1))) )
|
||||||
|
return errno = EINVAL, MAP_FAILED;
|
||||||
|
|
||||||
|
uintptr_t aligned_addr = Page::AlignDown(addr);
|
||||||
|
uintptr_t aligned_size = Page::AlignUp(size);
|
||||||
|
|
||||||
|
// Pick a good location near the end of user-space if no hint is given.
|
||||||
|
if ( !(flags & MAP_FIXED) && !aligned_addr )
|
||||||
|
{
|
||||||
|
uintptr_t userspace_addr;
|
||||||
|
size_t userspace_size;
|
||||||
|
Memory::GetUserVirtualArea(&userspace_addr, &userspace_size);
|
||||||
|
addr = aligned_addr =
|
||||||
|
Page::AlignDown(userspace_addr + userspace_size - aligned_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that the offset + size doesn't overflow.
|
||||||
|
if ( !(flags & MAP_ANONYMOUS) &&
|
||||||
|
(uintmax_t) (OFF_MAX - offset) < (uintmax_t) aligned_size )
|
||||||
|
return errno = EOVERFLOW, MAP_FAILED;
|
||||||
|
|
||||||
|
Process* process = CurrentProcess();
|
||||||
|
|
||||||
|
// Verify whether the backing file is usable for memory mapping.
|
||||||
|
ioctx_t ctx; SetupUserIOCtx(&ctx);
|
||||||
|
Ref<Descriptor> desc;
|
||||||
|
if ( !(flags & MAP_ANONYMOUS) )
|
||||||
|
{
|
||||||
|
if ( !(desc = process->GetDescriptor(fd)) )
|
||||||
|
return MAP_FAILED;
|
||||||
|
// Verify that the file is seekable.
|
||||||
|
if ( desc->lseek(&ctx, 0, SEEK_CUR) < 0 )
|
||||||
|
return errno = ENODEV, MAP_FAILED;
|
||||||
|
// Verify that we have read access to the file.
|
||||||
|
if ( desc->read(&ctx, NULL, 0) != 0 )
|
||||||
|
return errno = EACCES, MAP_FAILED;
|
||||||
|
// Verify that we have write access to the file if needed.
|
||||||
|
if ( (prot & PROT_WRITE) && !(flags & MAP_PRIVATE) &&
|
||||||
|
desc->write(&ctx, NULL, 0) != 0 )
|
||||||
|
return errno = EACCES, MAP_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedLock lock(&process->segment_lock);
|
||||||
|
|
||||||
|
// Determine where to put the new segment and its protection.
|
||||||
|
struct segment new_segment;
|
||||||
|
if ( flags & MAP_FIXED )
|
||||||
|
new_segment.addr = aligned_addr, new_segment.size = aligned_size;
|
||||||
|
else if ( !PlaceSegment(&new_segment, process, (void*) addr, aligned_size, flags) )
|
||||||
|
return errno = ENOMEM, MAP_FAILED;
|
||||||
|
new_segment.prot = prot | PROT_KREAD | PROT_KWRITE | PROT_FORK;
|
||||||
|
|
||||||
|
// Allocate a memory segment with the desired properties.
|
||||||
|
if ( !MapMemory(process, new_segment.addr, new_segment.size, new_segment.prot) )
|
||||||
|
return MAP_FAILED;
|
||||||
|
|
||||||
|
// Read the file contents into the newly allocated memory.
|
||||||
|
if ( !(flags & MAP_ANONYMOUS) )
|
||||||
|
{
|
||||||
|
for ( size_t so_far = 0; so_far < aligned_size; )
|
||||||
|
{
|
||||||
|
uint8_t* ptr = (uint8_t*) (new_segment.addr + so_far);
|
||||||
|
size_t left = aligned_size - so_far;
|
||||||
|
off_t pos = offset + so_far;
|
||||||
|
ssize_t num_bytes = desc->pread(&ctx, ptr, left, pos);
|
||||||
|
if ( num_bytes < 0 )
|
||||||
|
{
|
||||||
|
// TODO: How should this situation be handled? For now we'll
|
||||||
|
// just ignore the error condition.
|
||||||
|
errno = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ( !num_bytes )
|
||||||
|
{
|
||||||
|
// We got an unexpected early end-of-file condition, but that's
|
||||||
|
// alright as the MapMemory call zero'd the new memory and we
|
||||||
|
// are expected to zero the remainder.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
so_far += num_bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (void*) new_segment.addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sys_mprotect(const void* addr_ptr, size_t size, int prot)
|
||||||
|
{
|
||||||
|
// Verify that that the address is suitable aligned.
|
||||||
|
uintptr_t addr = (uintptr_t) addr_ptr;
|
||||||
|
if ( !Page::IsAligned(addr) )
|
||||||
|
return errno = EINVAL, -1;
|
||||||
|
// Verify that the user didn't request permissions not allowed.
|
||||||
|
if ( prot & ~USER_SETTABLE_PROT )
|
||||||
|
return errno = EINVAL, -1;
|
||||||
|
|
||||||
|
size = Page::AlignUp(size);
|
||||||
|
prot |= PROT_KREAD | PROT_KWRITE | PROT_FORK;
|
||||||
|
|
||||||
|
Process* process = CurrentProcess();
|
||||||
|
ScopedLock lock(&process->segment_lock);
|
||||||
|
|
||||||
|
if ( !ProtectMemory(process, addr, size, prot) )
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sys_munmap(void* addr_ptr, size_t size)
|
||||||
|
{
|
||||||
|
// Verify that that the address is suitable aligned.
|
||||||
|
uintptr_t addr = (uintptr_t) addr_ptr;
|
||||||
|
if ( !Page::IsAligned(addr) )
|
||||||
|
return errno = EINVAL, -1;
|
||||||
|
// We don't allow zero-size unmappings.
|
||||||
|
if ( size == 0 )
|
||||||
|
return errno = EINVAL, -1;
|
||||||
|
|
||||||
|
size = Page::AlignUp(size);
|
||||||
|
|
||||||
|
Process* process = CurrentProcess();
|
||||||
|
ScopedLock lock(&process->segment_lock);
|
||||||
|
|
||||||
|
UnmapMemory(process, addr, size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: We use a wrapper system call here because there are too many parameters
|
||||||
|
// to mmap for some platforms. We should extend the system call ABI so we
|
||||||
|
// can do system calls with huge parameter lists and huge return values
|
||||||
|
// portably - then we'll make sys_mmap use this mechanism if needed.
|
||||||
|
|
||||||
|
struct mmap_request /* duplicated in libc/sys/mman/mmap.cpp */
|
||||||
|
{
|
||||||
|
void* addr;
|
||||||
|
size_t size;
|
||||||
|
int prot;
|
||||||
|
int flags;
|
||||||
|
int fd;
|
||||||
|
off_t offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void* sys_mmap_wrapper(struct mmap_request* user_request)
|
||||||
|
{
|
||||||
|
struct mmap_request request;
|
||||||
|
if ( !CopyFromUser(&request, user_request, sizeof(request)) )
|
||||||
|
return MAP_FAILED;
|
||||||
|
return sys_mmap(request.addr, request.size, request.prot, request.flags,
|
||||||
|
request.fd, request.offset);
|
||||||
|
}
|
||||||
|
|
||||||
void InitCPU(multiboot_info_t* bootinfo);
|
void InitCPU(multiboot_info_t* bootinfo);
|
||||||
|
|
||||||
void Init(multiboot_info_t* bootinfo)
|
void Init(multiboot_info_t* bootinfo)
|
||||||
|
@ -253,6 +441,9 @@ void Init(multiboot_info_t* bootinfo)
|
||||||
InitCPU(bootinfo);
|
InitCPU(bootinfo);
|
||||||
|
|
||||||
Syscall::Register(SYSCALL_MEMSTAT, (void*) sys_memstat);
|
Syscall::Register(SYSCALL_MEMSTAT, (void*) sys_memstat);
|
||||||
|
Syscall::Register(SYSCALL_MMAP_WRAPPER, (void*) sys_mmap_wrapper);
|
||||||
|
Syscall::Register(SYSCALL_MPROTECT, (void*) sys_mprotect);
|
||||||
|
Syscall::Register(SYSCALL_MUNMAP, (void*) sys_munmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Memory
|
} // namespace Memory
|
||||||
|
|
Loading…
Add table
Reference in a new issue