UNPKG

@twilio/plugin-microvisor

Version:

Interact with your Twilio Microvisor devices

134 lines (133 loc) 5.82 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MemoryCache = void 0; const tslib_1 = require("tslib"); const proto_1 = require("../../../../proto_gen/proto"); const node_assert_1 = tslib_1.__importDefault(require("node:assert")); ; class MemoryCache { constructor(lineLength, memMap, debugProto) { this.cache = new Map(); this.lineLength = lineLength; this.offsetLength = Math.log2(lineLength); (0, node_assert_1.default)(Number.isInteger(this.offsetLength)); (0, node_assert_1.default)(this.offsetLength < 32); // e.g. offsetLength == 5 -> offsetMask == (1 << 5) - 1 == 0x1F this.offsetMask = (1 << this.offsetLength) - 1; this.memMap = memMap; this.debugProto = debugProto; } async read(address, byteCount) { let result = new Uint8Array(byteCount); let resultOffset = 0; while (byteCount > 0) { // For registers reading not quite the same addresses as were requested can be // disruptive, so we can't read in aligned lines in this case. // This works under an assumption that GDB and/or its user will not make read // requests spanning both cacheable and non-cacheable (i.e. register) memory let memSegment = this.memMap.getSegment(address); let chunkLen = 0; if (typeof memSegment !== 'undefined' && memSegment.cacheable) { let offset = address & this.offsetMask; let tag = address >> this.offsetLength; let line = this.cache.get(tag); if (typeof line === 'undefined') { let request = proto_1.ReadMemoryRequest.create({ address: address - offset, byteCount: this.lineLength }); // eslint-disable-next-line no-await-in-loop let response = await this.debugProto.readMemory(request); if (response.error) { return response.error; } if (!response.memory) { return 91; } line = response.memory; this.cache.set(tag, line); } chunkLen = this.lineLength - offset; if (byteCount < chunkLen) { chunkLen = byteCount; } let chunk = line.slice(offset, offset + chunkLen); result.set(chunk, resultOffset); } else if (typeof memSegment === 'undefined') { // memory segment not in the map, don't know what to do return 92; } else { // non-cacheable address chunkLen = (byteCount > this.lineLength) ? this.lineLength : byteCount; let endAddress = address + chunkLen - 1; let endSegment = this.memMap.getSegment(endAddress); if (typeof endSegment === 'undefined') { return 92; } if (endSegment.cacheable) { // assume segments are aligned by this.lineLength (true for IMP201) endAddress &= ~this.offsetMask; chunkLen = endAddress - address + 1; } let request = proto_1.ReadMemoryRequest.create({ address: address, byteCount: chunkLen }); // eslint-disable-next-line no-await-in-loop let response = await this.debugProto.readMemory(request); if (response.error) { return response.error; } if (!response.memory) { return 91; } result.set(response.memory, resultOffset); } resultOffset += chunkLen; address += chunkLen; byteCount -= chunkLen; } return result; } async writeChunk(address, memory) { // simple write-through mechanism. No new cache entries are created on write, // just make sure existing ones are updated // No more than one unaligned write request is issued, maximal size is this.lineLength (0, node_assert_1.default)(memory.length <= this.lineLength); // a chunk can span 1 or 2 cache lines let tag0 = address >> this.offsetLength; let offset = address & this.offsetMask; let tag1 = address + memory.length >> this.offsetLength; let tail = (tag1 === tag0) ? 0 : ((address + memory.length) & this.offsetMask); let line0 = this.cache.get(tag0); let line1 = this.cache.get(tag1); if (typeof line0 !== 'undefined') { line0.set(memory.slice(0, memory.length - tail - offset), offset); } if (tail != 0 && (typeof line1 !== 'undefined')) { line1.set(memory.slice(memory.length - tail, memory.length), 0); } let request = proto_1.WriteMemoryRequest.create({ address: address, memory: memory }); // eslint-disable-next-line no-await-in-loop let response = await this.debugProto.writeMemory(request); if (response.error) { // Some error occurred, invalidate cache if (typeof line0 !== 'undefined') { this.cache.delete(tag0); } if (tail !== 0 && typeof line1 !== 'undefined') { this.cache.delete(tag1); } return response.error; } } clear() { this.cache.clear(); } } exports.MemoryCache = MemoryCache;