kernel: add generic wrapper for accessing user memory

This patch adds user_access() which can be used to gracefully handle
page faults that may happen when accessing user memory. It is used
by arch_cpu_user{memcpy, memset, strlcpy}() to allow using optimized
functions from the standard library.

Currently only x64 uses this, but nothing really is arch specific here.

Signed-off-by: Paweł Dziepak <[email protected]>
This commit is contained in:
Paweł Dziepak
2014-09-14 22:39:07 +02:00
parent e81b792e8f
commit 95e97463d2
10 changed files with 170 additions and 170 deletions
-7
View File
@@ -34,13 +34,6 @@ void arch_cpu_invalidate_TLB_list(addr_t pages[], int num_pages);
void arch_cpu_user_TLB_invalidate(void);
void arch_cpu_global_TLB_invalidate(void);
status_t arch_cpu_user_memcpy(void *to, const void *from, size_t size,
addr_t *faultHandler);
ssize_t arch_cpu_user_strlcpy(char *to, const char *from, size_t size,
addr_t *faultHandler);
status_t arch_cpu_user_memset(void *s, char c, size_t count,
addr_t *faultHandler);
void arch_cpu_sync_icache(void *address, size_t length);
@@ -0,0 +1,86 @@
/*
* Copyright 2014, Paweł Dziepak, [email protected].
* Distributed under the terms of the MIT License.
*/
#ifndef _KERNEL_ARCH_GENERIC_USER_MEMORY_H
#define _KERNEL_ARCH_GENERIC_USER_MEMORY_H
#include <atomic>
#include <setjmp.h>
#include <string.h>
#include <thread.h>
namespace {
struct FaultHandlerGuard {
FaultHandlerGuard()
{
ASSERT(thread_get_current_thread()->fault_handler == nullptr);
thread_get_current_thread()->fault_handler = HandleFault;
std::atomic_signal_fence(std::memory_order_acq_rel);
}
~FaultHandlerGuard()
{
std::atomic_signal_fence(std::memory_order_acq_rel);
thread_get_current_thread()->fault_handler = nullptr;
}
[[noreturn]] static void HandleFault()
{
longjmp(thread_get_current_thread()->fault_handler_state, 1);
}
};
template<typename Function>
bool user_access(Function function)
{
FaultHandlerGuard guard;
// TODO: try { } catch (...) { } would be much nicer, wouldn't it?
// And faster... And world wouldn't end in a terrible disaster if function()
// or anything it calls created on stack an object with non-trivial
// destructor.
auto fail = setjmp(thread_get_current_thread()->fault_handler_state);
if (fail == 0) {
function();
return true;
}
dprintf("Hi! This is fault handler speaking.\n");
return false;
}
inline status_t
arch_cpu_user_memcpy(void* src, const void* dst, size_t n)
{
return user_access([=] { memcpy(src, dst, n); }) ? B_OK : B_ERROR;
}
inline status_t
arch_cpu_user_memset(void* src, char v, size_t n)
{
return user_access([=] { memset(src, v, n); }) ? B_OK : B_ERROR;
}
inline ssize_t
arch_cpu_user_strlcpy(char* src, const char* dst, size_t n)
{
ssize_t result;
return user_access([=, &result] { result = strlcpy(src, dst, n); })
? result : B_ERROR;
}
}
#endif // _KERNEL_ARCH_GENERIC_USER_MEMORY_H
+57
View File
@@ -0,0 +1,57 @@
/*
* Copyright 2014, Paweł Dziepak, [email protected].
* Distributed under the terms of the MIT License.
*/
#ifndef _KERNEL_ARCH_USER_MEMORY_H
#define _KERNEL_ARCH_USER_MEMORY_H
#include <OS.h>
#include <thread.h>
#ifdef __x86_64__
# include <arch/generic/user_memory.h>
#else
extern "C" {
status_t _arch_cpu_user_memcpy(void* to, const void* from, size_t size,
void (**faultHandler)(void));
ssize_t _arch_cpu_user_strlcpy(char* to, const char* from, size_t size,
void (**faultHandler)(void));
status_t _arch_cpu_user_memset(void* s, char c, size_t count,
void (**faultHandler)(void));
}
static inline status_t
arch_cpu_user_memcpy(void* to, const void* from, size_t size)
{
return _arch_cpu_user_memcpy(to, from, size,
&thread_get_current_thread()->fault_handler);
}
static inline ssize_t
arch_cpu_user_strlcpy(char* to, const char* from, size_t size)
{
return _arch_cpu_user_strlcpy(to, from, size,
&thread_get_current_thread()->fault_handler);
}
static inline status_t
arch_cpu_user_memset(void* s, char c, size_t count)
{
return _arch_cpu_user_memset(s, c, count,
&thread_get_current_thread()->fault_handler);
}
#endif
#endif // _KERNEL_ARCH_USER_MEMORY_H
+2 -1
View File
@@ -479,7 +479,8 @@ struct Thread : TeamThreadIteratorEntry<thread_id>, KernelReferenceable {
} msg; // write_sem/read_sem are protected by fLock when accessed by
// others, the other fields are protected by write_sem/read_sem
addr_t fault_handler;
void (*fault_handler)(void);
jmp_buf fault_handler_state;
int32 page_faults_allowed;
/* this field may only stay in debug builds in the future */