@twilio/plugin-microvisor
Version:
Interact with your Twilio Microvisor devices
632 lines (631 loc) • 29.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GdbToProtobuf = exports.GdbPacket = void 0;
const tslib_1 = require("tslib");
const memorycache_1 = require("./memorycache");
const memorymap_1 = require("./memorymap");
const node_events_1 = require("node:events");
const proto_1 = require("../../../../proto_gen/proto");
const node_assert_1 = tslib_1.__importDefault(require("node:assert"));
// Looks like GDB expects them?
var UnixSignals;
(function (UnixSignals) {
UnixSignals[UnixSignals["SIGINT"] = 2] = "SIGINT";
UnixSignals[UnixSignals["SIGTRAP"] = 5] = "SIGTRAP";
UnixSignals[UnixSignals["SIGSEGV"] = 11] = "SIGSEGV";
})(UnixSignals || (UnixSignals = {}));
var GdbPacketType;
(function (GdbPacketType) {
GdbPacketType[GdbPacketType["ReadRegisters"] = 'g'.charCodeAt(0)] = "ReadRegisters";
GdbPacketType[GdbPacketType["WriteRegisters"] = 'G'.charCodeAt(0)] = "WriteRegisters";
GdbPacketType[GdbPacketType["ReadMemory"] = 'm'.charCodeAt(0)] = "ReadMemory";
GdbPacketType[GdbPacketType["WriteMemory"] = 'M'.charCodeAt(0)] = "WriteMemory";
GdbPacketType[GdbPacketType["Continue"] = 'c'.charCodeAt(0)] = "Continue";
GdbPacketType[GdbPacketType["Step"] = 's'.charCodeAt(0)] = "Step";
GdbPacketType[GdbPacketType["GeneralQueryRead"] = 'q'.charCodeAt(0)] = "GeneralQueryRead";
GdbPacketType[GdbPacketType["GeneralQueryWrite"] = 'Q'.charCodeAt(0)] = "GeneralQueryWrite";
GdbPacketType[GdbPacketType["ExtendedMode"] = '!'.charCodeAt(0)] = "ExtendedMode";
GdbPacketType[GdbPacketType["SetThread"] = 'H'.charCodeAt(0)] = "SetThread";
GdbPacketType[GdbPacketType["GetHaltReason"] = '?'.charCodeAt(0)] = "GetHaltReason";
GdbPacketType[GdbPacketType["InsertBreakpoint"] = 'Z'.charCodeAt(0)] = "InsertBreakpoint";
GdbPacketType[GdbPacketType["RemoveBreakpoint"] = 'z'.charCodeAt(0)] = "RemoveBreakpoint";
GdbPacketType[GdbPacketType["ReadRegister"] = 'p'.charCodeAt(0)] = "ReadRegister";
GdbPacketType[GdbPacketType["WriteRegister"] = 'P'.charCodeAt(0)] = "WriteRegister";
GdbPacketType[GdbPacketType["ThreadStatus"] = 'T'.charCodeAt(0)] = "ThreadStatus";
GdbPacketType[GdbPacketType["ExtendedCommand"] = 'v'.charCodeAt(0)] = "ExtendedCommand";
GdbPacketType[GdbPacketType["KillRequest"] = 'k'.charCodeAt(0)] = "KillRequest";
})(GdbPacketType || (GdbPacketType = {}));
const targetXml = '<?xml version="1.0"?><!DOCTYPE target SYSTEM "gdb-target.dtd"><target><feature name="org.gnu.gdb.arm.m-profile"><reg name="r0" bitsize="32"/><reg name="r1" bitsize="32"/><reg name="r2" bitsize="32"/><reg name="r3" bitsize="32"/><reg name="r4" bitsize="32"/><reg name="r5" bitsize="32"/><reg name="r6" bitsize="32"/><reg name="r7" bitsize="32"/><reg name="r8" bitsize="32"/><reg name="r9" bitsize="32"/><reg name="r10" bitsize="32"/><reg name="r11" bitsize="32"/><reg name="r12" bitsize="32"/><reg name="sp" bitsize="32" type="data_ptr"/><reg name="lr" bitsize="32"/><reg name="pc" bitsize="32" type="code_ptr"/><reg name="xpsr" bitsize="32"/></feature><feature name="twimp.ns.regs"><reg name="primask" bitsize="32" regnum="34"/><reg name="basepri" bitsize="32" regnum="35"/><reg name="faultmask" bitsize="32" regnum="36"/><reg name="control" bitsize="32" regnum="37"/><reg name="msp" bitsize="32" regnum="38"/><reg name="psp" bitsize="32" regnum="39"/><reg name="msplim" bitsize="32" regnum="40"/><reg name="psplim" bitsize="32" regnum="41"/><reg name="sfsr" bitsize="32" regnum="42"/><reg name="sfar" bitsize="32" regnum="43"/><reg name="shcsr" bitsize="32" regnum="44"/><reg name="hfsr" bitsize="32" regnum="45"/><reg name="cfsr" bitsize="32" regnum="46"/><reg name="bfar" bitsize="32" regnum="47"/><reg name="mmfar" bitsize="32" regnum="48"/></feature></target>';
class ThreadInfo {
constructor(name) {
this.name = name;
this.fault = proto_1.HaltReasonResponse.HaltReason.NONE;
}
faultString(fault) {
switch (fault) {
case proto_1.HaltReasonResponse.HaltReason.NONE:
case proto_1.HaltReasonResponse.HaltReason.DEBUG_MONITOR:
case proto_1.HaltReasonResponse.HaltReason.HALT_REQUEST:
case proto_1.HaltReasonResponse.HaltReason.BREAK:
case proto_1.HaltReasonResponse.HaltReason.SINGLE_STEP:
return 'running';
case proto_1.HaltReasonResponse.HaltReason.HARD_FAULT:
return 'HardFault';
case proto_1.HaltReasonResponse.HaltReason.MEM_FAULT:
return 'MemManage Fault';
case proto_1.HaltReasonResponse.HaltReason.BUS_FAULT:
return 'BusFault';
case proto_1.HaltReasonResponse.HaltReason.USAGE_FAULT:
return 'UsageFault';
case proto_1.HaltReasonResponse.HaltReason.SECURE_FAULT:
return 'SecureFault';
default:
return 'Unknown';
}
}
toString() {
return `${this.name}: [${this.faultString(this.fault)}]`;
}
}
class GdbPacket {
constructor(content) {
this.content = ''; // raw (escaped) content of the packet
this.content = content;
}
getType() {
return this.content.charCodeAt(1);
}
getArgs() {
return GdbPacket.unescape(this.content).slice(2, -3);
}
getData() {
return GdbPacket.unescape(this.content).slice(1, -3);
}
static build(data) {
let cs = GdbPacket.checksum(data).toString(16).padStart(2, '0');
return new GdbPacket('$' + GdbPacket.escape(data) + '#' + cs);
}
static escape(data) {
let result = '';
for (const c of data) {
let cNumber = c.charCodeAt(0);
if ('#$}*'.includes(c)) {
result += '}';
result += String.fromCharCode(cNumber ^ 0x20);
}
else {
result += c;
}
}
return result;
}
static unescape(data) {
let result = '';
let escape = false;
for (const c of data) {
let cNumber = c.charCodeAt(0);
if (escape) {
result += String.fromCharCode(cNumber ^ 0x20);
escape = false;
}
else if (c === '}') {
escape = true;
}
else {
result += c;
}
}
return result;
}
static checksum(data) {
return [...data].reduce(((a, b) => (((a + b.charCodeAt(0)) & 0xFF))), 0);
}
}
exports.GdbPacket = GdbPacket;
class GdbToProtobuf extends node_events_1.EventEmitter {
constructor(protobufToTunnel) {
super();
this.packetHandlers = new Map([
[GdbPacketType.ReadRegisters, this.handleReadRegisters.bind(this)],
[GdbPacketType.WriteRegisters, this.handleWriteRegisters.bind(this)],
[GdbPacketType.ReadMemory, this.handleReadMemory.bind(this)],
[GdbPacketType.WriteMemory, this.handleWriteMemory.bind(this)],
[GdbPacketType.Continue, this.handleContinue.bind(this)],
[GdbPacketType.Step, this.handleStep.bind(this)],
[GdbPacketType.GeneralQueryRead, this.handleGeneralQueryRead.bind(this)],
[GdbPacketType.GeneralQueryWrite, this.handleGeneralQueryWrite.bind(this)],
[GdbPacketType.ExtendedMode, this.handleExtendedMode.bind(this)],
[GdbPacketType.SetThread, this.handleSetThread.bind(this)],
[GdbPacketType.GetHaltReason, this.handleGetHaltReason.bind(this)],
[GdbPacketType.InsertBreakpoint, this.handleInsertBreakpoint.bind(this)],
[GdbPacketType.RemoveBreakpoint, this.handleRemoveBreakpoint.bind(this)],
[GdbPacketType.ReadRegister, this.handleReadRegister.bind(this)],
[GdbPacketType.WriteRegister, this.handleWriteRegister.bind(this)],
[GdbPacketType.ExtendedCommand, this.handleExtendedCommand.bind(this)],
[GdbPacketType.ThreadStatus, this.handleThreadStatus.bind(this)]
]);
this.unsupportedPackets = new Set();
this.protobufToTunnel = protobufToTunnel;
this.protobufToTunnel.on('unsolicitedHalt', (data) => {
this.emit('unsolicitedHalt', GdbPacket.build(this.formatHaltReason(data)));
});
this.protobufToTunnel.on('open', () => {
// send a ping when the tunnel is opened: this checks things are working
// (and this is important for timely error detection of the end-to-end
// crypto), and we can use the opportunity to measure the base latency.
const pingStartTime = Date.now();
this.protobufToTunnel.sendPing(() => {
const now = Date.now();
this.emit('latency', now - pingStartTime);
this.emit('open');
});
});
this.protobufToTunnel.on('close', () => {
this.emit('close');
});
this.debugProto = proto_1.DebugProtocol.create(protobufToTunnel.rpcImpl.bind(protobufToTunnel), false, false);
this.threadInfo = new ThreadInfo('user code');
this.memMap = new memorymap_1.MemoryMap(memorymap_1.MicrovisorDevices.IMP201); // TODO: once more devices are
// supported this should come from
// arguments or e.g. HelloResponse
this.memMapXml = this.memMap.xml();
this.memCache = new memorycache_1.MemoryCache(GdbToProtobuf.MEMORY_LINE_LENGTH, this.memMap, this.debugProto);
}
/*
* Error codes:
* - E99 - generic error when exception is catched
* - E90 - bad parameters
* - E91 - generic error response from device
* - E01-E02 - ReadWriteError from device
*/
async packet(packet) {
let handler = this.packetHandlers.get(packet.getType());
if (handler) {
return handler(packet).catch(error => {
console.log(error.message);
this.emit('debug', error.message);
return GdbPacket.build('E99');
});
}
let packetType = packet.getType();
this.emit('debug', `No handler for packet of type ${packetType}`);
if (!this.unsupportedPackets.has(packetType)) {
this.unsupportedPackets.add(packetType);
this.emit('telemetry', proto_1.PluginTelemetryType.UNSUPPORTED_GDB_PACKET, String.fromCharCode(packetType));
}
return GdbPacket.build('');
}
async interrupt() {
return this.debugProto.halt({}).then((resp) => {
if (resp.haltReason) {
this.emit('unsolicitedHalt', GdbPacket.build(this.formatHaltReason(resp.haltReason)));
}
});
}
async hello() {
this.emit('debug', `MEMMAPXML: ${this.memMapXml}`);
await this.debugProto.hello({});
}
async goodbye() {
await this.debugProto.goodbye({});
}
async telemetry(type, data) {
let dataBuffer = new Uint8Array(data.length);
for (let i = 0; i < data.length; i++) {
dataBuffer[i] = data.charCodeAt(i);
}
let request = proto_1.PluginTelemetryRequest.create({
type: type,
data: dataBuffer
});
await this.debugProto.pluginTelemetry(request);
}
setFault(fault) {
this.threadInfo.fault = fault;
}
formatHaltReason(reason) {
let result = 'T';
const regs = reason.registers;
this.setFault(proto_1.HaltReasonResponse.HaltReason.NONE); // TODO: doesn't belong here
switch (reason.haltReason) {
case proto_1.HaltReasonResponse.HaltReason.BREAK:
result += UnixSignals.SIGTRAP.toString(16).padStart(2, '0');
result += 'hwbreak';
if (regs && regs.readWrite && regs.readWrite.basicRegisters && regs.readWrite.basicRegisters.pc) {
// TODO: look at the actual breakpoints set
result += ':' + regs.readWrite.basicRegisters.pc.toString(16).padStart(8, '0');
}
result += ';';
break;
case proto_1.HaltReasonResponse.HaltReason.SINGLE_STEP:
result += UnixSignals.SIGTRAP.toString(16).padStart(2, '0');
break;
case proto_1.HaltReasonResponse.HaltReason.HALT_REQUEST:
result += UnixSignals.SIGINT.toString(16).padStart(2, '0');
break;
case null:
/* eslint-disable unicorn/no-useless-undefined */
case undefined:
this.setFault(undefined); // TODO: doesn't belong here
result += UnixSignals.SIGSEGV.toString(16).padStart(2, '0');
break;
default:
this.setFault(reason.haltReason); // TODO: doesn't belong here
result += UnixSignals.SIGSEGV.toString(16).padStart(2, '0');
break;
}
if (regs && regs.readWrite && regs.readWrite.basicRegisters) {
const basicRegs = new proto_1.registers.BasicRegisters(regs.readWrite.basicRegisters);
result += '00:' + GdbToProtobuf.regToHex(basicRegs.r0) + ';';
result += '01:' + GdbToProtobuf.regToHex(basicRegs.r1) + ';';
result += '02:' + GdbToProtobuf.regToHex(basicRegs.r2) + ';';
result += '03:' + GdbToProtobuf.regToHex(basicRegs.r3) + ';';
result += '0C:' + GdbToProtobuf.regToHex(basicRegs.r12) + ';';
result += '0E:' + GdbToProtobuf.regToHex(basicRegs.lr) + ';';
result += '0F:' + GdbToProtobuf.regToHex(basicRegs.pc) + ';';
result += '10:' + GdbToProtobuf.regToHex(basicRegs.xpsr) + ';';
}
if (regs && regs.readWrite && regs.readWrite.extendedRegisters) {
const extendedRegs = new proto_1.registers.ExtendedRegisters(regs.readWrite.extendedRegisters);
result += '04:' + GdbToProtobuf.regToHex(extendedRegs.r4) + ';';
result += '05:' + GdbToProtobuf.regToHex(extendedRegs.r5) + ';';
result += '06:' + GdbToProtobuf.regToHex(extendedRegs.r6) + ';';
result += '07:' + GdbToProtobuf.regToHex(extendedRegs.r7) + ';';
result += '08:' + GdbToProtobuf.regToHex(extendedRegs.r8) + ';';
result += '09:' + GdbToProtobuf.regToHex(extendedRegs.r9) + ';';
result += '0A:' + GdbToProtobuf.regToHex(extendedRegs.r10) + ';';
result += '0B:' + GdbToProtobuf.regToHex(extendedRegs.r11) + ';';
result += '0D:' + GdbToProtobuf.regToHex(extendedRegs.sp) + ';';
}
return result;
}
formatReadRegisters(resp) {
let result;
if (resp.error === proto_1.ReadWriteError.NONE) {
let r0r3 = '';
let r4r11 = '';
let r12 = '';
let sp = '';
let lrPcXpsr = '';
if (resp.readWrite && resp.readWrite.basicRegisters) {
const basicRegs = new proto_1.registers.BasicRegisters(resp.readWrite.basicRegisters);
r0r3 = GdbToProtobuf.regToHex(basicRegs.r0) +
GdbToProtobuf.regToHex(basicRegs.r1) +
GdbToProtobuf.regToHex(basicRegs.r2) +
GdbToProtobuf.regToHex(basicRegs.r3);
r12 = GdbToProtobuf.regToHex(basicRegs.r12);
lrPcXpsr = GdbToProtobuf.regToHex(basicRegs.lr) +
GdbToProtobuf.regToHex(basicRegs.pc) +
GdbToProtobuf.regToHex(basicRegs.xpsr);
}
else {
r0r3 = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
r12 = 'xxxxxxxx';
lrPcXpsr = 'xxxxxxxxxxxxxxxxxxxxxxxx';
}
if (resp.readWrite && resp.readWrite.extendedRegisters) {
const extendedRegs = new proto_1.registers.ExtendedRegisters(resp.readWrite.extendedRegisters);
sp = GdbToProtobuf.regToHex(extendedRegs.sp);
r4r11 = GdbToProtobuf.regToHex(extendedRegs.r4) +
GdbToProtobuf.regToHex(extendedRegs.r5) +
GdbToProtobuf.regToHex(extendedRegs.r6) +
GdbToProtobuf.regToHex(extendedRegs.r7) +
GdbToProtobuf.regToHex(extendedRegs.r8) +
GdbToProtobuf.regToHex(extendedRegs.r9) +
GdbToProtobuf.regToHex(extendedRegs.r10) +
GdbToProtobuf.regToHex(extendedRegs.r11);
}
else {
r4r11 = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
sp = 'xxxxxxxx';
}
result = r0r3 + r4r11 + r12 + sp + lrPcXpsr;
}
else {
let errorString = resp.error.toString(16).padStart(2, '0');
result = `E${errorString}`;
}
return result;
}
static regToHex(reg) {
return (reg & 0xFF).toString(16).padStart(2, '0') +
((reg >> 8) & 0xFF).toString(16).padStart(2, '0') +
((reg >> 16) & 0xFF).toString(16).padStart(2, '0') +
((reg >> 24) & 0xFF).toString(16).padStart(2, '0');
}
static hexToReg(reg) {
if (typeof reg === 'undefined') {
return Number.NaN;
}
if (reg.length !== 8) {
return Number.NaN;
}
let regMatch = reg.match(/.{1,2}/g);
if (regMatch === null || regMatch.length !== 4) {
return Number.NaN;
}
let result = 0;
for (let i = 0; i < 4; i++) {
let b = Number.parseInt(regMatch[i], 16);
if (Number.isNaN(b)) {
return Number.NaN;
}
result += b << (8 * i);
}
return result;
}
async handleReadRegisters(_packet) {
return this.debugProto.readRegisters({}).then((resp) => {
return GdbPacket.build(this.formatReadRegisters(resp));
});
}
async handleWriteRegisters(packet) {
let packetArguments = packet.getArgs();
// TODO: should we support XXXX registers?
if (packetArguments.length !== 17 * 8) { // TODO: get it from target description, treat incomplete requests properly
this.emit('debug', 'Write registers request should contain all 17 registers');
return GdbPacket.build('E90');
}
let regMatch = packetArguments.match(/.{1,8}/g);
if (regMatch === null) {
return GdbPacket.build('E90');
}
// eslint-disable-next-line unicorn/no-array-callback-reference
let registers = regMatch.map(GdbToProtobuf.hexToReg);
if (!registers.every((r) => !Number.isNaN(r))) {
this.emit('debug', 'Cannot parse registers');
return GdbPacket.build('E90');
}
let request = proto_1.WriteRegistersRequest.create({
registers: {
basicRegisters: {
r0: registers[0],
r1: registers[1],
r2: registers[2],
r3: registers[3],
r12: registers[12],
lr: registers[14],
pc: registers[15],
xpsr: registers[16]
},
extendedRegisters: {
sp: registers[13],
r4: registers[4],
r5: registers[5],
r6: registers[6],
r7: registers[7],
r8: registers[8],
r9: registers[9],
r10: registers[10],
r11: registers[11]
}
}
});
return this.debugProto.writeRegisters(request).then((resp) => {
if (resp.error === proto_1.ReadWriteError.NONE) {
return GdbPacket.build('OK');
}
let errorString = resp.error.toString(16).padStart(2, '0');
return GdbPacket.build(`E${errorString}`);
});
}
async handleReadMemory(packet) {
let [address, byteCount] = packet.getArgs().split(',').map((s) => Number.parseInt(s, 16));
if (typeof address === 'undefined' || Number.isNaN(address) || typeof byteCount === 'undefined' || Number.isNaN(byteCount)) {
throw new TypeError('Read memory request requires two hex arguments');
}
let response = await this.memCache.read(address, byteCount);
if (typeof response === 'number') {
let errorString = response.toString(10).padStart(2, '0');
return GdbPacket.build(`E${errorString}`);
}
else {
let result = '';
for (const b of response) {
result += b.toString(16).padStart(2, '0');
}
return GdbPacket.build(result);
}
}
async handleWriteMemory(packet) {
let packetArguments = packet.getArgs().split(':');
if (packetArguments.length !== 2) {
throw new Error('Write memory request requires data');
}
let [address, byteCount] = packetArguments[0].split(',').map((s) => Number.parseInt(s, 16));
if (typeof address === 'undefined' || Number.isNaN(address) || typeof byteCount === 'undefined' || Number.isNaN(byteCount)) {
throw new TypeError('Write memory request requires address and length');
}
if (2 * byteCount !== packetArguments[1].length) {
throw new Error('Byte count and the amount of data don\'t match');
}
let chunkNo = 0;
while (byteCount > 0) {
let chunkSize = (byteCount > 64) ? 64 : byteCount;
let memory = new Uint8Array(chunkSize);
for (let i = 0; i < chunkSize * 2; i += 2) {
let bt = Number.parseInt(packetArguments[1].slice((64 * chunkNo) + i, (64 * chunkNo) + i + 2), 16);
(0, node_assert_1.default)(!Number.isNaN(bt));
memory[i / 2] = bt;
}
let result = await this.memCache.writeChunk(address, memory);
if (typeof result === 'number') {
let errorString = result.toString(10).padStart(2, '0');
return GdbPacket.build(`E${errorString}`);
}
byteCount -= chunkSize;
address += chunkSize;
chunkNo++;
}
return GdbPacket.build('OK');
}
async handleContinue(_packet) {
this.memCache.clear();
let promise = this.protobufToTunnel.waitHaltReason();
await this.debugProto.continue({});
return promise.then((r) => {
return GdbPacket.build(this.formatHaltReason(r));
});
}
async handleStep(_packet) {
this.memCache.clear();
let promise = this.protobufToTunnel.waitHaltReason();
await this.debugProto.step({});
return promise.then((r) => {
return GdbPacket.build(this.formatHaltReason(r));
});
}
readWindow(packetArguments, file) {
let packetArgumentsArray = packetArguments.split(':');
let [offset, length] = packetArgumentsArray[packetArgumentsArray.length - 1].split(',').map((a) => Number.parseInt(a, 16));
if (typeof offset !== 'number' || Number.isNaN(offset) || typeof length !== 'number' || Number.isNaN(length)) {
return 'E90';
}
if (offset >= file.length) {
return 'E90';
}
let result = (offset + length >= file.length) ? 'l' : 'm';
result += file.slice(offset, offset + length);
return result;
}
async handleGeneralQueryRead(packet) {
let packetArguments = packet.getArgs();
if (packetArguments === 'C') {
return GdbPacket.build('QC1');
}
if (packetArguments === 'fThreadInfo') {
return GdbPacket.build('m1');
}
if (packetArguments === 'sThreadInfo') {
return GdbPacket.build('l');
}
if (packetArguments === 'TStatus') {
return GdbPacket.build('T0');
}
if (packetArguments === 'TfP' || packetArguments === 'TsP' || packetArguments === 'TfV' || packetArguments === 'TsV') {
// TODO: tracepoints
return GdbPacket.build('l');
}
if (packetArguments === 'ThreadExtraInfo,1') {
let result = '';
for (const c of this.threadInfo.toString()) {
result += c.charCodeAt(0).toString(16).padStart(2, '0');
}
return GdbPacket.build(result);
}
if (packetArguments === 'Symbol::') {
return GdbPacket.build('OK');
}
if (packetArguments.startsWith('Supported')) {
return GdbPacket.build('PacketSize=200;qXfer:memory-map:read+;qXfer:features:read+;hwbreak+');
}
if (packetArguments.startsWith('Xfer:features:read:target.xml:')) {
return GdbPacket.build(this.readWindow(packetArguments, targetXml));
}
if (packetArguments.startsWith('Xfer:memory-map:read::')) {
return GdbPacket.build(this.readWindow(packetArguments, this.memMapXml));
}
return GdbPacket.build('');
}
async handleGeneralQueryWrite(_packet) {
return GdbPacket.build('');
}
async handleExtendedMode(_packet) {
return GdbPacket.build('OK');
}
async handleSetThread(_packet) {
return GdbPacket.build('OK');
}
async handleGetHaltReason(_packet) {
return this.debugProto.haltReason({}).then((resp) => {
return GdbPacket.build(this.formatHaltReason(resp));
});
}
async handleInsertBreakpoint(packet) {
let [type, addr, kind] = packet.getArgs().split(',');
if (type == '') {
return GdbPacket.build('E90');
}
if (type !== '0' && type !== '1') {
return GdbPacket.build('');
}
if (kind !== '2' && kind !== '3' && kind !== '4') {
return GdbPacket.build('E90');
}
let addrNumber = Number.parseInt(addr, 16);
if (Number.isNaN(addrNumber)) {
return GdbPacket.build('E90');
}
let request = proto_1.InsertBreakpointRequest.create({
address: addrNumber
});
return this.debugProto.insertBreakpoint(request).then((resp) => {
return GdbPacket.build((resp.success) ? 'OK' : 'E91');
});
}
async handleRemoveBreakpoint(packet) {
let [type, addr, kind] = packet.getArgs().split(',');
if (type == '') {
return GdbPacket.build('E90');
}
if (type !== '0' && type !== '1') {
return GdbPacket.build('');
}
if (kind !== '2' && kind !== '3' && kind !== '4') {
return GdbPacket.build('E90');
}
let addrNumber = Number.parseInt(addr, 16);
if (Number.isNaN(addrNumber)) {
return GdbPacket.build('E90');
}
let request = proto_1.RemoveBreakpointRequest.create({
address: addrNumber
});
return this.debugProto.removeBreakpoint(request).then((resp) => {
return GdbPacket.build((resp.success) ? 'OK' : 'E91');
});
}
async handleReadRegister(packet) {
let reg = Number.parseInt(packet.getArgs(), 16);
if (Number.isNaN(reg)) {
return GdbPacket.build('E90');
}
let request = proto_1.ReadRegisterRequest.create({
index: reg
});
return this.debugProto.readRegister(request).then((resp) => {
if (resp.error === proto_1.ReadWriteError.NONE) {
return GdbPacket.build(GdbToProtobuf.regToHex(resp.value));
}
let errorString = resp.error.toString(16).padStart(2, '0');
return GdbPacket.build(`E${errorString}`);
});
}
async handleWriteRegister(packet) {
let [reg, value] = packet.getArgs().split('=');
let registerNumber = Number.parseInt(reg, 16);
let valueNumber = GdbToProtobuf.hexToReg(value);
if (typeof registerNumber !== 'number' || Number.isNaN(registerNumber) || typeof valueNumber !== 'number' || Number.isNaN(valueNumber)) {
return GdbPacket.build('E90');
}
let request = proto_1.WriteRegisterRequest.create({
index: registerNumber,
value: valueNumber
});
return this.debugProto.writeRegister(request).then((resp) => {
if (resp.error === proto_1.ReadWriteError.NONE) {
return GdbPacket.build('OK');
}
let errorString = resp.error.toString(16).padStart(2, '0');
return GdbPacket.build(`E${errorString}`);
});
}
async handleThreadStatus(_packet) {
return GdbPacket.build('OK');
}
async handleExtendedCommand(_packet) {
return GdbPacket.build('');
}
}
exports.GdbToProtobuf = GdbToProtobuf;
GdbToProtobuf.MEMORY_LINE_LENGTH = 128;