blob: 26d79cb820c190f7b37f7d6618d1580ca056c016 [file] [log] [blame]
// 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 "cheriot/riscv_cheriot_a_instructions.h"
#include <cstdint>
#include "absl/status/status.h"
#include "cheriot/cheriot_state.h"
#include "mpact/sim/generic/instruction.h"
#include "mpact/sim/generic/type_helpers.h"
#include "mpact/sim/util/memory/memory_interface.h"
#include "riscv//riscv_state.h"
namespace mpact {
namespace sim {
namespace cheriot {
using ::mpact::sim::generic::operator*; // NOLINT: is used below (clang error).
// This file contains the definitions of the semantic functions for the A, or
// atomic, subset of the RiscV architecture. Each semantic function calls the
// helper function which does all the heavy lifting.
using Operation = util::AtomicMemoryOpInterface::Operation;
using RV_EC = ::mpact::sim::riscv::ExceptionCode;
// Helper function for the atomic memory operation semantic functions.
template <typename T>
static inline void AInstructionHelper(Instruction *inst, Operation op,
bool has_store_value) {
auto *state = static_cast<CheriotState *>(inst->state());
auto *atomic = state->atomic_tagged_memory();
// If the atomic memory operation interface is nullptr, this is an illegal
// instruction.
if (atomic == nullptr) {
state->Trap(/*is_interrupt*/ false, /*trap_value*/ 0,
*RV_EC::kIllegalInstruction, inst->address(), inst);
return;
}
// Submit the memory operation.
auto address = generic::GetInstructionSource<uint64_t>(inst, 0);
auto *db = inst->state()->db_factory()->Allocate<T>(1);
db->set_latency(0);
// Only access the operand if there is a value to be read.
if (has_store_value) {
db->template Set<T>(0, generic::GetInstructionSource<T>(inst, 1));
}
// This transfers ownership of db to context. Don't DecRef.
auto *context = new riscv::LoadContext(db);
auto status = state->atomic_tagged_memory()->PerformMemoryOp(
address, op, db, inst->child(), context);
// If the operation is unimplemented, this is an illegal instruction.
if (absl::IsUnimplemented(status)) {
state->Trap(/*is_interrupt*/ false, /*trap_value*/ 0,
*RV_EC::kIllegalInstruction, inst->address(), inst);
return;
}
context->DecRef();
}
void ALrw(Instruction *instruction) {
AInstructionHelper<uint32_t>(instruction, Operation::kLoadLinked,
/*has_store_value*/ false);
}
void AScw(Instruction *instruction) {
AInstructionHelper<uint32_t>(instruction, Operation::kStoreConditional,
/*has_store_value*/ true);
}
void AAmoswapw(Instruction *instruction) {
AInstructionHelper<uint32_t>(instruction, Operation::kAtomicSwap,
/*has_store_value*/ true);
}
void AAmoaddw(Instruction *instruction) {
AInstructionHelper<uint32_t>(instruction, Operation::kAtomicAdd,
/*has_store_value*/ true);
}
void AAmoandw(Instruction *instruction) {
AInstructionHelper<uint32_t>(instruction, Operation::kAtomicAnd,
/*has_store_value*/ true);
}
void AAmoorw(Instruction *instruction) {
AInstructionHelper<uint32_t>(instruction, Operation::kAtomicOr,
/*has_store_value*/ true);
}
void AAmoxorw(Instruction *instruction) {
AInstructionHelper<uint32_t>(instruction, Operation::kAtomicXor,
/*has_store_value*/ true);
}
void AAmomaxw(Instruction *instruction) {
AInstructionHelper<uint32_t>(instruction, Operation::kAtomicMax,
/*has_store_value*/ true);
}
void AAmomaxuw(Instruction *instruction) {
AInstructionHelper<uint32_t>(instruction, Operation::kAtomicMaxu,
/*has_store_value*/ true);
}
void AAmominw(Instruction *instruction) {
AInstructionHelper<uint32_t>(instruction, Operation::kAtomicMin,
/*has_store_value*/ true);
}
void AAmominuw(Instruction *instruction) {
AInstructionHelper<uint32_t>(instruction, Operation::kAtomicMinu,
/*has_store_value*/ true);
}
} // namespace cheriot
} // namespace sim
} // namespace mpact