blob: d754a7818da23ffc0ff72436d910841f9399fab5 [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 "sim/kelvin_action_point_memory_interface.h"
#include <cstdint>
#include <utility>
#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
namespace kelvin::sim {
KelvinActionPointMemoryInterface::KelvinActionPointMemoryInterface(
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);
}
KelvinActionPointMemoryInterface::~KelvinActionPointMemoryInterface() {
if (db4_ != nullptr) db4_->DecRef();
for (auto &[unused, inst_info_ptr] : instruction_map_) {
delete inst_info_ptr;
}
instruction_map_.clear();
}
absl::Status KelvinActionPointMemoryInterface::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)));
}
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 KelvinActionPointMemoryInterface::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)));
}
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.
db4_->Set<uint32_t>(0, kEBreak32);
memory_->Store(address, db4_);
invalidate_fcn_(address);
// Invalidate the instruction decode for this address.
return absl::OkStatus();
}
int KelvinActionPointMemoryInterface::GetInstructionSize(
uint32_t instruction_word) const {
return 4;
}
} // namespace kelvin::sim