From c3676b54bfd2e06b73646d1846b2ab0272cb96e2 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Tue, 13 Apr 2010 17:40:15 +0000 Subject: [PATCH] * 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 --- headers/private/kernel/debug.h | 32 ++++- headers/private/kernel/vm/vm.h | 3 + src/system/kernel/arch/m68k/arch_debug.cpp | 6 +- src/system/kernel/arch/ppc/arch_debug.cpp | 6 +- src/system/kernel/arch/x86/arch_debug.cpp | 146 +++++++++++++------- src/system/kernel/debug/debug.cpp | 148 ++++++++++++++++++--- src/system/kernel/debug/debug_parser.cpp | 6 +- src/system/kernel/debug/gdb.cpp | 5 +- src/system/kernel/elf.cpp | 10 +- src/system/kernel/vm/vm.cpp | 101 +++++++++++++- 10 files changed, 377 insertions(+), 86 deletions(-) diff --git a/headers/private/kernel/debug.h b/headers/private/kernel/debug.h index 4ef6f583af..b0c28bed93 100644 --- a/headers/private/kernel/debug.h +++ b/headers/private/kernel/debug.h @@ -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 */ diff --git a/headers/private/kernel/vm/vm.h b/headers/private/kernel/vm/vm.h index fc27f6ce43..c8b8eb90b2 100644 --- a/headers/private/kernel/vm/vm.h +++ b/headers/private/kernel/vm/vm.h @@ -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); diff --git a/src/system/kernel/arch/m68k/arch_debug.cpp b/src/system/kernel/arch/m68k/arch_debug.cpp index 0d9dd6b292..00d1ef3e47 100644 --- a/src/system/kernel/arch/m68k/arch_debug.cpp +++ b/src/system/kernel/arch/m68k/arch_debug.cpp @@ -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; diff --git a/src/system/kernel/arch/ppc/arch_debug.cpp b/src/system/kernel/arch/ppc/arch_debug.cpp index 9acbf22d7e..8da8ae62b1 100644 --- a/src/system/kernel/arch/ppc/arch_debug.cpp +++ b/src/system/kernel/arch/ppc/arch_debug.cpp @@ -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; diff --git a/src/system/kernel/arch/x86/arch_debug.cpp b/src/system/kernel/arch/x86/arch_debug.cpp index 7f2568be42..e52f488367 100644 --- a/src/system/kernel/arch/x86/arch_debug.cpp +++ b/src/system/kernel/arch/x86/arch_debug.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2009-2010, Ingo Weinhold, ingo_weinhold@gmx.de. * Copyright 2002-2008, Axel Dörfler, axeld@pinc-software.de. * 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 +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(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(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(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(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(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(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(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(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(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(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(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\"\"\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(); diff --git a/src/system/kernel/debug/debug.cpp b/src/system/kernel/debug/debug.cpp index 61f03ca409..62d0845fd2 100644 --- a/src/system/kernel/debug/debug.cpp +++ b/src/system/kernel/debug/debug.cpp @@ -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, ¶meters) != 0) { - return B_BAD_ADDRESS; + if (debug_call_with_fault_handler(gCPU[sDebuggerOnCPU].fault_jump_buffer, + &debug_memcpy_trampoline, ¶meters) == 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, ¶meters) != 0) { - return B_BAD_ADDRESS; + if (debug_call_with_fault_handler( + gCPU[sDebuggerOnCPU].fault_jump_buffer, + &debug_strlcpy_trampoline, ¶meters) == 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 diff --git a/src/system/kernel/debug/debug_parser.cpp b/src/system/kernel/debug/debug_parser.cpp index 13de89a58c..08cdee8d45 100644 --- a/src/system/kernel/debug/debug_parser.cpp +++ b/src/system/kernel/debug/debug_parser.cpp @@ -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); diff --git a/src/system/kernel/debug/gdb.cpp b/src/system/kernel/debug/gdb.cpp index c2084cb9b2..72b165e910 100644 --- a/src/system/kernel/debug/gdb.cpp +++ b/src/system/kernel/debug/gdb.cpp @@ -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; diff --git a/src/system/kernel/elf.cpp b/src/system/kernel/elf.cpp index 13cbb75435..a9605d0401 100644 --- a/src/system/kernel/elf.cpp +++ b/src/system/kernel/elf.cpp @@ -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; } diff --git a/src/system/kernel/vm/vm.cpp b/src/system/kernel/vm/vm.cpp index f36c1e7663..5b33ac69b9 100644 --- a/src/system/kernel/vm/vm.cpp +++ b/src/system/kernel/vm/vm.cpp @@ -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