blob: 01d9aa3603fb060c1355328e6e4713d7e4bc63cb [file]
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "riscv/riscv_action_point_memory_interface.h"
#include <cstdint>
#include <utility>
#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
namespace mpact::sim::riscv {
RiscVActionPointMemoryInterface::RiscVActionPointMemoryInterface(
MemoryInterface *memory, InvalidateFcn invalidate_fcn)
: memory_(memory), invalidate_fcn_(std::move(invalidate_fcn)) {
// Allocate two data buffers (32 and 16 bit) once, so we don't have to
// do it every time we access breakpoint instructions.
db4_ = db_factory_.Allocate<uint32_t>(1);
db2_ = db_factory_.Allocate<uint16_t>(1);
}
RiscVActionPointMemoryInterface::~RiscVActionPointMemoryInterface() {
if (db4_ != nullptr) db4_->DecRef();
if (db2_ != nullptr) db2_->DecRef();
for (auto &[unused, inst_info_ptr] : instruction_map_) {
delete inst_info_ptr;
}
instruction_map_.clear();
}
absl::Status RiscVActionPointMemoryInterface::WriteOriginalInstruction(
uint64_t address) {
auto it = instruction_map_.find(address);
if (it == instruction_map_.end()) {
return absl::NotFoundError(absl::StrCat(
"No instruction information exist for address: ", absl::Hex(address)));
}
if (it->second->size == 2) {
db2_->Set<uint16_t>(
0, static_cast<uint16_t>(it->second->og_instruction_word & 0xffff));
memory_->Store(address, db2_);
} else if (it->second->size == 4) {
db4_->Set<uint32_t>(0, it->second->og_instruction_word);
memory_->Store(address, db4_);
}
// Invalidate the instruction decode for this address.
invalidate_fcn_(address);
return absl::OkStatus();
}
absl::Status RiscVActionPointMemoryInterface::WriteBreakpointInstruction(
uint64_t address) {
auto it = instruction_map_.find(address);
if (it == instruction_map_.end()) {
// This is a new breakpoint. First find the instruction size, then save
// the original instruction.
memory_->Load(address, db4_, nullptr, nullptr);
uint32_t instruction_word = db4_->Get<uint32_t>(0);
int size = GetInstructionSize(instruction_word);
if (size == 0) {
return absl::InvalidArgumentError(absl::StrCat(
"Invalid instruction size: ", size, " at ", absl::Hex(address)));
}
if (size == 2) instruction_word &= 0xffff;
auto *inst_info = new InstructionInfo;
inst_info->og_instruction_word = instruction_word;
inst_info->size = size;
it = instruction_map_.insert(std::make_pair(address, inst_info)).first;
}
// Write the breakpoint instruction to memory.
if (it->second->size == 2) {
db2_->Set<uint16_t>(0, kEBreak16);
memory_->Store(address, db2_);
} else if (it->second->size == 4) {
db4_->Set<uint32_t>(0, kEBreak32);
memory_->Store(address, db4_);
}
invalidate_fcn_(address);
// Invalidate the instruction decode for this address.
return absl::OkStatus();
}
int RiscVActionPointMemoryInterface::GetInstructionSize(
uint32_t instruction_word) const {
if ((instruction_word & 0b11) != 0b11) return 2;
if ((instruction_word & 0b111'11) != 0b11111) return 4;
return 0;
}
} // namespace mpact::sim::riscv