genode/ports/src/app/gdb_monitor/gdbserver/genode-low.cc
2013-01-10 21:44:47 +01:00

346 lines
6.9 KiB
C++

/*
* \brief Genode backend for GDBServer (C++)
* \author Christian Prochaska
* \author Norman Feske
* \date 2011-05-06
*/
/*
* Copyright (C) 2011-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
extern "C" {
#define private _private
#include "genode-low.h"
#include "linux-low.h"
#define _private private
int linux_detach_one_lwp (struct inferior_list_entry *entry, void *args);
}
#include <base/printf.h>
#include <dataspace/client.h>
#include "cpu_session_component.h"
#include "gdb_stub_thread.h"
static bool verbose = false;
using namespace Genode;
using namespace Gdb_monitor;
static Lock &main_thread_ready_lock()
{
static Lock _main_thread_ready_lock(Lock::LOCKED);
return _main_thread_ready_lock;
}
static Lock &gdbserver_ready_lock()
{
static Lock _gdbserver_ready_lock(Lock::LOCKED);
return _gdbserver_ready_lock;
}
Gdb_stub_thread *gdb_stub_thread()
{
return (Gdb_stub_thread*)(current_process()->_private->gdb_stub_thread);
}
extern "C" int genode_signal_fd()
{
return gdb_stub_thread()->signal_fd();
}
void genode_add_thread(unsigned long lwpid)
{
if (lwpid == GENODE_LWP_BASE) {
main_thread_ready_lock().unlock();
} else {
if (lwpid == GENODE_LWP_BASE + 1) {
/* make sure gdbserver is ready to attach new threads */
gdbserver_ready_lock().lock();
}
linux_attach_lwp(lwpid);
}
}
void genode_remove_thread(unsigned long lwpid)
{
int pid = GENODE_LWP_BASE;
linux_detach_one_lwp((struct inferior_list_entry *)
find_thread_ptid(ptid_build(GENODE_LWP_BASE, lwpid, 0)), &pid);
}
void genode_wait_for_target_main_thread()
{
/* gdbserver is now ready to attach new threads */
gdbserver_ready_lock().unlock();
/* wait until the target's main thread has been created */
main_thread_ready_lock().lock();
}
extern "C" void genode_detect_all_threads()
{
Cpu_session_component *csc = gdb_stub_thread()->cpu_session_component();
Thread_capability thread_cap = csc->next(csc->first()); /* second thread */
while (thread_cap.valid()) {
linux_attach_lwp(csc->lwpid(thread_cap));
thread_cap = csc->next(thread_cap);
}
}
extern "C" void genode_stop_all_threads()
{
Cpu_session_component *csc = gdb_stub_thread()->cpu_session_component();
Thread_capability thread_cap = csc->first();
while (thread_cap.valid()) {
csc->pause(thread_cap);
thread_cap = csc->next(thread_cap);
}
}
extern "C" void genode_resume_all_threads()
{
Cpu_session_component *csc = gdb_stub_thread()->cpu_session_component();
Thread_capability thread_cap = csc->first();
while (thread_cap.valid()) {
csc->resume(thread_cap);
thread_cap = csc->next(thread_cap);
}
}
int genode_detach(int pid)
{
find_inferior (&all_threads, linux_detach_one_lwp, &pid);
genode_resume_all_threads();
return 0;
}
int genode_kill(int pid)
{
/* TODO */
if (verbose) PDBG("genode_kill() called - not implemented\n");
return genode_detach(pid);
}
void genode_interrupt_thread(unsigned long lwpid)
{
Cpu_session_component *csc = gdb_stub_thread()->cpu_session_component();
Thread_capability thread_cap = csc->thread_cap(lwpid);
if (!thread_cap.valid()) {
PERR("could not find thread capability for lwpid %lu", lwpid);
return;
}
csc->pause(thread_cap);
}
void genode_continue_thread(unsigned long lwpid, int single_step)
{
Cpu_session_component *csc = gdb_stub_thread()->cpu_session_component();
Thread_capability thread_cap = csc->thread_cap(lwpid);
if (!thread_cap.valid()) {
PERR("could not find thread capability for lwpid %lu", lwpid);
return;
}
csc->single_step(thread_cap, single_step);
csc->resume(thread_cap);
}
class Memory_model
{
private:
Lock _lock;
Rm_session_component * const _address_space;
/**
* Representation of a currently mapped region
*/
struct Mapped_region
{
Rm_session_component::Region *_region;
unsigned char *_local_base;
Mapped_region() : _region(0), _local_base(0) { }
bool valid() { return _region != 0; }
bool is_loaded(Rm_session_component::Region const * region)
{
return _region == region;
}
void flush()
{
if (!valid()) return;
env()->rm_session()->detach(_local_base);
_local_base = 0;
_region = 0;
}
void load(Rm_session_component::Region *region)
{
if (region == _region)
return;
if (!region || valid())
flush();
if (!region)
return;
try {
_region = region;
_local_base = env()->rm_session()->attach(_region->ds_cap(),
0, _region->offset());
} catch (Rm_session::Attach_failed) {
flush();
PERR("Memory_model: RM attach failed");
}
}
unsigned char *local_base() { return _local_base; }
};
enum { NUM_MAPPED_REGIONS = 1 };
Mapped_region _mapped_region[NUM_MAPPED_REGIONS];
unsigned _evict_idx;
/**
* Return local address of mapped region
*
* The function returns 0 if the mapping fails
*/
unsigned char *_update_curr_region(Rm_session_component::Region *region)
{
for (unsigned i = 0; i < NUM_MAPPED_REGIONS; i++) {
if (_mapped_region[i].is_loaded(region))
return _mapped_region[i].local_base();
}
/* flush one currently mapped region */
_evict_idx++;
if (_evict_idx == NUM_MAPPED_REGIONS)
_evict_idx = 0;
_mapped_region[_evict_idx].load(region);
return _mapped_region[_evict_idx].local_base();
}
public:
Memory_model(Rm_session_component *address_space)
:
_address_space(address_space), _evict_idx(0)
{ }
unsigned char read(void *addr)
{
Lock::Guard guard(_lock);
addr_t offset_in_region = 0;
Rm_session_component::Region *region =
_address_space->find_region(addr, &offset_in_region);
unsigned char *local_base = _update_curr_region(region);
if (!local_base) {
PWRN("Memory model: no memory at address %p", addr);
return 0;
}
unsigned char value =
local_base[offset_in_region];
if (verbose)
Genode::printf("read addr=%p, value=%x\n", addr, value);
return value;
}
void write(void *addr, unsigned char value)
{
if (verbose)
Genode::printf("write addr=%p, value=%x\n", addr, value);
Lock::Guard guard(_lock);
addr_t offset_in_region = 0;
Rm_session_component::Region *region =
_address_space->find_region(addr, &offset_in_region);
unsigned char *local_base = _update_curr_region(region);
if (!local_base) {
PWRN("Memory model: no memory at address %p", addr);
PWRN("(attempted to write %x)", (int)value);
return;
}
local_base[offset_in_region] = value;
}
};
/**
* Return singleton instance of memory model
*/
static Memory_model *memory_model()
{
static Memory_model inst(gdb_stub_thread()->rm_session_component());
return &inst;
}
unsigned char genode_read_memory_byte(void *addr)
{
return memory_model()->read(addr);
}
void genode_write_memory_byte(void *addr, unsigned char value)
{
return memory_model()->write(addr, value);
}