suyu/src/core/arm/arm_interface.h

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

114 lines
3.4 KiB
C++
Raw Normal View History

// SPDX-FileCopyrightText: 2014 Citra Emulator Project & 2024 suyu Emulator Project
chore: make yuzu REUSE compliant [REUSE] is a specification that aims at making file copyright information consistent, so that it can be both human and machine readable. It basically requires that all files have a header containing copyright and licensing information. When this isn't possible, like when dealing with binary assets, generated files or embedded third-party dependencies, it is permitted to insert copyright information in the `.reuse/dep5` file. Oh, and it also requires that all the licenses used in the project are present in the `LICENSES` folder, that's why the diff is so huge. This can be done automatically with `reuse download --all`. The `reuse` tool also contains a handy subcommand that analyzes the project and tells whether or not the project is (still) compliant, `reuse lint`. Following REUSE has a few advantages over the current approach: - Copyright information is easy to access for users / downstream - Files like `dist/license.md` do not need to exist anymore, as `.reuse/dep5` is used instead - `reuse lint` makes it easy to ensure that copyright information of files like binary assets / images is always accurate and up to date To add copyright information of files that didn't have it I looked up who committed what and when, for each file. As yuzu contributors do not have to sign a CLA or similar I couldn't assume that copyright ownership was of the "yuzu Emulator Project", so I used the name and/or email of the commit author instead. [REUSE]: https://reuse.software Follow-up to 01cf05bc75b1e47beb08937439f3ed9339e7b254
2022-05-15 02:06:02 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
2018-01-09 22:33:46 +01:00
#include <array>
#include <span>
#include <string>
#include <vector>
#include "common/common_funcs.h"
#include "common/common_types.h"
2020-02-29 18:58:50 +01:00
#include "core/hardware_properties.h"
#include "core/hle/kernel/svc_types.h"
core/cpu_core_manager: Create threads separately from initialization. Our initialization process is a little wonky than one would expect when it comes to code flow. We initialize the CPU last, as opposed to hardware, where the CPU obviously needs to be first, otherwise nothing else would work, and we have code that adds checks to get around this. For example, in the page table setting code, we check to see if the system is turned on before we even notify the CPU instances of a page table switch. This results in dead code (at the moment), because the only time a page table switch will occur is when the system is *not* running, preventing the emulated CPU instances from being notified of a page table switch in a convenient manner (technically the code path could be taken, but we don't emulate the process creation svc handlers yet). This moves the threads creation into its own member function of the core manager and restores a little order (and predictability) to our initialization process. Previously, in the multi-threaded cases, we'd kick off several threads before even the main kernel process was created and ready to execute (gross!). Now the initialization process is like so: Initialization: 1. Timers 2. CPU 3. Kernel 4. Filesystem stuff (kind of gross, but can be amended trivially) 5. Applet stuff (ditto in terms of being kind of gross) 6. Main process (will be moved into the loading step in a following change) 7. Telemetry (this should be initialized last in the future). 8. Services (4 and 5 should ideally be alongside this). 9. GDB (gross. Uses namespace scope state. Needs to be refactored into a class or booted altogether). 10. Renderer 11. GPU (will also have its threads created in a separate step in a following change). Which... isn't *ideal* per-se, however getting rid of the wonky intertwining of CPU state initialization out of this mix gets rid of most of the footguns when it comes to our initialization process.
2019-04-09 19:25:54 +02:00
namespace Common {
struct PageTable;
}
namespace Kernel {
enum class DebugWatchpointType : u8;
struct DebugWatchpoint;
class KThread;
class KProcess;
} // namespace Kernel
namespace Core {
using WatchpointArray = std::array<Kernel::DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS>;
2020-02-29 18:58:50 +01:00
// NOTE: these values match the HaltReason enum in Dynarmic
enum class HaltReason : u64 {
StepThread = 0x00000001,
DataAbort = 0x00000004,
BreakLoop = 0x02000000,
SupervisorCall = 0x04000000,
InstructionBreakpoint = 0x08000000,
PrefetchAbort = 0x20000000,
};
DECLARE_ENUM_FLAG_OPERATORS(HaltReason);
enum class Architecture {
AArch64,
AArch32,
};
/// Generic ARMv8 CPU interface
class ArmInterface {
public:
SUYU_NON_COPYABLE(ArmInterface);
SUYU_NON_MOVEABLE(ArmInterface);
explicit ArmInterface(bool uses_wall_clock) : m_uses_wall_clock{uses_wall_clock} {}
virtual ~ArmInterface() = default;
// Perform any backend-specific initialization.
virtual void Initialize() {}
// Runs the CPU until an event happens.
virtual HaltReason RunThread(Kernel::KThread* thread) = 0;
// Runs the CPU for one instruction or until an event happens.
virtual HaltReason StepThread(Kernel::KThread* thread) = 0;
// Admits a backend-specific mechanism to lock the thread context.
virtual void LockThread(Kernel::KThread* thread) {}
virtual void UnlockThread(Kernel::KThread* thread) {}
// Clear the entire instruction cache for this CPU.
virtual void ClearInstructionCache() = 0;
// Clear a range of the instruction cache for this CPU.
virtual void InvalidateCacheRange(u64 addr, std::size_t size) = 0;
// Get the current architecture.
// This returns AArch64 when PSTATE.nRW == 0 and AArch32 when PSTATE.nRW == 1.
virtual Architecture GetArchitecture() const = 0;
// Context accessors.
// These should not be called if the CPU is running.
virtual void GetContext(Kernel::Svc::ThreadContext& ctx) const = 0;
virtual void SetContext(const Kernel::Svc::ThreadContext& ctx) = 0;
virtual void SetTpidrroEl0(u64 value) = 0;
virtual void GetSvcArguments(std::span<uint64_t, 8> args) const = 0;
virtual void SetSvcArguments(std::span<const uint64_t, 8> args) = 0;
virtual u32 GetSvcNumber() const = 0;
2022-04-03 17:29:05 +02:00
void SetWatchpointArray(const WatchpointArray* watchpoints) {
m_watchpoints = watchpoints;
}
2022-07-08 02:06:46 +02:00
// Signal an interrupt for execution to halt as soon as possible.
// It is safe to call this if the CPU is not running.
virtual void SignalInterrupt(Kernel::KThread* thread) = 0;
// Stack trace generation.
void LogBacktrace(Kernel::KProcess* process) const;
// Debug functionality.
virtual const Kernel::DebugWatchpoint* HaltedWatchpoint() const = 0;
virtual void RewindBreakpointInstruction() = 0;
protected:
const Kernel::DebugWatchpoint* MatchingWatchpoint(
u64 addr, u64 size, Kernel::DebugWatchpointType access_type) const;
protected:
const WatchpointArray* m_watchpoints{};
bool m_uses_wall_clock{};
};
} // namespace Core