Ads Top

Create your own debug server in QEMU

In QEMU, it support gdb and it has built-in a gdb server, so if you know the gdb protocol, you can send some gdb commands to qemu and control it. But gd protocol is far from perfect, so there will be a reason for you to create your own debug server. In this article i will show you how to create a minimal debug server, receive command from tcp.

I will use qemu-kvm, not qemu in this article, but I roughly looked at the qemu source, it will be pretty same process than qemu-kvm.

I use eclipse+CDT to debug the qemu source, but i don't use its built-in compile, i will use the traditional Makefile instead.


Here we go:

All the variables i use started with peter, so you can search "peter" in the source code to see how many places i have changed.

Step 1) Qemu start in vl.c, line 2041, we need to add one identifier to the enum, just call it "DEV_PETER"


struct device_config {
    enum {
        DEV_USB,       /* -usbdevice     */
        DEV_BT,        /* -bt            */
        DEV_SERIAL,    /* -serial        */
        DEV_PARALLEL,  /* -parallel      */
        DEV_VIRTCON,   /* -virtioconsole */
        DEV_DEBUGCON,  /* -debugcon */
        DEV_GDB,       /* -gdb, -s */
        DEV_PETER   /* peter */
    } type;
    const char *cmdline;
    Location loc;
    QTAILQ_ENTRY(device_config) next;
};

Step 2) In line 2793, you will see a "switch loop", it used to parse the command line, because I want to specific a port for my debug server, "-peter 1234", so I have to save the command line parameter


            case QEMU_OPTION_s:
                add_device_config(DEV_GDB, "tcp::" DEFAULT_GDBSTUB_PORT);
                break;
            case QEMU_OPTION_gdb:
                add_device_config(DEV_GDB, optarg);
                break;
            case QEMU_OPTION_peter:
                peter_cmdline=optarg;
                break;
            case QEMU_OPTION_L:
                data_dir = optarg;
                break;

We have to modify the qemu-options.hx, so that the command-line-argument-parser can parse our "-peter 1234" from the command line. We add these to the line 2390:



DEF("peter", HAS_ARG, QEMU_OPTION_peter, \
    "-peter dev        wait for peter connection on 'dev'\n", QEMU_ARCH_ALL)
STEXI
@findex -peter
Wait for peter 
@example
(peter) target remote | exec qemu-system-i386 -peter stdio ...
@end example
ETEXI

Step 3) In the main(), we start our debug server, in line 3788:



    if (foreach_device_config(DEV_GDB, gdbserver_start) < 0) {
        exit(1);
    }

    if (peter_start(peter_cmdline)>0){
    fprintf(stderr, "start peter error\n");
    exit(0);
    }

    qdev_machine_creation_done();

    if (rom_load_all() != 0) {
        fprintf(stderr, "rom loading failed\n");
        exit(1);
    }

Step 4) Create our debug server in qemu source, just put peterstub.h and peterstub.h in the topmost directory (same as the gdbserver.c):

peterstub.c:

#include "config.h"
#include "qemu-common.h"
#ifdef CONFIG_USER_ONLY
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>

#include "qemu.h"
#else
#include "monitor.h"
#include "qemu-char.h"
#include "sysemu.h"
#include "gdbstub.h"
#endif

#include "peterstub.h"

static int peter_chr_can_receive(void *opaque) {
printf("peter_chr_can_receive\n");
return 4096;
}

static void peter_chr_receive(void *opaque, const uint8_t *buf, int size) {
printf("gdb_chr_receive, size=%d, buf=%s\n", size, buf);
}

static void peter_chr_event(void *opaque, int event) {
printf("gdb_chr_event, event=%d\n", event);
}

int peter_start(const char *device) {
char gdbstub_device_name[128];
printf("peter_start, cmdline=%s\n", device);
CharDriverState *chr = NULL;
if (strcmp(device, "none") != 0) {
if (strstart(device, "tcp:", NULL)) {
/* enforce required TCP attributes */
snprintf(gdbstub_device_name, sizeof(gdbstub_device_name),
"%s,nowait,nodelay,server", device);
device = gdbstub_device_name;
}
}

chr = qemu_chr_new("peter", device, NULL);
if (!chr)
return -1;
qemu_chr_add_handlers(chr, peter_chr_can_receive, peter_chr_receive,
peter_chr_event, NULL);
return 0;
}

peterstub.h:

#ifndef PETERSTUB_H_
#define PETERSTUB_H_

int peter_start(const char *device);

#endif /* PETERSTUB_H_ */

Step 5) add peterstub.o to Makefile.target


#########################################################
# System emulator target
ifdef CONFIG_SOFTMMU
CONFIG_NO_PCI = $(if $(subst n,,$(CONFIG_PCI)),n,y)
CONFIG_NO_KVM = $(if $(subst n,,$(CONFIG_KVM)),n,y)
CONFIG_NO_XEN = $(if $(subst n,,$(CONFIG_XEN)),n,y)
CONFIG_NO_GET_MEMORY_MAPPING = $(if $(subst n,,$(CONFIG_HAVE_GET_MEMORY_MAPPING)),n,y)
CONFIG_NO_CORE_DUMP = $(if $(subst n,,$(CONFIG_HAVE_CORE_DUMP)),n,y)

obj-y += arch_init.o cpus.o monitor.o gdbstub.o peterstub.o balloon.o ioport.o
obj-y += hw/
obj-$(CONFIG_KVM) += kvm-all.o
obj-$(CONFIG_NO_KVM) += kvm-stub.o
obj-y += memory.o savevm.o cputlb.o
obj-$(CONFIG_HAVE_GET_MEMORY_MAPPING) += memory_mapping.o
obj-$(CONFIG_HAVE_CORE_DUMP) += dump.o
obj-$(CONFIG_NO_GET_MEMORY_MAPPING) += memory_mapping-stub.o
obj-$(CONFIG_NO_CORE_DUMP) += dump-stub.o
LIBS+=-lz

Step 6) Final step, you can set the qemu by specific the command line parameter "-peter 1234"



After the qemu is started, you can use linux command "echo "petercheung" | nc localhost 1234" to the port 1234, your debug server should be running and able to get that.







沒有留言:

技術提供:Blogger.