2018-01-13 16:22:39 -05:00
|
|
|
// Copyright 2018 yuzu emulator team
|
2016-09-01 23:07:14 -04:00
|
|
|
// Licensed under GPLv2 or any later version
|
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
2018-01-09 16:33:46 -05:00
|
|
|
#include <memory>
|
2020-02-26 14:53:47 -05:00
|
|
|
#include <unordered_map>
|
|
|
|
|
2018-01-09 16:33:46 -05:00
|
|
|
#include <dynarmic/A64/a64.h>
|
2018-07-03 09:28:46 -04:00
|
|
|
#include <dynarmic/A64/exclusive_monitor.h>
|
2016-09-01 23:07:14 -04:00
|
|
|
#include "common/common_types.h"
|
2020-02-26 14:53:47 -05:00
|
|
|
#include "common/hash.h"
|
2016-09-01 23:07:14 -04:00
|
|
|
#include "core/arm/arm_interface.h"
|
2018-07-03 09:28:46 -04:00
|
|
|
#include "core/arm/exclusive_monitor.h"
|
2018-01-09 16:33:46 -05:00
|
|
|
#include "core/arm/unicorn/arm_unicorn.h"
|
|
|
|
|
2019-11-26 17:39:57 -05:00
|
|
|
namespace Memory {
|
|
|
|
class Memory;
|
|
|
|
}
|
|
|
|
|
2018-08-24 21:43:32 -04:00
|
|
|
namespace Core {
|
|
|
|
|
2018-01-09 16:33:46 -05:00
|
|
|
class ARM_Dynarmic_Callbacks;
|
2018-07-03 09:28:46 -04:00
|
|
|
class DynarmicExclusiveMonitor;
|
2019-04-06 18:46:18 -04:00
|
|
|
class System;
|
2016-09-01 23:07:14 -04:00
|
|
|
|
2020-02-26 14:53:47 -05:00
|
|
|
using JitCacheKey = std::pair<Common::PageTable*, std::size_t>;
|
|
|
|
using JitCacheType =
|
|
|
|
std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A64::Jit>, Common::PairHash>;
|
|
|
|
|
2016-09-02 08:40:49 -04:00
|
|
|
class ARM_Dynarmic final : public ARM_Interface {
|
2016-09-01 23:07:14 -04:00
|
|
|
public:
|
2019-04-06 18:46:18 -04:00
|
|
|
ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index);
|
2019-04-04 12:16:56 -04:00
|
|
|
~ARM_Dynarmic() override;
|
2016-09-01 23:07:14 -04:00
|
|
|
|
2017-08-28 21:09:42 -04:00
|
|
|
void SetPC(u64 pc) override;
|
|
|
|
u64 GetPC() const override;
|
|
|
|
u64 GetReg(int index) const override;
|
|
|
|
void SetReg(int index, u64 value) override;
|
2018-09-18 02:49:40 -04:00
|
|
|
u128 GetVectorReg(int index) const override;
|
|
|
|
void SetVectorReg(int index, u128 value) override;
|
|
|
|
u32 GetPSTATE() const override;
|
|
|
|
void SetPSTATE(u32 pstate) override;
|
2018-02-14 12:47:48 -05:00
|
|
|
void Run() override;
|
|
|
|
void Step() override;
|
2017-09-30 14:16:39 -04:00
|
|
|
VAddr GetTlsAddress() const override;
|
|
|
|
void SetTlsAddress(VAddr address) override;
|
2018-07-20 20:57:45 -04:00
|
|
|
void SetTPIDR_EL0(u64 value) override;
|
|
|
|
u64 GetTPIDR_EL0() const override;
|
2016-09-01 23:07:14 -04:00
|
|
|
|
2016-12-22 00:08:09 -05:00
|
|
|
void SaveContext(ThreadContext& ctx) override;
|
|
|
|
void LoadContext(const ThreadContext& ctx) override;
|
2016-09-01 23:07:14 -04:00
|
|
|
|
|
|
|
void PrepareReschedule() override;
|
2018-07-16 06:24:00 -04:00
|
|
|
void ClearExclusiveState() override;
|
2016-09-01 23:07:14 -04:00
|
|
|
|
|
|
|
void ClearInstructionCache() override;
|
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 13:25:54 -04:00
|
|
|
void PageTableChanged(Common::PageTable& new_page_table,
|
|
|
|
std::size_t new_address_space_size_in_bits) override;
|
2018-01-09 16:33:46 -05:00
|
|
|
|
|
|
|
private:
|
2020-02-26 14:53:47 -05:00
|
|
|
std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable& page_table,
|
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 13:25:54 -04:00
|
|
|
std::size_t address_space_bits) const;
|
2018-07-03 09:28:46 -04:00
|
|
|
|
2018-01-09 16:33:46 -05:00
|
|
|
friend class ARM_Dynarmic_Callbacks;
|
|
|
|
std::unique_ptr<ARM_Dynarmic_Callbacks> cb;
|
2020-02-26 14:53:47 -05:00
|
|
|
JitCacheType jit_cache;
|
|
|
|
std::shared_ptr<Dynarmic::A64::Jit> jit;
|
2018-01-09 16:33:46 -05:00
|
|
|
ARM_Unicorn inner_unicorn;
|
2018-02-14 12:47:48 -05:00
|
|
|
|
2018-09-15 09:21:06 -04:00
|
|
|
std::size_t core_index;
|
2018-10-15 08:53:01 -04:00
|
|
|
DynarmicExclusiveMonitor& exclusive_monitor;
|
2016-09-01 23:07:14 -04:00
|
|
|
};
|
2018-07-03 09:28:46 -04:00
|
|
|
|
|
|
|
class DynarmicExclusiveMonitor final : public ExclusiveMonitor {
|
|
|
|
public:
|
2019-11-26 17:39:57 -05:00
|
|
|
explicit DynarmicExclusiveMonitor(Memory::Memory& memory_, std::size_t core_count);
|
2019-04-04 12:16:56 -04:00
|
|
|
~DynarmicExclusiveMonitor() override;
|
2018-07-03 09:28:46 -04:00
|
|
|
|
2018-09-15 09:21:06 -04:00
|
|
|
void SetExclusive(std::size_t core_index, VAddr addr) override;
|
2018-07-03 09:28:46 -04:00
|
|
|
void ClearExclusive() override;
|
|
|
|
|
2018-09-15 09:21:06 -04:00
|
|
|
bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) override;
|
|
|
|
bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) override;
|
|
|
|
bool ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) override;
|
|
|
|
bool ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) override;
|
|
|
|
bool ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) override;
|
2018-07-03 09:28:46 -04:00
|
|
|
|
|
|
|
private:
|
|
|
|
friend class ARM_Dynarmic;
|
|
|
|
Dynarmic::A64::ExclusiveMonitor monitor;
|
2019-11-26 17:39:57 -05:00
|
|
|
Memory::Memory& memory;
|
2018-07-03 09:28:46 -04:00
|
|
|
};
|
2018-08-24 21:43:32 -04:00
|
|
|
|
|
|
|
} // namespace Core
|