blob: b32545fb362cadbff00b511752c201375a32ebf3 [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__DEBUG_COMMAND_SHELL_H_
#define MPACT_CHERIOT__DEBUG_COMMAND_SHELL_H_
#include <cstdint>
#include <deque>
#include <iostream>
#include <istream>
#include <ostream>
#include <string>
#include <utility>
#include <vector>
#include "absl/container/btree_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/functional/any_invocable.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "cheriot/cheriot_debug_interface.h"
#include "cheriot/cheriot_state.h"
#include "mpact/sim/generic/core_debug_interface.h"
#include "mpact/sim/generic/counters_base.h"
#include "mpact/sim/generic/debug_command_shell_interface.h"
#include "mpact/sim/generic/type_helpers.h"
#include "re2/re2.h"
namespace mpact::sim::generic {
class DataBuffer;
} // namespace mpact::sim::generic
namespace mpact {
namespace sim {
namespace cheriot {
using ::mpact::sim::generic::CounterValueSetInterface;
using ::mpact::sim::generic::DebugCommandShellInterface;
using HaltReason = ::mpact::sim::generic::CoreDebugInterface::HaltReason;
using ::mpact::sim::generic::operator*; // NOLINT: used below (clang error).
// This class implements an interactive command shell for a set of cores
// simulated by the MPact simulator using the CoreDebugInterface.
class DebugCommandShell : public DebugCommandShellInterface {
public:
DebugCommandShell();
~DebugCommandShell() override;
// Add core access to the system. All cores must be added before calling Run.
void AddCore(const CoreAccess &core_access) override;
void AddCores(const std::vector<CoreAccess> &core_access) override;
// The run method is the command interpreter. It parses the command strings,
// executes the corresponding commands, displays results and error messages.
void Run(std::istream &is, std::ostream &os) override;
// This adds a custom command to the command interpreter. Usage will be added
// to the standard command usage. The callable will be called before the
// standard commands are processed.
void AddCommand(absl::string_view usage,
CommandFunction command_function) override;
// This adds an action point at the given address with 'function' as the
// action. The string 'name' is used to give it an identifier to use when
// listed.
absl::Status SetActionPoint(uint64_t address, std::string name,
absl::AnyInvocable<void(uint64_t, int)> function);
private:
// Struct to track action point information.
struct ActionPointInfo {
uint64_t address;
int id;
std::string name;
bool is_enabled;
};
// The interrupt listener class is used to track interrupts/exceptions and
// returns from interrupts/exceptions, so that breakpoints can be set on these
// events.
class InterruptListener {
public:
// Convenience class to provide listeners to the counters.
class Listener : public CounterValueSetInterface<int64_t> {
public:
explicit Listener(absl::AnyInvocable<void(int64_t)> callback)
: callback_(std::move(callback)) {}
private:
void SetValue(const int64_t &value) override { callback_(value); }
absl::AnyInvocable<void(int64_t)> callback_;
};
static constexpr uint32_t kInterruptTaken =
*HaltReason::kUserSpecifiedMin + 1;
static constexpr uint32_t kInterruptReturn =
*HaltReason::kUserSpecifiedMin + 2;
static constexpr uint32_t kExceptionTaken =
*HaltReason::kUserSpecifiedMin + 3;
static constexpr uint32_t kExceptionReturn =
*HaltReason::kUserSpecifiedMin + 4;
explicit InterruptListener(CoreAccess *core_access);
void SetEnableExceptions(bool value) { exceptions_enabled_ = value; }
void SetEnableInterrupts(bool value) { interrupts_enabled_ = value; }
bool AreExceptionsEnabled() const { return exceptions_enabled_; }
bool AreInterruptsEnabled() const { return interrupts_enabled_; }
private:
void SetReturnValue(int64_t value);
void SetTakenValue(int64_t value);
CoreAccess *core_access_;
CheriotState *state_ = nullptr;
CheriotDebugInterface *dbg_if_ = nullptr;
bool interrupts_enabled_ = false;
bool exceptions_enabled_ = false;
Listener taken_listener_;
Listener return_listener_;
};
// Helper method to get the interrupt description.
std::string GetInterruptDescription(const InterruptInfo &info) const;
std::string GetExceptionDescription(const InterruptInfo &info) const;
// Helper method for formatting single data buffer value.
std::string FormatSingleDbValue(generic::DataBuffer *db,
const std::string &format, int width,
int index) const;
// Helper method for formatting multiple data buffer values.
std::string FormatAllDbValues(generic::DataBuffer *db,
const std::string &format, int width) const;
// Helper method for writing single data buffer value.
absl::Status WriteSingleValueToDb(absl::string_view str_value,
generic::DataBuffer *db, std::string format,
int width, int index) const;
// Helper method for processing read memory command.
std::string ReadMemory(int core, const std::string &str_value,
absl::string_view format);
// Helper method for processing write memory command.
std::string WriteMemory(int core, const std::string &str_value1,
const std::string &format,
const std::string &str_value2);
// Helper method used to parse a numeric string or use the string as a
// symbol name for lookup in the loader.
absl::StatusOr<uint64_t> GetValueFromString(int core,
const std::string &str_value,
int radix);
// Method to step over call instructions.
absl::Status StepOverCall(int core, std::ostream &os);
// Returns true if the given register name is a known capability register.
bool IsCapabilityRegister(const std::string &reg_name) const;
// Reads and formats a capability register.
std::string FormatCapabilityRegister(int core,
const std::string &reg_name) const;
// Reads and formats a register.
std::string FormatRegister(int core, const std::string &reg_name) const;
// Reads and formats $all registers - stored in reg_vec_.
std::string FormatAllRegisters(int core) const;
// Action point handling.
std::string ListActionPoints();
std::string EnableActionPointN(const std::string &index_str);
std::string DisableActionPointN(const std::string &index_str);
std::string ClearActionPointN(const std::string &index_str);
std::string ClearAllActionPoints();
std::vector<CoreAccess> core_access_;
// Help message displayed for command 'help'.
std::string help_message_;
// Regular expression variables used to parse commands in the shell.
LazyRE2 quit_re_;
LazyRE2 core_re_;
// Run control commands.
LazyRE2 run_re_;
LazyRE2 run_free_re_;
LazyRE2 wait_re_;
LazyRE2 step_1_re_;
LazyRE2 step_n_re_;
LazyRE2 halt_re_;
LazyRE2 next_re_;
// Read/write registers.
LazyRE2 read_reg_re_;
LazyRE2 read_reg2_re_;
LazyRE2 write_reg_re_;
LazyRE2 rd_vreg_re_;
LazyRE2 wr_vreg_re_;
// Read/write memory commands.
LazyRE2 read_mem_re_;
LazyRE2 read_mem2_re_;
LazyRE2 write_mem_re_;
// Breakpoint commands.
LazyRE2 set_break_re_;
LazyRE2 set_break2_re_;
LazyRE2 set_break_n_re_;
LazyRE2 list_break_re_;
LazyRE2 clear_break_n_re_;
LazyRE2 clear_break_re_;
LazyRE2 clear_all_break_re_;
// Watch point commands.
LazyRE2 set_watch_re_;
LazyRE2 set_watch2_re_;
LazyRE2 set_watch_n_re_;
LazyRE2 list_watch_re_;
LazyRE2 clear_watch_re_;
LazyRE2 clear_watch_n_re_;
LazyRE2 clear_all_watch_re_;
// Action point commands.
LazyRE2 list_action_re_;
LazyRE2 enable_action_n_re_;
LazyRE2 disable_action_n_re_;
LazyRE2 clear_action_n_re_;
LazyRE2 clear_all_action_re_;
// Branch trace
LazyRE2 branch_trace_re_;
// Execute commands from a file.
LazyRE2 exec_re_;
// Empty command.
LazyRE2 empty_re_;
// Help command.
LazyRE2 help_re_;
int current_core_;
uint8_t mem_buffer_[kMemBufferSize];
uint8_t tag_buffer_[kMemBufferSize >> 3];
std::vector<CommandFunction> command_functions_;
std::vector<std::string> command_usage_;
absl::flat_hash_set<std::string> capability_registers_;
std::vector<std::string> reg_vector_;
absl::flat_hash_set<std::string> exec_file_names_;
std::deque<std::istream *> command_streams_;
std::deque<std::string> previous_commands_;
std::vector<absl::btree_map<int, ActionPointInfo>> core_action_point_info_;
std::vector<int> core_action_point_id_;
std::vector<InterruptListener *> interrupt_listeners_;
};
} // namespace cheriot
} // namespace sim
} // namespace mpact
#endif // MPACT_CHERIOT__DEBUG_COMMAND_SHELL_H_