* Added vm_debug_copy_page_memory() which copies memory from a potentially not

mapped page.
* debug_{mem,strl}cpy():
  - Added "team" parameter for specifying the address space the address are
    to be interpreted in.
  - When the standard memcpy() (with fault handler) fails, fall back to
    vm_debug_copy_page_memory().
* Added debug_is_debugged_team(): Predicate returning true, if the supplied
  team_id refers to the same team debug_get_debugged_thread() belongs to.
* Added DebuggedThreadSetter class for scope-based debug_set_debugged_thread().
  Made use of it in several debugger functions.
* print_demangled_call() (x86): Fixed unsafe memory access.

Allows KDL stack traces to work correctly again, even if the page daemon has
already unmapped the concerned pages.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@36230 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2010-04-13 17:40:15 +00:00
parent ca4dd26afd
commit c3676b54bf
10 changed files with 377 additions and 86 deletions
+29 -3
View File
@@ -132,8 +132,10 @@ extern bool debug_is_kernel_memory_accessible(addr_t address, size_t size,
uint32 protection);
extern int debug_call_with_fault_handler(jmp_buf jumpBuffer,
void (*function)(void*), void* parameter);
extern status_t debug_memcpy(void* to, const void* from, size_t size);
extern ssize_t debug_strlcpy(char* to, const char* from, size_t size);
extern status_t debug_memcpy(team_id teamID, void* to, const void* from,
size_t size);
extern ssize_t debug_strlcpy(team_id teamID, char* to, const char* from,
size_t size);
extern char kgetc(void);
extern void kputs(const char *string);
@@ -172,8 +174,9 @@ extern status_t debug_get_next_demangled_argument(uint32* _cookie,
extern struct thread* debug_set_debugged_thread(struct thread* thread);
extern struct thread* debug_get_debugged_thread();
extern struct arch_debug_registers* debug_get_debug_registers(int32 cpu);
extern bool debug_is_debugged_team(team_id teamID);
extern struct arch_debug_registers* debug_get_debug_registers(int32 cpu);
extern status_t _user_kernel_debugger(const char *message);
extern void _user_debug_output(const char *userString);
@@ -182,4 +185,27 @@ extern void _user_debug_output(const char *userString);
}
#endif
#ifdef __cplusplus
struct DebuggedThreadSetter {
DebuggedThreadSetter(struct thread* thread)
:
fPreviousThread(debug_set_debugged_thread(thread))
{
}
~DebuggedThreadSetter()
{
debug_set_debugged_thread(fPreviousThread);
}
private:
struct thread* fPreviousThread;
};
#endif // __cplusplus
#endif /* _KERNEL_DEBUG_H */
+3
View File
@@ -140,6 +140,9 @@ status_t vm_memcpy_to_physical(addr_t to, const void* from, size_t length,
bool user);
void vm_memcpy_physical_page(addr_t to, addr_t from);
status_t vm_debug_copy_page_memory(team_id teamID, void* unsafeMemory,
void* buffer, size_t size, bool copyToUnsafe);
// user syscalls
area_id _user_create_area(const char *name, void **address, uint32 addressSpec,
size_t size, uint32 lock, uint32 protection);
+4 -2
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2003-2009, Haiku Inc. All rights reserved.
* Copyright 2003-2010, Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
@@ -66,8 +66,10 @@ static status_t
get_next_frame(addr_t framePointer, addr_t *next, addr_t *ip)
{
stack_frame frame;
if (debug_memcpy(&frame, (void*)framePointer, sizeof(frame)) != B_OK)
if (debug_memcpy(B_CURRENT_TEAM, &frame, (void*)framePointer, sizeof(frame))
!= B_OK) {
return B_BAD_ADDRESS;
}
*ip = frame.return_address;
*next = (addr_t)frame.previous;
+4 -2
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2003-2009, Haiku Inc. All rights reserved.
* Copyright 2003-2010, Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
@@ -65,8 +65,10 @@ static status_t
get_next_frame(addr_t framePointer, addr_t *next, addr_t *ip)
{
stack_frame frame;
if (debug_memcpy(&frame, (void*)framePointer, sizeof(frame)) != B_OK)
if (debug_memcpy(B_CURRENT_TEAM, &frame, (void*)framePointer, sizeof(frame))
!= B_OK) {
return B_BAD_ADDRESS;
}
*ip = frame.return_address;
*next = (addr_t)frame.previous;
+99 -47
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2009, Ingo Weinhold, [email protected].
* Copyright 2009-2010, Ingo Weinhold, [email protected].
* Copyright 2002-2008, Axel Dörfler, [email protected].
* Distributed under the terms of the MIT License.
*
@@ -81,7 +81,7 @@ static status_t
get_next_frame_debugger(addr_t ebp, addr_t *_next, addr_t *_eip)
{
stack_frame frame;
if (debug_memcpy(&frame, (void*)ebp, sizeof(frame)) != B_OK)
if (debug_memcpy(B_CURRENT_TEAM, &frame, (void*)ebp, sizeof(frame)) != B_OK)
return B_BAD_ADDRESS;
*_eip = frame.return_address;
@@ -126,6 +126,21 @@ set_debug_argument_variable(int32 index, uint64 value)
}
template<typename Type>
static Type
read_function_argument_value(void* argument, bool& _valueKnown)
{
Type value;
if (debug_memcpy(B_CURRENT_TEAM, &value, argument, sizeof(Type)) == B_OK) {
_valueKnown = true;
return value;
}
_valueKnown = false;
return 0;
}
static status_t
print_demangled_call(const char* image, const char* symbol, addr_t args,
bool noObjectMethod, bool addDebugVariables)
@@ -151,10 +166,15 @@ print_demangled_call(const char* image, const char* symbol, addr_t args,
const char* lastName = strrchr(name, ':') - 1;
int namespaceLength = lastName - name;
kprintf("<%s> %.*s<\33[32m%p\33[0m>%s", image, namespaceLength, name,
*(uint32 **)arg, lastName);
uint32 argValue = 0;
if (debug_memcpy(B_CURRENT_TEAM, &argValue, arg, 4) == B_OK) {
kprintf("<%s> %.*s<\33[32m%#" B_PRIx32 "\33[0m>%s", image,
namespaceLength, name, argValue, lastName);
} else
kprintf("<%s> %.*s<???>%s", image, namespaceLength, name, lastName);
if (addDebugVariables)
set_debug_variable("_this", *(uint32 *)arg);
set_debug_variable("_this", argValue);
arg++;
} else
kprintf("<%s> %s", image, name);
@@ -172,78 +192,103 @@ print_demangled_call(const char* image, const char* symbol, addr_t args,
// retrieve value and type identifier
uint64 value;
bool valueKnown = false;
switch (type) {
case B_INT64_TYPE:
value = *(int64*)arg;
kprintf("int64: \33[34m%Ld\33[0m", value);
value = read_function_argument_value<int64>(arg, valueKnown);
if (valueKnown)
kprintf("int64: \33[34m%Ld\33[0m", value);
break;
case B_INT32_TYPE:
value = *(int32*)arg;
kprintf("int32: \33[34m%ld\33[0m", (int32)value);
value = read_function_argument_value<int32>(arg, valueKnown);
if (valueKnown)
kprintf("int32: \33[34m%ld\33[0m", (int32)value);
break;
case B_INT16_TYPE:
value = *(int16*)arg;
kprintf("int16: \33[34m%d\33[0m", (int16)value);
value = read_function_argument_value<int16>(arg, valueKnown);
if (valueKnown)
kprintf("int16: \33[34m%d\33[0m", (int16)value);
break;
case B_INT8_TYPE:
value = *(int8*)arg;
kprintf("int8: \33[34m%d\33[0m", (int8)value);
value = read_function_argument_value<int8>(arg, valueKnown);
if (valueKnown)
kprintf("int8: \33[34m%d\33[0m", (int8)value);
break;
case B_UINT64_TYPE:
value = *(uint64*)arg;
kprintf("uint64: \33[34m%#Lx\33[0m", value);
if (value < 0x100000)
kprintf(" (\33[34m%Lu\33[0m)", value);
value = read_function_argument_value<uint64>(arg, valueKnown);
if (valueKnown) {
kprintf("uint64: \33[34m%#Lx\33[0m", value);
if (value < 0x100000)
kprintf(" (\33[34m%Lu\33[0m)", value);
}
break;
case B_UINT32_TYPE:
value = *(uint32*)arg;
kprintf("uint32: \33[34m%#lx\33[0m", (uint32)value);
if (value < 0x100000)
kprintf(" (\33[34m%lu\33[0m)", (uint32)value);
value = read_function_argument_value<uint32>(arg, valueKnown);
if (valueKnown) {
kprintf("uint32: \33[34m%#lx\33[0m", (uint32)value);
if (value < 0x100000)
kprintf(" (\33[34m%lu\33[0m)", (uint32)value);
}
break;
case B_UINT16_TYPE:
value = *(uint16*)arg;
kprintf("uint16: \33[34m%#x\33[0m (\33[34m%u\33[0m)",
(uint16)value, (uint16)value);
value = read_function_argument_value<uint16>(arg, valueKnown);
if (valueKnown) {
kprintf("uint16: \33[34m%#x\33[0m (\33[34m%u\33[0m)",
(uint16)value, (uint16)value);
}
break;
case B_UINT8_TYPE:
value = *(uint8*)arg;
kprintf("uint8: \33[34m%#x\33[0m (\33[34m%u\33[0m)",
(uint8)value, (uint8)value);
value = read_function_argument_value<uint8>(arg, valueKnown);
if (valueKnown) {
kprintf("uint8: \33[34m%#x\33[0m (\33[34m%u\33[0m)",
(uint8)value, (uint8)value);
}
break;
case B_BOOL_TYPE:
value = *(uint8*)arg;
kprintf("\33[34m%s\33[0m", value ? "true" : "false");
value = read_function_argument_value<uint8>(arg, valueKnown);
if (valueKnown)
kprintf("\33[34m%s\33[0m", value ? "true" : "false");
break;
default:
if (buffer[0])
kprintf("%s: ", buffer);
if (length == 4) {
value = *(uint32*)arg;
if (value == 0
&& (type == B_POINTER_TYPE || type == B_REF_TYPE))
kprintf("NULL");
else
kprintf("\33[34m%#lx\33[0m", (uint32)value);
value = read_function_argument_value<uint32>(arg,
valueKnown);
if (valueKnown) {
if (value == 0
&& (type == B_POINTER_TYPE || type == B_REF_TYPE))
kprintf("NULL");
else
kprintf("\33[34m%#lx\33[0m", (uint32)value);
}
break;
}
if (length == 8)
value = *(uint64*)arg;
else
if (length == 8) {
value = read_function_argument_value<uint64>(arg,
valueKnown);
} else
value = (uint64)arg;
kprintf("\33[34m%#Lx\33[0m", value);
if (valueKnown)
kprintf("\33[34m%#Lx\33[0m", value);
break;
}
if (type == B_STRING_TYPE) {
if (!valueKnown)
kprintf("???");
if (valueKnown && type == B_STRING_TYPE) {
if (value == 0)
kprintf(" \33[31m\"<NULL>\"\33[0m");
else if (debug_strlcpy(buffer, (char*)value, kBufferSize) < B_OK)
else if (debug_strlcpy(B_CURRENT_TEAM, buffer, (char*)value,
kBufferSize) < B_OK) {
kprintf(" \33[31m\"<???>\"\33[0m");
else
} else
kprintf(" \33[36m\"%s\"\33[0m", buffer);
}
@@ -564,6 +609,8 @@ stack_trace(int argc, char **argv)
&thread, &ebp, &oldPageDirectory))
return 0;
DebuggedThreadSetter threadSetter(thread);
if (thread != NULL) {
kprintf("stack trace for thread %ld \"%s\"\n", thread->id,
thread->name);
@@ -736,6 +783,8 @@ show_call(int argc, char **argv)
&oldPageDirectory))
return 0;
DebuggedThreadSetter threadSetter(thread);
int32 callIndex = strtoul(argv[argc == 3 ? 2 : 1], NULL, 0);
if (thread != NULL)
@@ -816,6 +865,8 @@ dump_iframes(int argc, char **argv)
if (thread != NULL)
kprintf("iframes for thread %ld \"%s\"\n", thread->id, thread->name);
DebuggedThreadSetter threadSetter(thread);
struct iframe* frame = find_previous_iframe(thread, x86_read_ebp());
while (frame != NULL) {
print_iframe(frame);
@@ -889,12 +940,11 @@ cmd_in_context(int argc, char** argv)
}
}
struct thread* previousThread = debug_set_debugged_thread(thread);
// execute the command
evaluate_debug_command(commandLine);
debug_set_debugged_thread(previousThread);
{
DebuggedThreadSetter threadSetter(thread);
evaluate_debug_command(commandLine);
}
// reset the page directory
if (oldPageDirectory)
@@ -927,6 +977,8 @@ bool
arch_debug_contains_call(struct thread *thread, const char *symbol,
addr_t start, addr_t end)
{
DebuggedThreadSetter threadSetter(thread);
addr_t ebp;
if (thread == thread_get_current_thread())
ebp = x86_read_ebp();
+131 -17
View File
@@ -1852,33 +1852,69 @@ debug_call_with_fault_handler(jmp_buf jumpBuffer, void (*function)(void*),
/*! Similar to user_memcpy(), but can only be invoked from within the kernel
debugger (and must not be used outside).
The supplied \a teamID specifies the address space in which to interpret
the addresses. It can be \c B_CURRENT_TEAM for debug_get_debugged_thread(),
or any valid team ID. If the addresses are both kernel addresses, the
argument is ignored and the current address space is used.
*/
status_t
debug_memcpy(void* to, const void* from, size_t size)
debug_memcpy(team_id teamID, void* to, const void* from, size_t size)
{
// don't allow address overflows
if ((addr_t)from + size < (addr_t)from || (addr_t)to + size < (addr_t)to)
return B_BAD_ADDRESS;
debug_memcpy_parameters parameters = {to, from, size};
// Try standard memcpy() with fault handler, if the addresses can be
// interpreted in the current address space.
if ((IS_KERNEL_ADDRESS(from) && IS_KERNEL_ADDRESS(to))
|| debug_is_debugged_team(teamID)) {
debug_memcpy_parameters parameters = {to, from, size};
if (debug_call_with_fault_handler(gCPU[sDebuggerOnCPU].fault_jump_buffer,
&debug_memcpy_trampoline, &parameters) != 0) {
return B_BAD_ADDRESS;
if (debug_call_with_fault_handler(gCPU[sDebuggerOnCPU].fault_jump_buffer,
&debug_memcpy_trampoline, &parameters) == 0) {
return B_OK;
}
}
// Try harder. The pages of the respective memory could be unmapped but
// still exist in a cache (the page daemon does that with inactive pages).
while (size > 0) {
uint8 buffer[32];
size_t toCopy = std::min(size, sizeof(buffer));
// restrict the size so we don't cross page boundaries
if (((addr_t)from + toCopy) % B_PAGE_SIZE < toCopy)
toCopy -= ((addr_t)from + toCopy) % B_PAGE_SIZE;
if (((addr_t)to + toCopy) % B_PAGE_SIZE < toCopy)
toCopy -= ((addr_t)to + toCopy) % B_PAGE_SIZE;
if (vm_debug_copy_page_memory(teamID, (void*)from, buffer, toCopy,
false) != B_OK
|| vm_debug_copy_page_memory(teamID, to, buffer, toCopy, true)
!= B_OK) {
return B_BAD_ADDRESS;
}
from = (const uint8*)from + toCopy;
to = (uint8*)to + toCopy;
size -= toCopy;
}
return B_OK;
}
/*! Similar to user_strlcpy(), but can only be invoked from within the kernel
debugger (and must not be used outside).
The supplied \a teamID specifies the address space in which to interpret
the addresses. It can be \c B_CURRENT_TEAM for debug_get_debugged_thread(),
or any valid team ID. If the addresses are both kernel addresses, the
argument is ignored and the current address space is used.
*/
ssize_t
debug_strlcpy(char* to, const char* from, size_t size)
debug_strlcpy(team_id teamID, char* to, const char* from, size_t size)
{
if (size == 0)
return 0;
if (from == NULL || to == NULL)
if (from == NULL || (to == NULL && size > 0))
return B_BAD_ADDRESS;
// limit size to avoid address overflows
@@ -1887,18 +1923,80 @@ debug_strlcpy(char* to, const char* from, size_t size)
// NOTE: Since strlcpy() determines the length of \a from, the source
// address might still overflow.
debug_strlcpy_parameters parameters = {to, from, maxSize};
// Try standard strlcpy() with fault handler, if the addresses can be
// interpreted in the current address space.
if ((IS_KERNEL_ADDRESS(from) && IS_KERNEL_ADDRESS(to))
|| debug_is_debugged_team(teamID)) {
debug_strlcpy_parameters parameters = {to, from, maxSize};
if (debug_call_with_fault_handler(gCPU[sDebuggerOnCPU].fault_jump_buffer,
&debug_strlcpy_trampoline, &parameters) != 0) {
return B_BAD_ADDRESS;
if (debug_call_with_fault_handler(
gCPU[sDebuggerOnCPU].fault_jump_buffer,
&debug_strlcpy_trampoline, &parameters) == 0) {
// If we hit the address overflow boundary, fail.
if (parameters.result >= maxSize && maxSize < size)
return B_BAD_ADDRESS;
return parameters.result;
}
}
// If we hit the address overflow boundary, fail.
if (parameters.result >= maxSize && maxSize < size)
return B_BAD_ADDRESS;
// Try harder. The pages of the respective memory could be unmapped but
// still exist in a cache (the page daemon does that with inactive pages).
size_t totalLength = 0;
while (maxSize > 0) {
char buffer[32];
size_t toCopy = std::min(maxSize, sizeof(buffer));
return parameters.result;
// restrict the size so we don't cross page boundaries
if (((addr_t)from + toCopy) % B_PAGE_SIZE < toCopy)
toCopy -= ((addr_t)from + toCopy) % B_PAGE_SIZE;
if (((addr_t)to + toCopy) % B_PAGE_SIZE < toCopy)
toCopy -= ((addr_t)to + toCopy) % B_PAGE_SIZE;
// copy the next part of the string from the source
if (vm_debug_copy_page_memory(teamID, (void*)from, buffer, toCopy,
false) != B_OK) {
return B_BAD_ADDRESS;
}
// determine the length of the part and whether we've reached the end
// of the string
size_t length = strnlen(buffer, toCopy);
bool endOfString = length < toCopy;
from = (const char*)from + toCopy;
totalLength += length;
maxSize -= length;
if (endOfString) {
// only copy the actual string, including the terminating null
toCopy = length + 1;
}
if (size > 0) {
// We still have space left in the target buffer.
if (size <= length) {
// Not enough space for the complete part. Null-terminate it and
// copy what we can.
buffer[size - 1] = '\0';
totalLength += length - size;
toCopy = size;
}
if (vm_debug_copy_page_memory(teamID, to, buffer, toCopy, true)
!= B_OK) {
return B_BAD_ADDRESS;
}
to = (char*)to + toCopy;
size -= toCopy;
}
if (endOfString)
return totalLength;
}
return totalLength;
}
@@ -2060,6 +2158,22 @@ debug_get_debugged_thread()
}
/*! Returns whether the supplied team ID refers to the same team the currently
debugged thread (debug_get_debugged_thread()) belongs to.
Always returns \c true, if \c B_CURRENT_TEAM is given.
*/
bool
debug_is_debugged_team(team_id teamID)
{
if (teamID == B_CURRENT_TEAM)
return true;
struct thread* thread = debug_get_debugged_thread();
return thread != NULL && thread->team != NULL
&& thread->team->id == teamID;
}
// #pragma mark -
// userland syscalls
+3 -3
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2008-2009, Ingo Weinhold, ingo_weinhold@gmx.de
* Copyright 2008-2010, Ingo Weinhold, ingo_weinhold@gmx.de
* Copyright 2006, Stephan Aßmus, superstippi@gmx.de
* Distributed under the terms of the MIT License.
*/
@@ -723,7 +723,7 @@ ExpressionParser::_ParseExpression(bool expectAssignment)
break;
}
if (debug_memcpy(address, &buffer, size) != B_OK) {
if (debug_memcpy(B_CURRENT_TEAM, address, &buffer, size) != B_OK) {
snprintf(sTempBuffer, sizeof(sTempBuffer),
"failed to write to address %p", address);
parse_exception(sTempBuffer, position);
@@ -1062,7 +1062,7 @@ ExpressionParser::_ParseDereference(void** _address, uint32* _size)
// read bytes from address into a tempory buffer
uint64 buffer;
if (debug_memcpy(&buffer, address, size) != B_OK) {
if (debug_memcpy(B_CURRENT_TEAM, &buffer, address, size) != B_OK) {
snprintf(sTempBuffer, sizeof(sTempBuffer),
"failed to dereference address %p", address);
parse_exception(sTempBuffer, starPosition);
+3 -2
View File
@@ -296,9 +296,10 @@ gdb_parse_command(void)
// for gdb may be trying to access an stray pointer
// We copy the memory to a safe buffer using
// the bulletproof debug_memcpy().
if (debug_memcpy(sSafeMemory, (char*)address, len) < 0)
if (debug_memcpy(B_CURRENT_TEAM, sSafeMemory, (char*)address, len)
< 0) {
gdb_reply("E02");
else
} else
gdb_memreply(sSafeMemory, len);
break;
+6 -4
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2009-2010, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2002-2009, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*
@@ -1467,8 +1467,10 @@ public:
if (!IS_USER_ADDRESS(address))
return false;
if (debug_debugger_running())
return debug_strlcpy(buffer, address, bufferSize) >= 0;
if (debug_debugger_running()) {
return debug_strlcpy(B_CURRENT_TEAM, buffer, address, bufferSize)
>= 0;
}
return user_strlcpy(buffer, address, bufferSize) >= 0;
}
@@ -1491,7 +1493,7 @@ UserSymbolLookup::_Read(const T* address, T& data)
return false;
if (debug_debugger_running())
return debug_memcpy(&data, address, sizeof(T)) == B_OK;
return debug_memcpy(B_CURRENT_TEAM, &data, address, sizeof(T)) == B_OK;
return user_memcpy(&data, address, sizeof(T)) == B_OK;
}
+95 -6
View File
@@ -2640,9 +2640,11 @@ display_mem(int argc, char** argv)
// string mode
for (i = 0; true; i++) {
char c;
if (debug_memcpy(&c, (char*)copyAddress + i, 1) != B_OK
|| c == '\0')
if (debug_memcpy(B_CURRENT_TEAM, &c, (char*)copyAddress + i, 1)
!= B_OK
|| c == '\0') {
break;
}
if (c == '\n')
kprintf("\\n");
@@ -2671,8 +2673,8 @@ display_mem(int argc, char** argv)
for (j = 0; j < displayed; j++) {
char c;
if (debug_memcpy(&c, (char*)copyAddress + i * itemSize + j,
1) != B_OK) {
if (debug_memcpy(B_CURRENT_TEAM, &c,
(char*)copyAddress + i * itemSize + j, 1) != B_OK) {
displayed = j;
break;
}
@@ -2689,8 +2691,8 @@ display_mem(int argc, char** argv)
kprintf(" ");
}
if (debug_memcpy(&value, (uint8*)copyAddress + i * itemSize,
itemSize) != B_OK) {
if (debug_memcpy(B_CURRENT_TEAM, &value,
(uint8*)copyAddress + i * itemSize, itemSize) != B_OK) {
kprintf("read fault");
break;
}
@@ -4658,6 +4660,93 @@ vm_memcpy_physical_page(addr_t to, addr_t from)
}
/*! Copies a range of memory directly from/to a page that might not be mapped
at the moment.
For \a unsafeMemory the current mapping (if any is ignored). The function
walks through the respective area's cache chain to find the physical page
and copies from/to it directly.
The memory range starting at \a unsafeMemory with a length of \a size bytes
must not cross a page boundary.
\param teamID The team ID identifying the address space \a unsafeMemory is
to be interpreted in. Ignored, if \a unsafeMemory is a kernel address
(the kernel address space is assumed in this case). If \c B_CURRENT_TEAM
is passed, the address space of the thread returned by
debug_get_debugged_thread() is used.
\param unsafeMemory The start of the unsafe memory range to be copied
from/to.
\param buffer A safely accessible kernel buffer to be copied from/to.
\param size The number of bytes to be copied.
\param copyToUnsafe If \c true, memory is copied from \a buffer to
\a unsafeMemory, the other way around otherwise.
*/
status_t
vm_debug_copy_page_memory(team_id teamID, void* unsafeMemory, void* buffer,
size_t size, bool copyToUnsafe)
{
if (size > B_PAGE_SIZE
|| ((addr_t)unsafeMemory + size) % B_PAGE_SIZE < size) {
return B_BAD_VALUE;
}
// get the address space for the debugged thread
VMAddressSpace* addressSpace;
if (IS_KERNEL_ADDRESS(unsafeMemory)) {
addressSpace = VMAddressSpace::Kernel();
} else if (teamID == B_CURRENT_TEAM) {
struct thread* thread = debug_get_debugged_thread();
if (thread == NULL || thread->team == NULL)
return B_BAD_ADDRESS;
addressSpace = thread->team->address_space;
} else
addressSpace = VMAddressSpace::DebugGet(teamID);
if (addressSpace == NULL)
return B_BAD_ADDRESS;
// get the area
VMArea* area = addressSpace->LookupArea((addr_t)unsafeMemory);
if (area == NULL)
return B_BAD_ADDRESS;
// search the page
off_t cacheOffset = (addr_t)unsafeMemory - area->Base()
+ area->cache_offset;
VMCache* cache = area->cache;
vm_page* page = NULL;
while (cache != NULL) {
page = cache->DebugLookupPage(cacheOffset);
if (page != NULL)
break;
// Page not found in this cache -- if it is paged out, we must not try
// to get it from lower caches.
if (cache->DebugHasPage(cacheOffset))
break;
cache = cache->source;
}
if (page == NULL)
return B_UNSUPPORTED;
// copy from/to physical memory
addr_t physicalAddress = page->physical_page_number * B_PAGE_SIZE
+ (addr_t)unsafeMemory % B_PAGE_SIZE;
if (copyToUnsafe) {
if (page->Cache() != area->cache)
return B_UNSUPPORTED;
return vm_memcpy_to_physical(physicalAddress, buffer, size, false);
}
return vm_memcpy_from_physical(buffer, physicalAddress, size, false);
}
// #pragma mark - kernel public API