mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-23 00:39:17 +01:00
Complete making the kernel thread-safe #2 + Fix Shared Memory Implementation
This commit makes the kernel completely thread-safe and fixes an issue that caused libNX games to not work due to an error with KSharedMemory. In addition, implement GroupMutex to allow the kernel threads to run in parallel but still allow them to not overlap with the JNI thread.
This commit is contained in:
parent
de6d8d8f48
commit
65018aedbc
@ -38,7 +38,6 @@ add_library(skyline SHARED
|
||||
${source_DIR}/skyline/loader/nro.cpp
|
||||
${source_DIR}/skyline/kernel/ipc.cpp
|
||||
${source_DIR}/skyline/kernel/svc.cpp
|
||||
${source_DIR}/skyline/kernel/types/KSyncObject.cpp
|
||||
${source_DIR}/skyline/kernel/types/KProcess.cpp
|
||||
${source_DIR}/skyline/kernel/types/KThread.cpp
|
||||
${source_DIR}/skyline/kernel/types/KSharedMemory.cpp
|
||||
@ -59,5 +58,5 @@ add_library(skyline SHARED
|
||||
${source_DIR}/skyline/services/vi/vi_m.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(skyline vulkan GLESv3 EGL android fmt tinyxml2)
|
||||
target_link_libraries(skyline vulkan android fmt tinyxml2)
|
||||
target_compile_options(skyline PRIVATE -Wno-c++17-extensions)
|
||||
|
155
app/src/main/cpp/.clang-tidy
Normal file
155
app/src/main/cpp/.clang-tidy
Normal file
@ -0,0 +1,155 @@
|
||||
---
|
||||
Checks: 'clang-diagnostic-*,clang-analyzer-*,*, android-*, -bugprone-bool-pointer-implicit-conversion, -cert-env33-c, -cert-dcl50-cpp, -cert-dcl59-cpp, -cppcoreguidelines-no-malloc, -cppcoreguidelines-owning-memory, -cppcoreguidelines-pro-bounds-array-to-pointer-decay, -cppcoreguidelines-pro-bounds-constant-array-index, -cppcoreguidelines-pro-bounds-pointer-arithmetic, -cppcoreguidelines-pro-type-const-cast,
|
||||
-cppcoreguidelines-pro-type-cstyle-cast, -cppcoreguidelines-pro-type-reinterpret-cast, -cppcoreguidelines-pro-type-union-access, -cppcoreguidelines-pro-type-vararg, -cppcoreguidelines-special-member-functions, -fuchsia-*, -google-*, google-default-arguments, google-explicit-constructor, google-runtime-member-string-references, google-runtime-operator, -hicpp-braces-around-statements,
|
||||
-hicpp-braces-around-statements, -hicpp-named-parameter, -hicpp-no-array-decay, -hicpp-no-assembler, -hicpp-no-malloc, -hicpp-function-size, -hicpp-special-member-functions, -hicpp-vararg, -llvm-*, -objc-*, -readability-else-after-return, -readability-implicit-bool-conversion, -readability-named-parameter, -readability-simplify-boolean-expr, -readability-braces-around-statements,
|
||||
-readability-identifier-naming, -readability-function-size, -readability-redundant-member-init, -misc-bool-pointer-implicit-conversion, -misc-definitions-in-headers, -misc-unused-alias-decls, -misc-unused-parameters, -misc-unused-using-decls, -modernize-use-using, -modernize-use-default-member-init, -clang-diagnostic-*, -clang-analyzer-*, -hicpp-signed-bitwise'
|
||||
WarningsAsErrors: ''
|
||||
HeaderFilterRegex: ''
|
||||
AnalyzeTemporaryDtors: false
|
||||
FormatStyle: none
|
||||
CheckOptions:
|
||||
- key: bugprone-argument-comment.StrictMode
|
||||
value: '0'
|
||||
- key: bugprone-assert-side-effect.AssertMacros
|
||||
value: assert
|
||||
- key: bugprone-assert-side-effect.CheckFunctionCalls
|
||||
value: '0'
|
||||
- key: bugprone-dangling-handle.HandleClasses
|
||||
value: 'std::basic_string_view;std::experimental::basic_string_view'
|
||||
- key: bugprone-string-constructor.LargeLengthThreshold
|
||||
value: '8388608'
|
||||
- key: bugprone-string-constructor.WarnOnLargeLength
|
||||
value: '1'
|
||||
- key: cert-err09-cpp.CheckThrowTemporaries
|
||||
value: '1'
|
||||
- key: cert-err61-cpp.CheckThrowTemporaries
|
||||
value: '1'
|
||||
- key: cert-oop11-cpp.IncludeStyle
|
||||
value: llvm
|
||||
- key: cppcoreguidelines-pro-type-member-init.IgnoreArrays
|
||||
value: '0'
|
||||
- key: google-readability-braces-around-statements.ShortStatementLines
|
||||
value: '1'
|
||||
- key: google-readability-function-size.StatementThreshold
|
||||
value: '800'
|
||||
- key: google-readability-namespace-comments.ShortNamespaceLines
|
||||
value: '10'
|
||||
- key: google-readability-namespace-comments.SpacesBeforeComments
|
||||
value: '2'
|
||||
- key: hicpp-member-init.IgnoreArrays
|
||||
value: '0'
|
||||
- key: hicpp-move-const-arg.CheckTriviallyCopyableMove
|
||||
value: '1'
|
||||
- key: hicpp-use-auto.RemoveStars
|
||||
value: '0'
|
||||
- key: hicpp-use-emplace.ContainersWithPushBack
|
||||
value: '::std::vector;::std::list;::std::deque'
|
||||
- key: hicpp-use-emplace.SmartPointers
|
||||
value: '::std::shared_ptr;::std::unique_ptr;::std::auto_ptr;::std::weak_ptr'
|
||||
- key: hicpp-use-emplace.TupleMakeFunctions
|
||||
value: '::std::make_pair;::std::make_tuple'
|
||||
- key: hicpp-use-emplace.TupleTypes
|
||||
value: '::std::pair;::std::tuple'
|
||||
- key: hicpp-use-equals-default.IgnoreMacros
|
||||
value: '1'
|
||||
- key: hicpp-use-noexcept.ReplacementString
|
||||
value: ''
|
||||
- key: hicpp-use-noexcept.UseNoexceptFalse
|
||||
value: '1'
|
||||
- key: hicpp-use-nullptr.NullMacros
|
||||
value: ''
|
||||
- key: misc-misplaced-widening-cast.CheckImplicitCasts
|
||||
value: '0'
|
||||
- key: misc-sizeof-expression.WarnOnSizeOfCompareToConstant
|
||||
value: '1'
|
||||
- key: misc-sizeof-expression.WarnOnSizeOfConstant
|
||||
value: '1'
|
||||
- key: misc-sizeof-expression.WarnOnSizeOfThis
|
||||
value: '1'
|
||||
- key: misc-suspicious-enum-usage.StrictMode
|
||||
value: '0'
|
||||
- key: misc-suspicious-missing-comma.MaxConcatenatedTokens
|
||||
value: '5'
|
||||
- key: misc-suspicious-missing-comma.RatioThreshold
|
||||
value: '0.200000'
|
||||
- key: misc-suspicious-missing-comma.SizeThreshold
|
||||
value: '5'
|
||||
- key: misc-suspicious-string-compare.StringCompareLikeFunctions
|
||||
value: ''
|
||||
- key: misc-suspicious-string-compare.WarnOnImplicitComparison
|
||||
value: '1'
|
||||
- key: misc-suspicious-string-compare.WarnOnLogicalNotComparison
|
||||
value: '0'
|
||||
- key: misc-throw-by-value-catch-by-reference.CheckThrowTemporaries
|
||||
value: '1'
|
||||
- key: modernize-loop-convert.MaxCopySize
|
||||
value: '16'
|
||||
- key: modernize-loop-convert.MinConfidence
|
||||
value: reasonable
|
||||
- key: modernize-loop-convert.NamingStyle
|
||||
value: CamelCase
|
||||
- key: modernize-make-shared.IgnoreMacros
|
||||
value: '1'
|
||||
- key: modernize-make-shared.IncludeStyle
|
||||
value: '0'
|
||||
- key: modernize-make-shared.MakeSmartPtrFunction
|
||||
value: 'std::make_shared'
|
||||
- key: modernize-make-shared.MakeSmartPtrFunctionHeader
|
||||
value: memory
|
||||
- key: modernize-make-unique.IgnoreMacros
|
||||
value: '1'
|
||||
- key: modernize-make-unique.IncludeStyle
|
||||
value: '0'
|
||||
- key: modernize-make-unique.MakeSmartPtrFunction
|
||||
value: 'std::make_unique'
|
||||
- key: modernize-make-unique.MakeSmartPtrFunctionHeader
|
||||
value: memory
|
||||
- key: modernize-pass-by-value.IncludeStyle
|
||||
value: llvm
|
||||
- key: modernize-pass-by-value.ValuesOnly
|
||||
value: '0'
|
||||
- key: modernize-raw-string-literal.ReplaceShorterLiterals
|
||||
value: '0'
|
||||
- key: modernize-replace-auto-ptr.IncludeStyle
|
||||
value: llvm
|
||||
- key: modernize-replace-random-shuffle.IncludeStyle
|
||||
value: llvm
|
||||
- key: modernize-use-auto.RemoveStars
|
||||
value: '0'
|
||||
- key: modernize-use-emplace.ContainersWithPushBack
|
||||
value: '::std::vector;::std::list;::std::deque'
|
||||
- key: modernize-use-emplace.SmartPointers
|
||||
value: '::std::shared_ptr;::std::unique_ptr;::std::auto_ptr;::std::weak_ptr'
|
||||
- key: modernize-use-emplace.TupleMakeFunctions
|
||||
value: '::std::make_pair;::std::make_tuple'
|
||||
- key: modernize-use-emplace.TupleTypes
|
||||
value: '::std::pair;::std::tuple'
|
||||
- key: modernize-use-equals-default.IgnoreMacros
|
||||
value: '1'
|
||||
- key: modernize-use-noexcept.ReplacementString
|
||||
value: ''
|
||||
- key: modernize-use-noexcept.UseNoexceptFalse
|
||||
value: '1'
|
||||
- key: modernize-use-nullptr.NullMacros
|
||||
value: 'NULL'
|
||||
- key: modernize-use-transparent-functors.SafeMode
|
||||
value: '0'
|
||||
- key: performance-faster-string-find.StringLikeClasses
|
||||
value: 'std::basic_string'
|
||||
- key: performance-for-range-copy.WarnOnAllAutoCopies
|
||||
value: '0'
|
||||
- key: performance-inefficient-string-concatenation.StrictMode
|
||||
value: '0'
|
||||
- key: performance-inefficient-vector-operation.VectorLikeClasses
|
||||
value: '::std::vector'
|
||||
- key: performance-move-const-arg.CheckTriviallyCopyableMove
|
||||
value: '1'
|
||||
- key: performance-move-constructor-init.IncludeStyle
|
||||
value: llvm
|
||||
- key: performance-type-promotion-in-math-fn.IncludeStyle
|
||||
value: llvm
|
||||
- key: performance-unnecessary-value-param.IncludeStyle
|
||||
value: llvm
|
||||
- key: readability-static-accessed-through-instance.NameSpecifierNestingThreshold
|
||||
value: '3'
|
||||
...
|
@ -7,7 +7,7 @@
|
||||
bool Halt;
|
||||
jobject Surface;
|
||||
uint FaultCount;
|
||||
skyline::Mutex jniMtx;
|
||||
skyline::GroupMutex jniMtx;
|
||||
|
||||
void signalHandler(int signal) {
|
||||
syslog(LOG_ERR, "Halting program due to signal: %s", strsignal(signal));
|
||||
@ -56,13 +56,13 @@ extern "C" JNIEXPORT void Java_emu_skyline_GameActivity_executeRom(JNIEnv *env,
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void Java_emu_skyline_GameActivity_setHalt(JNIEnv *env, jobject instance, jboolean halt) {
|
||||
jniMtx.lock();
|
||||
jniMtx.lock(skyline::GroupMutex::Group::Group2);
|
||||
Halt = halt;
|
||||
jniMtx.unlock();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void Java_emu_skyline_GameActivity_setSurface(JNIEnv *env, jobject instance, jobject surface) {
|
||||
jniMtx.lock();
|
||||
jniMtx.lock(skyline::GroupMutex::Group::Group2);
|
||||
if (!env->IsSameObject(Surface, nullptr))
|
||||
env->DeleteGlobalRef(Surface);
|
||||
if (!env->IsSameObject(surface, nullptr))
|
||||
|
@ -16,6 +16,17 @@ namespace skyline {
|
||||
return !flag.test_and_set(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
void GroupMutex::lock(Group group) {
|
||||
auto none = Group::None;
|
||||
while (!flag.compare_exchange_weak(none, group) && flag != group);
|
||||
num++;
|
||||
}
|
||||
|
||||
void GroupMutex::unlock() {
|
||||
if (!--num)
|
||||
flag.exchange(Group::None);
|
||||
}
|
||||
|
||||
Settings::Settings(const int preferenceFd) {
|
||||
tinyxml2::XMLDocument pref;
|
||||
if (pref.LoadFile(fdopen(preferenceFd, "r")))
|
||||
|
@ -42,7 +42,7 @@ namespace skyline {
|
||||
constexpr u16 BrkRdy = 0xFF; //!< This is reserved for our kernel's to know when a process/thread is ready
|
||||
constexpr u32 TpidrroEl0 = 0x5E83; //!< ID of TPIDRRO_EL0 in MRS
|
||||
constexpr u32 CntfrqEl0 = 0x5F00; //!< ID of CNTFRQ_EL0 in MRS
|
||||
constexpr u32 TegraX1Freq = 0x124F800; //!< The clock frequency of the Tegra X1
|
||||
constexpr u32 TegraX1Freq = 0x124F800; //!< The clock frequency of the Tegra X1 (19.2 MHz)
|
||||
constexpr u32 CntpctEl0 = 0x5F01; //!< ID of CNTPCT_EL0 in MRS
|
||||
constexpr u32 CntvctEl0 = 0x5F02; //!< ID of CNTVCT_EL0 in MRS
|
||||
// Kernel
|
||||
@ -111,7 +111,7 @@ namespace skyline {
|
||||
void lock();
|
||||
|
||||
/**
|
||||
* @brief Lock the mutex if it is unlocked else return
|
||||
* @brief Try to lock the mutex if it is unlocked else return
|
||||
* @return If the mutex was successfully locked or not
|
||||
*/
|
||||
bool try_lock();
|
||||
@ -122,6 +122,36 @@ namespace skyline {
|
||||
void unlock();
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The GroupMutex class is a special type of mutex that allows two groups of users and only allows one group to run in parallel
|
||||
*/
|
||||
class GroupMutex {
|
||||
public:
|
||||
/**
|
||||
* @brief This enumeration holds all the possible owners of the mutex
|
||||
*/
|
||||
enum class Group : u8 {
|
||||
None = 0, //!< No group owns this mutex
|
||||
Group1 = 1, //!< Group 1 owns this mutex
|
||||
Group2 = 2 //!< Group 2 owns this mutex
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Wait on and lock the mutex
|
||||
*/
|
||||
void lock(Group group = Group::Group1);
|
||||
|
||||
/**
|
||||
* @brief Unlock the mutex
|
||||
* @note Undefined behavior in case unlocked by thread in non-owner group
|
||||
*/
|
||||
void unlock();
|
||||
|
||||
private:
|
||||
std::atomic<Group> flag = Group::None; //!< An atomic flag to hold which group holds the mutex
|
||||
std::atomic<u8> num = 0; //!< An atomic u8 keeping track of how many users are holding the mutex
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The Logger class is to write log output to file and logcat
|
||||
*/
|
||||
|
@ -62,8 +62,8 @@ namespace skyline::gpu {
|
||||
u8 *inBlock = inBuffer;
|
||||
u8 *outBlock = outBuffer + (y * strideBytes) + x;
|
||||
for (u32 i = 0; i < 32; i++) {
|
||||
const u32 yT = ((i >> 1) & 0x06) | (i & 0x01); // NOLINT(hicpp-signed-bitwise)
|
||||
const u32 xT = ((i << 3) & 0x10) | ((i << 1) & 0x20); // NOLINT(hicpp-signed-bitwise)
|
||||
const u32 yT = ((i >> 1) & 0x06) | (i & 0x01);
|
||||
const u32 xT = ((i << 3) & 0x10) | ((i << 1) & 0x20);
|
||||
std::memcpy(outBlock + (yT * strideBytes) + xT, inBlock, sizeof(u128));
|
||||
inBlock += sizeof(u128);
|
||||
}
|
||||
|
@ -34,10 +34,6 @@ namespace skyline::gpu {
|
||||
state.process->ReadMemory(dataBuffer.data(), nvBuffer->address + gbpBuffer.offset, gbpBuffer.size);
|
||||
}
|
||||
|
||||
BufferQueue::WaitContext::WaitContext(std::shared_ptr<kernel::type::KThread> thread, DequeueIn input, kernel::ipc::OutputBuffer& buffer) : thread(std::move(thread)), input(input), buffer(buffer) {}
|
||||
|
||||
BufferQueue::DequeueOut::DequeueOut(u32 slot) : slot(slot), _unk0_(0x1), _unk1_(0x24) {}
|
||||
|
||||
BufferQueue::BufferQueue(const DeviceState &state) : state(state) {}
|
||||
|
||||
void BufferQueue::RequestBuffer(Parcel &in, Parcel &out) {
|
||||
@ -50,7 +46,13 @@ namespace skyline::gpu {
|
||||
}
|
||||
|
||||
void BufferQueue::DequeueBuffer(Parcel &in, Parcel &out) {
|
||||
auto *data = reinterpret_cast<DequeueIn *>(in.data.data() + constant::TokenLength);
|
||||
struct Data {
|
||||
u32 format;
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 timestamps;
|
||||
u32 usage;
|
||||
} *data = reinterpret_cast<Data *>(in.data.data() + constant::TokenLength);
|
||||
i64 slot{-1};
|
||||
while (slot == -1) {
|
||||
for (auto &buffer : queue) {
|
||||
@ -62,7 +64,12 @@ namespace skyline::gpu {
|
||||
}
|
||||
sched_yield();
|
||||
}
|
||||
DequeueOut output(static_cast<u32>(slot));
|
||||
struct {
|
||||
u32 slot;
|
||||
u32 _unk_[13];
|
||||
} output{
|
||||
.slot = static_cast<u32>(slot)
|
||||
};
|
||||
out.WriteData(output);
|
||||
state.logger->Debug("DequeueBuffer: Width: {}, Height: {}, Format: {}, Usage: {}, Timestamps: {}, Slot: {}", data->width, data->height, data->format, data->usage, data->timestamps, slot);
|
||||
}
|
||||
@ -118,28 +125,19 @@ namespace skyline::gpu {
|
||||
auto gbpBuffer = reinterpret_cast<GbpBuffer *>(pointer);
|
||||
queue[data->slot] = std::make_shared<Buffer>(state, data->slot, *gbpBuffer);
|
||||
state.gpu->bufferEvent->Signal();
|
||||
state.logger->Debug("SetPreallocatedBuffer: Slot: {}, Magic: 0x{:X}, Width: {}, Height: {}, Stride: {}, Format: {}, Usage: {}, Index: {}, ID: {}, Handle: {}, Offset: 0x{:X}, Block Height: {}, Size: 0x{:X}", data->slot, gbpBuffer->magic, gbpBuffer->width, gbpBuffer->height, gbpBuffer->stride, gbpBuffer->format, gbpBuffer->usage, gbpBuffer->index,gbpBuffer->nvmapId, gbpBuffer->nvmapHandle, gbpBuffer->offset, (1U << gbpBuffer->blockHeightLog2), gbpBuffer->size);
|
||||
}
|
||||
|
||||
void BufferQueue::FreeBuffer(u32 slotNo) {
|
||||
auto &slot = queue.at(slotNo);
|
||||
if (waitVec.empty())
|
||||
slot->status = BufferStatus::Free;
|
||||
else {
|
||||
auto context = waitVec.begin();
|
||||
while (context != waitVec.end()) {
|
||||
if (slot->resolution.width == context->input.width && slot->resolution.height == context->input.height && slot->gbpBuffer.usage == context->input.usage) {
|
||||
context->thread->WakeUp();
|
||||
gpu::Parcel out(state);
|
||||
DequeueOut output(slotNo);
|
||||
out.WriteData(output);
|
||||
out.WriteParcel(context->buffer);
|
||||
slot->status = BufferStatus::Dequeued;
|
||||
waitVec.erase(context);
|
||||
break;
|
||||
}
|
||||
context++;
|
||||
}
|
||||
}
|
||||
state.logger->Debug("SetPreallocatedBuffer: Slot: {}, Magic: 0x{:X}, Width: {}, Height: {}, Stride: {}, Format: {}, Usage: {}, Index: {}, ID: {}, Handle: {}, Offset: 0x{:X}, Block Height: {}, Size: 0x{:X}",
|
||||
data->slot,
|
||||
gbpBuffer->magic,
|
||||
gbpBuffer->width,
|
||||
gbpBuffer->height,
|
||||
gbpBuffer->stride,
|
||||
gbpBuffer->format,
|
||||
gbpBuffer->usage,
|
||||
gbpBuffer->index,
|
||||
gbpBuffer->nvmapId,
|
||||
gbpBuffer->nvmapHandle,
|
||||
gbpBuffer->offset,
|
||||
(1U << gbpBuffer->blockHeightLog2),
|
||||
gbpBuffer->size);
|
||||
}
|
||||
}
|
||||
|
@ -13,11 +13,11 @@ namespace skyline::gpu {
|
||||
u32 width; //!< The width component of the resolution
|
||||
u32 height; //!< The height component of the resolution
|
||||
|
||||
bool operator==(const Resolution &r) {
|
||||
inline bool operator==(const Resolution &r) {
|
||||
return (width == r.width) && (height == r.height);
|
||||
}
|
||||
|
||||
bool operator!=(const Resolution &r) {
|
||||
inline bool operator!=(const Resolution &r) {
|
||||
return !operator==(r);
|
||||
}
|
||||
};
|
||||
@ -58,8 +58,7 @@ namespace skyline::gpu {
|
||||
enum class BufferStatus {
|
||||
Free,
|
||||
Dequeued,
|
||||
Queued,
|
||||
Acquired
|
||||
Queued
|
||||
};
|
||||
|
||||
/**
|
||||
@ -106,7 +105,6 @@ namespace skyline::gpu {
|
||||
GbpBuffer gbpBuffer; //!< The information about the underlying buffer
|
||||
BufferStatus status{BufferStatus::Free}; //!< The status of this buffer
|
||||
std::vector<u8> dataBuffer; //!< The vector holding the actual pixel data
|
||||
std::vector<u8> swizzBuffer; //!< The vector holding the swizzled pixel data
|
||||
std::shared_ptr<device::NvMap::NvMapObject> nvBuffer{}; //!< A shared pointer to the buffer's nvmap object
|
||||
|
||||
/**
|
||||
@ -123,55 +121,12 @@ namespace skyline::gpu {
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This holds the state of all the buffers used by the guest application
|
||||
* @brief This is used to manage and queue up all display buffers to be shown
|
||||
*/
|
||||
class BufferQueue {
|
||||
private:
|
||||
const DeviceState &state; //!< The state of the device
|
||||
|
||||
/**
|
||||
* @brief This is the input struct for DequeueBuffer
|
||||
*/
|
||||
struct DequeueIn {
|
||||
u32 format;
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 timestamps;
|
||||
u32 usage;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This is the output struct for DequeueBuffer
|
||||
*/
|
||||
struct DequeueOut {
|
||||
u32 slot; //!< The slot of the dequeued buffer
|
||||
u32 _unk0_;
|
||||
u32 _unk1_;
|
||||
u32 _unk2_[11]{};
|
||||
|
||||
/**
|
||||
* @param slot The slot of the dequeued buffer
|
||||
*/
|
||||
DequeueOut(u32 slot);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This holds the context of a thread waiting on a buffer
|
||||
*/
|
||||
struct WaitContext {
|
||||
std::shared_ptr<kernel::type::KThread> thread; //!< The thread that is waiting on a buffer
|
||||
DequeueIn input; //!< The input of DequeueBuffer
|
||||
kernel::ipc::OutputBuffer buffer; //!< The output buffer to write the parcel into
|
||||
|
||||
/**
|
||||
* @param thread The thread that is waiting on a buffer
|
||||
* @param input The input of DequeueBuffer
|
||||
* @param buffer The output buffer to write the parcel into
|
||||
*/
|
||||
WaitContext(std::shared_ptr<kernel::type::KThread> thread, DequeueIn input, kernel::ipc::OutputBuffer& buffer);
|
||||
};
|
||||
std::vector<WaitContext> waitVec; //!< A vector of shared pointers to threads waiting on a buffer
|
||||
|
||||
public:
|
||||
std::unordered_map<u32, std::shared_ptr<Buffer>> queue; //!< A vector of shared pointers to all the queued buffers
|
||||
std::queue<std::shared_ptr<Buffer>> displayQueue; //!< A queue of all the buffers to be posted to the display
|
||||
@ -210,6 +165,8 @@ namespace skyline::gpu {
|
||||
* @brief This frees a buffer which is currently queued
|
||||
* @param slotNo The slot of the buffer
|
||||
*/
|
||||
void FreeBuffer(u32 slotNo);
|
||||
inline void FreeBuffer(u32 slotNo) {
|
||||
queue.at(slotNo)->status = BufferStatus::Free;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ namespace skyline::kernel::svc {
|
||||
|
||||
void QueryMemory(DeviceState &state) {
|
||||
memory::MemoryInfo memInfo{};
|
||||
u64 addr = (state.ctx->registers.x2 & ~(PAGE_SIZE - 1));
|
||||
u64 addr = state.ctx->registers.x2 & ~(PAGE_SIZE - 1);
|
||||
bool found = false;
|
||||
for (const auto&[address, region] : state.process->memoryMap) {
|
||||
if (addr >= address && addr < (address + region->size)) {
|
||||
@ -161,12 +161,9 @@ namespace skyline::kernel::svc {
|
||||
case 1:
|
||||
case 2:
|
||||
state.logger->Debug("svcSleepThread: Yielding thread: {}", in);
|
||||
state.thread->status = type::KThread::Status::Runnable; // Will cause the application to awaken on the next iteration of the main loop
|
||||
break;
|
||||
default:
|
||||
state.logger->Debug("svcSleepThread: Thread sleeping for {} ns", in);
|
||||
state.thread->timeout = GetCurrTimeNs() + in;
|
||||
state.thread->status = type::KThread::Status::Sleeping;
|
||||
}
|
||||
}
|
||||
|
||||
@ -298,9 +295,9 @@ namespace skyline::kernel::svc {
|
||||
return;
|
||||
}
|
||||
std::vector<handle_t> waitHandles(numHandles);
|
||||
std::vector<std::shared_ptr<type::KSyncObject>> objectTable;
|
||||
state.process->ReadMemory(waitHandles.data(), state.ctx->registers.x1, numHandles * sizeof(handle_t));
|
||||
std::string handleStr;
|
||||
uint index{};
|
||||
for (const auto &handle : waitHandles) {
|
||||
handleStr += fmt::format("* 0x{:X}\n", handle);
|
||||
auto object = state.process->handleTable.at(handle);
|
||||
@ -312,28 +309,29 @@ namespace skyline::kernel::svc {
|
||||
break;
|
||||
default: {
|
||||
state.ctx->registers.w0 = constant::status::InvHandle;
|
||||
state.thread->ClearWaitObjects();
|
||||
return;
|
||||
}
|
||||
}
|
||||
auto syncObject = std::static_pointer_cast<type::KSyncObject>(object);
|
||||
if (syncObject->signalled) {
|
||||
state.logger->Debug("svcWaitSynchronization: Signalled handle: 0x{:X}", handle);
|
||||
objectTable.push_back(std::static_pointer_cast<type::KSyncObject>(object));
|
||||
}
|
||||
state.logger->Debug("svcWaitSynchronization: Waiting on handles:\n{}Timeout: 0x{:X} ns", handleStr, state.ctx->registers.x3);
|
||||
auto timeout = state.ctx->registers.x3 + GetCurrTimeNs();
|
||||
while (true) {
|
||||
uint index{};
|
||||
for (const auto &object : objectTable) {
|
||||
if (object->signalled) {
|
||||
state.logger->Debug("svcWaitSynchronization: Signalled handle: 0x{:X}", waitHandles.at(index));
|
||||
state.ctx->registers.w0 = constant::status::Success;
|
||||
state.ctx->registers.w1 = index;
|
||||
state.thread->ClearWaitObjects();
|
||||
return;
|
||||
}
|
||||
state.thread->waitObjects.push_back(syncObject);
|
||||
syncObject->waitThreads.emplace_back(state.thread->pid, index);
|
||||
index++;
|
||||
}
|
||||
if (GetCurrTimeNs() >= timeout) {
|
||||
state.ctx->registers.w0 = constant::status::Timeout;
|
||||
return;
|
||||
}
|
||||
}
|
||||
auto timeout = state.ctx->registers.x3;
|
||||
state.logger->Debug("svcWaitSynchronization: Waiting on handles:\n{}Timeout: 0x{:X} ns", handleStr, timeout);
|
||||
if (state.ctx->registers.x3 != std::numeric_limits<u64>::max())
|
||||
state.thread->timeout = GetCurrTimeNs() + timeout;
|
||||
else
|
||||
state.thread->timeout = 0;
|
||||
state.thread->status = type::KThread::Status::WaitSync;
|
||||
}
|
||||
|
||||
void ArbitrateLock(DeviceState &state) {
|
||||
@ -365,6 +363,9 @@ namespace skyline::kernel::svc {
|
||||
|
||||
void WaitProcessWideKeyAtomic(DeviceState &state) {
|
||||
auto mtxAddr = state.ctx->registers.x0;
|
||||
auto condAddr = state.ctx->registers.x1;
|
||||
try {
|
||||
auto &cvar = state.process->condVarMap.at(condAddr);
|
||||
if ((mtxAddr & ((1UL << WORD_BIT) - 1U))) {
|
||||
state.ctx->registers.w0 = constant::status::InvAddress;
|
||||
state.logger->Warn("svcWaitProcessWideKeyAtomic: mutex address not word aligned: {}", mtxAddr);
|
||||
@ -373,41 +374,42 @@ namespace skyline::kernel::svc {
|
||||
auto handle = state.ctx->registers.w2;
|
||||
if (handle != state.thread->handle)
|
||||
throw exception("svcWaitProcessWideKeyAtomic: Called from another thread");
|
||||
state.process->MutexUnlock(mtxAddr);
|
||||
auto condAddr = state.ctx->registers.x1;
|
||||
auto &cvarVec = state.process->condVarMap[condAddr];
|
||||
for (auto thread = cvarVec.begin();; thread++) {
|
||||
if ((*thread)->priority < state.thread->priority) {
|
||||
cvarVec.insert(thread, state.thread);
|
||||
break;
|
||||
} else if (thread + 1 == cvarVec.end()) {
|
||||
cvarVec.push_back(state.thread);
|
||||
break;
|
||||
}
|
||||
}
|
||||
state.process->MutexLock(mtxAddr);
|
||||
auto &mutex = state.process->mutexMap.at(mtxAddr);
|
||||
auto timeout = state.ctx->registers.x3;
|
||||
state.logger->Debug("svcWaitProcessWideKeyAtomic: Mutex: 0x{:X}, Conditional-Variable: 0x:{:X}, Timeout: {} ns", mtxAddr, condAddr, timeout);
|
||||
state.thread->status = type::KThread::Status::WaitCondVar;
|
||||
state.thread->timeout = GetCurrTimeNs() + timeout;
|
||||
timespec spec{};
|
||||
clock_gettime(CLOCK_REALTIME, &spec);
|
||||
u128 time = u128(spec.tv_sec * 1000000000U + spec.tv_nsec) + timeout; // u128 to prevent overflow
|
||||
spec.tv_sec = static_cast<time_t>(time / 1000000000U);
|
||||
spec.tv_nsec = static_cast<long>(time % 1000000000U);
|
||||
if (pthread_cond_timedwait(&cvar, &mutex, &spec) == ETIMEDOUT)
|
||||
state.ctx->registers.w0 = constant::status::Timeout;
|
||||
else
|
||||
state.ctx->registers.w0 = constant::status::Success;
|
||||
state.process->MutexUnlock(mtxAddr);
|
||||
} catch (const std::out_of_range &) {
|
||||
state.logger->Debug("svcWaitProcessWideKeyAtomic: No Conditional-Variable at 0x{:X}", condAddr);
|
||||
state.process->condVarMap[condAddr] = PTHREAD_COND_INITIALIZER;
|
||||
state.ctx->registers.w0 = constant::status::Success;
|
||||
}
|
||||
}
|
||||
|
||||
void SignalProcessWideKey(DeviceState &state) {
|
||||
auto address = state.ctx->registers.x0;
|
||||
auto count = state.ctx->registers.w1;
|
||||
state.ctx->registers.w0 = constant::status::Success;
|
||||
if (!state.process->condVarMap.count(address)) {
|
||||
state.logger->Debug("svcSignalProcessWideKey: No Conditional-Variable at 0x{:X}", address);
|
||||
return;
|
||||
}
|
||||
auto &cvarVec = state.process->condVarMap.at(address);
|
||||
count = std::min(count, static_cast<u32>(cvarVec.size()));
|
||||
for (uint index = 0; index < count; index++)
|
||||
cvarVec[index]->status = type::KThread::Status::Runnable;
|
||||
cvarVec.erase(cvarVec.begin(), cvarVec.begin() + count);
|
||||
if (cvarVec.empty())
|
||||
state.process->condVarMap.erase(address);
|
||||
auto count = state.ctx->registers.x1;
|
||||
try {
|
||||
state.logger->Debug("svcSignalProcessWideKey: Signalling Conditional-Variable at 0x{:X} for {}", address, count);
|
||||
auto &cvar = state.process->condVarMap.at(address);
|
||||
/*
|
||||
for (u32 iter = 0; iter < count; iter++)
|
||||
pthread_cond_signal(&cvar);
|
||||
*/
|
||||
} catch (const std::out_of_range &) {
|
||||
state.logger->Debug("svcSignalProcessWideKey: No Conditional-Variable at 0x{:X}", address);
|
||||
state.process->condVarMap[address] = PTHREAD_COND_INITIALIZER;
|
||||
}
|
||||
state.ctx->registers.w0 = constant::status::Success;
|
||||
}
|
||||
|
||||
void GetSystemTick(DeviceState &state) {
|
||||
|
@ -1,534 +0,0 @@
|
||||
#include "svc.h"
|
||||
#include <os.h>
|
||||
|
||||
namespace skyline::kernel::svc {
|
||||
void SetHeapSize(DeviceState &state) {
|
||||
const u32 size = state.ctx->registers.w1;
|
||||
if(size%constant::HeapSizeDiv != 0) {
|
||||
state.ctx->registers.x1 = 0;
|
||||
state.ctx->registers.w0 = constant::status::InvSize;
|
||||
state.logger->Warn("svcSetHeapSize: 'size' not divisible by 2MB: {}", size);
|
||||
return;
|
||||
}
|
||||
std::shared_ptr<type::KPrivateMemory> heap;
|
||||
try {
|
||||
heap = state.process->memoryRegionMap.at(memory::Region::Heap);
|
||||
heap->Resize(size, true);
|
||||
} catch (const exception &) {
|
||||
state.logger->Warn("svcSetHeapSize: Falling back to recreating memory");
|
||||
state.process->UnmapPrivateRegion(memory::Region::Heap);
|
||||
heap = state.process->MapPrivateRegion(constant::HeapAddr, size, {true, true, false}, memory::Type::Heap, memory::Region::Heap).item;
|
||||
}
|
||||
state.ctx->registers.w0 = constant::status::Success;
|
||||
state.ctx->registers.x1 = heap->address;
|
||||
state.logger->Debug("svcSetHeapSize: Allocated at 0x{:X} for 0x{:X} bytes", heap->address, heap->size);
|
||||
}
|
||||
|
||||
void SetMemoryAttribute(DeviceState &state) {
|
||||
const u64 addr = state.ctx->registers.x0;
|
||||
if((addr & (PAGE_SIZE - 1U))) {
|
||||
state.ctx->registers.w0 = constant::status::InvAddress;
|
||||
state.logger->Warn("svcSetMemoryAttribute: 'address' not page aligned: {}", addr);
|
||||
return;
|
||||
}
|
||||
const u64 size = state.ctx->registers.x1;
|
||||
if((size & (PAGE_SIZE - 1U)) || !size) {
|
||||
state.ctx->registers.w0 = constant::status::InvSize;
|
||||
state.logger->Warn("svcSetMemoryAttribute: 'size' {}: {}", size ? "not page aligned" : "is zero", size);
|
||||
return;
|
||||
}
|
||||
u32 mask = state.ctx->registers.w2;
|
||||
u32 value = state.ctx->registers.w3;
|
||||
u32 maskedValue = mask | value;
|
||||
if(maskedValue != mask) {
|
||||
state.ctx->registers.w0 = constant::status::InvCombination;
|
||||
state.logger->Warn("svcSetMemoryAttribute: 'mask' invalid: 0x{:X}, 0x{:X}", mask, value);
|
||||
return;
|
||||
}
|
||||
memory::MemoryAttribute attribute = *reinterpret_cast<memory::MemoryAttribute*>(&maskedValue);
|
||||
bool found = false;
|
||||
for (const auto&[address, region] : state.process->memoryMap) {
|
||||
if (addr >= address && addr < (address + region->size)) {
|
||||
bool subFound = false;
|
||||
for (auto &subregion : region->regionInfoVec) {
|
||||
if ((address >= subregion.address) && (address < (subregion.address + subregion.size)))
|
||||
subregion.isUncached = attribute.isUncached;
|
||||
subFound = true;
|
||||
break;
|
||||
}
|
||||
if (!subFound)
|
||||
region->regionInfoVec.emplace_back(addr, size, static_cast<bool>(attribute.isUncached));
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found) {
|
||||
state.ctx->registers.w0 = constant::status::InvAddress;
|
||||
state.logger->Warn("svcSetMemoryAttribute: Cannot find memory region: 0x{:X}", addr);
|
||||
return;
|
||||
}
|
||||
state.logger->Debug("svcSetMemoryAttribute: Set caching to {} at 0x{:X} for 0x{:X} bytes", !attribute.isUncached, addr, size);
|
||||
state.ctx->registers.w0 = constant::status::Success;
|
||||
}
|
||||
|
||||
void QueryMemory(DeviceState &state) {
|
||||
memory::MemoryInfo memInfo{};
|
||||
u64 addr = (state.ctx->registers.x2 & ~(PAGE_SIZE-1));
|
||||
bool found = false;
|
||||
for (const auto&[address, region] : state.process->memoryMap) {
|
||||
if (addr >= address && addr < (address + region->size)) {
|
||||
memInfo = region->GetInfo(addr);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
for (const auto &object : state.process->handleTable) {
|
||||
if (object.second->objectType == type::KType::KSharedMemory) {
|
||||
const auto &mem = state.process->GetHandle<type::KSharedMemory>(object.first);
|
||||
if (mem->guest.valid()) {
|
||||
if (addr >= mem->guest.address && addr < (mem->guest.address + mem->guest.size)) {
|
||||
memInfo = mem->GetInfo();
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (object.second->objectType == type::KType::KTransferMemory) {
|
||||
const auto &mem = state.process->GetHandle<type::KTransferMemory>(object.first);
|
||||
if (addr >= mem->cAddress && addr < (mem->cAddress + mem->cSize)) {
|
||||
memInfo = mem->GetInfo();
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
memInfo = {
|
||||
.baseAddress = constant::BaseAddr,
|
||||
.size = static_cast<u64>(constant::BaseEnd),
|
||||
.type = static_cast<u64>(memory::Type::Unmapped)
|
||||
};
|
||||
state.logger->Debug("svcQueryMemory: Cannot find block of address: 0x{:X}", addr);
|
||||
}
|
||||
}
|
||||
state.logger->Debug("svcQueryMemory: Address: 0x{:X}, Size: 0x{:X}, Type: 0x{:X}, Is Uncached: {}, Permissions: {}{}{}", memInfo.baseAddress, memInfo.size, memInfo.type, static_cast<bool>(memInfo.memoryAttribute.isUncached), memInfo.r ? "R" : "-", memInfo.w ? "W" : "-", memInfo.x ? "X" : "-");
|
||||
state.process->WriteMemory<memory::MemoryInfo>(memInfo, state.ctx->registers.x0);
|
||||
state.ctx->registers.w0 = constant::status::Success;
|
||||
}
|
||||
|
||||
void ExitProcess(DeviceState &state) {
|
||||
state.logger->Debug("svcExitProcess: Exiting current process: {}", state.process->pid);
|
||||
state.os->KillThread(state.process->pid);
|
||||
}
|
||||
|
||||
void CreateThread(DeviceState &state) {
|
||||
u64 entryAddr = state.ctx->registers.x1;
|
||||
u64 entryArg = state.ctx->registers.x2;
|
||||
u64 stackTop = state.ctx->registers.x3;
|
||||
u8 priority = static_cast<u8>(state.ctx->registers.w4);
|
||||
if((priority < constant::PriorityNin.first) && (priority > constant::PriorityNin.second)) { // NOLINT(misc-redundant-expression)
|
||||
state.ctx->registers.w0 = constant::status::InvAddress;
|
||||
state.logger->Warn("svcCreateThread: 'priority' invalid: {}", priority);
|
||||
return;
|
||||
}
|
||||
auto thread = state.process->CreateThread(entryAddr, entryArg, stackTop, priority);
|
||||
state.logger->Debug("svcCreateThread: Created thread with handle 0x{:X} (Entry Point: 0x{:X}, Argument: 0x{:X}, Stack Pointer: 0x{:X}, Priority: {}, PID: {})", thread->handle, entryAddr, entryArg, stackTop, priority, thread->pid);
|
||||
state.ctx->registers.w1 = thread->handle;
|
||||
state.ctx->registers.w0 = constant::status::Success;
|
||||
}
|
||||
|
||||
void StartThread(DeviceState &state) {
|
||||
auto handle = state.ctx->registers.w0;
|
||||
try {
|
||||
auto thread = state.process->GetHandle<type::KThread>(handle);
|
||||
state.logger->Debug("svcStartThread: Starting thread: 0x{:X}, PID: {}", handle, thread->pid);
|
||||
thread->Start();
|
||||
} catch (const std::exception&) {
|
||||
state.logger->Warn("svcStartThread: 'handle' invalid: 0x{:X}", handle);
|
||||
state.ctx->registers.w0 = constant::status::InvHandle;
|
||||
}
|
||||
}
|
||||
|
||||
void ExitThread(DeviceState &state) {
|
||||
state.logger->Debug("svcExitProcess: Exiting current thread: {}", state.thread->pid);
|
||||
state.os->KillThread(state.thread->pid);
|
||||
}
|
||||
|
||||
void SleepThread(DeviceState &state) {
|
||||
auto in = state.ctx->registers.x0;
|
||||
switch (in) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
state.logger->Debug("svcSleepThread: Yielding thread: {}", in);
|
||||
state.thread->status = type::KThread::Status::Runnable; // Will cause the application to awaken on the next iteration of the main loop
|
||||
break;
|
||||
default:
|
||||
state.logger->Debug("svcSleepThread: Thread sleeping for {} ns", in);
|
||||
state.thread->timeout = GetCurrTimeNs() + in;
|
||||
state.thread->status = type::KThread::Status::Sleeping;
|
||||
}
|
||||
}
|
||||
|
||||
void GetThreadPriority(DeviceState &state) {
|
||||
auto handle = state.ctx->registers.w0;
|
||||
try {
|
||||
auto priority = state.process->GetHandle<type::KThread>(handle)->priority;
|
||||
state.ctx->registers.w1 = priority;
|
||||
state.ctx->registers.w0 = constant::status::Success;
|
||||
state.logger->Debug("svcGetThreadPriority: Writing thread priority {}", priority);
|
||||
} catch (const std::exception&) {
|
||||
state.logger->Warn("svcGetThreadPriority: 'handle' invalid: 0x{:X}", handle);
|
||||
state.ctx->registers.w0 = constant::status::InvHandle;
|
||||
}
|
||||
}
|
||||
|
||||
void SetThreadPriority(DeviceState &state) {
|
||||
auto handle = state.ctx->registers.w0;
|
||||
auto priority = state.ctx->registers.w1;
|
||||
try {
|
||||
state.process->GetHandle<type::KThread>(handle)->UpdatePriority(static_cast<u8>(priority));
|
||||
state.ctx->registers.w0 = constant::status::Success;
|
||||
state.logger->Debug("svcSetThreadPriority: Setting thread priority to {}", priority);
|
||||
} catch (const std::exception&) {
|
||||
state.logger->Warn("svcSetThreadPriority: 'handle' invalid: 0x{:X}", handle);
|
||||
state.ctx->registers.w0 = constant::status::InvHandle;
|
||||
}
|
||||
}
|
||||
|
||||
void MapSharedMemory(DeviceState &state) {
|
||||
try {
|
||||
auto object = state.process->GetHandle<type::KSharedMemory>(state.ctx->registers.w0);
|
||||
u64 addr = state.ctx->registers.x1;
|
||||
if ((addr & (PAGE_SIZE - 1U))) {
|
||||
state.ctx->registers.w0 = constant::status::InvAddress;
|
||||
state.logger->Warn("svcMapSharedMemory: 'address' not page aligned: 0x{:X}", addr);
|
||||
return;
|
||||
}
|
||||
const u64 size = state.ctx->registers.x2;
|
||||
if ((size & (PAGE_SIZE - 1U)) || !size) {
|
||||
state.ctx->registers.w0 = constant::status::InvSize;
|
||||
state.logger->Warn("svcMapSharedMemory: 'size' {}: {}", size ? "not page aligned" : "is zero", size);
|
||||
return;
|
||||
}
|
||||
u32 perm = state.ctx->registers.w3;
|
||||
memory::Permission permission = *reinterpret_cast<memory::Permission *>(&perm);
|
||||
if ((permission.w && !permission.r) || (permission.x && !permission.r)) {
|
||||
state.logger->Warn("svcMapSharedMemory: 'permission' invalid: {}{}{}", permission.r ? "R" : "-", permission.w ? "W" : "-", permission.x ? "X" : "-");
|
||||
state.ctx->registers.w0 = constant::status::InvPermission;
|
||||
return;
|
||||
}
|
||||
state.logger->Debug("svcMapSharedMemory: Mapping shared memory at 0x{:X} for {} bytes ({}{}{})", addr, size, permission.r ? "R" : "-", permission.w ? "W" : "-", permission.x ? "X" : "-");
|
||||
object->Map(addr, size, permission);
|
||||
state.ctx->registers.w0 = constant::status::Success;
|
||||
} catch (const std::exception &) {
|
||||
state.logger->Warn("svcMapSharedMemory: 'handle' invalid: 0x{:X}", state.ctx->registers.w0);
|
||||
state.ctx->registers.w0 = constant::status::InvHandle;
|
||||
}
|
||||
}
|
||||
|
||||
void CreateTransferMemory(DeviceState &state) {
|
||||
u64 addr = state.ctx->registers.x1;
|
||||
if ((addr & (PAGE_SIZE - 1U))) {
|
||||
state.ctx->registers.w0 = constant::status::InvAddress;
|
||||
state.logger->Warn("svcCreateTransferMemory: 'address' not page aligned: {}", addr);
|
||||
return;
|
||||
}
|
||||
u64 size = state.ctx->registers.x2;
|
||||
if ((size & (PAGE_SIZE - 1U)) || !size) {
|
||||
state.ctx->registers.w0 = constant::status::InvSize;
|
||||
state.logger->Warn("svcCreateTransferMemory: 'size' {}: {}", size ? "not page aligned" : "is zero", size);
|
||||
return;
|
||||
}
|
||||
u32 perm = state.ctx->registers.w3;
|
||||
memory::Permission permission = *reinterpret_cast<memory::Permission *>(&perm);
|
||||
if ((permission.w && !permission.r) || (permission.x && !permission.r)) {
|
||||
state.logger->Warn("svcCreateTransferMemory: 'permission' invalid: {}{}{}", permission.r ? "R" : "-", permission.w ? "W" : "-", permission.x ? "X" : "-");
|
||||
state.ctx->registers.w0 = constant::status::InvPermission;
|
||||
return;
|
||||
}
|
||||
state.logger->Debug("svcCreateTransferMemory: Creating transfer memory at 0x{:X} for {} bytes ({}{}{})", addr, size, permission.r ? "R" : "-", permission.w ? "W" : "-", permission.x ? "X" : "-");
|
||||
auto shmem = state.process->NewHandle<type::KTransferMemory>(state.process->pid, addr, size, permission);
|
||||
state.ctx->registers.w0 = constant::status::Success;
|
||||
state.ctx->registers.w1 = shmem.handle;
|
||||
}
|
||||
|
||||
void CloseHandle(DeviceState &state) {
|
||||
auto handle = static_cast<handle_t>(state.ctx->registers.w0);
|
||||
try {
|
||||
state.process->handleTable.erase(handle);
|
||||
state.logger->Debug("svcCloseHandle: Closing handle: 0x{:X}", handle);
|
||||
state.ctx->registers.w0 = constant::status::Success;
|
||||
} catch(const std::exception&) {
|
||||
state.logger->Warn("svcCloseHandle: 'handle' invalid: 0x{:X}", handle);
|
||||
state.ctx->registers.w0 = constant::status::InvHandle;
|
||||
}
|
||||
}
|
||||
|
||||
void ResetSignal(DeviceState &state) {
|
||||
auto handle = state.ctx->registers.w0;
|
||||
try {
|
||||
auto &object = state.process->handleTable.at(handle);
|
||||
switch (object->objectType) {
|
||||
case (type::KType::KEvent):
|
||||
std::static_pointer_cast<type::KEvent>(object)->ResetSignal();
|
||||
break;
|
||||
case (type::KType::KProcess):
|
||||
std::static_pointer_cast<type::KProcess>(object)->ResetSignal();
|
||||
break;
|
||||
default: {
|
||||
state.logger->Warn("svcResetSignal: 'handle' type invalid: 0x{:X} ({})", handle, object->objectType);
|
||||
state.ctx->registers.w0 = constant::status::InvHandle;
|
||||
return;
|
||||
}
|
||||
}
|
||||
state.logger->Debug("svcResetSignal: Resetting signal: 0x{:X}", handle);
|
||||
state.ctx->registers.w0 = constant::status::Success;
|
||||
} catch(const std::out_of_range&) {
|
||||
state.logger->Warn("svcResetSignal: 'handle' invalid: 0x{:X}", handle);
|
||||
state.ctx->registers.w0 = constant::status::InvHandle;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void WaitSynchronization(DeviceState &state) {
|
||||
auto numHandles = state.ctx->registers.w2;
|
||||
if (numHandles > constant::MaxSyncHandles) {
|
||||
state.ctx->registers.w0 = constant::status::MaxHandles;
|
||||
return;
|
||||
}
|
||||
std::vector<handle_t> waitHandles(numHandles);
|
||||
state.process->ReadMemory(waitHandles.data(), state.ctx->registers.x1, numHandles * sizeof(handle_t));
|
||||
std::string handleStr;
|
||||
uint index{};
|
||||
for (const auto &handle : waitHandles) {
|
||||
handleStr += fmt::format("* 0x{:X}\n", handle);
|
||||
auto object = state.process->handleTable.at(handle);
|
||||
switch (object->objectType) {
|
||||
case type::KType::KProcess:
|
||||
case type::KType::KThread:
|
||||
case type::KType::KEvent:
|
||||
case type::KType::KSession:
|
||||
break;
|
||||
default: {
|
||||
state.ctx->registers.w0 = constant::status::InvHandle;
|
||||
state.thread->ClearWaitObjects();
|
||||
return;
|
||||
}
|
||||
}
|
||||
auto syncObject = std::static_pointer_cast<type::KSyncObject>(object);
|
||||
if (syncObject->signalled) {
|
||||
state.logger->Debug("svcWaitSynchronization: Signalled handle: 0x{:X}", handle);
|
||||
state.ctx->registers.w0 = constant::status::Success;
|
||||
state.ctx->registers.w1 = index;
|
||||
state.thread->ClearWaitObjects();
|
||||
return;
|
||||
}
|
||||
state.thread->waitObjects.push_back(syncObject);
|
||||
syncObject->waitThreads.emplace_back(state.thread->pid, index);
|
||||
}
|
||||
auto timeout = state.ctx->registers.x3;
|
||||
state.logger->Debug("svcWaitSynchronization: Waiting on handles:\n{}Timeout: 0x{:X} ns", handleStr, timeout);
|
||||
if (state.ctx->registers.x3 != std::numeric_limits<u64>::max())
|
||||
state.thread->timeout = GetCurrTimeNs() + timeout;
|
||||
else
|
||||
state.thread->timeout = 0;
|
||||
state.thread->status = type::KThread::Status::WaitSync;
|
||||
}
|
||||
|
||||
void ArbitrateLock(DeviceState &state) {
|
||||
auto addr = state.ctx->registers.x1;
|
||||
if((addr & ((1UL << WORD_BIT) - 1U))) {
|
||||
state.ctx->registers.w0 = constant::status::InvAddress;
|
||||
state.logger->Warn("svcArbitrateLock: 'address' not word aligned: {}", addr);
|
||||
return;
|
||||
}
|
||||
auto handle = state.ctx->registers.w2;
|
||||
if (handle != state.thread->handle)
|
||||
throw exception("svcArbitrateLock: Called from another thread");
|
||||
state.logger->Debug("svcArbitrateLock: Locking mutex at 0x{:X} for thread 0x{:X}", addr, handle);
|
||||
state.process->MutexLock(addr);
|
||||
state.ctx->registers.w0 = constant::status::Success;
|
||||
}
|
||||
|
||||
void ArbitrateUnlock(DeviceState &state) {
|
||||
auto addr = state.ctx->registers.x0;
|
||||
if((addr & ((1UL << WORD_BIT) - 1U))) {
|
||||
state.ctx->registers.w0 = constant::status::InvAddress;
|
||||
state.logger->Warn("svcArbitrateUnlock: 'address' not word aligned: {}", addr);
|
||||
return;
|
||||
}
|
||||
state.logger->Debug("svcArbitrateUnlock: Unlocking mutex at 0x{:X}", addr);
|
||||
state.process->MutexUnlock(addr);
|
||||
state.ctx->registers.w0 = constant::status::Success;
|
||||
}
|
||||
|
||||
void WaitProcessWideKeyAtomic(DeviceState &state) {
|
||||
auto mtxAddr = state.ctx->registers.x0;
|
||||
if((mtxAddr & ((1UL << WORD_BIT) - 1U))) {
|
||||
state.ctx->registers.w0 = constant::status::InvAddress;
|
||||
state.logger->Warn("svcWaitProcessWideKeyAtomic: mutex address not word aligned: {}", mtxAddr);
|
||||
return;
|
||||
}
|
||||
auto handle = state.ctx->registers.w2;
|
||||
if (handle != state.thread->handle)
|
||||
throw exception("svcWaitProcessWideKeyAtomic: Called from another thread");
|
||||
state.process->MutexUnlock(mtxAddr);
|
||||
auto condAddr = state.ctx->registers.x1;
|
||||
auto &cvarVec = state.process->condVarMap[condAddr];
|
||||
for (auto thread = cvarVec.begin();; thread++) {
|
||||
if ((*thread)->priority < state.thread->priority) {
|
||||
cvarVec.insert(thread, state.thread);
|
||||
break;
|
||||
} else if (thread + 1 == cvarVec.end()) {
|
||||
cvarVec.push_back(state.thread);
|
||||
break;
|
||||
}
|
||||
}
|
||||
auto timeout = state.ctx->registers.x3;
|
||||
state.logger->Debug("svcWaitProcessWideKeyAtomic: Mutex: 0x{:X}, Conditional-Variable: 0x:{:X}, Timeout: {} ns", mtxAddr, condAddr, timeout);
|
||||
state.thread->status = type::KThread::Status::WaitCondVar;
|
||||
state.thread->timeout = GetCurrTimeNs() + timeout;
|
||||
state.ctx->registers.w0 = constant::status::Success;
|
||||
}
|
||||
|
||||
void SignalProcessWideKey(DeviceState &state) {
|
||||
auto address = state.ctx->registers.x0;
|
||||
auto count = state.ctx->registers.w1;
|
||||
state.ctx->registers.w0 = constant::status::Success;
|
||||
if (!state.process->condVarMap.count(address)) {
|
||||
state.logger->Debug("svcSignalProcessWideKey: No Conditional-Variable at 0x{:X}", address);
|
||||
return;
|
||||
}
|
||||
auto &cvarVec = state.process->condVarMap.at(address);
|
||||
count = std::min(count, static_cast<u32>(cvarVec.size()));
|
||||
for (uint index = 0; index < count; index++)
|
||||
cvarVec[index]->status = type::KThread::Status::Runnable;
|
||||
cvarVec.erase(cvarVec.begin(), cvarVec.begin() + count);
|
||||
if (cvarVec.empty())
|
||||
state.process->condVarMap.erase(address);
|
||||
state.logger->Debug("svcSignalProcessWideKey: Signalling Conditional-Variable at 0x{:X} for {}", address, count);
|
||||
}
|
||||
|
||||
void GetSystemTick(DeviceState &state) {
|
||||
u64 tick;
|
||||
asm("STR X1, [SP, #-16]!\n\t"
|
||||
"MRS %0, CNTVCT_EL0\n\t"
|
||||
"MOV X1, #0xF800\n\t"
|
||||
"MOVK X1, #0x124, lsl #16\n\t"
|
||||
"MUL %0, %0, X1\n\t"
|
||||
"MRS X1, CNTFRQ_EL0\n\t"
|
||||
"UDIV %0, %0, X1\n\t"
|
||||
"LDR X1, [SP], #16" : "=r"(tick));
|
||||
state.ctx->registers.x0 = tick;
|
||||
}
|
||||
|
||||
void ConnectToNamedPort(DeviceState &state) {
|
||||
char port[constant::PortSize + 1]{0};
|
||||
state.process->ReadMemory(port, state.ctx->registers.x1, constant::PortSize);
|
||||
handle_t handle{};
|
||||
if (std::strcmp(port, "sm:") == 0)
|
||||
handle = state.os->serviceManager.NewSession(service::Service::sm);
|
||||
else {
|
||||
state.logger->Warn("svcConnectToNamedPort: Connecting to invalid port: '{}'", port);
|
||||
state.ctx->registers.w0 = constant::status::NotFound;
|
||||
return;
|
||||
}
|
||||
state.logger->Debug("svcConnectToNamedPort: Connecting to port '{}' at 0x{:X}", port, handle);
|
||||
state.ctx->registers.w1 = handle;
|
||||
state.ctx->registers.w0 = constant::status::Success;
|
||||
}
|
||||
|
||||
void SendSyncRequest(DeviceState &state) {
|
||||
state.os->serviceManager.SyncRequestHandler(static_cast<handle_t>(state.ctx->registers.x0));
|
||||
state.ctx->registers.w0 = constant::status::Success;
|
||||
}
|
||||
|
||||
void GetThreadId(DeviceState &state) {
|
||||
pid_t pid{};
|
||||
auto handle = state.ctx->registers.w1;
|
||||
if (handle != constant::ThreadSelf) {
|
||||
pid = state.process->GetHandle<type::KThread>(handle)->pid;
|
||||
} else
|
||||
pid = state.thread->pid;
|
||||
state.logger->Debug("svcGetThreadId: Handle: 0x{:X}, PID: {}", handle, pid);
|
||||
state.ctx->registers.x1 = static_cast<u64>(pid);
|
||||
state.ctx->registers.w0 = constant::status::Success;
|
||||
}
|
||||
|
||||
void OutputDebugString(DeviceState &state) {
|
||||
std::string debug(state.ctx->registers.x1, '\0');
|
||||
state.process->ReadMemory(debug.data(), state.ctx->registers.x0, state.ctx->registers.x1);
|
||||
if(debug.back() == '\n')
|
||||
debug.pop_back();
|
||||
state.logger->Info("Debug Output: {}", debug);
|
||||
state.ctx->registers.w0 = constant::status::Success;
|
||||
}
|
||||
|
||||
void GetInfo(DeviceState &state) {
|
||||
auto id0 = state.ctx->registers.w1;
|
||||
auto handle = state.ctx->registers.w2;
|
||||
auto id1 = state.ctx->registers.x3;
|
||||
u64 out{};
|
||||
switch (id0) {
|
||||
case constant::infoState::AllowedCpuIdBitmask:
|
||||
case constant::infoState::AllowedThreadPriorityMask:
|
||||
case constant::infoState::IsCurrentProcessBeingDebugged:
|
||||
case constant::infoState::TitleId:
|
||||
case constant::infoState::PrivilegedProcessId:
|
||||
break;
|
||||
case constant::infoState::AliasRegionBaseAddr:
|
||||
out = constant::MapAddr;
|
||||
break;
|
||||
case constant::infoState::AliasRegionSize:
|
||||
out = constant::MapSize;
|
||||
break;
|
||||
case constant::infoState::HeapRegionBaseAddr:
|
||||
out = state.process->memoryRegionMap.at(memory::Region::Heap)->address;
|
||||
break;
|
||||
case constant::infoState::HeapRegionSize:
|
||||
out = state.process->memoryRegionMap.at(memory::Region::Heap)->size;
|
||||
break;
|
||||
case constant::infoState::TotalMemoryAvailable:
|
||||
out = constant::TotalPhyMem;
|
||||
break;
|
||||
case constant::infoState::TotalMemoryUsage:
|
||||
out = state.process->memoryRegionMap.at(memory::Region::Heap)->address + state.process->mainThreadStackSz + state.process->GetProgramSize();
|
||||
break;
|
||||
case constant::infoState::AddressSpaceBaseAddr:
|
||||
out = constant::BaseAddr;
|
||||
break;
|
||||
case constant::infoState::AddressSpaceSize:
|
||||
out = constant::BaseEnd;
|
||||
break;
|
||||
case constant::infoState::StackRegionBaseAddr:
|
||||
out = state.thread->stackTop;
|
||||
break;
|
||||
case constant::infoState::StackRegionSize:
|
||||
out = state.process->mainThreadStackSz;
|
||||
break;
|
||||
case constant::infoState::PersonalMmHeapSize:
|
||||
out = constant::TotalPhyMem;
|
||||
break;
|
||||
case constant::infoState::PersonalMmHeapUsage:
|
||||
out = state.process->memoryRegionMap.at(memory::Region::Heap)->address + state.process->mainThreadStackSz;
|
||||
break;
|
||||
case constant::infoState::TotalMemoryAvailableWithoutMmHeap:
|
||||
out = constant::TotalPhyMem; // TODO: NPDM specifies SystemResourceSize, subtract that from this
|
||||
break;
|
||||
case constant::infoState::TotalMemoryUsedWithoutMmHeap:
|
||||
out = state.process->memoryRegionMap.at(memory::Region::Heap)->address + state.process->mainThreadStackSz; // TODO: Same as above
|
||||
break;
|
||||
case constant::infoState::UserExceptionContextAddr:
|
||||
out = state.process->tlsPages[0]->Get(0);
|
||||
break;
|
||||
default:
|
||||
state.logger->Warn("svcGetInfo: Unimplemented case ID0: {}, ID1: {}", id0, id1);
|
||||
state.ctx->registers.w0 = constant::status::Unimpl;
|
||||
return;
|
||||
}
|
||||
state.logger->Debug("svcGetInfo: ID0: {}, ID1: {}, Out: 0x{:X}", id0, id1, out);
|
||||
state.ctx->registers.x1 = out;
|
||||
state.ctx->registers.w0 = constant::status::Success;
|
||||
}
|
||||
}
|
@ -110,6 +110,7 @@ namespace skyline {
|
||||
* @brief Stalls a thread till a KSyncObject signals or the timeout has ended (https://switchbrew.org/wiki/SVC#svcWaitSynchronization)
|
||||
*/
|
||||
void WaitSynchronization(DeviceState &state);
|
||||
|
||||
/**
|
||||
* @brief Locks a specified mutex
|
||||
*/
|
||||
|
@ -13,16 +13,6 @@ namespace skyline::kernel::type {
|
||||
*/
|
||||
KEvent(const DeviceState &state) : KSyncObject(state, KType::KEvent) {}
|
||||
|
||||
/**
|
||||
* @brief Signals all threads waiting on this object
|
||||
*/
|
||||
virtual inline void Signal() {
|
||||
if (!signalled) {
|
||||
KSyncObject::Signal();
|
||||
signalled = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Resets the KEvent to an unsignalled state
|
||||
*/
|
||||
|
@ -9,7 +9,7 @@ namespace skyline::kernel::type {
|
||||
fregs.x0 = dstAddress;
|
||||
fregs.x1 = size;
|
||||
fregs.x2 = static_cast<u64>(permission.Get());
|
||||
fregs.x3 = static_cast<u64>(MAP_PRIVATE | MAP_ANONYMOUS | ((dstAddress) ? MAP_FIXED : 0)); // NOLINT(hicpp-signed-bitwise)
|
||||
fregs.x3 = static_cast<u64>(MAP_PRIVATE | MAP_ANONYMOUS | ((dstAddress) ? MAP_FIXED : 0));
|
||||
fregs.x4 = static_cast<u64>(-1);
|
||||
fregs.x8 = __NR_mmap;
|
||||
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, thread);
|
||||
@ -86,6 +86,7 @@ namespace skyline::kernel::type {
|
||||
fregs.x8 = __NR_munmap;
|
||||
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.process->pid);
|
||||
}
|
||||
} catch (const std::exception &) {}
|
||||
} catch (const std::exception &) {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -3,7 +3,6 @@
|
||||
#include <os.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <asm/unistd.h>
|
||||
|
||||
namespace skyline::kernel::type {
|
||||
KProcess::TlsPage::TlsPage(u64 address) : address(address) {}
|
||||
@ -46,13 +45,14 @@ namespace skyline::kernel::type {
|
||||
state.nce->WaitThreadInit(thread);
|
||||
thread->tls = GetTlsSlot();
|
||||
MapPrivateRegion(constant::HeapAddr, constant::DefHeapSize, {true, true, false}, memory::Type::Heap, memory::Region::Heap);
|
||||
memFd = open(fmt::format("/proc/{}/mem", pid).c_str(), O_RDWR | O_CLOEXEC); // NOLINT(hicpp-signed-bitwise)
|
||||
memFd = open(fmt::format("/proc/{}/mem", pid).c_str(), O_RDWR | O_CLOEXEC);
|
||||
if (memFd == -1)
|
||||
throw exception("Cannot open file descriptor to /proc/{}/mem, \"{}\"", pid, strerror(errno));
|
||||
}
|
||||
|
||||
KProcess::~KProcess() {
|
||||
close(memFd);
|
||||
status = Status::Exiting;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -64,7 +64,7 @@ namespace skyline::kernel::type {
|
||||
}
|
||||
|
||||
u64 CreateThreadFunc(u64 stackTop) {
|
||||
pid_t pid = clone(&ExecuteChild, reinterpret_cast<void *>(stackTop), CLONE_THREAD | CLONE_SIGHAND | CLONE_PTRACE | CLONE_FS | CLONE_VM | CLONE_FILES | CLONE_IO, nullptr); // NOLINT(hicpp-signed-bitwise)
|
||||
pid_t pid = clone(&ExecuteChild, reinterpret_cast<void *>(stackTop), CLONE_THREAD | CLONE_SIGHAND | CLONE_PTRACE | CLONE_FS | CLONE_VM | CLONE_FILES | CLONE_IO, nullptr);
|
||||
return static_cast<u64>(pid);
|
||||
}
|
||||
|
||||
@ -85,7 +85,7 @@ namespace skyline::kernel::type {
|
||||
threadMap[pid] = process;
|
||||
return process;
|
||||
*/
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void KProcess::ReadMemory(void *destination, u64 offset, size_t size) const {
|
||||
@ -96,10 +96,6 @@ namespace skyline::kernel::type {
|
||||
pwrite64(memFd, source, size, offset);
|
||||
}
|
||||
|
||||
int KProcess::GetMemoryFd() const {
|
||||
return memFd;
|
||||
}
|
||||
|
||||
KProcess::HandleOut<KPrivateMemory> KProcess::MapPrivateRegion(u64 address, size_t size, const memory::Permission perms, const memory::Type type, const memory::Region region) {
|
||||
auto mem = NewHandle<KPrivateMemory>(address, size, perms, type, threadMap.at(pid));
|
||||
memoryMap[mem.item->address] = mem.item;
|
||||
@ -123,44 +119,28 @@ namespace skyline::kernel::type {
|
||||
}
|
||||
|
||||
void KProcess::MutexLock(u64 address) {
|
||||
auto mtxVec = state.process->mutexMap[address];
|
||||
u32 mtxVal = state.process->ReadMemory<u32>(address);
|
||||
if (mtxVec.empty()) {
|
||||
try {
|
||||
auto mtx = mutexMap.at(address);
|
||||
pthread_mutex_lock(&mtx);
|
||||
u32 mtxVal = ReadMemory<u32>(address);
|
||||
mtxVal = (mtxVal & ~constant::MtxOwnerMask) | state.thread->handle;
|
||||
state.process->WriteMemory(mtxVal, address);
|
||||
} else {
|
||||
for (auto thread = mtxVec.begin();; thread++) {
|
||||
if ((*thread)->priority < state.thread->priority) {
|
||||
mtxVec.insert(thread, state.thread);
|
||||
break;
|
||||
} else if (thread + 1 == mtxVec.end()) {
|
||||
mtxVec.push_back(state.thread);
|
||||
break;
|
||||
}
|
||||
}
|
||||
state.thread->status = KThread::Status::WaitMutex;
|
||||
WriteMemory(mtxVal, address);
|
||||
} catch (const std::out_of_range &) {
|
||||
mutexMap[address] = PTHREAD_MUTEX_INITIALIZER;
|
||||
}
|
||||
}
|
||||
|
||||
void KProcess::MutexUnlock(u64 address) {
|
||||
auto mtxVec = state.process->mutexMap[address];
|
||||
u32 mtxVal = state.process->ReadMemory<u32>(address);
|
||||
if ((mtxVal & constant::MtxOwnerMask) != state.thread->pid)
|
||||
try {
|
||||
auto mtx = mutexMap.at(address);
|
||||
u32 mtxVal = ReadMemory<u32>(address);
|
||||
if ((mtxVal & constant::MtxOwnerMask) != state.thread->handle)
|
||||
throw exception("A non-owner thread tried to release a mutex");
|
||||
if (mtxVec.empty()) {
|
||||
mtxVal = 0;
|
||||
} else {
|
||||
auto &thread = mtxVec.front();
|
||||
mtxVal = (mtxVal & ~constant::MtxOwnerMask) | thread->handle;
|
||||
thread->status = KThread::Status::Runnable;
|
||||
mtxVec.erase(mtxVec.begin());
|
||||
if (!mtxVec.empty())
|
||||
mtxVal |= (~constant::MtxOwnerMask);
|
||||
}
|
||||
state.process->WriteMemory(mtxVal, address);
|
||||
}
|
||||
|
||||
void KProcess::ResetSignal() {
|
||||
signalled = false;
|
||||
WriteMemory(mtxVal, address);
|
||||
pthread_mutex_unlock(&mtx);
|
||||
} catch (const std::out_of_range &) {
|
||||
mutexMap[address] = PTHREAD_MUTEX_INITIALIZER;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "KSharedMemory.h"
|
||||
#include "KSession.h"
|
||||
#include "KEvent.h"
|
||||
#include <condition_variable>
|
||||
|
||||
namespace skyline::kernel::type {
|
||||
/**
|
||||
@ -70,10 +71,9 @@ namespace skyline::kernel::type {
|
||||
std::unordered_map<memory::Region, std::shared_ptr<KPrivateMemory>> memoryRegionMap; //!< A mapping from every MemoryRegion to a shared pointer of it's corresponding KPrivateMemory
|
||||
std::unordered_map<handle_t, std::shared_ptr<KObject>> handleTable; //!< A mapping from a handle_t to it's corresponding KObject which is the actual underlying object
|
||||
std::unordered_map<pid_t, std::shared_ptr<KThread>> threadMap; //!< A mapping from a PID to it's corresponding KThread object
|
||||
std::unordered_map<u64, std::vector<std::shared_ptr<KThread>>> mutexMap; //!< A map from a mutex's address to a vector of threads waiting on it (Sorted by priority)
|
||||
std::unordered_map<u64, std::vector<std::shared_ptr<KThread>>> condVarMap; //!< A map from a conditional variable's address to a vector of threads waiting on it (Sorted by priority)
|
||||
std::unordered_map<u64, pthread_mutex_t> mutexMap; //!< A map from a mutex's address to a vector of threads waiting on it
|
||||
std::unordered_map<u64, pthread_cond_t> condVarMap; //!< A map from a conditional variable's address to a vector of threads waiting on it
|
||||
std::vector<std::shared_ptr<TlsPage>> tlsPages; //!< A vector of all allocated TLS pages
|
||||
|
||||
/**
|
||||
* This is used as the output for functions that return created kernel objects
|
||||
* @tparam objectClass The class of the kernel object
|
||||
@ -150,12 +150,6 @@ namespace skyline::kernel::type {
|
||||
*/
|
||||
void WriteMemory(void *source, u64 offset, size_t size) const;
|
||||
|
||||
/**
|
||||
* @brief Returns the FD of the memory for the process
|
||||
* @return The FD of the memory for the process
|
||||
*/
|
||||
int GetMemoryFd() const;
|
||||
|
||||
/**
|
||||
* @brief Map a chunk of process local memory (private memory)
|
||||
* @param address The address to map to (Can be 0 if address doesn't matter)
|
||||
@ -258,6 +252,8 @@ namespace skyline::kernel::type {
|
||||
/**
|
||||
* @brief This resets the object to an unsignalled state
|
||||
*/
|
||||
void ResetSignal();
|
||||
inline void ResetSignal() {
|
||||
signalled = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
namespace skyline::kernel::type {
|
||||
u64 MapSharedFunc(u64 address, size_t size, u64 perms, u64 fd) {
|
||||
return reinterpret_cast<u64>(mmap(reinterpret_cast<void *>(address), size, static_cast<int>(perms), MAP_SHARED | ((address) ? MAP_FIXED : 0), static_cast<int>(fd), 0)); // NOLINT(hicpp-signed-bitwise)
|
||||
return reinterpret_cast<u64>(mmap(reinterpret_cast<void *>(address), size, static_cast<int>(perms), MAP_SHARED | ((address) ? MAP_FIXED : 0), static_cast<int>(fd), 0));
|
||||
}
|
||||
|
||||
KSharedMemory::KSharedMemory(const DeviceState &state, u64 address, size_t size, const memory::Permission permission, memory::Type type) : type(type), KObject(state, KType::KSharedMemory) {
|
||||
@ -15,7 +15,7 @@ namespace skyline::kernel::type {
|
||||
if (fd < 0)
|
||||
throw exception("An error occurred while creating shared memory: {}", fd);
|
||||
address = reinterpret_cast<u64>(mmap(reinterpret_cast<void *>(address), size, permission.Get(), MAP_SHARED | ((address) ? MAP_FIXED : 0), static_cast<int>(fd), 0));
|
||||
if (address == reinterpret_cast<u64>(MAP_FAILED)) // NOLINT(hicpp-signed-bitwise)
|
||||
if (address == reinterpret_cast<u64>(MAP_FAILED))
|
||||
throw exception("An occurred while mapping shared region: {}", strerror(errno));
|
||||
kernel = {address, size, permission};
|
||||
}
|
||||
@ -25,9 +25,8 @@ namespace skyline::kernel::type {
|
||||
fregs.x0 = address;
|
||||
fregs.x1 = size;
|
||||
fregs.x2 = static_cast<u64>(permission.Get());
|
||||
fregs.x3 = static_cast<u64>(MAP_SHARED | ((address) ? MAP_FIXED : 0)); // NOLINT(hicpp-signed-bitwise)
|
||||
fregs.x3 = static_cast<u64>(MAP_SHARED | ((address) ? MAP_FIXED : 0));
|
||||
fregs.x4 = static_cast<u64>(fd);
|
||||
fregs.x4 = static_cast<u64>(-1);
|
||||
fregs.x8 = __NR_mmap;
|
||||
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.thread->pid);
|
||||
if (fregs.x0 < 0)
|
||||
@ -51,7 +50,8 @@ namespace skyline::kernel::type {
|
||||
}
|
||||
if (kernel.valid())
|
||||
UnmapSharedFunc(kernel.address, kernel.size);
|
||||
} catch (const std::exception &) {}
|
||||
} catch (const std::exception &) {
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
|
@ -1,16 +0,0 @@
|
||||
#include "KSyncObject.h"
|
||||
#include <os.h>
|
||||
|
||||
namespace skyline::kernel::type {
|
||||
KSyncObject::KSyncObject(const skyline::DeviceState &state, skyline::kernel::type::KType type) : KObject(state, type) {}
|
||||
|
||||
KSyncObject::threadInfo::threadInfo(pid_t process, u32 index) : process(process), index(index) {}
|
||||
|
||||
void KSyncObject::Signal() {
|
||||
for (const auto &info : waitThreads) {
|
||||
state.ctx->registers.w1 = info.index;
|
||||
state.process->threadMap.at(info.process)->status = KThread::Status::Runnable;
|
||||
}
|
||||
waitThreads.clear();
|
||||
}
|
||||
}
|
@ -9,27 +9,19 @@ namespace skyline::kernel::type {
|
||||
*/
|
||||
class KSyncObject : public KObject {
|
||||
public:
|
||||
bool signalled{false}; //!< If the current object is signalled (Used by KEvent as it stays signalled till svcClearEvent or svcClearSignal is called)
|
||||
/**
|
||||
* @brief This holds information about a specific thread that's waiting on this object
|
||||
*/
|
||||
struct threadInfo {
|
||||
pid_t process; //!< The PID of the waiting thread
|
||||
u32 index; //!< The index of the object in the wait list
|
||||
|
||||
threadInfo(pid_t process, u32 index);
|
||||
};
|
||||
std::vector<threadInfo> waitThreads; //!< A vector of threads waiting on this object
|
||||
std::atomic<bool> signalled{false}; //!< If the current object is signalled (Used as object stays signalled till the signal is consumed)
|
||||
|
||||
/**
|
||||
* @param state The state of the device
|
||||
* @param type The type of the object
|
||||
*/
|
||||
KSyncObject(const DeviceState &state, skyline::kernel::type::KType type);
|
||||
KSyncObject(const DeviceState &state, skyline::kernel::type::KType type) : KObject(state, type) {};
|
||||
|
||||
/**
|
||||
* @brief A function for calling when a particular KSyncObject is signalled
|
||||
*/
|
||||
virtual void Signal();
|
||||
virtual void Signal() {
|
||||
signalled = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -25,7 +25,6 @@ namespace skyline::kernel::type {
|
||||
void KThread::Kill() {
|
||||
if (status != Status::Dead) {
|
||||
status = Status::Dead;
|
||||
kill(pid, SIGKILL);
|
||||
Signal();
|
||||
}
|
||||
}
|
||||
@ -36,30 +35,4 @@ namespace skyline::kernel::type {
|
||||
if (setpriority(PRIO_PROCESS, static_cast<id_t>(pid), liPriority) == -1)
|
||||
throw exception("Couldn't set process priority to {} for PID: {}", liPriority, pid);
|
||||
}
|
||||
|
||||
void KThread::Sleep() {
|
||||
if (status == Status::Running) {
|
||||
status = Status::Sleeping;
|
||||
timeout = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void KThread::WakeUp() {
|
||||
if (status == Status::Sleeping)
|
||||
status = Status::Runnable;
|
||||
}
|
||||
|
||||
void KThread::ClearWaitObjects() {
|
||||
for (auto &object : waitObjects) {
|
||||
auto iter = object->waitThreads.begin();
|
||||
while (iter != object->waitThreads.end()) {
|
||||
if (iter->process == pid) {
|
||||
object->waitThreads.erase(iter);
|
||||
break;
|
||||
}
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
waitObjects.clear();
|
||||
}
|
||||
}
|
||||
|
@ -17,16 +17,9 @@ namespace skyline::kernel::type {
|
||||
enum class Status {
|
||||
Created, //!< The thread has been created but has not been started yet
|
||||
Running, //!< The thread is running currently
|
||||
Sleeping, //!< The thread is sleeping due to svcSleepThread
|
||||
WaitSync, //!< The thread is waiting for a KSyncObject signal
|
||||
WaitMutex, //!< The thread is waiting on a Mutex
|
||||
WaitCondVar, //!< The thread is waiting on a Conditional Variable
|
||||
Runnable, //!< The thread is ready to run after waiting
|
||||
Dead //!< The thread is dead and not running
|
||||
} status = Status::Created; //!< The state of the thread
|
||||
std::shared_ptr<type::KSharedMemory> ctxMemory; //!< The KSharedMemory of the shared memory allocated by the guest process TLS
|
||||
std::vector<std::shared_ptr<KSyncObject>> waitObjects; //!< A vector holding the objects this thread is waiting for
|
||||
u64 timeout{}; //!< The end of a timeout for svcWaitSynchronization or the end of the sleep period for svcSleepThread
|
||||
handle_t handle; // The handle of the object in the handle table
|
||||
pid_t pid; //!< The PID of the current thread (As in kernel PID and not PGID [In short, Linux implements threads as processes that share a lot of stuff at the kernel level])
|
||||
u64 stackTop; //!< The top of the stack (Where it starts growing downwards from)
|
||||
@ -62,21 +55,11 @@ namespace skyline::kernel::type {
|
||||
*/
|
||||
void Kill();
|
||||
|
||||
/**
|
||||
* @brief This causes this thread to sleep indefinitely (no-op if thread is already sleeping)
|
||||
*/
|
||||
void Sleep();
|
||||
|
||||
/**
|
||||
* @brief This wakes up the thread from it's sleep (no-op if thread is already awake)
|
||||
*/
|
||||
void WakeUp();
|
||||
|
||||
/**
|
||||
* @brief This clears all the objects in the waitObjects vector
|
||||
*/
|
||||
void ClearWaitObjects();
|
||||
|
||||
/**
|
||||
* @brief Update the priority level for the process.
|
||||
* @details Set the priority of the current thread to `priority` using setpriority [https://linux.die.net/man/3/setpriority]. We rescale the priority from Nintendo scale to that of Android.
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
namespace skyline::kernel::type {
|
||||
u64 MapTransferFunc(u64 address, size_t size, u64 perms) {
|
||||
return reinterpret_cast<u64>(mmap(reinterpret_cast<void *>(address), size, static_cast<int>(perms), MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0), -1, 0)); // NOLINT(hicpp-signed-bitwise)
|
||||
return reinterpret_cast<u64>(mmap(reinterpret_cast<void *>(address), size, static_cast<int>(perms), MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0), -1, 0));
|
||||
}
|
||||
|
||||
KTransferMemory::KTransferMemory(const DeviceState &state, pid_t pid, u64 address, size_t size, const memory::Permission permission) : owner(pid), cSize(size), permission(permission), KObject(state, KType::KTransferMemory) {
|
||||
@ -14,7 +14,7 @@ namespace skyline::kernel::type {
|
||||
fregs.x0 = address;
|
||||
fregs.x1 = size;
|
||||
fregs.x2 = static_cast<u64 >(permission.Get());
|
||||
fregs.x3 = static_cast<u64>(MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0)); // NOLINT(hicpp-signed-bitwise)
|
||||
fregs.x3 = static_cast<u64>(MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0));
|
||||
fregs.x4 = static_cast<u64>(-1);
|
||||
fregs.x8 = __NR_mmap;
|
||||
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, pid);
|
||||
@ -22,28 +22,23 @@ namespace skyline::kernel::type {
|
||||
throw exception("An error occurred while mapping shared region in child process");
|
||||
cAddress = fregs.x0;
|
||||
} else {
|
||||
address = MapTransferFunc(address, size, static_cast<u64>(permission.Get()));
|
||||
address = reinterpret_cast<u64>(mmap(reinterpret_cast<void *>(address), size, permission.Get(), MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0), -1, 0));
|
||||
if (reinterpret_cast<void *>(address) == MAP_FAILED)
|
||||
throw exception("An error occurred while mapping transfer memory in kernel");
|
||||
cAddress = address;
|
||||
}
|
||||
}
|
||||
|
||||
u64 UnmapTransferFunc(u64 address, size_t size) {
|
||||
return static_cast<u64>(munmap(reinterpret_cast<void *>(address), size));
|
||||
}
|
||||
|
||||
u64 KTransferMemory::Transfer(pid_t process, u64 address, u64 size) {
|
||||
if (process) {
|
||||
Registers fregs{};
|
||||
fregs.x0 = address;
|
||||
fregs.x1 = size;
|
||||
fregs.x2 = static_cast<u64 >(permission.Get());
|
||||
fregs.x3 = static_cast<u64>(MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0)); // NOLINT(hicpp-signed-bitwise)
|
||||
fregs.x3 = static_cast<u64>(MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0));
|
||||
fregs.x4 = static_cast<u64>(-1);
|
||||
fregs.x8 = __NR_mmap;
|
||||
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, process);
|
||||
// state.nce->ExecuteFunction(reinterpret_cast<void *>(MapTransferFunc), fregs, process);
|
||||
if (fregs.x0 < 0)
|
||||
throw exception("An error occurred while mapping transfer memory in child process");
|
||||
address = fregs.x0;
|
||||
@ -65,11 +60,10 @@ namespace skyline::kernel::type {
|
||||
fregs.x1 = size;
|
||||
fregs.x8 = __NR_munmap;
|
||||
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, owner);
|
||||
// state.nce->ExecuteFunction(reinterpret_cast<void *>(UnmapTransferFunc), fregs, owner);
|
||||
if (fregs.x0 < 0)
|
||||
throw exception("An error occurred while unmapping transfer memory in child process");
|
||||
} else {
|
||||
if (reinterpret_cast<void *>(UnmapTransferFunc(address, size)) == MAP_FAILED)
|
||||
if (reinterpret_cast<void *>(munmap(reinterpret_cast<void *>(address), size)) == MAP_FAILED)
|
||||
throw exception("An error occurred while unmapping transfer memory in kernel");
|
||||
}
|
||||
owner = process;
|
||||
@ -102,8 +96,9 @@ namespace skyline::kernel::type {
|
||||
fregs.x1 = cSize;
|
||||
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs, state.process->pid);
|
||||
}
|
||||
} catch (const std::exception &) {}
|
||||
} catch (const std::exception &) {
|
||||
}
|
||||
} else
|
||||
UnmapTransferFunc(cAddress, cSize);
|
||||
munmap(reinterpret_cast<void *>(cAddress), cSize);
|
||||
}
|
||||
};
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <sched.h>
|
||||
#include <unistd.h>
|
||||
#include "os.h"
|
||||
#include "jvm.h"
|
||||
#include "nce/guest.h"
|
||||
@ -6,14 +7,17 @@
|
||||
#include "kernel/svc.h"
|
||||
|
||||
extern bool Halt;
|
||||
extern skyline::Mutex jniMtx;
|
||||
extern skyline::GroupMutex jniMtx;
|
||||
|
||||
namespace skyline {
|
||||
void NCE::KernelThread(pid_t thread) {
|
||||
try {
|
||||
state.thread = state.process->threadMap.at(thread);
|
||||
state.ctx = reinterpret_cast<ThreadContext *>(state.thread->ctxMemory->guest.address);
|
||||
while (!Halt) {
|
||||
while (true) {
|
||||
std::lock_guard jniGd(jniMtx);
|
||||
if (Halt)
|
||||
break;
|
||||
if (state.ctx->state == ThreadState::WaitKernel) {
|
||||
const u16 svc = static_cast<const u16>(state.ctx->commandId);
|
||||
try {
|
||||
@ -29,6 +33,7 @@ namespace skyline {
|
||||
} else if (state.ctx->state == ThreadState::GuestCrash) {
|
||||
state.logger->Warn("Thread with PID {} has crashed due to signal: {}", thread, strsignal(state.ctx->commandId));
|
||||
ThreadTrace();
|
||||
state.ctx->state = ThreadState::WaitRun;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -37,7 +42,12 @@ namespace skyline {
|
||||
} catch (...) {
|
||||
state.logger->Error("An unknown exception has occurred");
|
||||
}
|
||||
if (thread == state.process->pid) {
|
||||
jniMtx.lock(GroupMutex::Group::Group2);
|
||||
state.os->KillThread(thread);
|
||||
Halt = true;
|
||||
jniMtx.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
NCE::NCE(DeviceState &state) : state(state) {}
|
||||
@ -48,15 +58,25 @@ namespace skyline {
|
||||
}
|
||||
|
||||
void NCE::Execute() {
|
||||
while (!Halt && state.os->process) {
|
||||
while (true) {
|
||||
std::lock_guard jniGd(jniMtx);
|
||||
if (Halt)
|
||||
break;
|
||||
state.os->serviceManager.Loop();
|
||||
state.gpu->Loop();
|
||||
}
|
||||
Halt = false;
|
||||
jniMtx.lock(GroupMutex::Group::Group2);
|
||||
Halt = true;
|
||||
jniMtx.unlock();
|
||||
}
|
||||
|
||||
void ExecuteFunctionCtx(ThreadCall call, Registers &funcRegs, ThreadContext *ctx) {
|
||||
/**
|
||||
* This function will not work if optimizations are enabled as ThreadContext isn't volatile
|
||||
* and due to that is not read on every iteration of the while loop.
|
||||
* However, making ThreadContext or parts of it volatile slows down the applications as a whole.
|
||||
* So, we opted to use the hacky solution and disable optimizations for this single function.
|
||||
*/
|
||||
void ExecuteFunctionCtx(ThreadCall call, Registers &funcRegs, ThreadContext *ctx) __attribute__ ((optnone)) {
|
||||
ctx->commandId = static_cast<u32>(call);
|
||||
Registers registers = ctx->registers;
|
||||
while (ctx->state != ThreadState::WaitInit && ctx->state != ThreadState::WaitKernel);
|
||||
@ -72,10 +92,13 @@ namespace skyline {
|
||||
}
|
||||
|
||||
void NCE::ExecuteFunction(ThreadCall call, Registers &funcRegs, pid_t pid) {
|
||||
if (state.process->status != kernel::type::KProcess::Status::Exiting)
|
||||
ExecuteFunctionCtx(call, funcRegs, reinterpret_cast<ThreadContext *>(state.process->threadMap.at(pid)->ctxMemory->kernel.address));
|
||||
else
|
||||
throw std::out_of_range("The KProcess object is missing");
|
||||
}
|
||||
|
||||
void NCE::WaitThreadInit(std::shared_ptr<kernel::type::KThread> &thread) {
|
||||
void NCE::WaitThreadInit(std::shared_ptr<kernel::type::KThread> &thread) __attribute__ ((optnone)) {
|
||||
auto ctx = reinterpret_cast<ThreadContext *>(thread->ctxMemory->kernel.address);
|
||||
while (ctx->state == ThreadState::NotReady);
|
||||
}
|
||||
@ -98,7 +121,7 @@ namespace skyline {
|
||||
ctx = ctx ? ctx : state.ctx;
|
||||
if (numHist) {
|
||||
std::vector<u32> instrs(numHist);
|
||||
u64 size = (sizeof(u32) * numHist);
|
||||
u64 size = sizeof(u32) * numHist;
|
||||
u64 offset = ctx->pc - size + (2 * sizeof(u32));
|
||||
state.process->ReadMemory(instrs.data(), offset, size);
|
||||
for (auto &instr : instrs) {
|
||||
@ -111,17 +134,22 @@ namespace skyline {
|
||||
offset += sizeof(u32);
|
||||
}
|
||||
}
|
||||
if (ctx->faultAddress)
|
||||
regStr += fmt::format("\nFault Address: 0x{:X}", ctx->faultAddress);
|
||||
if (ctx->sp)
|
||||
regStr += fmt::format("\nStack Pointer: 0x{:X}", ctx->sp);
|
||||
for (u16 index = 0; index < constant::NumRegs - 1; index += 2) {
|
||||
regStr += fmt::format("\nX{}: 0x{:X}, X{}: 0x{:X}", index, ctx->registers.regs[index], index + 1, ctx->registers.regs[index + 1]);
|
||||
}
|
||||
if (numHist) {
|
||||
state.logger->Debug("Process Trace:{}", trace);
|
||||
state.logger->Debug("Raw Instructions: 0x{}\nCPU Context:{}", raw, regStr);
|
||||
state.logger->Debug("Raw Instructions: 0x{}", raw);
|
||||
state.logger->Debug("CPU Context:{}", regStr);
|
||||
} else
|
||||
state.logger->Warn("CPU Context:{}", regStr);
|
||||
state.logger->Debug("CPU Context:{}", regStr);
|
||||
}
|
||||
|
||||
const std::array<u32, 17> cntpctEl0X0 = {
|
||||
const std::array<u32, 18> cntpctEl0X0 = {
|
||||
0xA9BF0BE1, // STP X1, X2, [SP, #-16]!
|
||||
0x3C9F0FE0, // STR Q0, [SP, #-16]!
|
||||
0x3C9F0FE1, // STR Q1, [SP, #-16]!
|
||||
@ -138,10 +166,11 @@ namespace skyline {
|
||||
0x9E790000, // FCVTZU X0, D0
|
||||
0x3CC107E2, // LDR Q2, [SP], #16
|
||||
0x3CC107E1, // LDR Q1, [SP], #16
|
||||
0x3CC107E0, // LDR Q0, [SP], #16
|
||||
0xA8C10BE1, // LDP X1, X2, [SP], #16
|
||||
};
|
||||
|
||||
const std::array<u32, 17> cntpctEl0X1 = {
|
||||
const std::array<u32, 18> cntpctEl0X1 = {
|
||||
0xA9BF0BE0, // STP X0, X2, [SP, #-16]!
|
||||
0x3C9F0FE0, // STR Q0, [SP, #-16]!
|
||||
0x3C9F0FE1, // STR Q1, [SP, #-16]!
|
||||
@ -158,10 +187,11 @@ namespace skyline {
|
||||
0x9E790001, // FCVTZU X0, D0
|
||||
0x3CC107E2, // LDR Q2, [SP], #16
|
||||
0x3CC107E1, // LDR Q1, [SP], #16
|
||||
0x3CC107E0, // LDR Q0, [SP], #16
|
||||
0xA8C10BE0, // LDP X0, X2, [SP], #16
|
||||
};
|
||||
|
||||
std::array<u32, 17> cntpctEl0Xn = {
|
||||
std::array<u32, 18> cntpctEl0Xn = {
|
||||
0xA9BF07E0, // STP X0, X1, [SP, #-16]!
|
||||
0x3C9F0FE0, // STR Q0, [SP, #-16]!
|
||||
0x3C9F0FE1, // STR Q1, [SP, #-16]!
|
||||
@ -178,6 +208,7 @@ namespace skyline {
|
||||
0x00000000, // FCVTZU Xn, D0 (Set at runtime)
|
||||
0x3CC107E2, // LDR Q2, [SP], #16
|
||||
0x3CC107E1, // LDR Q1, [SP], #16
|
||||
0x3CC107E0, // LDR Q0, [SP], #16
|
||||
0xA8C107E0, // LDP X0, X1, [SP], #16
|
||||
};
|
||||
|
||||
@ -268,9 +299,6 @@ namespace skyline {
|
||||
patch.push_back(ldrX0);
|
||||
patch.push_back(bret.raw);
|
||||
} else if (instrMrs->srcReg == constant::CntpctEl0) {
|
||||
instr::Mrs mrs(constant::CntvctEl0, regs::X(instrMrs->destReg));
|
||||
*address = mrs.raw;
|
||||
/*
|
||||
instr::B bjunc(offset);
|
||||
if (instrMrs->destReg == 0)
|
||||
offset += cntpctEl0X0.size() * sizeof(u32);
|
||||
@ -294,7 +322,6 @@ namespace skyline {
|
||||
patch.push_back(instr);
|
||||
}
|
||||
patch.push_back(bret.raw);
|
||||
*/
|
||||
} else if (instrMrs->srcReg == constant::CntfrqEl0) {
|
||||
instr::B bjunc(offset);
|
||||
auto movFreq = instr::MoveU32Reg(static_cast<regs::X>(instrMrs->destReg), constant::TegraX1Freq);
|
||||
@ -311,6 +338,7 @@ namespace skyline {
|
||||
offset -= sizeof(u32);
|
||||
patchOffset -= sizeof(u32);
|
||||
}
|
||||
patch.resize(patch.size() + PAGE_SIZE - 1 & ~(PAGE_SIZE - 1), 0x0);
|
||||
return patch;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include <asm/siginfo.h>
|
||||
#include <signal.h>
|
||||
#include <common.h>
|
||||
#include <csignal>
|
||||
#include <cstdlib>
|
||||
#include <initializer_list> // This is used implicitly
|
||||
#include "guest_common.h"
|
||||
|
||||
#define FORCE_INLINE __attribute__((always_inline)) inline // NOLINT(cppcoreguidelines-macro-usage)
|
||||
@ -95,11 +96,71 @@ namespace skyline::guest {
|
||||
asm("MRS %0, TPIDR_EL0":"=r"(ctx));
|
||||
ctx->pc = pc;
|
||||
ctx->commandId = svc;
|
||||
if (svc == 0xB) { // svcSleepThread
|
||||
switch (ctx->registers.x0) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2: {
|
||||
asm("MOV X0, XZR\n\t"
|
||||
"MOV X1, XZR\n\t"
|
||||
"MOV X2, XZR\n\t"
|
||||
"MOV X3, XZR\n\t"
|
||||
"MOV X4, XZR\n\t"
|
||||
"MOV X5, XZR\n\t"
|
||||
"MOV X8, #124\n\t" // __NR_sched_yield
|
||||
"STR LR, [SP, #-16]!\n\t"
|
||||
"MOV LR, SP\n\t"
|
||||
"SVC #0\n\t"
|
||||
"MOV SP, LR\n\t"
|
||||
"LDR LR, [SP], #16":: : "x0", "x1", "x2", "x3", "x4", "x5", "x8");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
struct timespec spec = {
|
||||
.tv_sec = static_cast<time_t>(ctx->registers.x0 / 1000000000),
|
||||
.tv_nsec = static_cast<long>(ctx->registers.x0 % 1000000000)
|
||||
};
|
||||
volatile register __unused timespec *specPtr asm("x0") = &spec;
|
||||
asm("MOV X1, XZR\n\t"
|
||||
"MOV X2, XZR\n\t"
|
||||
"MOV X3, XZR\n\t"
|
||||
"MOV X4, XZR\n\t"
|
||||
"MOV X5, XZR\n\t"
|
||||
"MOV X8, #101\n\t" // __NR_nanosleep
|
||||
"STR LR, [SP, #-16]!\n\t"
|
||||
"MOV LR, SP\n\t"
|
||||
"SVC #0\n\t"
|
||||
"MOV SP, LR\n\t"
|
||||
"LDR LR, [SP], #16":: : "x0", "x1", "x2", "x3", "x4", "x5", "x8");
|
||||
}
|
||||
}
|
||||
return;
|
||||
} else if (svc == 0x1E) {
|
||||
asm("STP X1, X2, [SP, #-16]!\n\t"
|
||||
"STR Q0, [SP, #-16]!\n\t"
|
||||
"STR Q1, [SP, #-16]!\n\t"
|
||||
"STR Q2, [SP, #-16]!\n\t"
|
||||
"MRS X1, CNTFRQ_EL0\n\t"
|
||||
"MRS X2, CNTVCT_EL0\n\t"
|
||||
"UCVTF D0, X0\n\t"
|
||||
"MOV X1, 87411174408192\n\t"
|
||||
"MOVK X1, 0x4172, LSL 48\n\t"
|
||||
"FMOV D2, X1\n\t"
|
||||
"UCVTF D1, X1\n\t"
|
||||
"FDIV D0, D0, D2\n\t"
|
||||
"FMUL D0, D0, D1\n\t"
|
||||
"FCVTZU %0, D0\n\t"
|
||||
"LDR Q2, [SP], #16\n\t"
|
||||
"LDR Q1, [SP], #16\n\t"
|
||||
"LDR Q0, [SP], #16\n\t"
|
||||
"LDP X1, X2, [SP], #16"::"r"(ctx->registers.x0));
|
||||
return;
|
||||
}
|
||||
while (true) {
|
||||
ctx->state = ThreadState::WaitKernel;
|
||||
while (ctx->state == ThreadState::WaitKernel);
|
||||
if (ctx->state == ThreadState::WaitRun)
|
||||
return;
|
||||
break;
|
||||
else if (ctx->state == ThreadState::WaitFunc) {
|
||||
if (ctx->commandId == static_cast<u32>(ThreadCall::Syscall)) {
|
||||
saveCtxStack();
|
||||
@ -114,18 +175,24 @@ namespace skyline::guest {
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx->state = ThreadState::Running;
|
||||
}
|
||||
|
||||
void signalHandler(int signal, siginfo_t *info, ucontext_t *ucontext) {
|
||||
volatile ThreadContext *ctx;
|
||||
asm("MRS %0, TPIDR_EL0":"=r"(ctx));
|
||||
for (u8 index = 0; index < constant::NumRegs; index++)
|
||||
for (u8 index = 0; index < 30; index++)
|
||||
ctx->registers.regs[index] = ucontext->uc_mcontext.regs[index];
|
||||
ctx->pc = ucontext->uc_mcontext.pc;
|
||||
ctx->commandId = static_cast<u32>(signal);
|
||||
ctx->faultAddress = ucontext->uc_mcontext.fault_address;
|
||||
ctx->sp = ucontext->uc_mcontext.sp;
|
||||
while (true) {
|
||||
ctx->state = ThreadState::GuestCrash;
|
||||
if (ctx->state == ThreadState::WaitRun)
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
void entry(u64 address) {
|
||||
volatile ThreadContext *ctx;
|
||||
|
@ -4,10 +4,17 @@ namespace skyline {
|
||||
namespace guest {
|
||||
constexpr size_t saveCtxSize = 20 * sizeof(u32);
|
||||
constexpr size_t loadCtxSize = 20 * sizeof(u32);
|
||||
constexpr size_t svcHandlerSize = 200 * sizeof(u32);
|
||||
#ifdef NDEBUG
|
||||
constexpr size_t svcHandlerSize = 150 * sizeof(u32);
|
||||
#else
|
||||
constexpr size_t svcHandlerSize = 250 * sizeof(u32);
|
||||
#endif
|
||||
|
||||
void entry(u64 address);
|
||||
|
||||
extern "C" void saveCtx(void);
|
||||
extern "C" void loadCtx(void);
|
||||
|
||||
void svcHandler(u64 pc, u32 svc);
|
||||
}
|
||||
}
|
||||
|
@ -146,5 +146,7 @@ namespace skyline {
|
||||
u64 pc; //!< The program counter register on the guest
|
||||
Registers registers; //!< The general purpose registers on the guest
|
||||
u64 tpidrroEl0; //!< The value for TPIDRRO_EL0 for the current thread
|
||||
u64 faultAddress; //!< The address a fault has occurred at during guest crash
|
||||
u64 sp; //!< The current location of the stack pointer set during guest crash
|
||||
};
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ namespace skyline::kernel {
|
||||
|
||||
std::shared_ptr<type::KProcess> OS::CreateProcess(u64 entry, u64 argument, size_t stackSize) {
|
||||
madvise(reinterpret_cast<void *>(constant::BaseAddr), constant::BaseEnd, MADV_DONTFORK);
|
||||
auto *stack = static_cast<u8 *>(mmap(nullptr, stackSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_NORESERVE | MAP_ANONYMOUS | MAP_STACK, -1, 0)); // NOLINT(hicpp-signed-bitwise)
|
||||
auto *stack = static_cast<u8 *>(mmap(nullptr, stackSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_NORESERVE | MAP_ANONYMOUS | MAP_STACK, -1, 0));
|
||||
madvise(stack, reinterpret_cast<size_t>(stack) + stackSize, MADV_DOFORK);
|
||||
if (stack == MAP_FAILED)
|
||||
throw exception("Failed to allocate stack memory");
|
||||
@ -27,12 +27,13 @@ namespace skyline::kernel {
|
||||
munmap(stack, stackSize);
|
||||
throw exception("Failed to create guard pages");
|
||||
}
|
||||
auto tlsMem = std::make_shared<type::KSharedMemory>(state, 0, (sizeof(ThreadContext) + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1), memory::Permission(true, true, 0), memory::Type::Reserved); // NOLINT(hicpp-signed-bitwise)
|
||||
auto tlsMem = std::make_shared<type::KSharedMemory>(state, 0, (sizeof(ThreadContext) + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1), memory::Permission(true, true, false), memory::Type::Reserved);
|
||||
tlsMem->guest = tlsMem->kernel;
|
||||
madvise(reinterpret_cast<void *>(tlsMem->guest.address), tlsMem->guest.size, MADV_DOFORK);
|
||||
pid_t pid = clone(reinterpret_cast<int (*)(void *)>(&guest::entry), stack + stackSize, CLONE_FILES | CLONE_FS | CLONE_SETTLS | SIGCHLD, reinterpret_cast<void *>(entry), nullptr, reinterpret_cast<void*>(tlsMem->guest.address)); // NOLINT(hicpp-signed-bitwise)
|
||||
pid_t pid = clone(reinterpret_cast<int (*)(void *)>(&guest::entry), stack + stackSize, CLONE_FILES | CLONE_FS | CLONE_SETTLS | SIGCHLD, reinterpret_cast<void *>(entry), nullptr, reinterpret_cast<void *>(tlsMem->guest.address));
|
||||
if (pid == -1)
|
||||
throw exception("Call to clone() has failed: {}", strerror(errno));
|
||||
state.logger->Debug("Successfully created process with PID: {}", pid);
|
||||
process = std::make_shared<kernel::type::KProcess>(state, pid, argument, reinterpret_cast<u64>(stack), stackSize, tlsMem);
|
||||
state.logger->Debug("Successfully created process with PID: {}", pid);
|
||||
return process;
|
||||
@ -41,14 +42,11 @@ namespace skyline::kernel {
|
||||
void OS::KillThread(pid_t pid) {
|
||||
if (process->pid == pid) {
|
||||
state.logger->Debug("Killing process with PID: {}", pid);
|
||||
for (auto&[key, value]: process->threadMap) {
|
||||
value->Kill();
|
||||
}
|
||||
process.reset();
|
||||
for (auto &thread: process->threadMap)
|
||||
thread.second->Kill();
|
||||
} else {
|
||||
state.logger->Debug("Killing thread with TID: {}", pid);
|
||||
process->threadMap.at(pid)->Kill();
|
||||
process->threadMap.erase(pid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user