@twilio/plugin-microvisor
Version:
Interact with your Twilio Microvisor devices
134 lines (133 loc) • 5.82 kB
JavaScript
"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;