blob: ee5af8daba3bd8f4083ceef0530924f0fc3f4253 [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.
*/
#ifndef MPACT_CHERIOT__RISCV_CHERIOT_INSTRUCTION_HELPERS_H_
#define MPACT_CHERIOT__RISCV_CHERIOT_INSTRUCTION_HELPERS_H_
#include <any>
#include <cstdint>
#include <functional>
#include <limits>
#include <tuple>
#include <type_traits>
#include "absl/log/log.h"
#include "cheriot/cheriot_register.h"
#include "cheriot/cheriot_state.h"
#include "mpact/sim/generic/arch_state.h"
#include "mpact/sim/generic/data_buffer.h"
#include "mpact/sim/generic/instruction.h"
#include "mpact/sim/generic/operand_interface.h"
#include "mpact/sim/generic/register.h"
#include "mpact/sim/generic/type_helpers.h"
#include "riscv//riscv_state.h"
namespace mpact {
namespace sim {
namespace cheriot {
using ::mpact::sim::generic::Instruction;
using ::mpact::sim::generic::operator*;
using ::mpact::sim::generic::RegisterBase;
using CapReg = CheriotRegister;
using PB = ::mpact::sim::cheriot::CheriotRegister::PermissionBits;
// Get the destination capability register.
static inline CapReg *GetCapDest(const Instruction *instruction, int i) {
return static_cast<CapReg *>(
std::any_cast<RegisterBase *>(instruction->Destination(i)->GetObject()));
}
// Writing an integer result requires invalidating the capability and setting
// it to null.
template <typename Result>
static inline void WriteCapIntResult(const Instruction *instruction, int i,
Result value) {
auto *cap_reg = GetCapDest(instruction, i);
cap_reg->data_buffer()->Set<Result>(0, value);
cap_reg->Invalidate();
cap_reg->set_is_null();
}
template <typename Register, typename Result, typename Argument>
inline void RiscVBinaryOp(const Instruction *instruction,
std::function<Result(Argument, Argument)> operation) {
using RegValue = typename Register::ValueType;
Argument lhs = generic::GetInstructionSource<Argument>(instruction, 0);
Argument rhs = generic::GetInstructionSource<Argument>(instruction, 1);
Result dest_value = operation(lhs, rhs);
auto *reg = static_cast<generic::RegisterDestinationOperand<RegValue> *>(
instruction->Destination(0))
->GetRegister();
reg->data_buffer()->template Set<Result>(0, dest_value);
}
// Generic helper functions for binary instructions. Clears the tag bit for
// the capability register and sets it to null.
template <typename Register, typename Result, typename Argument1,
typename Argument2>
inline void RVCheriotBinaryOp(
const Instruction *instruction,
std::function<Result(Argument1, Argument2)> operation) {
Argument1 lhs = generic::GetInstructionSource<Argument1>(instruction, 0);
Argument2 rhs = generic::GetInstructionSource<Argument2>(instruction, 1);
Result dest_value = operation(lhs, rhs);
WriteCapIntResult(instruction, 0, dest_value);
}
template <typename Register, typename Result, typename Argument>
inline void RVCheriotBinaryOp(
const Instruction *instruction,
std::function<Result(Argument, Argument)> operation) {
Argument lhs = generic::GetInstructionSource<Argument>(instruction, 0);
Argument rhs = generic::GetInstructionSource<Argument>(instruction, 1);
Result dest_value = operation(lhs, rhs);
WriteCapIntResult(instruction, 0, dest_value);
}
template <typename Register, typename Result>
inline void RVCheriotBinaryOp(const Instruction *instruction,
std::function<Result(Result, Result)> operation) {
Result lhs = generic::GetInstructionSource<Result>(instruction, 0);
Result rhs = generic::GetInstructionSource<Result>(instruction, 1);
Result dest_value = operation(lhs, rhs);
WriteCapIntResult(instruction, 0, dest_value);
}
// Generic helper function for unary instructions.
template <typename Register, typename Result, typename Argument>
inline void RVCheriotUnaryOp(const Instruction *instruction,
std::function<Result(Argument)> operation) {
auto lhs = generic::GetInstructionSource<Argument>(instruction, 0);
Result dest_value = operation(lhs);
WriteCapIntResult(instruction, 0, dest_value);
}
// Helper function for conditional branches.
template <typename RegisterType, typename ValueType>
static inline void RVCheriotBranchConditional(
const Instruction *instruction,
std::function<bool(ValueType, ValueType)> cond) {
using UIntType =
typename std::make_unsigned<typename RegisterType::ValueType>::type;
ValueType a = generic::GetInstructionSource<ValueType>(instruction, 0);
ValueType b = generic::GetInstructionSource<ValueType>(instruction, 1);
if (cond(a, b)) {
UIntType offset = generic::GetInstructionSource<UIntType>(instruction, 2);
UIntType target = offset + instruction->address();
auto *state = static_cast<CheriotState *>(instruction->state());
auto *pcc = state->pcc();
if (!pcc->HasPermission(PB::kPermitExecute)) {
state->HandleCheriRegException(
instruction, pcc->address(),
ExceptionCode::kCapExPermitExecuteViolation, pcc);
return;
}
pcc->set_address(target);
state->set_branch(true);
}
}
// Generic helper function for load instructions.
template <typename Register, typename ValueType>
inline void RVCheriotLoad(const Instruction *instruction) {
using RegVal = typename Register::ValueType;
using URegVal = typename std::make_unsigned<RegVal>::type;
// Bet the capability.
auto *cap_reg = static_cast<CheriotRegister *>(
static_cast<generic::RegisterSourceOperand<RegVal> *>(
instruction->Source(0))
->GetRegister());
URegVal base = cap_reg->address();
RegVal offset = generic::GetInstructionSource<RegVal>(instruction, 1);
URegVal address = base + offset;
auto *state = static_cast<CheriotState *>(instruction->state());
// Check for tag unset.
if (!cap_reg->tag()) {
state->HandleCheriRegException(instruction, instruction->address(),
ExceptionCode::kCapExTagViolation, cap_reg);
return;
}
// Check for sealed.
if (cap_reg->IsSealed()) {
state->HandleCheriRegException(instruction, instruction->address(),
ExceptionCode::kCapExSealViolation, cap_reg);
return;
}
// Check for permissions.
if (!cap_reg->HasPermission(CheriotRegister::kPermitLoad)) {
state->HandleCheriRegException(instruction, instruction->address(),
ExceptionCode::kCapExPermitLoadViolation,
cap_reg);
return;
}
// Check for bounds.
if (!cap_reg->IsInBounds(address, sizeof(ValueType))) {
state->HandleCheriRegException(instruction, instruction->address(),
ExceptionCode::kCapExBoundsViolation,
cap_reg);
return;
}
auto *value_db =
instruction->state()->db_factory()->Allocate(sizeof(ValueType));
value_db->set_latency(0);
auto *context = new riscv::LoadContext(value_db);
state->LoadMemory(instruction, address, value_db, instruction->child(),
context);
context->DecRef();
}
// Generic helper function for load instructions' "child instruction".
template <typename Register, typename ValueType>
inline void RVCheriotLoadChild(const Instruction *instruction) {
using RegVal = typename Register::ValueType;
using URegVal = typename std::make_unsigned<RegVal>::type;
using SRegVal = typename std::make_signed<URegVal>::type;
riscv::LoadContext *context =
static_cast<riscv::LoadContext *>(instruction->context());
if (std::is_signed<ValueType>::value) {
SRegVal value = static_cast<SRegVal>(context->value_db->Get<ValueType>(0));
WriteCapIntResult(instruction, 0, value);
} else {
URegVal value = static_cast<URegVal>(context->value_db->Get<ValueType>(0));
WriteCapIntResult(instruction, 0, value);
}
}
// Generic helper function for store instructions.
template <typename RegisterType, typename ValueType>
inline void RVCheriotStore(const Instruction *instruction) {
using URegVal =
typename std::make_unsigned<typename RegisterType::ValueType>::type;
using SRegVal = typename std::make_signed<URegVal>::type;
ValueType value = generic::GetInstructionSource<ValueType>(instruction, 2);
auto *cap_reg = static_cast<CheriotRegister *>(
static_cast<
generic::RegisterSourceOperand<typename RegisterType::ValueType> *>(
instruction->Source(0))
->GetRegister());
URegVal base = cap_reg->address();
SRegVal offset = generic::GetInstructionSource<SRegVal>(instruction, 1);
URegVal address = base + offset;
auto *state = static_cast<CheriotState *>(instruction->state());
// Check for tag unset.
if (!cap_reg->tag()) {
state->HandleCheriRegException(instruction, instruction->address(),
ExceptionCode::kCapExTagViolation, cap_reg);
return;
}
// Check for sealed.
if (cap_reg->IsSealed()) {
state->HandleCheriRegException(instruction, instruction->address(),
ExceptionCode::kCapExSealViolation, cap_reg);
return;
}
// Check for permissions.
if (!cap_reg->HasPermission(CheriotRegister::kPermitStore)) {
state->HandleCheriRegException(instruction, instruction->address(),
ExceptionCode::kCapExPermitStoreViolation,
cap_reg);
return;
}
// Check for bounds.
if (!cap_reg->IsInBounds(address, sizeof(ValueType))) {
state->HandleCheriRegException(instruction, instruction->address(),
ExceptionCode::kCapExBoundsViolation,
cap_reg);
return;
}
auto *db = state->db_factory()->Allocate(sizeof(ValueType));
db->Set<ValueType>(0, value);
state->StoreMemory(instruction, address, db);
db->DecRef();
}
} // namespace cheriot
} // namespace sim
} // namespace mpact
#endif // MPACT_CHERIOT__RISCV_CHERIOT_INSTRUCTION_HELPERS_H_