NJU-ProjectN/nemu ics2023 initialized
NJU-ProjectN/nemu eb63cf3568dbf4e0c3c6ef462e6ec685550fabbc Merge pull request #76 from rijuyuezhu/master
This commit is contained in:
parent
1efe03efb9
commit
2824efad33
141 changed files with 19573 additions and 0 deletions
23
nemu/tools/qemu-diff/Makefile
Normal file
23
nemu/tools/qemu-diff/Makefile
Normal file
|
@ -0,0 +1,23 @@
|
|||
#***************************************************************************************
|
||||
# Copyright (c) 2014-2022 Zihao Yu, Nanjing University
|
||||
#
|
||||
# NEMU is licensed under Mulan PSL v2.
|
||||
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
# You may obtain a copy of Mulan PSL v2 at:
|
||||
# http://license.coscl.org.cn/MulanPSL2
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
# See the Mulan PSL v2 for more details.
|
||||
#**************************************************************************************/
|
||||
|
||||
NAME = $(GUEST_ISA)-qemu
|
||||
SRCS = $(shell find src/ -name "*.c")
|
||||
|
||||
SHARE = 1
|
||||
CFLAGS += -DNEMU_HOME=\"$(NEMU_HOME)\" -DCONFIG_ISA_$(GUEST_ISA)
|
||||
INC_PATH += $(NEMU_HOME)/include
|
||||
|
||||
include $(NEMU_HOME)/scripts/build.mk
|
32
nemu/tools/qemu-diff/include/common.h
Normal file
32
nemu/tools/qemu-diff/include/common.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/***************************************************************************************
|
||||
* Copyright (c) 2014-2022 Zihao Yu, Nanjing University
|
||||
*
|
||||
* NEMU is licensed under Mulan PSL v2.
|
||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
* You may obtain a copy of Mulan PSL v2 at:
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* See the Mulan PSL v2 for more details.
|
||||
***************************************************************************************/
|
||||
|
||||
#ifndef __COMMON_H__
|
||||
#define __COMMON_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef uint32_t paddr_t;
|
||||
|
||||
#include "isa.h"
|
||||
#include "protocol.h"
|
||||
|
||||
#endif
|
59
nemu/tools/qemu-diff/include/isa.h
Normal file
59
nemu/tools/qemu-diff/include/isa.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
/***************************************************************************************
|
||||
* Copyright (c) 2014-2022 Zihao Yu, Nanjing University
|
||||
*
|
||||
* NEMU is licensed under Mulan PSL v2.
|
||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
* You may obtain a copy of Mulan PSL v2 at:
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* See the Mulan PSL v2 for more details.
|
||||
***************************************************************************************/
|
||||
|
||||
#ifndef __ISA_H__
|
||||
#define __ISA_H__
|
||||
|
||||
#if defined(CONFIG_ISA_mips32)
|
||||
#define ISA_QEMU_BIN "qemu-system-mipsel"
|
||||
#define ISA_QEMU_ARGS "-machine", "mipssim",\
|
||||
"-kernel", NEMU_HOME "/resource/mips-elf/mips.dummy",
|
||||
#elif defined(CONFIG_ISA_riscv) && !defined(CONFIG_RV64)
|
||||
#define ISA_QEMU_BIN "qemu-system-riscv32"
|
||||
#define ISA_QEMU_ARGS "-bios", "none",
|
||||
#elif defined(CONFIG_ISA_riscv) && defined(CONFIG_RV64)
|
||||
#define ISA_QEMU_BIN "qemu-system-riscv64"
|
||||
#define ISA_QEMU_ARGS
|
||||
#elif defined(CONFIG_ISA_x86)
|
||||
#define ISA_QEMU_BIN "qemu-system-i386"
|
||||
#define ISA_QEMU_ARGS
|
||||
#else
|
||||
#error Unsupport ISA
|
||||
#endif
|
||||
|
||||
union isa_gdb_regs {
|
||||
struct {
|
||||
#if defined(CONFIG_ISA_mips32)
|
||||
uint32_t gpr[32];
|
||||
uint32_t status, lo, hi, badvaddr, cause, pc;
|
||||
#elif defined(CONFIG_ISA_riscv) && !defined(CONFIG_RV64)
|
||||
uint32_t gpr[32];
|
||||
uint32_t pc;
|
||||
#elif defined(CONFIG_ISA_riscv) && defined(CONFIG_RV64)
|
||||
uint64_t gpr[32];
|
||||
uint64_t fpr[32];
|
||||
uint64_t pc;
|
||||
#elif defined(CONFIG_ISA_x86)
|
||||
uint32_t eax, ecx, edx, ebx, esp, ebp, esi, edi;
|
||||
uint32_t eip, eflags;
|
||||
uint32_t cs, ss, ds, es, fs, gs;
|
||||
#endif
|
||||
};
|
||||
struct {
|
||||
uint32_t array[77];
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
37
nemu/tools/qemu-diff/include/protocol.h
Normal file
37
nemu/tools/qemu-diff/include/protocol.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/* Simple interface of a GDB remote protocol client.
|
||||
* Copyright (C) 2015 Red Hat Inc.
|
||||
*
|
||||
* This file is part of gdb-toys.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 3 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct gdb_conn;
|
||||
|
||||
uint16_t gdb_decode_hex(uint8_t msb, uint8_t lsb);
|
||||
uint64_t gdb_decode_hex_str(uint8_t *bytes);
|
||||
|
||||
uint8_t hex_encode(uint8_t digit);
|
||||
|
||||
struct gdb_conn *gdb_begin_inet(const char *addr, uint16_t port);
|
||||
|
||||
void gdb_end(struct gdb_conn *conn);
|
||||
|
||||
void gdb_send(struct gdb_conn *conn, const uint8_t *command, size_t size);
|
||||
|
||||
uint8_t *gdb_recv(struct gdb_conn *conn, size_t *size);
|
||||
|
||||
const char * gdb_start_noack(struct gdb_conn *conn);
|
99
nemu/tools/qemu-diff/src/diff-test.c
Normal file
99
nemu/tools/qemu-diff/src/diff-test.c
Normal file
|
@ -0,0 +1,99 @@
|
|||
/***************************************************************************************
|
||||
* Copyright (c) 2014-2022 Zihao Yu, Nanjing University
|
||||
*
|
||||
* NEMU is licensed under Mulan PSL v2.
|
||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
* You may obtain a copy of Mulan PSL v2 at:
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* See the Mulan PSL v2 for more details.
|
||||
***************************************************************************************/
|
||||
|
||||
#include "common.h"
|
||||
#include <difftest-def.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <signal.h>
|
||||
|
||||
bool gdb_connect_qemu(int);
|
||||
bool gdb_memcpy_to_qemu(uint32_t, void *, int);
|
||||
bool gdb_getregs(union isa_gdb_regs *);
|
||||
bool gdb_setregs(union isa_gdb_regs *);
|
||||
bool gdb_si();
|
||||
void gdb_exit();
|
||||
|
||||
void init_isa();
|
||||
|
||||
__EXPORT void difftest_memcpy(paddr_t addr, void *buf, size_t n, bool direction) {
|
||||
assert(direction == DIFFTEST_TO_REF);
|
||||
if (direction == DIFFTEST_TO_REF) {
|
||||
bool ok = gdb_memcpy_to_qemu(addr, buf, n);
|
||||
assert(ok == 1);
|
||||
}
|
||||
}
|
||||
|
||||
__EXPORT void difftest_regcpy(void *dut, bool direction) {
|
||||
union isa_gdb_regs qemu_r;
|
||||
gdb_getregs(&qemu_r);
|
||||
if (direction == DIFFTEST_TO_REF) {
|
||||
memcpy(&qemu_r, dut, DIFFTEST_REG_SIZE);
|
||||
gdb_setregs(&qemu_r);
|
||||
} else {
|
||||
memcpy(dut, &qemu_r, DIFFTEST_REG_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
__EXPORT void difftest_exec(uint64_t n) {
|
||||
while (n --) gdb_si();
|
||||
}
|
||||
|
||||
__EXPORT void difftest_init(int port) {
|
||||
char buf[32];
|
||||
sprintf(buf, "tcp::%d", port);
|
||||
|
||||
int ppid_before_fork = getpid();
|
||||
int pid = fork();
|
||||
if (pid == -1) {
|
||||
perror("fork");
|
||||
assert(0);
|
||||
}
|
||||
else if (pid == 0) {
|
||||
// child
|
||||
|
||||
// install a parent death signal in the chlid
|
||||
int r = prctl(PR_SET_PDEATHSIG, SIGTERM);
|
||||
if (r == -1) {
|
||||
perror("prctl error");
|
||||
assert(0);
|
||||
}
|
||||
|
||||
if (getppid() != ppid_before_fork) {
|
||||
printf("parent has died!\n");
|
||||
assert(0);
|
||||
}
|
||||
|
||||
close(STDIN_FILENO);
|
||||
execlp(ISA_QEMU_BIN, ISA_QEMU_BIN, ISA_QEMU_ARGS "-S", "-gdb", buf, "-nographic",
|
||||
"-serial", "none", "-monitor", "none", NULL);
|
||||
perror("exec");
|
||||
assert(0);
|
||||
}
|
||||
else {
|
||||
// father
|
||||
|
||||
gdb_connect_qemu(port);
|
||||
printf("Connect to QEMU with %s successfully\n", buf);
|
||||
|
||||
atexit(gdb_exit);
|
||||
|
||||
init_isa();
|
||||
}
|
||||
}
|
||||
|
||||
__EXPORT void difftest_raise_intr(uint64_t NO) {
|
||||
printf("raise_intr is not supported\n");
|
||||
assert(0);
|
||||
}
|
118
nemu/tools/qemu-diff/src/gdb-host.c
Normal file
118
nemu/tools/qemu-diff/src/gdb-host.c
Normal file
|
@ -0,0 +1,118 @@
|
|||
/***************************************************************************************
|
||||
* Copyright (c) 2014-2022 Zihao Yu, Nanjing University
|
||||
*
|
||||
* NEMU is licensed under Mulan PSL v2.
|
||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
* You may obtain a copy of Mulan PSL v2 at:
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* See the Mulan PSL v2 for more details.
|
||||
***************************************************************************************/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
static struct gdb_conn *conn;
|
||||
|
||||
bool gdb_connect_qemu(int port) {
|
||||
// connect to gdbserver on localhost port 1234
|
||||
while ((conn = gdb_begin_inet("127.0.0.1", port)) == NULL) {
|
||||
usleep(1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gdb_memcpy_to_qemu_small(uint32_t dest, void *src, int len) {
|
||||
char *buf = malloc(len * 2 + 128);
|
||||
assert(buf != NULL);
|
||||
int p = sprintf(buf, "M0x%x,%x:", dest, len);
|
||||
int i;
|
||||
for (i = 0; i < len; i ++) {
|
||||
p += sprintf(buf + p, "%c%c", hex_encode(((uint8_t *)src)[i] >> 4), hex_encode(((uint8_t *)src)[i] & 0xf));
|
||||
}
|
||||
|
||||
gdb_send(conn, (const uint8_t *)buf, strlen(buf));
|
||||
free(buf);
|
||||
|
||||
size_t size;
|
||||
uint8_t *reply = gdb_recv(conn, &size);
|
||||
bool ok = !strcmp((const char*)reply, "OK");
|
||||
free(reply);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool gdb_memcpy_to_qemu(uint32_t dest, void *src, int len) {
|
||||
const int mtu = 1500;
|
||||
bool ok = true;
|
||||
while (len > mtu) {
|
||||
ok &= gdb_memcpy_to_qemu_small(dest, src, mtu);
|
||||
dest += mtu;
|
||||
src += mtu;
|
||||
len -= mtu;
|
||||
}
|
||||
ok &= gdb_memcpy_to_qemu_small(dest, src, len);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool gdb_getregs(union isa_gdb_regs *r) {
|
||||
gdb_send(conn, (const uint8_t *)"g", 1);
|
||||
size_t size;
|
||||
uint8_t *reply = gdb_recv(conn, &size);
|
||||
|
||||
int i;
|
||||
uint8_t *p = reply;
|
||||
uint8_t c;
|
||||
for (i = 0; i < sizeof(union isa_gdb_regs) / sizeof(uint32_t); i ++) {
|
||||
c = p[8];
|
||||
p[8] = '\0';
|
||||
r->array[i] = gdb_decode_hex_str(p);
|
||||
p[8] = c;
|
||||
p += 8;
|
||||
}
|
||||
|
||||
free(reply);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gdb_setregs(union isa_gdb_regs *r) {
|
||||
int len = sizeof(union isa_gdb_regs);
|
||||
char *buf = malloc(len * 2 + 128);
|
||||
assert(buf != NULL);
|
||||
buf[0] = 'G';
|
||||
|
||||
void *src = r;
|
||||
int p = 1;
|
||||
int i;
|
||||
for (i = 0; i < len; i ++) {
|
||||
p += sprintf(buf + p, "%c%c", hex_encode(((uint8_t *)src)[i] >> 4), hex_encode(((uint8_t *)src)[i] & 0xf));
|
||||
}
|
||||
|
||||
gdb_send(conn, (const uint8_t *)buf, strlen(buf));
|
||||
free(buf);
|
||||
|
||||
size_t size;
|
||||
uint8_t *reply = gdb_recv(conn, &size);
|
||||
bool ok = !strcmp((const char*)reply, "OK");
|
||||
free(reply);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool gdb_si() {
|
||||
char buf[] = "vCont;s:1";
|
||||
gdb_send(conn, (const uint8_t *)buf, strlen(buf));
|
||||
size_t size;
|
||||
uint8_t *reply = gdb_recv(conn, &size);
|
||||
free(reply);
|
||||
return true;
|
||||
}
|
||||
|
||||
void gdb_exit() {
|
||||
gdb_end(conn);
|
||||
}
|
78
nemu/tools/qemu-diff/src/isa.c
Normal file
78
nemu/tools/qemu-diff/src/isa.c
Normal file
|
@ -0,0 +1,78 @@
|
|||
/***************************************************************************************
|
||||
* Copyright (c) 2014-2022 Zihao Yu, Nanjing University
|
||||
*
|
||||
* NEMU is licensed under Mulan PSL v2.
|
||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
* You may obtain a copy of Mulan PSL v2 at:
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* See the Mulan PSL v2 for more details.
|
||||
***************************************************************************************/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#if defined(CONFIG_ISA_x86)
|
||||
|
||||
bool gdb_memcpy_to_qemu(uint32_t, void *, int);
|
||||
bool gdb_getregs(union isa_gdb_regs *);
|
||||
bool gdb_setregs(union isa_gdb_regs *);
|
||||
void difftest_exec(uint64_t n);
|
||||
|
||||
static uint8_t mbr[] = {
|
||||
// start16:
|
||||
0xfa, // cli
|
||||
0x31, 0xc0, // xorw %ax,%ax
|
||||
0x8e, 0xd8, // movw %ax,%ds
|
||||
0x8e, 0xc0, // movw %ax,%es
|
||||
0x8e, 0xd0, // movw %ax,%ss
|
||||
0x0f, 0x01, 0x16, 0x44, 0x7c, // lgdt gdtdesc
|
||||
0x0f, 0x20, 0xc0, // movl %cr0,%eax
|
||||
0x66, 0x83, 0xc8, 0x01, // orl $CR0_PE,%eax
|
||||
0x0f, 0x22, 0xc0, // movl %eax,%cr0
|
||||
0xea, 0x1d, 0x7c, 0x08, 0x00, // ljmp $GDT_ENTRY(1),$start32
|
||||
|
||||
// start32:
|
||||
0x66, 0xb8, 0x10, 0x00, // movw $0x10,%ax
|
||||
0x8e, 0xd8, // movw %ax, %ds
|
||||
0x8e, 0xc0, // movw %ax, %es
|
||||
0x8e, 0xd0, // movw %ax, %ss
|
||||
0xeb, 0xfe, // jmp 7c27
|
||||
0x8d, 0x76, 0x00, // lea 0x0(%esi),%esi
|
||||
|
||||
// GDT
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0xff, 0x00, 0x00, 0x00, 0x9a, 0xcf, 0x00,
|
||||
0xff, 0xff, 0x00, 0x00, 0x00, 0x92, 0xcf, 0x00,
|
||||
|
||||
// GDT descriptor
|
||||
0x17, 0x00, 0x2c, 0x7c, 0x00, 0x00
|
||||
};
|
||||
|
||||
void init_isa() {
|
||||
// put the MBR code to QEMU to enable protected mode
|
||||
bool ok = gdb_memcpy_to_qemu(0x7c00, mbr, sizeof(mbr));
|
||||
assert(ok == 1);
|
||||
|
||||
union isa_gdb_regs r;
|
||||
gdb_getregs(&r);
|
||||
|
||||
// set cs:eip to 0000:7c00
|
||||
r.eip = 0x7c00;
|
||||
r.cs = 0x0000;
|
||||
ok = gdb_setregs(&r);
|
||||
assert(ok == 1);
|
||||
|
||||
// execute enough instructions to enter protected mode
|
||||
difftest_exec(20);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void init_isa() {
|
||||
}
|
||||
|
||||
#endif
|
305
nemu/tools/qemu-diff/src/protocol.c
Normal file
305
nemu/tools/qemu-diff/src/protocol.c
Normal file
|
@ -0,0 +1,305 @@
|
|||
/* Simple implementation of a GDB remote protocol client.
|
||||
* Copyright (C) 2015 Red Hat Inc.
|
||||
*
|
||||
* This file is part of gdb-toys.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 3 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
struct gdb_conn {
|
||||
FILE *in;
|
||||
FILE *out;
|
||||
bool ack;
|
||||
};
|
||||
|
||||
|
||||
static uint8_t
|
||||
hex_nibble(uint8_t hex) {
|
||||
return isdigit(hex) ? hex - '0' : tolower(hex) - 'a' + 10;
|
||||
}
|
||||
|
||||
uint8_t hex_encode(uint8_t digit) {
|
||||
return digit > 9 ? 'a' + digit - 10 : '0' + digit;
|
||||
}
|
||||
|
||||
uint16_t gdb_decode_hex(uint8_t msb, uint8_t lsb) {
|
||||
if (!isxdigit(msb) || !isxdigit(lsb))
|
||||
return UINT16_MAX;
|
||||
return 16 * hex_nibble(msb) + hex_nibble(lsb);
|
||||
}
|
||||
|
||||
uint64_t gdb_decode_hex_str(uint8_t *bytes) {
|
||||
uint64_t value = 0;
|
||||
uint64_t weight = 1;
|
||||
while (isxdigit(bytes[0]) && isxdigit(bytes[1])) {
|
||||
value += weight * gdb_decode_hex(bytes[0], bytes[1]);
|
||||
bytes += 2;
|
||||
weight *= 16 * 16;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
static struct gdb_conn* gdb_begin(int fd) {
|
||||
struct gdb_conn *conn = calloc(1, sizeof(struct gdb_conn));
|
||||
if (conn == NULL)
|
||||
err(1, "calloc");
|
||||
|
||||
conn->ack = true;
|
||||
|
||||
// duplicate the handle to separate read/write state
|
||||
int fd2 = dup(fd);
|
||||
if (fd2 < 0)
|
||||
err(1, "dup");
|
||||
|
||||
// open a FILE* for reading
|
||||
conn->in = fdopen(fd, "rb");
|
||||
if (conn->in == NULL)
|
||||
err(1, "fdopen");
|
||||
|
||||
// open a FILE* for writing
|
||||
conn->out = fdopen(fd2, "wb");
|
||||
if (conn->out == NULL)
|
||||
err(1, "fdopen");
|
||||
|
||||
// reset line state by acking any earlier input
|
||||
fputc('+', conn->out);
|
||||
fflush(conn->out);
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
struct gdb_conn* gdb_begin_inet(const char *addr, uint16_t port) {
|
||||
// fill the socket information
|
||||
struct sockaddr_in sa = {
|
||||
.sin_family = AF_INET,
|
||||
.sin_port = htons(port),
|
||||
};
|
||||
if (inet_aton(addr, &sa.sin_addr) == 0)
|
||||
errx(1, "Invalid address: %s", addr);
|
||||
|
||||
// open the socket and start the tcp connection
|
||||
int fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (fd < 0)
|
||||
err(1, "socket");
|
||||
if (connect(fd, (const struct sockaddr *)&sa, sizeof(sa)) != 0) {
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
socklen_t tmp;
|
||||
tmp = 1;
|
||||
int r = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&tmp, sizeof(tmp));
|
||||
if (r) {
|
||||
perror("setsockopt");
|
||||
assert(0);
|
||||
}
|
||||
tmp = 1;
|
||||
r = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&tmp, sizeof(tmp));
|
||||
if (r) {
|
||||
perror("setsockopt");
|
||||
assert(0);
|
||||
}
|
||||
|
||||
// initialize the rest of gdb on this handle
|
||||
return gdb_begin(fd);
|
||||
}
|
||||
|
||||
|
||||
void gdb_end(struct gdb_conn *conn) {
|
||||
fclose(conn->in);
|
||||
fclose(conn->out);
|
||||
free(conn);
|
||||
}
|
||||
|
||||
static void send_packet(FILE *out, const uint8_t *command, size_t size) {
|
||||
// compute the checksum -- simple mod256 addition
|
||||
uint8_t sum = 0;
|
||||
size_t i;
|
||||
for (i = 0; i < size; ++i)
|
||||
sum += command[i];
|
||||
|
||||
// NB: seems neither escaping nor RLE is generally expected by
|
||||
// gdbserver. e.g. giving "invalid hex digit" on an RLE'd address.
|
||||
// So just write raw here, and maybe let higher levels escape/RLE.
|
||||
|
||||
fputc('$', out); // packet start
|
||||
fwrite(command, 1, size, out); // payload
|
||||
fprintf(out, "#%02X", sum); // packet end, checksum
|
||||
fflush(out);
|
||||
|
||||
if (ferror(out))
|
||||
err(1, "send");
|
||||
else if (feof(out))
|
||||
errx(0, "send: Connection closed");
|
||||
}
|
||||
|
||||
void gdb_send(struct gdb_conn *conn, const uint8_t *command, size_t size) {
|
||||
bool acked = false;
|
||||
do {
|
||||
send_packet(conn->out, command, size);
|
||||
|
||||
if (!conn->ack)
|
||||
break;
|
||||
|
||||
// look for '+' ACK or '-' NACK/resend
|
||||
acked = fgetc(conn->in) == '+';
|
||||
} while (!acked);
|
||||
}
|
||||
|
||||
static uint8_t* recv_packet(FILE *in, size_t *ret_size, bool* ret_sum_ok) {
|
||||
size_t i = 0;
|
||||
size_t size = 4096;
|
||||
uint8_t *reply = malloc(size);
|
||||
if (reply == NULL)
|
||||
err(1, "malloc");
|
||||
|
||||
int c;
|
||||
uint8_t sum = 0;
|
||||
bool escape = false;
|
||||
|
||||
// fast-forward to the first start of packet
|
||||
while ((c = fgetc(in)) != EOF && c != '$');
|
||||
|
||||
while ((c = fgetc(in)) != EOF) {
|
||||
sum += c;
|
||||
switch (c) {
|
||||
case '$': // new packet? start over...
|
||||
i = 0;
|
||||
sum = 0;
|
||||
escape = false;
|
||||
continue;
|
||||
|
||||
case '#': // end of packet
|
||||
sum -= c; // not part of the checksum
|
||||
{
|
||||
uint8_t msb = fgetc(in);
|
||||
uint8_t lsb = fgetc(in);
|
||||
*ret_sum_ok = sum == gdb_decode_hex(msb, lsb);
|
||||
}
|
||||
*ret_size = i;
|
||||
|
||||
// terminate it for good measure
|
||||
if (i == size) {
|
||||
reply = realloc(reply, size + 1);
|
||||
if (reply == NULL)
|
||||
err(1, "realloc");
|
||||
}
|
||||
reply[i] = '\0';
|
||||
|
||||
return reply;
|
||||
|
||||
case '}': // escape: next char is XOR 0x20
|
||||
escape = true;
|
||||
continue;
|
||||
|
||||
case '*': // run-length-encoding
|
||||
// The next character tells how many times to repeat the last
|
||||
// character we saw. The count is added to 29, so that the
|
||||
// minimum-beneficial RLE 3 is the first printable character ' '.
|
||||
// The count character can't be >126 or '$'/'#' packet markers.
|
||||
|
||||
if (i > 0) { // need something to repeat!
|
||||
int c2 = fgetc(in);
|
||||
if (c2 < 29 || c2 > 126 || c2 == '$' || c2 == '#') {
|
||||
// invalid count character!
|
||||
ungetc(c2, in);
|
||||
} else {
|
||||
int count = c2 - 29;
|
||||
|
||||
// get a bigger buffer if needed
|
||||
if (i + count > size) {
|
||||
size *= 2;
|
||||
reply = realloc(reply, size);
|
||||
if (reply == NULL)
|
||||
err(1, "realloc");
|
||||
}
|
||||
|
||||
// fill the repeated character
|
||||
memset(&reply[i], reply[i - 1], count);
|
||||
i += count;
|
||||
sum += c2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// XOR an escaped character
|
||||
if (escape) {
|
||||
c ^= 0x20;
|
||||
escape = false;
|
||||
}
|
||||
|
||||
// get a bigger buffer if needed
|
||||
if (i == size) {
|
||||
size *= 2;
|
||||
reply = realloc(reply, size);
|
||||
if (reply == NULL)
|
||||
err(1, "realloc");
|
||||
}
|
||||
|
||||
// add one character
|
||||
reply[i++] = c;
|
||||
}
|
||||
|
||||
if (ferror(in))
|
||||
err(1, "recv");
|
||||
else if (feof(in))
|
||||
errx(0, "recv: Connection closed");
|
||||
else
|
||||
errx(1, "recv: Unknown connection error");
|
||||
}
|
||||
|
||||
uint8_t* gdb_recv(struct gdb_conn *conn, size_t *size) {
|
||||
uint8_t *reply;
|
||||
bool acked = false;
|
||||
do {
|
||||
reply = recv_packet(conn->in, size, &acked);
|
||||
|
||||
if (!conn->ack)
|
||||
break;
|
||||
|
||||
// send +/- depending on checksum result, retry if needed
|
||||
fputc(acked ? '+' : '-', conn->out);
|
||||
fflush(conn->out);
|
||||
} while (!acked);
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
const char* gdb_start_noack(struct gdb_conn *conn) {
|
||||
static const char cmd[] = "QStartNoAckMode";
|
||||
gdb_send(conn, (const uint8_t *)cmd, sizeof(cmd) - 1);
|
||||
|
||||
size_t size;
|
||||
uint8_t *reply = gdb_recv(conn, &size);
|
||||
bool ok = size == 2 && !strcmp((const char*)reply, "OK");
|
||||
free(reply);
|
||||
|
||||
if (ok)
|
||||
conn->ack = false;
|
||||
return ok ? "OK" : "";
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue