mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-22 20:09:17 +01:00
Milestone 2 - Memory & IPC Marshalling
This commit introduces a new memory model that supports true shared memory with separate permissions for remote and local processes, implements svcQueryMemory and completes svcGetInfo further, adds IPC support with the IpcRequest and IpcResponse classes.
This commit is contained in:
parent
9e1e06c64b
commit
a54f5ff578
8
.gitignore
vendored
8
.gitignore
vendored
@ -61,7 +61,8 @@ build/
|
|||||||
local.properties
|
local.properties
|
||||||
|
|
||||||
# External native build folder generated in Android Studio 2.2 and later
|
# External native build folder generated in Android Studio 2.2 and later
|
||||||
.externalNativeBuild
|
.externalNativeBuild/
|
||||||
|
.cxx/
|
||||||
|
|
||||||
# IntelliJ
|
# IntelliJ
|
||||||
out/
|
out/
|
||||||
@ -93,6 +94,5 @@ captures/
|
|||||||
# VSCode
|
# VSCode
|
||||||
.vscode/
|
.vscode/
|
||||||
|
|
||||||
# Android Studio
|
# Discord plugin for IntelliJ IDEA
|
||||||
.cxx/
|
.idea\discord.xml
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
<option name="WRAP_WHEN_TYPING_REACHES_RIGHT_MARGIN" value="true" />
|
<option name="WRAP_WHEN_TYPING_REACHES_RIGHT_MARGIN" value="true" />
|
||||||
<option name="SOFT_MARGINS" value="80,140" />
|
<option name="SOFT_MARGINS" value="80,140" />
|
||||||
<Objective-C>
|
<Objective-C>
|
||||||
|
<option name="INDENT_VISIBILITY_KEYWORDS" value="2" />
|
||||||
|
<option name="INDENT_PREPROCESSOR_DIRECTIVE" value="4" />
|
||||||
<option name="INDENT_DIRECTIVE_AS_CODE" value="true" />
|
<option name="INDENT_DIRECTIVE_AS_CODE" value="true" />
|
||||||
<option name="KEEP_STRUCTURES_IN_ONE_LINE" value="true" />
|
<option name="KEEP_STRUCTURES_IN_ONE_LINE" value="true" />
|
||||||
<option name="FUNCTION_PARAMETERS_WRAP" value="0" />
|
<option name="FUNCTION_PARAMETERS_WRAP" value="0" />
|
||||||
@ -31,6 +33,12 @@
|
|||||||
<option name="KEEP_MULTIPLE_EXPRESSIONS_IN_ONE_LINE" value="true" />
|
<option name="KEEP_MULTIPLE_EXPRESSIONS_IN_ONE_LINE" value="true" />
|
||||||
<option name="WRAP_LONG_LINES" value="true" />
|
<option name="WRAP_LONG_LINES" value="true" />
|
||||||
</codeStyleSettings>
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="ObjectiveC">
|
||||||
|
<indentOptions>
|
||||||
|
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||||
|
<option name="LABEL_INDENT_SIZE" value="-1" />
|
||||||
|
</indentOptions>
|
||||||
|
</codeStyleSettings>
|
||||||
<codeStyleSettings language="XML">
|
<codeStyleSettings language="XML">
|
||||||
<arrangement>
|
<arrangement>
|
||||||
<rules>
|
<rules>
|
||||||
|
@ -4,7 +4,4 @@
|
|||||||
<component name="DiscordProjectSettings">
|
<component name="DiscordProjectSettings">
|
||||||
<option name="show" value="true" />
|
<option name="show" value="true" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectNotificationSettings">
|
|
||||||
<option name="askShowProject" value="false" />
|
|
||||||
</component>
|
|
||||||
</project>
|
</project>
|
@ -1,6 +1,5 @@
|
|||||||
cmake_minimum_required(VERSION 3.8)
|
cmake_minimum_required(VERSION 3.8)
|
||||||
project(Lightswitch VERSION 1 LANGUAGES CXX)
|
project(Lightswitch VERSION 1 LANGUAGES CXX)
|
||||||
set_property(GLOBAL PROPERTY CMAKE_CXX_STANDARD 17 PROPERTY CMAKE_CXX_STANDARD_REQUIRED TRUE)
|
|
||||||
|
|
||||||
set(BUILD_TESTING OFF)
|
set(BUILD_TESTING OFF)
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
@ -25,6 +24,8 @@ add_library(lightswitch SHARED
|
|||||||
${source_DIR}/switch/kernel/service.cpp
|
${source_DIR}/switch/kernel/service.cpp
|
||||||
${source_DIR}/switch/kernel/types/KProcess.cpp
|
${source_DIR}/switch/kernel/types/KProcess.cpp
|
||||||
${source_DIR}/switch/kernel/types/KThread.cpp
|
${source_DIR}/switch/kernel/types/KThread.cpp
|
||||||
|
${source_DIR}/switch/kernel/types/KSharedMemory.cpp
|
||||||
|
${source_DIR}/switch/kernel/types/KPrivateMemory.cpp
|
||||||
)
|
)
|
||||||
target_link_libraries(lightswitch fmt tinyxml2 android)
|
target_link_libraries(lightswitch fmt tinyxml2 android)
|
||||||
target_compile_options(lightswitch PRIVATE -Wno-c++17-extensions)
|
target_compile_options(lightswitch PRIVATE -Wno-c++17-extensions)
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
package="emu.lightswitch">
|
package="emu.lightswitch">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.READ_INTERNAL_STORAGE"/>
|
<uses-permission android:name="android.permission.READ_INTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
@ -15,12 +15,13 @@
|
|||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme"
|
android:theme="@style/AppTheme"
|
||||||
tools:ignore="GoogleAppIndexingWarning">
|
tools:ignore="GoogleAppIndexingWarning">
|
||||||
<activity android:name=".LogActivity"
|
<activity
|
||||||
android:label="@string/log"
|
android:name=".LogActivity"
|
||||||
android:parentActivityName=".MainActivity">
|
android:label="@string/log"
|
||||||
|
android:parentActivityName=".MainActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="emu.lightswitch.MainActivity"/>
|
android:value="emu.lightswitch.MainActivity" />
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".SettingsActivity"
|
android:name=".SettingsActivity"
|
||||||
@ -28,13 +29,13 @@
|
|||||||
android:parentActivityName=".MainActivity">
|
android:parentActivityName=".MainActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="emu.lightswitch.MainActivity"/>
|
android:value="emu.lightswitch.MainActivity" />
|
||||||
</activity>
|
</activity>
|
||||||
<activity android:name=".MainActivity">
|
<activity android:name=".MainActivity">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
</application>
|
</application>
|
||||||
|
@ -32,6 +32,7 @@ extern "C" JNIEXPORT void JNICALL Java_emu_lightswitch_MainActivity_loadFile(JNI
|
|||||||
if (emu_thread) {
|
if (emu_thread) {
|
||||||
halt = true; // This'll cause execution to stop after the next breakpoint
|
halt = true; // This'll cause execution to stop after the next breakpoint
|
||||||
emu_thread->join();
|
emu_thread->join();
|
||||||
|
halt = false; // Or the current instance will halt immediately
|
||||||
}
|
}
|
||||||
|
|
||||||
// Running on UI thread is not a good idea as the UI will remain unresponsive
|
// Running on UI thread is not a good idea as the UI will remain unresponsive
|
||||||
|
@ -3,33 +3,7 @@
|
|||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
|
|
||||||
namespace lightSwitch {
|
namespace lightSwitch {
|
||||||
namespace memory {
|
Settings::Settings(std::string &pref_xml) {
|
||||||
Permission::Permission() {
|
|
||||||
r = 0;
|
|
||||||
w = 0;
|
|
||||||
x = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Permission::Permission(bool read, bool write, bool execute) {
|
|
||||||
r = read;
|
|
||||||
w = write;
|
|
||||||
x = execute;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Permission::operator==(const Permission &rhs) const { return (this->r == rhs.r && this->w == rhs.w && this->x == rhs.x); }
|
|
||||||
|
|
||||||
bool Permission::operator!=(const Permission &rhs) const { return !operator==(rhs); }
|
|
||||||
|
|
||||||
int Permission::get() const {
|
|
||||||
int perm = 0;
|
|
||||||
if (r) perm |= PROT_READ;
|
|
||||||
if (w) perm |= PROT_WRITE;
|
|
||||||
if (x) perm |= PROT_EXEC;
|
|
||||||
return perm;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Settings::Settings(std::string pref_xml) {
|
|
||||||
tinyxml2::XMLDocument pref;
|
tinyxml2::XMLDocument pref;
|
||||||
if (pref.LoadFile(pref_xml.c_str()))
|
if (pref.LoadFile(pref_xml.c_str()))
|
||||||
throw exception("TinyXML2 Error: " + std::string(pref.ErrorStr()));
|
throw exception("TinyXML2 Error: " + std::string(pref.ErrorStr()));
|
||||||
@ -38,13 +12,10 @@ namespace lightSwitch {
|
|||||||
switch (elem->Value()[0]) {
|
switch (elem->Value()[0]) {
|
||||||
case 's':
|
case 's':
|
||||||
string_map.insert(
|
string_map.insert(
|
||||||
std::pair<char *, char *>((char *) elem->FindAttribute("name")->Value(),
|
std::pair<char *, char *>((char *) elem->FindAttribute("name")->Value(), (char *) elem->GetText()));
|
||||||
(char *) elem->GetText()));
|
|
||||||
break;
|
break;
|
||||||
case 'b':
|
case 'b':
|
||||||
bool_map.insert(
|
bool_map.insert(std::pair<char *, bool>((char *) elem->FindAttribute("name")->Value(), elem->FindAttribute("value")->BoolValue()));
|
||||||
std::pair<char *, bool>((char *) elem->FindAttribute("name")->Value(),
|
|
||||||
elem->FindAttribute("value")->BoolValue()));
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
@ -78,7 +49,7 @@ namespace lightSwitch {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::Logger(const std::string & log_path) {
|
Logger::Logger(const std::string &log_path) {
|
||||||
log_file.open(log_path, std::ios::app);
|
log_file.open(log_path, std::ios::app);
|
||||||
WriteHeader("Logging started");
|
WriteHeader("Logging started");
|
||||||
}
|
}
|
||||||
@ -87,13 +58,13 @@ namespace lightSwitch {
|
|||||||
WriteHeader("Logging ended");
|
WriteHeader("Logging ended");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::WriteHeader(const std::string& str) {
|
void Logger::WriteHeader(const std::string &str) {
|
||||||
syslog(LOG_ALERT, "%s", str.c_str());
|
syslog(LOG_ALERT, "%s", str.c_str());
|
||||||
log_file << "0|" << str << "\n";
|
log_file << "0|" << str << "\n";
|
||||||
log_file.flush();
|
log_file.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::Write(const LogLevel level, const std::string& str) {
|
void Logger::Write(const LogLevel level, const std::string &str) {
|
||||||
#ifdef NDEBUG
|
#ifdef NDEBUG
|
||||||
if (level == DEBUG) return;
|
if (level == DEBUG) return;
|
||||||
#endif
|
#endif
|
||||||
|
@ -18,49 +18,71 @@
|
|||||||
|
|
||||||
namespace lightSwitch {
|
namespace lightSwitch {
|
||||||
// Global typedefs
|
// Global typedefs
|
||||||
|
typedef __uint128_t u128;
|
||||||
|
typedef __uint64_t u64;
|
||||||
|
typedef __uint32_t u32;
|
||||||
|
typedef __uint16_t u16;
|
||||||
|
typedef __uint8_t u8;
|
||||||
|
typedef __int128_t i128;
|
||||||
|
typedef __int64_t i64;
|
||||||
|
typedef __int32_t i32;
|
||||||
|
typedef __int16_t i16;
|
||||||
|
typedef __int8_t i8;
|
||||||
typedef std::runtime_error exception; //!< This is used as the default exception
|
typedef std::runtime_error exception; //!< This is used as the default exception
|
||||||
typedef uint32_t handle_t; //!< The type of an handle
|
typedef u32 handle_t; //!< The type of an handle
|
||||||
|
|
||||||
namespace constant {
|
namespace constant {
|
||||||
// Memory
|
// Memory
|
||||||
constexpr uint64_t base_addr = 0x8000000; //!< The address space base
|
constexpr u64 base_addr = 0x8000000; //!< The address space base
|
||||||
constexpr uint64_t base_size = 0x7FF8000000; //!< The size of the address space
|
constexpr u64 map_addr = base_addr + 0x80000000; //!< The address of the map region
|
||||||
constexpr uint64_t total_phy_mem = 0xF8000000; // ~4 GB of RAM
|
constexpr u64 base_size = 0x7FF8000000; //!< The size of the address space
|
||||||
|
constexpr u64 map_size = 0x1000000000; //!< The size of the map region
|
||||||
|
constexpr u64 total_phy_mem = 0xF8000000; // ~4 GB of RAM
|
||||||
constexpr size_t def_stack_size = 0x1E8480; //!< The default amount of stack: 2 MB
|
constexpr size_t def_stack_size = 0x1E8480; //!< The default amount of stack: 2 MB
|
||||||
|
constexpr size_t def_heap_size = PAGE_SIZE; //!< The default amount of heap
|
||||||
constexpr size_t tls_slot_size = 0x200; //!< The size of a single TLS slot
|
constexpr size_t tls_slot_size = 0x200; //!< The size of a single TLS slot
|
||||||
constexpr uint8_t tls_slots = PAGE_SIZE / constant::tls_slot_size; //!< The amount of TLS slots in a single page
|
constexpr u8 tls_slots = PAGE_SIZE / tls_slot_size; //!< The amount of TLS slots in a single page
|
||||||
// Loader
|
// Loader
|
||||||
constexpr uint32_t nro_magic = 0x304F524E; //!< "NRO0" in reverse, this is written at the start of every NRO file
|
constexpr u32 nro_magic = 0x304F524E; //!< "NRO0" in reverse, this is written at the start of every NRO file
|
||||||
// NCE
|
// NCE
|
||||||
constexpr uint8_t num_regs = 31; //!< The amount of registers that ARMv8 has
|
constexpr u8 num_regs = 31; //!< The amount of registers that ARMv8 has
|
||||||
constexpr uint16_t svc_last = 0x7F; //!< The index of the last SVC
|
constexpr u16 svc_last = 0x7F; //!< The index of the last SVC
|
||||||
constexpr uint16_t brk_rdy = 0xFF; //!< This is reserved for our kernel's to know when a process/thread is ready
|
constexpr u16 brk_rdy = 0xFF; //!< This is reserved for our kernel's to know when a process/thread is ready
|
||||||
constexpr uint32_t tpidrro_el0 = 0x5E83; //!< ID of tpidrro_el0 in MRS
|
constexpr u32 tpidrro_el0 = 0x5E83; //!< ID of tpidrro_el0 in MRS
|
||||||
// IPC
|
// IPC
|
||||||
constexpr size_t tls_ipc_size = 0x100; //!< The size of the IPC command buffer in a TLS slot
|
constexpr size_t tls_ipc_size = 0x100; //!< The size of the IPC command buffer in a TLS slot
|
||||||
constexpr uint64_t sm_handle = 0xd000; //!< sm:'s handle
|
constexpr handle_t sm_handle = 0xD000; //!< sm:'s handle
|
||||||
constexpr uint8_t port_size = 0x8; //!< The size of a port name string
|
constexpr u8 port_size = 0x8; //!< The size of a port name string
|
||||||
constexpr uint32_t ipc_sfco = 0x4F434653; //!< SFCO in reverse
|
constexpr u32 sfco_magic = 0x4F434653; //!< SFCO in reverse, written to IPC messages
|
||||||
|
constexpr u32 sfci_magic = 0x49434653; //!< SFCI in reverse, present in received IPC messages
|
||||||
|
constexpr u64 padding_sum = 0x10; //!< The sum of the padding surrounding DataPayload
|
||||||
// Process
|
// Process
|
||||||
constexpr uint32_t base_handle_index = 0xD001; // The index of the base handle
|
constexpr handle_t base_handle_index = sm_handle + 1; // The index of the base handle
|
||||||
constexpr uint8_t default_priority = 31; //!< The default priority of a process
|
constexpr u8 default_priority = 31; //!< The default priority of a process
|
||||||
constexpr std::pair<int8_t, int8_t> priority_an = {19, -8}; //!< The range of priority for Android, taken from https://medium.com/mindorks/exploring-android-thread-priority-5d0542eebbd1
|
constexpr std::pair<int8_t, int8_t> priority_an = {19, -8}; //!< The range of priority for Android, taken from https://medium.com/mindorks/exploring-android-thread-priority-5d0542eebbd1
|
||||||
constexpr std::pair<uint8_t, uint8_t> priority_nin = {0, 63}; //!< The range of priority for the Nintendo Switch
|
constexpr std::pair<u8, u8> priority_nin = {0, 63}; //!< The range of priority for the Nintendo Switch
|
||||||
|
// Status codes
|
||||||
|
namespace status {
|
||||||
|
constexpr u32 success = 0x0; //!< "Success"
|
||||||
|
constexpr u32 inv_address = 0xCC01; //!< "Invalid address"
|
||||||
|
constexpr u32 inv_handle = 0xE401; //!< "Invalid handle"
|
||||||
|
constexpr u32 unimpl = 0x177202; //!< "Unimplemented behaviour"
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace instr {
|
namespace instr {
|
||||||
/**
|
/**
|
||||||
* A bitfield struct that encapsulates a BRK instruction. It can be used to generate as well as parse the instruction's opcode. See https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/brk-breakpoint-instruction.
|
* A bit-field struct that encapsulates a BRK instruction. It can be used to generate as well as parse the instruction's opcode. See https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/brk-breakpoint-instruction.
|
||||||
*/
|
*/
|
||||||
struct brk {
|
struct brk {
|
||||||
/**
|
/**
|
||||||
* Creates a BRK instruction with a specific immediate value, used for generating BRK opcodes
|
* Creates a BRK instruction with a specific immediate value, used for generating BRK opcodes
|
||||||
* @param val The immediate value of the instruction
|
* @param val The immediate value of the instruction
|
||||||
*/
|
*/
|
||||||
brk(uint16_t val) {
|
brk(u16 val) {
|
||||||
start = 0x0; // First 5 bits of an BRK instruction are 0
|
start = 0x0; // First 5 bits of an BRK instruction are 0
|
||||||
value = val;
|
value = val;
|
||||||
end = 0x6A1; // Last 11 bits of an BRK instruction stored as uint16_t
|
end = 0x6A1; // Last 11 bits of an BRK instruction stored as u16
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -70,13 +92,15 @@ namespace lightSwitch {
|
|||||||
return (start == 0x0 && end == 0x6A1);
|
return (start == 0x0 && end == 0x6A1);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t start : 5;
|
u8 start : 5;
|
||||||
uint32_t value : 16;
|
u32 value : 16;
|
||||||
uint16_t end : 11;
|
u16 end : 11;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(brk) == sizeof(u32));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A bitfield struct that encapsulates a SVC instruction. See https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/svc-supervisor-call.
|
* A bit-field struct that encapsulates a SVC instruction. See https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/svc-supervisor-call.
|
||||||
*/
|
*/
|
||||||
struct svc {
|
struct svc {
|
||||||
/**
|
/**
|
||||||
@ -86,13 +110,15 @@ namespace lightSwitch {
|
|||||||
return (start == 0x1 && end == 0x6A0);
|
return (start == 0x1 && end == 0x6A0);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t start : 5;
|
u8 start : 5;
|
||||||
uint32_t value : 16;
|
u32 value : 16;
|
||||||
uint16_t end : 11;
|
u16 end : 11;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(svc) == sizeof(u32));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A bitfield struct that encapsulates a MRS instruction. See https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/mrs-move-system-register.
|
* A bit-field struct that encapsulates a MRS instruction. See https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/mrs-move-system-register.
|
||||||
*/
|
*/
|
||||||
struct mrs {
|
struct mrs {
|
||||||
/**
|
/**
|
||||||
@ -102,79 +128,26 @@ namespace lightSwitch {
|
|||||||
return (end == 0xD53);
|
return (end == 0xD53);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t dst_reg : 5;
|
u8 dst_reg : 5;
|
||||||
uint32_t src_reg : 15;
|
u32 src_reg : 15;
|
||||||
uint16_t end : 12;
|
u16 end : 12;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(mrs) == sizeof(u32));
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read about ARMv8 registers here: https://developer.arm.com/docs/100878/latest/registers
|
* Read about ARMv8 registers here: https://developer.arm.com/docs/100878/latest/registers
|
||||||
*/
|
*/
|
||||||
namespace regs {
|
enum class xreg { x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, x30 };
|
||||||
enum xreg { x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, x30 };
|
enum class wreg { w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15, w16, w17, w18, w19, w20, w21, w22, w23, w24, w25, w26, w27, w28, w29, w30 };
|
||||||
enum wreg { w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15, w16, w17, w18, w19, w20, w21, w22, w23, w24, w25, w26, w27, w28, w29, w30 };
|
enum class sreg { sp, pc, pstate };
|
||||||
enum sreg { sp, pc, pstate };
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace memory {
|
|
||||||
/**
|
|
||||||
* The Permission struct holds the permission of a particular chunk of memory
|
|
||||||
*/
|
|
||||||
struct Permission {
|
|
||||||
/**
|
|
||||||
* Initializes all values to false
|
|
||||||
*/
|
|
||||||
Permission();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param read If memory has read permission
|
|
||||||
* @param write If memory has write permission
|
|
||||||
* @param execute If memory has execute permission
|
|
||||||
*/
|
|
||||||
Permission(bool read, bool write, bool execute);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Equality operator between two Permission objects
|
|
||||||
*/
|
|
||||||
bool operator==(const Permission &rhs) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inequality operator between two Permission objects
|
|
||||||
*/
|
|
||||||
bool operator!=(const Permission &rhs) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The value of the permission struct in mmap(2) format
|
|
||||||
*/
|
|
||||||
int get() const;
|
|
||||||
|
|
||||||
bool r, w, x;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Memory Regions that are mapped by the kernel
|
|
||||||
*/
|
|
||||||
enum class Region {
|
|
||||||
heap, tls, text, rodata, data, bss
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The RegionData struct holds information about a corresponding Region of memory such as address and size
|
|
||||||
*/
|
|
||||||
struct RegionData {
|
|
||||||
uint64_t address;
|
|
||||||
size_t size;
|
|
||||||
Permission perms;
|
|
||||||
int fd;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Settings class is used to access the parameters set in the Java component of the application
|
* The Settings class is used to access the parameters set in the Java component of the application
|
||||||
*/
|
*/
|
||||||
class Settings {
|
class Settings {
|
||||||
private:
|
private:
|
||||||
struct KeyCompare {
|
struct KeyCompare {
|
||||||
bool operator()(char const *a, char const *b) const {
|
bool operator()(char const *a, char const *b) const {
|
||||||
return std::strcmp(a, b) < 0;
|
return std::strcmp(a, b) < 0;
|
||||||
@ -184,11 +157,11 @@ namespace lightSwitch {
|
|||||||
std::map<char *, char *, KeyCompare> string_map; //!< A mapping from all keys to their corresponding string value
|
std::map<char *, char *, KeyCompare> string_map; //!< A mapping from all keys to their corresponding string value
|
||||||
std::map<char *, bool, KeyCompare> bool_map; //!< A mapping from all keys to their corresponding boolean value
|
std::map<char *, bool, KeyCompare> bool_map; //!< A mapping from all keys to their corresponding boolean value
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @param pref_xml The path to the preference XML file
|
* @param pref_xml The path to the preference XML file
|
||||||
*/
|
*/
|
||||||
Settings(std::string pref_xml);
|
Settings(std::string &pref_xml);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param key The key of the setting
|
* @param key The key of the setting
|
||||||
@ -212,13 +185,13 @@ namespace lightSwitch {
|
|||||||
* The Logger class is to generate a log of the program
|
* The Logger class is to generate a log of the program
|
||||||
*/
|
*/
|
||||||
class Logger {
|
class Logger {
|
||||||
private:
|
private:
|
||||||
std::ofstream log_file; //!< An output stream to the log file
|
std::ofstream log_file; //!< An output stream to the log file
|
||||||
const char *level_str[4] = {"0", "1", "2", "3"}; //!< This is used to denote the LogLevel when written out to a file
|
const char *level_str[4] = {"0", "1", "2", "3"}; //!< This is used to denote the LogLevel when written out to a file
|
||||||
static constexpr int level_syslog[4] = {LOG_ERR, LOG_WARNING, LOG_INFO, LOG_DEBUG}; //!< This corresponds to LogLevel and provides it's equivalent for syslog
|
static constexpr int level_syslog[4] = {LOG_ERR, LOG_WARNING, LOG_INFO, LOG_DEBUG}; //!< This corresponds to LogLevel and provides it's equivalent for syslog
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum LogLevel {ERROR, WARN, INFO, DEBUG}; //!< The level of a particular log
|
enum LogLevel { ERROR, WARN, INFO, DEBUG }; //!< The level of a particular log
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param log_path The path to the log file
|
* @param log_path The path to the log file
|
||||||
@ -263,6 +236,7 @@ namespace lightSwitch {
|
|||||||
namespace kernel {
|
namespace kernel {
|
||||||
namespace type {
|
namespace type {
|
||||||
class KProcess;
|
class KProcess;
|
||||||
|
|
||||||
class KThread;
|
class KThread;
|
||||||
}
|
}
|
||||||
class OS;
|
class OS;
|
||||||
@ -275,8 +249,8 @@ namespace lightSwitch {
|
|||||||
device_state(kernel::OS *os, std::shared_ptr<kernel::type::KProcess> &this_process, std::shared_ptr<kernel::type::KThread> &this_thread, std::shared_ptr<NCE> nce, std::shared_ptr<Settings> settings, std::shared_ptr<Logger> logger) : os(os), nce(nce), settings(settings), logger(logger), this_process(this_process), this_thread(this_thread) {}
|
device_state(kernel::OS *os, std::shared_ptr<kernel::type::KProcess> &this_process, std::shared_ptr<kernel::type::KThread> &this_thread, std::shared_ptr<NCE> nce, std::shared_ptr<Settings> settings, std::shared_ptr<Logger> logger) : os(os), nce(nce), settings(settings), logger(logger), this_process(this_process), this_thread(this_thread) {}
|
||||||
|
|
||||||
kernel::OS *os; // Because OS holds the device_state struct, it's destruction will accompany that of device_state
|
kernel::OS *os; // Because OS holds the device_state struct, it's destruction will accompany that of device_state
|
||||||
std::shared_ptr<kernel::type::KProcess>& this_process;
|
std::shared_ptr<kernel::type::KProcess> &this_process;
|
||||||
std::shared_ptr<kernel::type::KThread>& this_thread;
|
std::shared_ptr<kernel::type::KThread> &this_thread;
|
||||||
std::shared_ptr<NCE> nce;
|
std::shared_ptr<NCE> nce;
|
||||||
std::shared_ptr<Settings> settings;
|
std::shared_ptr<Settings> settings;
|
||||||
std::shared_ptr<Logger> logger;
|
std::shared_ptr<Logger> logger;
|
||||||
|
@ -4,99 +4,130 @@
|
|||||||
#include "types/KProcess.h"
|
#include "types/KProcess.h"
|
||||||
|
|
||||||
namespace lightSwitch::kernel::ipc {
|
namespace lightSwitch::kernel::ipc {
|
||||||
IpcRequest::IpcRequest(uint8_t *tls_ptr, device_state &state) : req_info((CommandStruct *) tls_ptr) {
|
IpcRequest::IpcRequest(bool is_domain, device_state &state) : is_domain(is_domain), state(state), tls() {
|
||||||
state.logger->Write(Logger::DEBUG, "Enable handle descriptor: {0}", (bool) req_info->handle_desc);
|
u8 *curr_ptr = tls.data();
|
||||||
|
state.this_process->ReadMemory(curr_ptr, state.this_thread->tls, constant::tls_ipc_size);
|
||||||
data_offset = 8;
|
header = reinterpret_cast<CommandHeader *>(curr_ptr);
|
||||||
if (req_info->handle_desc) {
|
curr_ptr += sizeof(CommandHeader);
|
||||||
HandleDescriptor handledesc = *(HandleDescriptor *) (&tls_ptr[8]);
|
if (header->handle_desc) {
|
||||||
state.logger->Write(Logger::DEBUG, "Moving {} handles and copying {} handles (0x{:X})", uint8_t(handledesc.move_count), uint8_t(handledesc.copy_count), tls_ptr[2]);
|
handle_desc = reinterpret_cast<HandleDescriptor *>(curr_ptr);
|
||||||
data_offset += 4 + handledesc.copy_count * 4 + handledesc.move_count * 4;
|
curr_ptr += sizeof(HandleDescriptor) + (handle_desc->send_pid ? sizeof(u8) : 0);
|
||||||
}
|
for (uint index = 0; handle_desc->copy_count > index; index++) {
|
||||||
|
copy_handles.push_back(*reinterpret_cast<handle_t *>(curr_ptr));
|
||||||
if (req_info->x_no || req_info->a_no || req_info->b_no || req_info->w_no)
|
curr_ptr += sizeof(handle_t);
|
||||||
state.logger->Write(Logger::ERROR, "IPC - Descriptors");
|
|
||||||
|
|
||||||
// Align to 16 bytes
|
|
||||||
data_offset = ((data_offset - 1) & ~(15U)) + 16; // ceil data_offset with multiples 16
|
|
||||||
data_ptr = &tls_ptr[data_offset + sizeof(req_info)];
|
|
||||||
|
|
||||||
state.logger->Write(Logger::DEBUG, "Type: 0x{:X}", (uint8_t) req_info->type);
|
|
||||||
state.logger->Write(Logger::DEBUG, "X descriptors: {}", (uint8_t) req_info->x_no);
|
|
||||||
state.logger->Write(Logger::DEBUG, "A descriptors: {}", (uint8_t) req_info->a_no);
|
|
||||||
state.logger->Write(Logger::DEBUG, "B descriptors: {}", (uint8_t) req_info->b_no);
|
|
||||||
state.logger->Write(Logger::DEBUG, "W descriptors: {}", (uint8_t) req_info->w_no);
|
|
||||||
state.logger->Write(Logger::DEBUG, "Raw data offset: 0x{:X}", data_offset);
|
|
||||||
state.logger->Write(Logger::DEBUG, "Raw data size: {}", (uint16_t) req_info->data_sz);
|
|
||||||
state.logger->Write(Logger::DEBUG, "Payload Command ID: {0} (0x{0:X})", *((uint32_t *) &tls_ptr[data_offset + 8]));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
T IpcRequest::GetValue() {
|
|
||||||
data_offset += sizeof(T);
|
|
||||||
return *reinterpret_cast<T *>(&data_ptr[data_offset - sizeof(T)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
IpcResponse::IpcResponse() : resp_info{0} {
|
|
||||||
tls_ptr = reinterpret_cast<uint32_t *>(new uint8_t[constant::tls_ipc_size]);
|
|
||||||
}
|
|
||||||
|
|
||||||
IpcResponse::~IpcResponse() {
|
|
||||||
delete[] tls_ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void IpcResponse::Generate(device_state &state) {
|
|
||||||
state.logger->Write(Logger::DEBUG, "Moving {} handles and copying {} handles", moved_handles.size(), copied_handles.size());
|
|
||||||
resp_info.type = 0;
|
|
||||||
data_offset = 8;
|
|
||||||
if (!moved_handles.empty() || !copied_handles.empty()) {
|
|
||||||
resp_info.handle_desc = true;
|
|
||||||
HandleDescriptor handledesc{};
|
|
||||||
handledesc.copy_count = static_cast<uint8_t>(copied_handles.size());
|
|
||||||
handledesc.move_count = static_cast<uint8_t>(moved_handles.size());
|
|
||||||
*(HandleDescriptor *) &tls_ptr[2] = handledesc;
|
|
||||||
|
|
||||||
uint64_t handle_index = 0;
|
|
||||||
for (auto& copied_handle : copied_handles) {
|
|
||||||
state.logger->Write(Logger::DEBUG, "Copying handle to 0x{:X}", 12 + handle_index * 4);
|
|
||||||
tls_ptr[3 + handle_index] = copied_handle;
|
|
||||||
handle_index += 1;
|
|
||||||
}
|
}
|
||||||
for (auto& moved_handle : moved_handles) {
|
for (uint index = 0; handle_desc->move_count > index; index++) {
|
||||||
state.logger->Write(Logger::DEBUG, "Moving handle to 0x{:X}", 12 + handle_index * 4);
|
move_handles.push_back(*reinterpret_cast<handle_t *>(curr_ptr));
|
||||||
tls_ptr[3 + handle_index] = moved_handle;
|
curr_ptr += sizeof(handle_t);
|
||||||
handle_index += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data_offset += 4 + copied_handles.size() * 4 + moved_handles.size() * 4;
|
|
||||||
}
|
}
|
||||||
|
for (uint index = 0; header->x_no > index; index++) {
|
||||||
state.logger->Write(Logger::DEBUG, "Data offset: 0x{:X}", data_offset);
|
vec_buf_x.push_back(reinterpret_cast<BufferDescriptorX *>(curr_ptr));
|
||||||
|
curr_ptr += sizeof(BufferDescriptorX);
|
||||||
// Align to 16 bytes
|
}
|
||||||
data_offset = ((data_offset - 1) & ~(15U)) + 16; // ceil data_offset with multiples 16
|
for (uint index = 0; header->a_no > index; index++) {
|
||||||
|
vec_buf_a.push_back(reinterpret_cast<BufferDescriptorABW *>(curr_ptr));
|
||||||
|
curr_ptr += sizeof(BufferDescriptorABW);
|
||||||
|
}
|
||||||
|
for (uint index = 0; header->b_no > index; index++) {
|
||||||
|
vec_buf_b.push_back(reinterpret_cast<BufferDescriptorABW *>(curr_ptr));
|
||||||
|
curr_ptr += sizeof(BufferDescriptorABW);
|
||||||
|
}
|
||||||
|
for (uint index = 0; header->w_no > index; index++) {
|
||||||
|
vec_buf_w.push_back(reinterpret_cast<BufferDescriptorABW *>(curr_ptr));
|
||||||
|
curr_ptr += sizeof(BufferDescriptorABW);
|
||||||
|
}
|
||||||
|
auto raw_ptr = reinterpret_cast<u8 *>((((reinterpret_cast<u64>(curr_ptr) - reinterpret_cast<u64>(tls.data())) - 1U) & ~(constant::padding_sum - 1U)) + constant::padding_sum + reinterpret_cast<u64>(tls.data())); // Align to 16 bytes relative to start of TLS
|
||||||
if (is_domain) {
|
if (is_domain) {
|
||||||
tls_ptr[data_offset >> 2] = static_cast<uint32_t>(moved_handles.size());
|
domain = reinterpret_cast<DomainHeaderRequest *>(raw_ptr);
|
||||||
data_offset += 0x10;
|
payload = reinterpret_cast<PayloadHeader *>(raw_ptr + sizeof(DomainHeaderRequest));
|
||||||
|
cmd_arg_sz = domain->payload_sz - sizeof(PayloadHeader);
|
||||||
|
} else {
|
||||||
|
payload = reinterpret_cast<PayloadHeader *>(raw_ptr);
|
||||||
|
cmd_arg_sz = (header->raw_sz * sizeof(u32)) - (constant::padding_sum + sizeof(PayloadHeader));
|
||||||
|
}
|
||||||
|
if (payload->magic != constant::sfci_magic) throw exception(fmt::format("Unexpected Magic in PayloadHeader: 0x{:X}", reinterpret_cast<u32>(payload->magic)));
|
||||||
|
cmd_arg = reinterpret_cast<u8 *>(payload) + sizeof(PayloadHeader);
|
||||||
|
curr_ptr += header->raw_sz * sizeof(u32);
|
||||||
|
if (header->c_flag == static_cast<u8>(BufferCFlag::SingleDescriptor)) {
|
||||||
|
vec_buf_c.push_back(reinterpret_cast<BufferDescriptorC *>(curr_ptr));
|
||||||
|
} else if (header->c_flag > static_cast<u8>(BufferCFlag::SingleDescriptor)) {
|
||||||
|
for (uint index = 0; (header->c_flag - 2) > index; index++) { // (c_flag - 2) C descriptors are present
|
||||||
|
vec_buf_c.push_back(reinterpret_cast<BufferDescriptorC *>(curr_ptr));
|
||||||
|
curr_ptr += sizeof(BufferDescriptorC);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data_offset >>= 2;
|
|
||||||
|
|
||||||
// TODO: Calculate data size
|
|
||||||
resp_info.data_sz = static_cast<uint16_t>(16 + (is_domain ? 4 : 0)); // + data_words;
|
|
||||||
tls_ptr[data_offset] = constant::ipc_sfco;
|
|
||||||
tls_ptr[data_offset + 2] = error_code;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IpcResponse::SetError(uint32_t _error_code) { error_code = _error_code; }
|
IpcResponse::IpcResponse(bool is_domain, device_state &state) : is_domain(is_domain), state(state) {}
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void IpcResponse::WriteValue() {
|
|
||||||
|
|
||||||
|
void IpcResponse::WriteTls() {
|
||||||
|
std::array<u8, constant::tls_ipc_size> tls{};
|
||||||
|
u8 *curr_ptr = tls.data();
|
||||||
|
auto header = reinterpret_cast<CommandHeader *>(curr_ptr);
|
||||||
|
header->x_no = static_cast<u8>(vec_buf_x.size());
|
||||||
|
header->a_no = static_cast<u8>(vec_buf_a.size());
|
||||||
|
header->b_no = static_cast<u8>(vec_buf_b.size());
|
||||||
|
header->w_no = static_cast<u8>(vec_buf_w.size());
|
||||||
|
header->raw_sz = static_cast<u32>((sizeof(PayloadHeader) + arg_vec.size() + constant::padding_sum + (is_domain ? sizeof(DomainHeaderRequest) : 0)) / sizeof(u32)); // Size is in 32-bit units because Nintendo
|
||||||
|
if (!vec_buf_c.empty())
|
||||||
|
header->c_flag = (vec_buf_c.size() == 1) ? static_cast<u8>(BufferCFlag::SingleDescriptor) : static_cast<u8>(vec_buf_c.size() + static_cast<u8>(BufferCFlag::SingleDescriptor));
|
||||||
|
header->handle_desc = (!copy_handles.empty() || !move_handles.empty());
|
||||||
|
curr_ptr += sizeof(CommandHeader);
|
||||||
|
if (header->handle_desc) {
|
||||||
|
auto handle_desc = reinterpret_cast<HandleDescriptor *>(curr_ptr);
|
||||||
|
handle_desc->send_pid = false; // TODO: Figure this out ?
|
||||||
|
handle_desc->copy_count = static_cast<u8>(copy_handles.size());
|
||||||
|
handle_desc->move_count = static_cast<u8>(move_handles.size());
|
||||||
|
curr_ptr += sizeof(HandleDescriptor);
|
||||||
|
for (uint index = 0; handle_desc->copy_count > index; index++) {
|
||||||
|
*reinterpret_cast<handle_t *>(curr_ptr) = copy_handles[index];
|
||||||
|
curr_ptr += sizeof(handle_t);
|
||||||
|
}
|
||||||
|
for (uint index = 0; handle_desc->move_count > index; index++) {
|
||||||
|
*reinterpret_cast<handle_t *>(curr_ptr) = move_handles[index];
|
||||||
|
curr_ptr += sizeof(handle_t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (uint index = 0; header->x_no > index; index++) {
|
||||||
|
*reinterpret_cast<BufferDescriptorX *>(curr_ptr) = vec_buf_x[index];
|
||||||
|
curr_ptr += sizeof(BufferDescriptorX);
|
||||||
|
}
|
||||||
|
for (uint index = 0; header->a_no > index; index++) {
|
||||||
|
*reinterpret_cast<BufferDescriptorABW *>(curr_ptr) = vec_buf_a[index];
|
||||||
|
curr_ptr += sizeof(BufferDescriptorABW);
|
||||||
|
}
|
||||||
|
for (uint index = 0; header->b_no > index; index++) {
|
||||||
|
*reinterpret_cast<BufferDescriptorABW *>(curr_ptr) = vec_buf_b[index];
|
||||||
|
curr_ptr += sizeof(BufferDescriptorABW);
|
||||||
|
}
|
||||||
|
for (uint index = 0; header->w_no > index; index++) {
|
||||||
|
*reinterpret_cast<BufferDescriptorABW *>(curr_ptr) = vec_buf_w[index];
|
||||||
|
curr_ptr += sizeof(BufferDescriptorABW);
|
||||||
|
}
|
||||||
|
u64 padding = ((((reinterpret_cast<u64>(curr_ptr) - reinterpret_cast<u64>(tls.data())) - 1U) & ~(constant::padding_sum - 1U)) + constant::padding_sum + (reinterpret_cast<u64>(tls.data()) - reinterpret_cast<u64>(curr_ptr))); // Calculate the amount of padding at the front
|
||||||
|
curr_ptr += padding;
|
||||||
|
PayloadHeader *payload;
|
||||||
|
if (is_domain) {
|
||||||
|
auto domain = reinterpret_cast<DomainHeaderResponse *>(curr_ptr);
|
||||||
|
domain->output_count = 0; // TODO: Figure this out
|
||||||
|
payload = reinterpret_cast<PayloadHeader *>(curr_ptr + sizeof(DomainHeaderResponse));
|
||||||
|
} else {
|
||||||
|
payload = reinterpret_cast<PayloadHeader *>(curr_ptr);
|
||||||
|
}
|
||||||
|
payload->magic = constant::sfco_magic;
|
||||||
|
payload->version = 1;
|
||||||
|
payload->value = error_code;
|
||||||
|
curr_ptr += sizeof(PayloadHeader);
|
||||||
|
if (!arg_vec.empty()) memcpy(curr_ptr, arg_vec.data(), arg_vec.size());
|
||||||
|
curr_ptr += arg_vec.size() + (constant::padding_sum - padding);
|
||||||
|
if (header->c_flag == static_cast<u8>(BufferCFlag::SingleDescriptor)) {
|
||||||
|
*reinterpret_cast<BufferDescriptorC *>(curr_ptr) = vec_buf_c[0];
|
||||||
|
} else if (header->c_flag > static_cast<u8>(BufferCFlag::SingleDescriptor)) {
|
||||||
|
for (uint index = 0; (header->c_flag - 2) > index; index++) {
|
||||||
|
*reinterpret_cast<BufferDescriptorC *>(curr_ptr) = vec_buf_c[index];
|
||||||
|
curr_ptr += sizeof(BufferDescriptorC);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void IpcResponse::CopyHandle(uint32_t handle) { copied_handles.push_back(handle); }
|
|
||||||
|
|
||||||
void IpcResponse::MoveHandle(uint32_t handle) { moved_handles.push_back(handle); }
|
|
||||||
}
|
}
|
||||||
|
@ -2,68 +2,224 @@
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <array>
|
||||||
#include "switch/common.h"
|
#include "switch/common.h"
|
||||||
|
|
||||||
namespace lightSwitch::kernel::ipc {
|
namespace lightSwitch::kernel::ipc {
|
||||||
struct CommandStruct {
|
/**
|
||||||
// https://switchbrew.org/wiki/IPC_Marshalling#IPC_Command_Structure
|
* This bit-field structure holds the header of an IPC command.
|
||||||
uint16_t type : 16;
|
* https://switchbrew.org/wiki/IPC_Marshalling#IPC_Command_Structure
|
||||||
uint8_t x_no : 4;
|
*/
|
||||||
uint8_t a_no : 4;
|
struct CommandHeader {
|
||||||
uint8_t b_no : 4;
|
u16 type : 16;
|
||||||
uint8_t w_no : 4;
|
u8 x_no : 4;
|
||||||
uint16_t data_sz : 10;
|
u8 a_no : 4;
|
||||||
uint8_t c_flags : 4;
|
u8 b_no : 4;
|
||||||
uint32_t : 17;
|
u8 w_no : 4;
|
||||||
|
u32 raw_sz : 10;
|
||||||
|
u8 c_flag : 4;
|
||||||
|
u32 : 17;
|
||||||
bool handle_desc : 1;
|
bool handle_desc : 1;
|
||||||
};
|
};
|
||||||
|
static_assert(sizeof(CommandHeader) == 8);
|
||||||
|
|
||||||
struct HandleDescriptor {
|
/**
|
||||||
bool send_pid : 1;
|
* This reflects the value in CommandStruct::type
|
||||||
uint8_t copy_count : 4;
|
*/
|
||||||
uint8_t move_count : 4;
|
enum class CommandType : u16 {
|
||||||
|
Invalid = 0, LegacyRequest = 1, Close = 2, LegacyControl = 3, Request = 4, Control = 5, RequestWithContext = 6, ControlWithContext = 7
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This reflects the value in CommandStruct::c_flags
|
||||||
|
*/
|
||||||
|
enum class BufferCFlag : u8 {
|
||||||
|
None = 0, InlineDescriptor = 1, SingleDescriptor = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This bit-field structure holds the handle descriptor of a recieved IPC command.
|
||||||
|
* https://switchbrew.org/wiki/IPC_Marshalling#Handle_descriptor
|
||||||
|
*/
|
||||||
|
struct HandleDescriptor {
|
||||||
|
bool send_pid : 1;
|
||||||
|
u32 copy_count : 4;
|
||||||
|
u32 move_count : 4;
|
||||||
|
u32 : 23;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(HandleDescriptor) == 4);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This bit-field structure holds the domain's header of an IPC request command.
|
||||||
|
* https://switchbrew.org/wiki/IPC_Marshalling#Domains
|
||||||
|
*/
|
||||||
|
struct DomainHeaderRequest {
|
||||||
|
u8 command : 8;
|
||||||
|
u8 input_count : 8;
|
||||||
|
u16 payload_sz : 16;
|
||||||
|
u32 object_id : 32;
|
||||||
|
u32 : 32;
|
||||||
|
u32 token : 32;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(DomainHeaderRequest) == 16);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This bit-field structure holds the domain's header of an IPC response command.
|
||||||
|
* https://switchbrew.org/wiki/IPC_Marshalling#Domains
|
||||||
|
*/
|
||||||
|
struct DomainHeaderResponse {
|
||||||
|
u64 output_count : 32;
|
||||||
|
u64 : 32;
|
||||||
|
u64 : 64;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(DomainHeaderResponse) == 16);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This bit-field structure holds the data payload of an IPC command.
|
||||||
|
* https://switchbrew.org/wiki/IPC_Marshalling#Data_payload
|
||||||
|
*/
|
||||||
|
struct PayloadHeader {
|
||||||
|
u32 magic : 32;
|
||||||
|
u32 version : 32;
|
||||||
|
u32 value : 32;
|
||||||
|
u32 token : 32;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(PayloadHeader) == 16);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a buffer descriptor for X buffers: https://switchbrew.org/wiki/IPC_Marshalling#Buffer_descriptor_X_.22Pointer.22
|
||||||
|
*/
|
||||||
|
struct BufferDescriptorX {
|
||||||
|
u16 counter_0_5 : 6;
|
||||||
|
u16 address_36_38 : 3;
|
||||||
|
u16 counter_9_11 : 3;
|
||||||
|
u16 address_32_35 : 4;
|
||||||
|
u16 size : 16;
|
||||||
|
u32 address_0_31 : 32;
|
||||||
|
|
||||||
|
BufferDescriptorX(u64 address, u16 counter, u16 size) : size(size) {
|
||||||
|
// Test: The AND mask might be the other way around
|
||||||
|
address_0_31 = static_cast<u32>(address & 0x7FFFFFFF80000000);
|
||||||
|
address_32_35 = static_cast<u16>(address & 0x78000000);
|
||||||
|
address_36_38 = static_cast<u16>(address & 0x7000000);
|
||||||
|
counter_0_5 = static_cast<u16>(address & 0x7E00);
|
||||||
|
counter_9_11 = static_cast<u16>(address & 0x38);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline u64 Address() const {
|
||||||
|
return static_cast<u64>(address_0_31) | static_cast<u64>(address_32_35) << 32 | static_cast<u64>(address_36_38) << 36;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline u16 Counter() const {
|
||||||
|
return static_cast<u16>(counter_0_5) | static_cast<u16>(counter_9_11) << 9;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(BufferDescriptorX) == 8);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a buffer descriptor for A/B/W buffers: https://switchbrew.org/wiki/IPC_Marshalling#Buffer_descriptor_A.2FB.2FW_.22Send.22.2F.22Receive.22.2F.22Exchange.22
|
||||||
|
*/
|
||||||
|
struct BufferDescriptorABW {
|
||||||
|
u32 size_0_31 : 32;
|
||||||
|
u32 address_0_31 : 32;
|
||||||
|
u8 flags : 2;
|
||||||
|
u8 address_36_38 : 3;
|
||||||
|
u32 : 19;
|
||||||
|
u8 size_32_35 : 4;
|
||||||
|
u8 address_32_35 : 4;
|
||||||
|
|
||||||
|
BufferDescriptorABW(u64 address, u64 size) {
|
||||||
|
address_0_31 = static_cast<u32>(address & 0x7FFFFFFF80000000);
|
||||||
|
address_32_35 = static_cast<u8>(address & 0x78000000);
|
||||||
|
address_36_38 = static_cast<u8>(address & 0x7000000);
|
||||||
|
size_0_31 = static_cast<u32>(size & 0x7FFFFFFF80000000);
|
||||||
|
size_32_35 = static_cast<u8>(size & 0x78000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline u64 Address() const {
|
||||||
|
return static_cast<u64>(address_0_31) | static_cast<u64>(address_32_35) << 32 | static_cast<u64>(address_36_38) << 36;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline u64 Size() const {
|
||||||
|
return static_cast<u64>(size_0_31) | static_cast<u64>(size_32_35) << 32;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(BufferDescriptorABW) == 12);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a buffer descriptor for C buffers: https://switchbrew.org/wiki/IPC_Marshalling#Buffer_descriptor_C_.22ReceiveList.22
|
||||||
|
*/
|
||||||
|
struct BufferDescriptorC {
|
||||||
|
u32 address_0_31 : 32;
|
||||||
|
u16 address_32_48 : 16;
|
||||||
|
u16 size : 16;
|
||||||
|
|
||||||
|
BufferDescriptorC(u64 address, u16 size) : size(size) {
|
||||||
|
address_0_31 = static_cast<u32>(address & 0x7FFFFFFF80000000);
|
||||||
|
address_32_48 = static_cast<u16>(address & 0x7FFFC000);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline u64 Address() const {
|
||||||
|
return static_cast<u64>(address_0_31) | static_cast<u64>(address_32_48) << 32;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(BufferDescriptorC) == 8);
|
||||||
|
|
||||||
class IpcRequest {
|
class IpcRequest {
|
||||||
private:
|
private:
|
||||||
uint8_t *data_ptr;
|
device_state &state;
|
||||||
uint32_t data_offset;
|
|
||||||
public:
|
|
||||||
CommandStruct *req_info;
|
|
||||||
|
|
||||||
IpcRequest(uint8_t *tlsPtr, device_state &state);
|
public:
|
||||||
|
std::array<u8, constant::tls_ipc_size> tls;
|
||||||
|
CommandHeader *header{};
|
||||||
|
HandleDescriptor *handle_desc{};
|
||||||
|
bool is_domain{};
|
||||||
|
DomainHeaderRequest *domain{};
|
||||||
|
PayloadHeader *payload{};
|
||||||
|
u8 *cmd_arg{};
|
||||||
|
u64 cmd_arg_sz{};
|
||||||
|
std::vector<handle_t> copy_handles;
|
||||||
|
std::vector<handle_t> move_handles;
|
||||||
|
std::vector<BufferDescriptorX *> vec_buf_x;
|
||||||
|
std::vector<BufferDescriptorABW *> vec_buf_a;
|
||||||
|
std::vector<BufferDescriptorABW *> vec_buf_b;
|
||||||
|
std::vector<BufferDescriptorABW *> vec_buf_w;
|
||||||
|
std::vector<BufferDescriptorC *> vec_buf_c;
|
||||||
|
|
||||||
template<typename T>
|
IpcRequest(bool is_domain, device_state &state);
|
||||||
T GetValue();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class IpcResponse {
|
class IpcResponse {
|
||||||
private:
|
private:
|
||||||
uint32_t *tls_ptr{};
|
std::vector<u8> arg_vec;
|
||||||
uint32_t data_offset{}; // Offset to raw data relative to tls_ptr
|
device_state &state;
|
||||||
|
|
||||||
bool is_domain{}; // TODO
|
public:
|
||||||
uint32_t error_code{};
|
bool is_domain{};
|
||||||
|
u32 error_code{};
|
||||||
|
std::vector<handle_t> copy_handles;
|
||||||
|
std::vector<handle_t> move_handles;
|
||||||
|
std::vector<BufferDescriptorX> vec_buf_x;
|
||||||
|
std::vector<BufferDescriptorABW> vec_buf_a;
|
||||||
|
std::vector<BufferDescriptorABW> vec_buf_b;
|
||||||
|
std::vector<BufferDescriptorABW> vec_buf_w;
|
||||||
|
std::vector<BufferDescriptorC> vec_buf_c;
|
||||||
|
|
||||||
CommandStruct resp_info;
|
IpcResponse(bool is_domain, device_state &state);
|
||||||
std::vector<uint32_t> copied_handles;
|
|
||||||
std::vector<uint32_t> moved_handles;
|
|
||||||
std::vector<uint8_t> data;
|
|
||||||
uint16_t data_pos{}; // Position in raw data relative to data_offset
|
|
||||||
public:
|
|
||||||
IpcResponse();
|
|
||||||
|
|
||||||
~IpcResponse();
|
|
||||||
|
|
||||||
void Generate(device_state &state);
|
|
||||||
|
|
||||||
void SetError(uint32_t _error_code);
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void WriteValue(); // TODO
|
void WriteValue(const T &value) {
|
||||||
|
arg_vec.reserve(arg_vec.size() + sizeof(T));
|
||||||
|
auto item = reinterpret_cast<const u8 *>(&value);
|
||||||
|
for (uint index = 0; sizeof(T) > index; index++) {
|
||||||
|
arg_vec.push_back(*item);
|
||||||
|
item++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CopyHandle(uint32_t handle);
|
void WriteTls();
|
||||||
|
|
||||||
void MoveHandle(uint32_t handle);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include "service.h"
|
#include "service.h"
|
||||||
|
|
||||||
namespace lightSwitch::kernel::service {
|
namespace lightSwitch::kernel::service {
|
||||||
|
|
||||||
Service::Service() {
|
Service::Service() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
#include "../nce.h"
|
#include "../nce.h"
|
||||||
|
|
||||||
namespace lightSwitch::kernel::service {
|
namespace lightSwitch::kernel::service {
|
||||||
class Service {
|
class Service {
|
||||||
Service();
|
Service();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,18 @@
|
|||||||
#include "../../common.h"
|
#include "../../common.h"
|
||||||
|
#include "../ipc.h"
|
||||||
|
|
||||||
namespace lightSwitch::kernel::service {
|
namespace lightSwitch::kernel::service {
|
||||||
class BaseService {
|
class BaseService {
|
||||||
virtual const char* name() = 0;
|
protected:
|
||||||
BaseService() {}
|
device_state &state;
|
||||||
|
|
||||||
|
public:
|
||||||
|
BaseService(device_state &state) : state(state) {}
|
||||||
|
|
||||||
|
virtual const char *Name() = 0;
|
||||||
|
|
||||||
|
virtual ipc::IpcResponse HandleSyncRequest(ipc::IpcRequest &request) = 0;
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -6,21 +6,37 @@
|
|||||||
|
|
||||||
namespace lightSwitch::kernel::svc {
|
namespace lightSwitch::kernel::svc {
|
||||||
void SetHeapSize(device_state &state) {
|
void SetHeapSize(device_state &state) {
|
||||||
uint64_t addr = state.this_process->MapPrivate(0, state.nce->GetRegister(regs::w1), {true, true, false}, memory::Region::heap);
|
auto heap = state.this_process->MapPrivateRegion(0, state.nce->GetRegister(wreg::w1), {true, true, false}, Memory::Type::Heap, Memory::Region::heap);
|
||||||
state.nce->SetRegister(regs::w0, constant::status::success);
|
state.nce->SetRegister(wreg::w0, constant::status::success);
|
||||||
state.nce->SetRegister(regs::x1, addr);
|
state.nce->SetRegister(xreg::x1, heap->address);
|
||||||
|
state.logger->Write(Logger::DEBUG, "Heap size was set to 0x{:X}", state.nce->GetRegister(wreg::w1));
|
||||||
|
}
|
||||||
|
|
||||||
|
void QueryMemory(device_state &state) {
|
||||||
|
Memory::MemoryInfo mem_inf;
|
||||||
|
u64 addr = state.nce->GetRegister(xreg::x2);
|
||||||
|
if (state.nce->memory_map.count(addr)) {
|
||||||
|
mem_inf = state.nce->memory_map.at(addr)->GetInfo(state.this_process->main_thread);
|
||||||
|
} else if (state.this_process->memory_map.count(addr)) {
|
||||||
|
mem_inf = state.this_process->memory_map.at(addr)->GetInfo();
|
||||||
|
} else {
|
||||||
|
state.nce->SetRegister(wreg::w0, constant::status::inv_address);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state.this_process->WriteMemory<Memory::MemoryInfo>(mem_inf, state.nce->GetRegister(xreg::x0));
|
||||||
|
state.nce->SetRegister(wreg::w0, constant::status::success);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CreateThread(device_state &state) {
|
void CreateThread(device_state &state) {
|
||||||
// TODO: Check if the values supplied by the process are actually valid & Support Core Mask potentially ?
|
// TODO: Check if the values supplied by the process are actually valid & Support Core Mask potentially ?
|
||||||
auto thread = state.this_process->CreateThread(state.nce->GetRegister(regs::x1), state.nce->GetRegister(regs::x2), state.nce->GetRegister(regs::x3), static_cast<uint8_t>(state.nce->GetRegister(regs::w4)));
|
auto thread = state.this_process->CreateThread(state.nce->GetRegister(xreg::x1), state.nce->GetRegister(xreg::x2), state.nce->GetRegister(xreg::x3), static_cast<u8>(state.nce->GetRegister(wreg::w4)));
|
||||||
state.nce->SetRegister(regs::w0, constant::status::success);
|
state.nce->SetRegister(wreg::w0, constant::status::success);
|
||||||
state.nce->SetRegister(regs::w1, thread->handle);
|
state.nce->SetRegister(wreg::w1, thread->handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StartThread(device_state &state) {
|
void StartThread(device_state &state) {
|
||||||
auto& object = state.this_process->handle_table.at(static_cast<const unsigned int &>(state.nce->GetRegister(regs::w0)));
|
auto &object = state.this_process->handle_table.at(static_cast<const unsigned int &>(state.nce->GetRegister(wreg::w0)));
|
||||||
if(object->type==type::KObjectType::KThread) {
|
if (object->type == type::KObjectType::KThread) {
|
||||||
std::static_pointer_cast<type::KThread>(object)->Start();
|
std::static_pointer_cast<type::KThread>(object)->Start();
|
||||||
} else
|
} else
|
||||||
throw exception("StartThread was called on a non-KThread object");
|
throw exception("StartThread was called on a non-KThread object");
|
||||||
@ -31,103 +47,125 @@ namespace lightSwitch::kernel::svc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GetThreadPriority(device_state &state) {
|
void GetThreadPriority(device_state &state) {
|
||||||
auto& object = state.this_process->handle_table.at(static_cast<const unsigned int &>(state.nce->GetRegister(regs::w0)));
|
auto &object = state.this_process->handle_table.at(static_cast<const unsigned int &>(state.nce->GetRegister(wreg::w0)));
|
||||||
if(object->type==type::KObjectType::KThread) {
|
if (object->type == type::KObjectType::KThread) {
|
||||||
state.nce->SetRegister(regs::w0, constant::status::success);
|
state.nce->SetRegister(wreg::w0, constant::status::success);
|
||||||
state.nce->SetRegister(regs::w1, std::static_pointer_cast<type::KThread>(object)->priority);
|
state.nce->SetRegister(wreg::w1, std::static_pointer_cast<type::KThread>(object)->priority);
|
||||||
} else
|
} else
|
||||||
throw exception("GetThreadPriority was called on a non-KThread object");
|
throw exception("GetThreadPriority was called on a non-KThread object");
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetThreadPriority(device_state &state) {
|
void SetThreadPriority(device_state &state) {
|
||||||
auto& object = state.this_process->handle_table.at(static_cast<const unsigned int &>(state.nce->GetRegister(regs::w0)));
|
auto &object = state.this_process->handle_table.at(static_cast<const unsigned int &>(state.nce->GetRegister(wreg::w0)));
|
||||||
if(object->type==type::KObjectType::KThread) {
|
if (object->type == type::KObjectType::KThread) {
|
||||||
std::static_pointer_cast<type::KThread>(object)->Start();
|
std::static_pointer_cast<type::KThread>(object)->Start();
|
||||||
} else
|
} else
|
||||||
throw exception("SetThreadPriority was called on a non-KThread object");
|
throw exception("SetThreadPriority was called on a non-KThread object");
|
||||||
}
|
}
|
||||||
|
|
||||||
void CloseHandle(device_state &state) {
|
void CloseHandle(device_state &state) {
|
||||||
auto& object = state.this_process->handle_table.at(static_cast<const unsigned int &>(state.nce->GetRegister(regs::w0)));
|
auto &object = state.this_process->handle_table.at(static_cast<const unsigned int &>(state.nce->GetRegister(wreg::w0)));
|
||||||
switch (object->type) {
|
switch (object->type) {
|
||||||
case(type::KObjectType::KThread):
|
case (type::KObjectType::KThread):
|
||||||
state.os->KillThread(std::static_pointer_cast<type::KThread>(object)->pid);
|
state.os->KillThread(std::static_pointer_cast<type::KThread>(object)->pid);
|
||||||
break;
|
break;
|
||||||
case(type::KObjectType::KProcess):
|
case (type::KObjectType::KProcess):
|
||||||
state.os->KillThread(std::static_pointer_cast<type::KProcess>(object)->main_thread);
|
state.os->KillThread(std::static_pointer_cast<type::KProcess>(object)->main_thread);
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
state.nce->SetRegister(wreg::w0, constant::status::inv_handle);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
state.nce->SetRegister(regs::w0, constant::status::success);
|
state.nce->SetRegister(wreg::w0, constant::status::success);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectToNamedPort(device_state &state) {
|
void ConnectToNamedPort(device_state &state) {
|
||||||
char port[constant::port_size]{0};
|
char port[constant::port_size]{0};
|
||||||
state.os->this_process->ReadMemory(port, state.nce->GetRegister(regs::x1), constant::port_size);
|
state.os->this_process->ReadMemory(port, state.nce->GetRegister(xreg::x1), constant::port_size);
|
||||||
if (std::strcmp(port, "sm:") == 0)
|
if (std::strcmp(port, "sm:") == 0)
|
||||||
state.nce->SetRegister(regs::w1, constant::sm_handle);
|
state.nce->SetRegister(wreg::w1, constant::sm_handle);
|
||||||
else
|
else
|
||||||
throw exception(fmt::format("svcConnectToNamedPort tried connecting to invalid port: \"{}\"", port));
|
throw exception(fmt::format("svcConnectToNamedPort tried connecting to invalid port: \"{}\"", port));
|
||||||
state.nce->SetRegister(regs::w0, constant::status::success);
|
state.nce->SetRegister(wreg::w0, constant::status::success);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendSyncRequest(device_state &state) {
|
void SendSyncRequest(device_state &state) {
|
||||||
state.logger->Write(Logger::DEBUG, "svcSendSyncRequest called for handle 0x{:X}.", state.nce->GetRegister(regs::x0));
|
state.logger->Write(Logger::DEBUG, "----------------------------svcSendSyncRequest Start-----------------------");
|
||||||
uint8_t tls[constant::tls_ipc_size];
|
state.logger->Write(Logger::DEBUG, "svcSendSyncRequest called for handle 0x{:X}.", state.nce->GetRegister(xreg::x0));
|
||||||
state.os->this_process->ReadMemory(&tls, state.this_thread->tls, constant::tls_ipc_size);
|
state.os->IpcHandler(static_cast<handle_t>(state.nce->GetRegister(xreg::x0)));
|
||||||
ipc::IpcRequest request(tls, state);
|
state.nce->SetRegister(wreg::w0, constant::status::success);
|
||||||
state.os->IpcHandler(request);
|
state.nce->SetRegister(wreg::w19, constant::status::success);
|
||||||
|
state.logger->Write(Logger::DEBUG, "----------------------------svcSendSyncRequest End-------------------------");
|
||||||
}
|
}
|
||||||
|
|
||||||
void OutputDebugString(device_state &state) {
|
void OutputDebugString(device_state &state) {
|
||||||
std::string debug(state.nce->GetRegister(regs::x1), '\0');
|
std::string debug(state.nce->GetRegister(xreg::x1), '\0');
|
||||||
state.os->this_process->ReadMemory((void *) debug.data(), state.nce->GetRegister(regs::x0), state.nce->GetRegister(regs::x1));
|
state.os->this_process->ReadMemory((void *) debug.data(), state.nce->GetRegister(xreg::x0), state.nce->GetRegister(xreg::x1));
|
||||||
state.logger->Write(Logger::INFO, "svcOutputDebugString: {}", debug.c_str());
|
state.logger->Write(Logger::INFO, "svcOutputDebugString: {}", debug.c_str());
|
||||||
state.nce->SetRegister(regs::w0, 0);
|
state.nce->SetRegister(wreg::w0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetInfo(device_state &state) {
|
void GetInfo(device_state &state) {
|
||||||
state.logger->Write(Logger::DEBUG, "svcGetInfo called with ID0: {}, ID1: {}", state.nce->GetRegister(regs::w1), state.nce->GetRegister(regs::x3));
|
state.logger->Write(Logger::DEBUG, "svcGetInfo called with ID0: {}, ID1: {}", state.nce->GetRegister(wreg::w1), state.nce->GetRegister(xreg::x3));
|
||||||
switch (state.nce->GetRegister(regs::w1)) {
|
switch (state.nce->GetRegister(wreg::w1)) {
|
||||||
case constant::infoState::AllowedCpuIdBitmask:
|
case constant::infoState::AllowedCpuIdBitmask:
|
||||||
case constant::infoState::AllowedThreadPriorityMask:
|
case constant::infoState::AllowedThreadPriorityMask:
|
||||||
case constant::infoState::IsCurrentProcessBeingDebugged:
|
case constant::infoState::IsCurrentProcessBeingDebugged:
|
||||||
case constant::infoState::TitleId:
|
case constant::infoState::TitleId:
|
||||||
case constant::infoState::PrivilegedProcessId:
|
case constant::infoState::PrivilegedProcessId:
|
||||||
state.nce->SetRegister(regs::x1, 0);
|
state.nce->SetRegister(xreg::x1, 0);
|
||||||
|
break;
|
||||||
|
case constant::infoState::AliasRegionBaseAddr:
|
||||||
|
state.nce->SetRegister(xreg::x1, constant::map_addr);
|
||||||
|
break;
|
||||||
|
case constant::infoState::AliasRegionSize:
|
||||||
|
state.nce->SetRegister(xreg::x1, constant::map_size);
|
||||||
break;
|
break;
|
||||||
case constant::infoState::HeapRegionBaseAddr:
|
case constant::infoState::HeapRegionBaseAddr:
|
||||||
state.nce->SetRegister(regs::x1, state.os->this_process->memory_map.at(memory::Region::heap).address);
|
state.nce->SetRegister(xreg::x1, state.os->this_process->memory_region_map.at(Memory::Region::heap)->address);
|
||||||
break;
|
break;
|
||||||
case constant::infoState::HeapRegionSize:
|
case constant::infoState::HeapRegionSize:
|
||||||
state.nce->SetRegister(regs::x1, state.os->this_process->memory_map.at(memory::Region::heap).size);
|
state.nce->SetRegister(xreg::x1, state.os->this_process->memory_region_map.at(Memory::Region::heap)->size);
|
||||||
|
break;
|
||||||
|
case constant::infoState::TotalMemoryAvailable:
|
||||||
|
state.nce->SetRegister(xreg::x1, constant::total_phy_mem);
|
||||||
|
break;
|
||||||
|
case constant::infoState::TotalMemoryUsage:
|
||||||
|
state.nce->SetRegister(xreg::x1, state.os->this_process->memory_region_map.at(Memory::Region::heap)->address + state.this_process->main_thread_stack_sz + state.nce->GetSharedSize());
|
||||||
break;
|
break;
|
||||||
case constant::infoState::AddressSpaceBaseAddr:
|
case constant::infoState::AddressSpaceBaseAddr:
|
||||||
state.nce->SetRegister(regs::x1, constant::base_addr);
|
state.nce->SetRegister(xreg::x1, constant::base_addr);
|
||||||
break;
|
break;
|
||||||
case constant::infoState::AddressSpaceSize:
|
case constant::infoState::AddressSpaceSize:
|
||||||
state.nce->SetRegister(regs::x1, constant::base_size);
|
state.nce->SetRegister(xreg::x1, constant::base_size);
|
||||||
|
break;
|
||||||
|
case constant::infoState::StackRegionBaseAddr:
|
||||||
|
state.nce->SetRegister(xreg::x1, state.this_thread->stack_top);
|
||||||
|
break;
|
||||||
|
case constant::infoState::StackRegionSize:
|
||||||
|
state.nce->SetRegister(xreg::x1, state.this_process->main_thread_stack_sz);
|
||||||
break;
|
break;
|
||||||
case constant::infoState::PersonalMmHeapSize:
|
case constant::infoState::PersonalMmHeapSize:
|
||||||
state.nce->SetRegister(regs::x1, constant::total_phy_mem);
|
state.nce->SetRegister(xreg::x1, constant::total_phy_mem);
|
||||||
break;
|
break;
|
||||||
case constant::infoState::PersonalMmHeapUsage:
|
case constant::infoState::PersonalMmHeapUsage:
|
||||||
state.nce->SetRegister(regs::x1, state.os->this_process->memory_map.at(memory::Region::heap).address + state.this_process->main_thread_stack_sz);
|
state.nce->SetRegister(xreg::x1, state.os->this_process->memory_region_map.at(Memory::Region::heap)->address + state.this_process->main_thread_stack_sz);
|
||||||
break;
|
break;
|
||||||
case constant::infoState::TotalMemoryAvailableWithoutMmHeap:
|
case constant::infoState::TotalMemoryAvailableWithoutMmHeap:
|
||||||
state.nce->SetRegister(regs::x1, constant::total_phy_mem); // TODO: NPDM specifies SystemResourceSize, subtract that from this
|
state.nce->SetRegister(xreg::x1, constant::total_phy_mem); // TODO: NPDM specifies SystemResourceSize, subtract that from this
|
||||||
break;
|
break;
|
||||||
case constant::infoState::TotalMemoryUsedWithoutMmHeap:
|
case constant::infoState::TotalMemoryUsedWithoutMmHeap:
|
||||||
state.nce->SetRegister(regs::x1, state.os->this_process->memory_map.at(memory::Region::heap).address + state.this_process->main_thread_stack_sz); // TODO: Same as above
|
state.nce->SetRegister(xreg::x1, state.os->this_process->memory_region_map.at(Memory::Region::heap)->address + state.this_process->main_thread_stack_sz); // TODO: Same as above
|
||||||
break;
|
break;
|
||||||
case constant::infoState::UserExceptionContextAddr:
|
case constant::infoState::UserExceptionContextAddr:
|
||||||
state.nce->SetRegister(regs::x1, state.this_process->tls_pages[0]->Get(0));
|
state.nce->SetRegister(xreg::x1, state.this_process->tls_pages[0]->Get(0));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
state.logger->Write(Logger::WARN, "Unimplemented svcGetInfo with ID0: {}, ID1: {}", state.nce->GetRegister(regs::w1), state.nce->GetRegister(regs::x3));
|
state.logger->Write(Logger::WARN, "Unimplemented svcGetInfo with ID0: {}, ID1: {}", state.nce->GetRegister(wreg::w1), state.nce->GetRegister(xreg::x3));
|
||||||
state.nce->SetRegister(regs::w0, constant::status::unimpl);
|
state.nce->SetRegister(wreg::w0, constant::status::unimpl);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
state.nce->SetRegister(regs::w0, constant::status::success);
|
state.nce->SetRegister(wreg::w0, constant::status::success);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExitProcess(device_state &state) {
|
void ExitProcess(device_state &state) {
|
||||||
|
@ -4,116 +4,119 @@
|
|||||||
#include "../common.h"
|
#include "../common.h"
|
||||||
#include "switch/os.h"
|
#include "switch/os.h"
|
||||||
|
|
||||||
namespace lightSwitch::constant {
|
namespace lightSwitch {
|
||||||
namespace infoState {
|
namespace constant::infoState {
|
||||||
// 1.0.0+
|
// 1.0.0+
|
||||||
constexpr uint8_t AllowedCpuIdBitmask = 0x0;
|
constexpr u8 AllowedCpuIdBitmask = 0x0;
|
||||||
constexpr uint8_t AllowedThreadPriorityMask = 0x1;
|
constexpr u8 AllowedThreadPriorityMask = 0x1;
|
||||||
constexpr uint8_t AliasRegionBaseAddr = 0x2;
|
constexpr u8 AliasRegionBaseAddr = 0x2;
|
||||||
constexpr uint8_t AliasRegionSize = 0x3;
|
constexpr u8 AliasRegionSize = 0x3;
|
||||||
constexpr uint8_t HeapRegionBaseAddr = 0x4;
|
constexpr u8 HeapRegionBaseAddr = 0x4;
|
||||||
constexpr uint8_t HeapRegionSize = 0x5;
|
constexpr u8 HeapRegionSize = 0x5;
|
||||||
constexpr uint8_t TotalMemoryAvailable = 0x6;
|
constexpr u8 TotalMemoryAvailable = 0x6;
|
||||||
constexpr uint8_t TotalMemoryUsage = 0x7;
|
constexpr u8 TotalMemoryUsage = 0x7;
|
||||||
constexpr uint8_t IsCurrentProcessBeingDebugged = 0x8;
|
constexpr u8 IsCurrentProcessBeingDebugged = 0x8;
|
||||||
constexpr uint8_t ResourceLimit = 0x9;
|
constexpr u8 ResourceLimit = 0x9;
|
||||||
constexpr uint8_t IdleTickCount = 0xA;
|
constexpr u8 IdleTickCount = 0xA;
|
||||||
constexpr uint8_t RandomEntropy = 0xB;
|
constexpr u8 RandomEntropy = 0xB;
|
||||||
// 2.0.0+
|
// 2.0.0+
|
||||||
constexpr uint8_t AddressSpaceBaseAddr = 0xC;
|
constexpr u8 AddressSpaceBaseAddr = 0xC;
|
||||||
constexpr uint8_t AddressSpaceSize = 0xD;
|
constexpr u8 AddressSpaceSize = 0xD;
|
||||||
constexpr uint8_t StackRegionBaseAddr = 0xE;
|
constexpr u8 StackRegionBaseAddr = 0xE;
|
||||||
constexpr uint8_t StackRegionSize = 0xF;
|
constexpr u8 StackRegionSize = 0xF;
|
||||||
// 3.0.0+
|
// 3.0.0+
|
||||||
constexpr uint8_t PersonalMmHeapSize = 0x10;
|
constexpr u8 PersonalMmHeapSize = 0x10;
|
||||||
constexpr uint8_t PersonalMmHeapUsage = 0x11;
|
constexpr u8 PersonalMmHeapUsage = 0x11;
|
||||||
constexpr uint8_t TitleId = 0x12;
|
constexpr u8 TitleId = 0x12;
|
||||||
// 4.0.0+
|
// 4.0.0+
|
||||||
constexpr uint8_t PrivilegedProcessId = 0x13;
|
constexpr u8 PrivilegedProcessId = 0x13;
|
||||||
// 5.0.0+
|
// 5.0.0+
|
||||||
constexpr uint8_t UserExceptionContextAddr = 0x14;
|
constexpr u8 UserExceptionContextAddr = 0x14;
|
||||||
// 6.0.0+
|
// 6.0.0+
|
||||||
constexpr uint8_t TotalMemoryAvailableWithoutMmHeap = 0x15;
|
constexpr u8 TotalMemoryAvailableWithoutMmHeap = 0x15;
|
||||||
constexpr uint8_t TotalMemoryUsedWithoutMmHeap = 0x16;
|
constexpr u8 TotalMemoryUsedWithoutMmHeap = 0x16;
|
||||||
};
|
};
|
||||||
namespace status {
|
namespace kernel::svc {
|
||||||
constexpr uint32_t success = 0x0; //!< "Success"
|
namespace structs {
|
||||||
constexpr uint32_t unimpl = 0x177202; //!< "Unimplemented behaviour"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace lightSwitch::kernel::svc {
|
}
|
||||||
/**
|
|
||||||
* Set the process heap to a given size (https://switchbrew.org/wiki/SVC#svcSetHeapSize)
|
|
||||||
*/
|
|
||||||
void SetHeapSize(device_state &state);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exits the current process (https://switchbrew.org/wiki/SVC#svcExitProcess)
|
* Set the process heap to a given size (https://switchbrew.org/wiki/SVC#svcSetHeapSize)
|
||||||
*/
|
*/
|
||||||
void ExitProcess(device_state &state);
|
void SetHeapSize(device_state &state);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a thread in the current process (https://switchbrew.org/wiki/SVC#svcCreateThread)
|
* Query information about an address. Will always fetch the lowest page-aligned mapping that contains the provided address (https://switchbrew.org/wiki/SVC#svcQueryMemory)
|
||||||
*/
|
*/
|
||||||
void CreateThread(device_state &state);
|
void QueryMemory(device_state &state);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts the thread for the provided handle (https://switchbrew.org/wiki/SVC#svcStartThread)
|
* Exits the current process (https://switchbrew.org/wiki/SVC#svcExitProcess)
|
||||||
*/
|
*/
|
||||||
void StartThread(device_state &state);
|
void ExitProcess(device_state &state);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exits the current thread (https://switchbrew.org/wiki/SVC#svcExitThread)
|
* Create a thread in the current process (https://switchbrew.org/wiki/SVC#svcCreateThread)
|
||||||
*/
|
*/
|
||||||
void ExitThread(device_state &state);
|
void CreateThread(device_state &state);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get priority of provided thread handle (https://switchbrew.org/wiki/SVC#svcGetThreadPriority)
|
* Starts the thread for the provided handle (https://switchbrew.org/wiki/SVC#svcStartThread)
|
||||||
*/
|
*/
|
||||||
void GetThreadPriority(device_state &state);
|
void StartThread(device_state &state);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set priority of provided thread handle (https://switchbrew.org/wiki/SVC#svcSetThreadPriority)
|
* Exits the current thread (https://switchbrew.org/wiki/SVC#svcExitThread)
|
||||||
*/
|
*/
|
||||||
void SetThreadPriority(device_state &state);
|
void ExitThread(device_state &state);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the specified handle
|
* Get priority of provided thread handle (https://switchbrew.org/wiki/SVC#svcGetThreadPriority)
|
||||||
*/
|
*/
|
||||||
void CloseHandle(device_state &state);
|
void GetThreadPriority(device_state &state);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connects to a named IPC port
|
* Set priority of provided thread handle (https://switchbrew.org/wiki/SVC#svcSetThreadPriority)
|
||||||
*/
|
*/
|
||||||
void ConnectToNamedPort(device_state &state);
|
void SetThreadPriority(device_state &state);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a synchronous IPC request to a service
|
* Closes the specified handle
|
||||||
*/
|
*/
|
||||||
void SendSyncRequest(device_state &state);
|
void CloseHandle(device_state &state);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Outputs a debug string
|
* Connects to a named IPC port
|
||||||
*/
|
*/
|
||||||
void OutputDebugString(device_state &state);
|
void ConnectToNamedPort(device_state &state);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a piece of information (https://switchbrew.org/wiki/SVC#svcGetInfo)
|
* Send a synchronous IPC request to a service
|
||||||
*/
|
*/
|
||||||
void GetInfo(device_state &state);
|
void SendSyncRequest(device_state &state);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The SVC Table maps all SVCs to their corresponding functions
|
* Outputs a debug string
|
||||||
*/
|
*/
|
||||||
void static (*svcTable[0x80])(device_state &) = {
|
void OutputDebugString(device_state &state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a piece of information (https://switchbrew.org/wiki/SVC#svcGetInfo)
|
||||||
|
*/
|
||||||
|
void GetInfo(device_state &state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The SVC Table maps all SVCs to their corresponding functions
|
||||||
|
*/
|
||||||
|
void static (*svcTable[0x80])(device_state &) = {
|
||||||
nullptr, // 0x00 (Does not exist)
|
nullptr, // 0x00 (Does not exist)
|
||||||
SetHeapSize, // 0x01
|
SetHeapSize, // 0x01
|
||||||
nullptr, // 0x02
|
nullptr, // 0x02
|
||||||
nullptr, // 0x03
|
nullptr, // 0x03
|
||||||
nullptr, // 0x04
|
nullptr, // 0x04
|
||||||
nullptr, // 0x05
|
nullptr, // 0x05
|
||||||
nullptr, // 0x06
|
QueryMemory, // 0x06
|
||||||
ExitProcess, // 0x07
|
ExitProcess, // 0x07
|
||||||
CreateThread, // 0x08
|
CreateThread, // 0x08
|
||||||
StartThread, // 0x09
|
StartThread, // 0x09
|
||||||
@ -235,5 +238,6 @@ namespace lightSwitch::kernel::svc {
|
|||||||
nullptr, // 0x7d
|
nullptr, // 0x7d
|
||||||
nullptr, // 0x7e
|
nullptr, // 0x7e
|
||||||
nullptr // 0x7f
|
nullptr // 0x7f
|
||||||
};
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,14 @@
|
|||||||
|
|
||||||
namespace lightSwitch::kernel::type {
|
namespace lightSwitch::kernel::type {
|
||||||
enum class KObjectType {
|
enum class KObjectType {
|
||||||
KThread, KProcess
|
KThread, KProcess, KSharedMemory
|
||||||
};
|
};
|
||||||
|
|
||||||
class KObject {
|
class KObject {
|
||||||
public:
|
public:
|
||||||
uint32_t handle;
|
u32 handle;
|
||||||
KObjectType type;
|
KObjectType type;
|
||||||
|
|
||||||
KObject(handle_t handle, KObjectType type) : handle(handle), type(type) {}
|
KObject(handle_t handle, KObjectType type) : handle(handle), type(type) {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
76
app/src/main/cpp/switch/kernel/types/KPrivateMemory.cpp
Normal file
76
app/src/main/cpp/switch/kernel/types/KPrivateMemory.cpp
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
#include "KPrivateMemory.h"
|
||||||
|
#include "../../nce.h"
|
||||||
|
#include "../../os.h"
|
||||||
|
#include <android/sharedmem.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
namespace lightSwitch::kernel::type {
|
||||||
|
u64 MapPrivateFunc(u64 dst_address, u64 src_address, size_t size, u64 perms) {
|
||||||
|
dst_address = reinterpret_cast<u64>(mmap(reinterpret_cast<void *>(dst_address), size, static_cast<int>(perms), MAP_PRIVATE | MAP_ANONYMOUS | ((dst_address) ? MAP_FIXED : 0), -1, 0)); // NOLINT(hicpp-signed-bitwise)
|
||||||
|
if (src_address) {
|
||||||
|
memcpy(reinterpret_cast<void *>(dst_address), reinterpret_cast<const void *>(src_address), size);
|
||||||
|
mprotect(reinterpret_cast<void *>(src_address), size, PROT_NONE);
|
||||||
|
}
|
||||||
|
return dst_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
KPrivateMemory::KPrivateMemory(const device_state &state, u64 dst_address, u64 src_address, size_t size, Memory::Permission permission, const Memory::Type type, pid_t owner_pid) : state(state), address(dst_address), size(size), permission(permission), type(type), owner_pid(owner_pid) {
|
||||||
|
user_pt_regs fregs = {0};
|
||||||
|
fregs.regs[0] = dst_address;
|
||||||
|
fregs.regs[1] = src_address;
|
||||||
|
fregs.regs[2] = size;
|
||||||
|
fregs.regs[3] = static_cast<u64>(permission.get());
|
||||||
|
state.nce->ExecuteFunction(reinterpret_cast<void *>(MapPrivateFunc), fregs, owner_pid);
|
||||||
|
if (reinterpret_cast<void *>(fregs.regs[0]) == MAP_FAILED)
|
||||||
|
throw exception("An error occurred while mapping private region in child process");
|
||||||
|
if (!this->address) this->address = fregs.regs[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 UnmapPrivateFunc(u64 address, size_t size) {
|
||||||
|
return static_cast<u64>(munmap(reinterpret_cast<void *>(address), size));
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 RemapPrivateFunc(u64 address, size_t old_size, size_t size) {
|
||||||
|
return reinterpret_cast<u64>(mremap(reinterpret_cast<void *>(address), old_size, size, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
void KPrivateMemory::Resize(size_t new_size) {
|
||||||
|
user_pt_regs fregs = {0};
|
||||||
|
fregs.regs[0] = address;
|
||||||
|
fregs.regs[1] = size;
|
||||||
|
fregs.regs[2] = new_size;
|
||||||
|
state.nce->ExecuteFunction(reinterpret_cast<void *>(RemapPrivateFunc), fregs, owner_pid);
|
||||||
|
if (reinterpret_cast<void *>(fregs.regs[0]) == MAP_FAILED)
|
||||||
|
throw exception("An error occurred while remapping private region in child process");
|
||||||
|
size = new_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 UpdatePermissionPrivateFunc(u64 address, size_t size, u64 perms) {
|
||||||
|
return static_cast<u64>(mprotect(reinterpret_cast<void *>(address), size, static_cast<int>(perms)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void KPrivateMemory::UpdatePermission(Memory::Permission new_perms) {
|
||||||
|
user_pt_regs fregs = {0};
|
||||||
|
fregs.regs[0] = address;
|
||||||
|
fregs.regs[1] = size;
|
||||||
|
fregs.regs[2] = static_cast<u64>(new_perms.get());
|
||||||
|
state.nce->ExecuteFunction(reinterpret_cast<void *>(UpdatePermissionPrivateFunc), fregs, owner_pid);
|
||||||
|
if (static_cast<int>(fregs.regs[0]) == -1)
|
||||||
|
throw exception("An error occurred while updating private region's permissions in child process");
|
||||||
|
permission = new_perms;
|
||||||
|
}
|
||||||
|
|
||||||
|
Memory::MemoryInfo KPrivateMemory::GetInfo() {
|
||||||
|
Memory::MemoryInfo info{};
|
||||||
|
info.base_address = address;
|
||||||
|
info.size = size;
|
||||||
|
info.type = static_cast<u64>(type);
|
||||||
|
info.memory_attribute.IsIpcLocked = (info.ipc_ref_count > 0);
|
||||||
|
info.memory_attribute.IsDeviceShared = (info.device_ref_count > 0);
|
||||||
|
info.perms = permission;
|
||||||
|
info.ipc_ref_count = ipc_ref_count;
|
||||||
|
info.device_ref_count = device_ref_count;
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
};
|
50
app/src/main/cpp/switch/kernel/types/KPrivateMemory.h
Normal file
50
app/src/main/cpp/switch/kernel/types/KPrivateMemory.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../../memory.h"
|
||||||
|
#include "KObject.h"
|
||||||
|
|
||||||
|
namespace lightSwitch::kernel::type {
|
||||||
|
class KPrivateMemory {
|
||||||
|
private:
|
||||||
|
const device_state &state; //!< The state of the device
|
||||||
|
|
||||||
|
public:
|
||||||
|
u64 address; //!< The address of the allocated memory
|
||||||
|
size_t size; //!< The size of the allocated memory
|
||||||
|
u16 ipc_ref_count{}; //!< The amount of reference to this memory for IPC
|
||||||
|
u16 device_ref_count{}; //!< The amount of reference to this memory for IPC
|
||||||
|
Memory::Permission permission; //!< The amount of reference to this memory for IPC
|
||||||
|
const Memory::Type type; //!< The type of this memory allocation
|
||||||
|
const pid_t owner_pid; //!< The PID of the owner process
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor of a private memory object
|
||||||
|
* @param dst_address The address to map to (If NULL then an arbitrary address is picked)
|
||||||
|
* @param src_address The address to map from (If NULL then no copy is performed)
|
||||||
|
* @param size The size of the allocation
|
||||||
|
* @param permission The permissions for the memory
|
||||||
|
* @param owner_pid The PID of the owner process
|
||||||
|
*/
|
||||||
|
KPrivateMemory(const device_state &state, u64 dst_address, u64 src_address, size_t size, Memory::Permission permission, const Memory::Type type, const pid_t owner_pid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remap a chunk of memory as to change the size occupied by it
|
||||||
|
* @param address The address of the mapped memory
|
||||||
|
* @param old_size The current size of the memory
|
||||||
|
* @param size The new size of the memory
|
||||||
|
*/
|
||||||
|
void Resize(size_t new_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the permissions of a chunk of mapped memory
|
||||||
|
* @param perms The new permissions to be set for the memory
|
||||||
|
*/
|
||||||
|
void UpdatePermission(Memory::Permission new_perms);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param pid The PID of the requesting process
|
||||||
|
* @return A Memory::MemoryInfo struct based on attributes of the memory
|
||||||
|
*/
|
||||||
|
Memory::MemoryInfo GetInfo();
|
||||||
|
};
|
||||||
|
}
|
@ -5,17 +5,17 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace lightSwitch::kernel::type {
|
namespace lightSwitch::kernel::type {
|
||||||
KProcess::tls_page_t::tls_page_t(uint64_t address) : address(address) {}
|
KProcess::tls_page_t::tls_page_t(u64 address) : address(address) {}
|
||||||
|
|
||||||
uint64_t KProcess::tls_page_t::ReserveSlot() {
|
u64 KProcess::tls_page_t::ReserveSlot() {
|
||||||
if (Full())
|
if (Full())
|
||||||
throw exception("Trying to get TLS slot from full page");
|
throw exception("Trying to get TLS slot from full page");
|
||||||
slot[index] = true;
|
slot[index] = true;
|
||||||
return Get(index++); // ++ on right will cause increment after evaluation of expression
|
return Get(index++); // ++ on right will cause increment after evaluation of expression
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t KProcess::tls_page_t::Get(uint8_t slot_no) {
|
u64 KProcess::tls_page_t::Get(u8 slot_no) {
|
||||||
if(slot_no>=constant::tls_slots)
|
if (slot_no >= constant::tls_slots)
|
||||||
throw exception("TLS slot is out of range");
|
throw exception("TLS slot is out of range");
|
||||||
return address + (constant::tls_slot_size * slot_no);
|
return address + (constant::tls_slot_size * slot_no);
|
||||||
}
|
}
|
||||||
@ -24,26 +24,31 @@ namespace lightSwitch::kernel::type {
|
|||||||
return slot[constant::tls_slots - 1];
|
return slot[constant::tls_slots - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t KProcess::GetTLSSlot(bool init) {
|
u64 KProcess::GetTLSSlot(bool init) {
|
||||||
if (!init)
|
if (!init)
|
||||||
for (auto &tls_page: tls_pages) {
|
for (auto &tls_page: tls_pages) {
|
||||||
if (!tls_page->Full())
|
if (!tls_page->Full())
|
||||||
return tls_page->ReserveSlot();
|
return tls_page->ReserveSlot();
|
||||||
}
|
}
|
||||||
uint64_t address = MapPrivate(0, PAGE_SIZE, {true, true, false}, memory::Region::tls);
|
auto tls_mem = std::make_shared<KPrivateMemory>(KPrivateMemory(state, 0, 0, PAGE_SIZE, {true, true, false}, Memory::Type::ThreadLocal, main_thread));
|
||||||
tls_pages.push_back(std::make_shared<tls_page_t>(address));
|
memory_map[tls_mem->address] = tls_mem;
|
||||||
|
tls_pages.push_back(std::make_shared<tls_page_t>(tls_mem->address));
|
||||||
auto &tls_page = tls_pages.back();
|
auto &tls_page = tls_pages.back();
|
||||||
if (init)
|
if (init)
|
||||||
tls_page->ReserveSlot(); // User-mode exception handling
|
tls_page->ReserveSlot(); // User-mode exception handling
|
||||||
return tls_page->ReserveSlot();
|
return tls_page->ReserveSlot();
|
||||||
}
|
}
|
||||||
|
|
||||||
KProcess::KProcess(pid_t pid, uint64_t entry_point, uint64_t stack_base, uint64_t stack_size, const device_state &state, handle_t handle) : state(state), handle(handle), main_thread_stack_sz(stack_size), KObject(handle, KObjectType::KProcess) {
|
KProcess::KProcess(pid_t pid, u64 entry_point, u64 stack_base, u64 stack_size, const device_state &state, handle_t handle) : state(state), handle(handle), main_thread_stack_sz(stack_size), KObject(handle, KObjectType::KProcess) {
|
||||||
process_state = process_state_t::Created;
|
process_state = process_state_t::Created;
|
||||||
main_thread = pid;
|
main_thread = pid;
|
||||||
state.nce->WaitRdy(pid);
|
state.nce->WaitRdy(pid);
|
||||||
thread_map[main_thread] = std::make_shared<KThread>(handle_index, pid, entry_point, 0, stack_base + stack_size, GetTLSSlot(true), constant::default_priority, this, state);
|
thread_map[main_thread] = std::make_shared<KThread>(handle_index, pid, entry_point, 0, stack_base + stack_size, GetTLSSlot(true), constant::default_priority, this, state);
|
||||||
NewHandle(std::static_pointer_cast<KObject>(thread_map[main_thread]));
|
NewHandle(std::static_pointer_cast<KObject>(thread_map[main_thread]));
|
||||||
|
MapPrivateRegion(0, constant::def_heap_size, {true, true, true}, Memory::Type::Heap, Memory::Region::heap);
|
||||||
|
for (auto ®ion : state.nce->memory_map) {
|
||||||
|
region.second->InitiateProcess(pid);
|
||||||
|
}
|
||||||
mem_fd = open(fmt::format("/proc/{}/mem", pid).c_str(), O_RDWR | O_CLOEXEC); // NOLINT(hicpp-signed-bitwise)
|
mem_fd = open(fmt::format("/proc/{}/mem", pid).c_str(), O_RDWR | O_CLOEXEC); // NOLINT(hicpp-signed-bitwise)
|
||||||
if (mem_fd == -1) throw exception(fmt::format("Cannot open file descriptor to /proc/{}/mem", pid));
|
if (mem_fd == -1) throw exception(fmt::format("Cannot open file descriptor to /proc/{}/mem", pid));
|
||||||
}
|
}
|
||||||
@ -61,128 +66,36 @@ namespace lightSwitch::kernel::type {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t CreateThreadFunc(uint64_t stack_top) {
|
u64 CreateThreadFunc(u64 stack_top) {
|
||||||
pid_t pid = clone(&ExecuteChild, reinterpret_cast<void *>(stack_top), CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM, nullptr); // NOLINT(hicpp-signed-bitwise)
|
pid_t pid = clone(&ExecuteChild, reinterpret_cast<void *>(stack_top), CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM, nullptr); // NOLINT(hicpp-signed-bitwise)
|
||||||
return static_cast<uint64_t>(pid);
|
return static_cast<u64>(pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<KThread> KProcess::CreateThread(uint64_t entry_point, uint64_t entry_arg, uint64_t stack_top, uint8_t priority) {
|
std::shared_ptr<KThread> KProcess::CreateThread(u64 entry_point, u64 entry_arg, u64 stack_top, u8 priority) {
|
||||||
user_pt_regs fregs = {0};
|
user_pt_regs fregs = {0};
|
||||||
fregs.regs[0] = entry_point;
|
fregs.regs[0] = entry_point;
|
||||||
fregs.regs[1] = stack_top;
|
fregs.regs[1] = stack_top;
|
||||||
state.nce->ExecuteFunction((void *) CreateThreadFunc, fregs, main_thread);
|
state.nce->ExecuteFunction((void *) CreateThreadFunc, fregs, main_thread);
|
||||||
if (fregs.regs[0]==-1)
|
if (fregs.regs[0] == -1)
|
||||||
throw exception(fmt::format("Cannot create thread: Address: {}, Stack Top: {}", entry_point, stack_top));
|
throw exception(fmt::format("Cannot create thread: Address: {}, Stack Top: {}", entry_point, stack_top));
|
||||||
auto thread = std::make_shared<kernel::type::KThread>(handle_index, static_cast<pid_t>(fregs.regs[0]), entry_point, entry_arg, stack_top, GetTLSSlot(false), priority, this, state);
|
auto thread = std::make_shared<kernel::type::KThread>(handle_index, static_cast<pid_t>(fregs.regs[0]), entry_point, entry_arg, stack_top, GetTLSSlot(false), priority, this, state);
|
||||||
NewHandle(std::static_pointer_cast<KObject>(thread));
|
NewHandle(std::static_pointer_cast<KObject>(thread));
|
||||||
return thread;
|
return thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
void KProcess::ReadMemory(void *destination, u64 offset, size_t size) const {
|
||||||
T KProcess::ReadMemory(uint64_t address) const {
|
|
||||||
T item{};
|
|
||||||
ReadMemory(&item, address, sizeof(T));
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void KProcess::WriteMemory(T &item, uint64_t address) const {
|
|
||||||
WriteMemory(&item, address, sizeof(T));
|
|
||||||
}
|
|
||||||
|
|
||||||
void KProcess::ReadMemory(void *destination, uint64_t offset, size_t size) const {
|
|
||||||
state.logger->Write(Logger::DEBUG, "ReadMemory for DE: 0x{:X}, OF: 0x{:X}, SZ: 0x{:X}", (uint64_t) destination, offset, size);
|
|
||||||
pread64(mem_fd, destination, size, offset);
|
pread64(mem_fd, destination, size, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void KProcess::WriteMemory(void *source, uint64_t offset, size_t size) const {
|
void KProcess::WriteMemory(void *source, u64 offset, size_t size) const {
|
||||||
state.logger->Write(Logger::DEBUG, "WriteMemory for SRC: 0x{:X}, OF: 0x{:X}, SZ: 0x{:X}", (uint64_t) source, offset, size);
|
|
||||||
pwrite64(mem_fd, source, size, offset);
|
pwrite64(mem_fd, source, size, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t MapPrivateFunc(uint64_t address, size_t size, uint64_t perms) {
|
std::shared_ptr<KPrivateMemory> KProcess::MapPrivateRegion(u64 address, size_t size, const Memory::Permission perms, const Memory::Type type, const Memory::Region region) {
|
||||||
int flags = MAP_PRIVATE | MAP_ANONYMOUS; // NOLINT(hicpp-signed-bitwise)
|
auto item = std::make_shared<KPrivateMemory>(state, address, 0, size, perms, type, main_thread);
|
||||||
if (address) flags |= MAP_FIXED; // NOLINT(hicpp-signed-bitwise)
|
memory_map[item->address] = item;
|
||||||
return reinterpret_cast<uint64_t>(mmap(reinterpret_cast<void *>(address), size, static_cast<int>(perms), flags, -1, 0));
|
memory_region_map[region] = item;
|
||||||
}
|
return item;
|
||||||
|
|
||||||
uint64_t KProcess::MapPrivate(uint64_t address, size_t size, const memory::Permission perms) {
|
|
||||||
user_pt_regs fregs = {0};
|
|
||||||
fregs.regs[0] = address;
|
|
||||||
fregs.regs[1] = size;
|
|
||||||
fregs.regs[2] = static_cast<uint64_t>(perms.get());
|
|
||||||
state.nce->ExecuteFunction(reinterpret_cast<void *>(MapPrivateFunc), fregs, main_thread);
|
|
||||||
if (reinterpret_cast<void *>(fregs.regs[0]) == MAP_FAILED)
|
|
||||||
throw exception("An error occurred while mapping private region");
|
|
||||||
state.logger->Write(Logger::DEBUG, "MapPrivate for ADR: 0x{:X}, SZ: 0x{:X}", fregs.regs[0], size);
|
|
||||||
return fregs.regs[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t KProcess::MapPrivate(uint64_t address, size_t size, const memory::Permission perms, const memory::Region region) {
|
|
||||||
uint64_t addr = MapPrivate(address,size, perms);
|
|
||||||
memory_map.insert(std::pair<memory::Region, memory::RegionData>(region, {addr, size, perms}));
|
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t RemapPrivateFunc(uint64_t address, size_t old_size, size_t size) {
|
|
||||||
return (uint64_t) mremap(reinterpret_cast<void *>(address), old_size, size, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void KProcess::RemapPrivate(uint64_t address, size_t old_size, size_t size) {
|
|
||||||
user_pt_regs fregs = {0};
|
|
||||||
fregs.regs[0] = address;
|
|
||||||
fregs.regs[1] = old_size;
|
|
||||||
fregs.regs[2] = size;
|
|
||||||
state.nce->ExecuteFunction(reinterpret_cast<void *>(RemapPrivateFunc), fregs, main_thread);
|
|
||||||
if (reinterpret_cast<void *>(fregs.regs[0]) == MAP_FAILED)
|
|
||||||
throw exception("An error occurred while remapping private region");
|
|
||||||
}
|
|
||||||
|
|
||||||
void KProcess::RemapPrivate(const memory::Region region, size_t size) {
|
|
||||||
memory::RegionData region_data = memory_map.at(region);
|
|
||||||
RemapPrivate(region_data.address, region_data.size, size);
|
|
||||||
region_data.size = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
int UpdatePermissionPrivateFunc(uint64_t address, size_t size, uint64_t perms) {
|
|
||||||
return mprotect(reinterpret_cast<void *>(address), size, static_cast<int>(perms));
|
|
||||||
}
|
|
||||||
|
|
||||||
void KProcess::UpdatePermissionPrivate(uint64_t address, size_t size, const memory::Permission perms) {
|
|
||||||
user_pt_regs fregs = {0};
|
|
||||||
fregs.regs[0] = address;
|
|
||||||
fregs.regs[1] = size;
|
|
||||||
fregs.regs[2] = static_cast<uint64_t>(perms.get());
|
|
||||||
state.nce->ExecuteFunction(reinterpret_cast<void *>(UpdatePermissionPrivateFunc), fregs, main_thread);
|
|
||||||
if (static_cast<int>(fregs.regs[0]) == -1)
|
|
||||||
throw exception("An error occurred while updating private region's permissions");
|
|
||||||
}
|
|
||||||
|
|
||||||
void KProcess::UpdatePermissionPrivate(const memory::Region region, const memory::Permission perms) {
|
|
||||||
memory::RegionData region_data = memory_map.at(region);
|
|
||||||
if (region_data.perms != perms) {
|
|
||||||
UpdatePermissionPrivate(region_data.address, region_data.size, perms);
|
|
||||||
region_data.perms = perms;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t UnmapPrivateFunc(uint64_t address, size_t size) {
|
|
||||||
return static_cast<uint64_t>(munmap(reinterpret_cast<void *>(address), size));
|
|
||||||
}
|
|
||||||
|
|
||||||
void KProcess::UnmapPrivate(uint64_t address, size_t size) {
|
|
||||||
user_pt_regs fregs = {0};
|
|
||||||
fregs.regs[0] = address;
|
|
||||||
fregs.regs[1] = size;
|
|
||||||
state.nce->ExecuteFunction(reinterpret_cast<void *>(UnmapPrivateFunc), fregs, main_thread);
|
|
||||||
if (static_cast<int>(fregs.regs[0]) == -1)
|
|
||||||
throw exception("An error occurred while unmapping private region");
|
|
||||||
}
|
|
||||||
|
|
||||||
void KProcess::UnmapPrivate(const memory::Region region) {
|
|
||||||
memory::RegionData region_data = memory_map.at(region);
|
|
||||||
UnmapPrivate(region_data.address, region_data.size);
|
|
||||||
memory_map.erase(region);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handle_t KProcess::NewHandle(std::shared_ptr<KObject> obj) {
|
handle_t KProcess::NewHandle(std::shared_ptr<KObject> obj) {
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "KThread.h"
|
#include "KThread.h"
|
||||||
|
#include "KPrivateMemory.h"
|
||||||
|
|
||||||
namespace lightSwitch::kernel::type {
|
namespace lightSwitch::kernel::type {
|
||||||
/**
|
/**
|
||||||
* The KProcess class is responsible for holding the state of a process
|
* The KProcess class is responsible for holding the state of a process
|
||||||
*/
|
*/
|
||||||
class KProcess : public KObject {
|
class KProcess : public KObject {
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* tls_page_t holds the status of a single TLS page (A page is 4096 bytes on ARMv8).
|
* tls_page_t holds the status of a single TLS page (A page is 4096 bytes on ARMv8).
|
||||||
* Each TLS page has 8 slots, each 0x200 (512) bytes in size.
|
* Each TLS page has 8 slots, each 0x200 (512) bytes in size.
|
||||||
@ -15,46 +16,51 @@ namespace lightSwitch::kernel::type {
|
|||||||
* Read more about TLS here: https://switchbrew.org/wiki/Thread_Local_Storage
|
* Read more about TLS here: https://switchbrew.org/wiki/Thread_Local_Storage
|
||||||
*/
|
*/
|
||||||
struct tls_page_t {
|
struct tls_page_t {
|
||||||
uint64_t address; //!< The address of the page allocated for TLS
|
u64 address; //!< The address of the page allocated for TLS
|
||||||
uint8_t index = 0; //!< The slots are assigned sequentially, this holds the index of the last TLS slot reserved
|
u8 index = 0; //!< The slots are assigned sequentially, this holds the index of the last TLS slot reserved
|
||||||
bool slot[constant::tls_slots]{0}; //!< An array of booleans denoting which TLS slots are reserved
|
bool slot[constant::tls_slots]{0}; //!< An array of booleans denoting which TLS slots are reserved
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param address The address of the allocated page
|
* @param address The address of the allocated page
|
||||||
*/
|
*/
|
||||||
tls_page_t(uint64_t address);
|
tls_page_t(u64 address);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reserves a single 0x200 byte TLS slot
|
* Reserves a single 0x200 byte TLS slot
|
||||||
* @return The address of the reserved slot
|
* @return The address of the reserved slot
|
||||||
*/
|
*/
|
||||||
uint64_t ReserveSlot();
|
u64 ReserveSlot();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the address of a particular slot
|
* Returns the address of a particular slot
|
||||||
* @param slot_no The number of the slot to be returned
|
* @param slot_no The number of the slot to be returned
|
||||||
* @return The address of the specified slot
|
* @return The address of the specified slot
|
||||||
*/
|
*/
|
||||||
uint64_t Get(uint8_t slot_no);
|
u64 Get(u8 slot_no);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return If the whole page is full or not
|
* @return If the whole page is full or not
|
||||||
*/
|
*/
|
||||||
bool Full();
|
bool Full();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param init If this initializes the first page (As the first TLS slot is reserved)
|
* @param init If this initializes the first page (As the first TLS slot is reserved)
|
||||||
* @return The address of a free TLS slot
|
* @return The address of a free TLS slot
|
||||||
*/
|
*/
|
||||||
uint64_t GetTLSSlot(bool init);
|
u64 GetTLSSlot(bool init);
|
||||||
|
|
||||||
int mem_fd; //!< The file descriptor to the memory of the process
|
int mem_fd; //!< The file descriptor to the memory of the process
|
||||||
const device_state& state; //!< The state of the device
|
const device_state &state; //!< The state of the device
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum class process_state_t { Created, CreatedAttached, Started, Crashed, StartedAttached, Exiting, Exited, DebugSuspended } process_state; //!< The state of the process
|
enum class process_state_t { Created, CreatedAttached, Started, Crashed, StartedAttached, Exiting, Exited, DebugSuspended } process_state; //!< The state of the process
|
||||||
handle_t handle; //!< The handle of the current process in it's parent process's handle table (Will be 0 if this is the main process)
|
handle_t handle; //!< The handle of the current process in it's parent process's handle table (Will be 0 if this is the main process)
|
||||||
handle_t handle_index = constant::base_handle_index; //!< This is used to keep track of what to map as an handle
|
handle_t handle_index = constant::base_handle_index; //!< This is used to keep track of what to map as an handle
|
||||||
pid_t main_thread; //!< The PID of the main thread
|
pid_t main_thread; //!< The PID of the main thread
|
||||||
size_t main_thread_stack_sz; //!< The size of the main thread's stack (All other threads map stack themselves so we don't know the size per-se)
|
size_t main_thread_stack_sz; //!< The size of the main thread's stack (All other threads map stack themselves so we don't know the size per-se)
|
||||||
std::map<memory::Region, memory::RegionData> memory_map; //!< A mapping from every memory::Region to it's corresponding memory::RegionData which holds it's address and size
|
std::map<u64, std::shared_ptr<KPrivateMemory>> memory_map; //!< A mapping from every address to a shared pointer of it's corresponding KPrivateMemory
|
||||||
|
std::map<Memory::Region, std::shared_ptr<KPrivateMemory>> memory_region_map; //!< A mapping from every MemoryRegion to a shared pointer of it's corresponding KPrivateMemory
|
||||||
std::map<handle_t, std::shared_ptr<KObject>> handle_table; //!< A mapping from a handle_t to it's corresponding KObject which is the actual underlying object
|
std::map<handle_t, std::shared_ptr<KObject>> handle_table; //!< A mapping from a handle_t to it's corresponding KObject which is the actual underlying object
|
||||||
std::map<pid_t, std::shared_ptr<KThread>> thread_map; //!< A mapping from a PID to it's corresponding KThread object
|
std::map<pid_t, std::shared_ptr<KThread>> thread_map; //!< A mapping from a PID to it's corresponding KThread object
|
||||||
std::vector<std::shared_ptr<tls_page_t>> tls_pages; //!< A vector of all allocated TLS pages
|
std::vector<std::shared_ptr<tls_page_t>> tls_pages; //!< A vector of all allocated TLS pages
|
||||||
@ -68,7 +74,7 @@ namespace lightSwitch::kernel::type {
|
|||||||
* @param state The state of the device
|
* @param state The state of the device
|
||||||
* @param handle A handle to the process, this isn't used if the kernel creates the process
|
* @param handle A handle to the process, this isn't used if the kernel creates the process
|
||||||
*/
|
*/
|
||||||
KProcess(pid_t pid, uint64_t entry_point, uint64_t stack_base, uint64_t stack_size, const device_state& state, handle_t handle=0);
|
KProcess(pid_t pid, u64 entry_point, u64 stack_base, u64 stack_size, const device_state &state, handle_t handle = 0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close the file descriptor to the process's memory
|
* Close the file descriptor to the process's memory
|
||||||
@ -83,7 +89,7 @@ namespace lightSwitch::kernel::type {
|
|||||||
* @param priority The priority of the thread
|
* @param priority The priority of the thread
|
||||||
* @return An instance of KThread class for the corresponding thread
|
* @return An instance of KThread class for the corresponding thread
|
||||||
*/
|
*/
|
||||||
std::shared_ptr<KThread> CreateThread(uint64_t entry_point, uint64_t entry_arg, uint64_t stack_top, uint8_t priority);
|
std::shared_ptr<KThread> CreateThread(u64 entry_point, u64 entry_arg, u64 stack_top, u8 priority);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an object of type T from process memory
|
* Returns an object of type T from process memory
|
||||||
@ -91,8 +97,12 @@ namespace lightSwitch::kernel::type {
|
|||||||
* @param address The address of the object
|
* @param address The address of the object
|
||||||
* @return An object of type T with read data
|
* @return An object of type T with read data
|
||||||
*/
|
*/
|
||||||
template <typename T>
|
template<typename T>
|
||||||
T ReadMemory(uint64_t address) const;
|
T ReadMemory(u64 address) const {
|
||||||
|
T item{};
|
||||||
|
ReadMemory(&item, address, sizeof(T));
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes an object of type T to process memory
|
* Writes an object of type T to process memory
|
||||||
@ -101,7 +111,9 @@ namespace lightSwitch::kernel::type {
|
|||||||
* @param address The address of the object
|
* @param address The address of the object
|
||||||
*/
|
*/
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void WriteMemory(T &item, uint64_t address) const;
|
void WriteMemory(T &item, u64 address) const {
|
||||||
|
WriteMemory(&item, address, sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a piece of process memory
|
* Read a piece of process memory
|
||||||
@ -109,7 +121,7 @@ namespace lightSwitch::kernel::type {
|
|||||||
* @param offset The address to read from in process memory
|
* @param offset The address to read from in process memory
|
||||||
* @param size The amount of memory to be read
|
* @param size The amount of memory to be read
|
||||||
*/
|
*/
|
||||||
void ReadMemory(void *destination, uint64_t offset, size_t size) const;
|
void ReadMemory(void *destination, u64 offset, size_t size) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write a piece of process memory
|
* Write a piece of process memory
|
||||||
@ -117,69 +129,18 @@ namespace lightSwitch::kernel::type {
|
|||||||
* @param offset The address to write to in process memory
|
* @param offset The address to write to in process memory
|
||||||
* @param size The amount of memory to be written
|
* @param size The amount of memory to be written
|
||||||
*/
|
*/
|
||||||
void WriteMemory(void *source, uint64_t offset, size_t size) const;
|
void WriteMemory(void *source, u64 offset, size_t size) const;
|
||||||
|
|
||||||
/**
|
|
||||||
* Map a chunk of process local memory (private memory)
|
|
||||||
* @param address The address to map to (Can be 0 if address doesn't matter)
|
|
||||||
* @param size The size of the chunk of memory
|
|
||||||
* @param perms The permissions of the memory
|
|
||||||
* @return The address of the mapped chunk (Use when address is 0)
|
|
||||||
*/
|
|
||||||
uint64_t MapPrivate(uint64_t address, size_t size, const memory::Permission perms);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map a chunk of process local memory (private memory)
|
* Map a chunk of process local memory (private memory)
|
||||||
* @param address The address to map to (Can be 0 if address doesn't matter)
|
* @param address The address to map to (Can be 0 if address doesn't matter)
|
||||||
* @param size The size of the chunk of memory
|
* @param size The size of the chunk of memory
|
||||||
* @param perms The permissions of the memory
|
* @param perms The permissions of the memory
|
||||||
|
* @param type The type of the memory
|
||||||
* @param region The specific region this memory is mapped for
|
* @param region The specific region this memory is mapped for
|
||||||
* @return The address of the mapped chunk (Use when address is 0)
|
* @return The address of the mapped chunk (Use when address is 0)
|
||||||
*/
|
*/
|
||||||
uint64_t MapPrivate(uint64_t address, size_t size, const memory::Permission perms, const memory::Region region);
|
std::shared_ptr<KPrivateMemory> MapPrivateRegion(u64 address, size_t size, const Memory::Permission perms, const Memory::Type type, const Memory::Region region);
|
||||||
|
|
||||||
/**
|
|
||||||
* Remap a chunk of memory as to change the size occupied by it
|
|
||||||
* @param address The address of the mapped memory
|
|
||||||
* @param old_size The current size of the memory
|
|
||||||
* @param size The new size of the memory
|
|
||||||
*/
|
|
||||||
void RemapPrivate(uint64_t address, size_t old_size, size_t size);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remap a chunk of memory as to change the size occupied by it
|
|
||||||
* @param region The region of memory that was mapped
|
|
||||||
* @param size The new size of the memory
|
|
||||||
*/
|
|
||||||
void RemapPrivate(const memory::Region region, size_t size);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the permissions of a chunk of mapped memory
|
|
||||||
* @param address The address of the mapped memory
|
|
||||||
* @param size The size of the mapped memory
|
|
||||||
* @param perms The new permissions to be set for the memory
|
|
||||||
*/
|
|
||||||
void UpdatePermissionPrivate(uint64_t address, size_t size, const memory::Permission perms);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the permissions of a chunk of mapped memory
|
|
||||||
* @param region The region of memory that was mapped
|
|
||||||
* @param perms The new permissions to be set for the memory
|
|
||||||
*/
|
|
||||||
void UpdatePermissionPrivate(const memory::Region region, const memory::Permission perms);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unmap a particular chunk of mapped memory
|
|
||||||
* @param address The address of the mapped memory
|
|
||||||
* @param size The size of the mapped memory
|
|
||||||
*/
|
|
||||||
void UnmapPrivate(uint64_t address, size_t size);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unmap a particular chunk of mapped memory
|
|
||||||
* @param region The region of mapped memory
|
|
||||||
*/
|
|
||||||
void UnmapPrivate(const memory::Region region);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new handle to a KObject and adds it to the process handle_table
|
* Creates a new handle to a KObject and adds it to the process handle_table
|
||||||
|
119
app/src/main/cpp/switch/kernel/types/KSharedMemory.cpp
Normal file
119
app/src/main/cpp/switch/kernel/types/KSharedMemory.cpp
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
#include "KSharedMemory.h"
|
||||||
|
#include "../../nce.h"
|
||||||
|
#include "../../os.h"
|
||||||
|
#include <android/sharedmem.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
namespace lightSwitch::kernel::type {
|
||||||
|
u64 MapFunc(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)
|
||||||
|
}
|
||||||
|
|
||||||
|
KSharedMemory::KSharedMemory(const device_state &state, size_t size, const Memory::Permission local_permission, const Memory::Permission remote_permission, Memory::Type type, handle_t handle, pid_t owner_pid) : state(state), size(size), local_permission(local_permission), remote_permission(remote_permission), type(type), owner_pid(owner_pid), KObject(handle, KObjectType::KSharedMemory) {
|
||||||
|
fd = ASharedMemory_create("", size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void KSharedMemory::Map(u64 address) {
|
||||||
|
this->address = address;
|
||||||
|
for (auto process : state.os->process_vec) {
|
||||||
|
user_pt_regs fregs = {0};
|
||||||
|
fregs.regs[0] = this->address;
|
||||||
|
fregs.regs[1] = size;
|
||||||
|
if (process == owner_pid)
|
||||||
|
fregs.regs[2] = static_cast<u64 >(local_permission.get());
|
||||||
|
else
|
||||||
|
fregs.regs[2] = static_cast<u64>(remote_permission.get());
|
||||||
|
fregs.regs[3] = static_cast<u64>(fd);
|
||||||
|
state.nce->ExecuteFunction(reinterpret_cast<void *>(MapFunc), fregs, process);
|
||||||
|
if (reinterpret_cast<void *>(fregs.regs[0]) == MAP_FAILED)
|
||||||
|
throw exception("An error occurred while mapping shared region in child process");
|
||||||
|
if (!this->address) this->address = fregs.regs[0];
|
||||||
|
}
|
||||||
|
this->address = MapFunc(this->address, size, static_cast<u64>(owner_pid ? remote_permission.get() : local_permission.get()), static_cast<u64>(fd));
|
||||||
|
if (this->address == reinterpret_cast<u64>(MAP_FAILED)) // NOLINT(hicpp-signed-bitwise)
|
||||||
|
throw exception(fmt::format("An occurred while mapping shared region: {}", strerror(errno)));
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 UnmapFunc(u64 address, size_t size) {
|
||||||
|
return static_cast<u64>(munmap(reinterpret_cast<void *>(address), size));
|
||||||
|
}
|
||||||
|
|
||||||
|
KSharedMemory::~KSharedMemory() {
|
||||||
|
for (auto process : state.os->process_vec) {
|
||||||
|
user_pt_regs fregs = {0};
|
||||||
|
fregs.regs[0] = address;
|
||||||
|
fregs.regs[1] = size;
|
||||||
|
state.nce->ExecuteFunction(reinterpret_cast<void *>(UnmapFunc), fregs, process);
|
||||||
|
}
|
||||||
|
UnmapFunc(address, size);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 RemapFunc(u64 address, size_t old_size, size_t size) {
|
||||||
|
return reinterpret_cast<u64>(mremap(reinterpret_cast<void *>(address), old_size, size, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
void KSharedMemory::Resize(size_t new_size) {
|
||||||
|
for (auto process : state.os->process_vec) {
|
||||||
|
user_pt_regs fregs = {0};
|
||||||
|
fregs.regs[0] = address;
|
||||||
|
fregs.regs[1] = size;
|
||||||
|
fregs.regs[2] = new_size;
|
||||||
|
state.nce->ExecuteFunction(reinterpret_cast<void *>(RemapFunc), fregs, process);
|
||||||
|
if (reinterpret_cast<void *>(fregs.regs[0]) == MAP_FAILED)
|
||||||
|
throw exception("An error occurred while remapping shared region in child process");
|
||||||
|
}
|
||||||
|
if (RemapFunc(address, size, new_size) == reinterpret_cast<u64>(MAP_FAILED))
|
||||||
|
throw exception(fmt::format("An occurred while remapping shared region: {}", strerror(errno)));
|
||||||
|
size = new_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 UpdatePermissionFunc(u64 address, size_t size, u64 perms) {
|
||||||
|
return static_cast<u64>(mprotect(reinterpret_cast<void *>(address), size, static_cast<int>(perms)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void KSharedMemory::UpdatePermission(bool local, Memory::Permission new_perms) {
|
||||||
|
for (auto process : state.os->process_vec) {
|
||||||
|
if ((local && process == owner_pid) || (!local && process != owner_pid)) {
|
||||||
|
user_pt_regs fregs = {0};
|
||||||
|
fregs.regs[0] = address;
|
||||||
|
fregs.regs[1] = size;
|
||||||
|
fregs.regs[2] = static_cast<u64>(new_perms.get());
|
||||||
|
state.nce->ExecuteFunction(reinterpret_cast<void *>(UpdatePermissionFunc), fregs, process);
|
||||||
|
if (static_cast<int>(fregs.regs[0]) == -1)
|
||||||
|
throw exception("An error occurred while updating shared region's permissions in child process");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((local && owner_pid == 0) || (!local && owner_pid != 0))
|
||||||
|
if (mprotect(reinterpret_cast<void *>(address), size, new_perms.get()) == -1)
|
||||||
|
throw exception(fmt::format("An occurred while updating shared region's permissions: {}", strerror(errno)));
|
||||||
|
if (local)
|
||||||
|
local_permission = new_perms;
|
||||||
|
else
|
||||||
|
remote_permission = new_perms;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KSharedMemory::InitiateProcess(pid_t pid) {
|
||||||
|
user_pt_regs fregs = {0};
|
||||||
|
fregs.regs[0] = address;
|
||||||
|
fregs.regs[1] = size;
|
||||||
|
fregs.regs[2] = static_cast<u64>(remote_permission.get());
|
||||||
|
state.nce->ExecuteFunction(reinterpret_cast<void *>(UpdatePermissionFunc), fregs, pid);
|
||||||
|
if (static_cast<int>(fregs.regs[0]) == -1)
|
||||||
|
throw exception("An error occurred while setting shared region's permissions in child process");
|
||||||
|
}
|
||||||
|
|
||||||
|
Memory::MemoryInfo KSharedMemory::GetInfo(pid_t pid) {
|
||||||
|
Memory::MemoryInfo info{};
|
||||||
|
info.base_address = address;
|
||||||
|
info.size = size;
|
||||||
|
info.type = static_cast<u64>(type);
|
||||||
|
info.memory_attribute.IsIpcLocked = (info.ipc_ref_count > 0);
|
||||||
|
info.memory_attribute.IsDeviceShared = (info.device_ref_count > 0);
|
||||||
|
info.perms = (pid == owner_pid) ? local_permission : remote_permission;
|
||||||
|
info.ipc_ref_count = ipc_ref_count;
|
||||||
|
info.device_ref_count = device_ref_count;
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
};
|
67
app/src/main/cpp/switch/kernel/types/KSharedMemory.h
Normal file
67
app/src/main/cpp/switch/kernel/types/KSharedMemory.h
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../../memory.h"
|
||||||
|
#include "KObject.h"
|
||||||
|
|
||||||
|
namespace lightSwitch::kernel::type {
|
||||||
|
class KSharedMemory : public KObject {
|
||||||
|
private:
|
||||||
|
const device_state &state; //!< The state of the device
|
||||||
|
int fd; //!< A file descriptor to the underlying shared memory
|
||||||
|
|
||||||
|
public:
|
||||||
|
u64 address; //!< The address of the allocated memory
|
||||||
|
size_t size; //!< The size of the allocated memory
|
||||||
|
u16 ipc_ref_count{}; //!< The amount of reference to this memory for IPC
|
||||||
|
u16 device_ref_count{}; //!< The amount of reference to this memory for IPC
|
||||||
|
Memory::Permission local_permission; //!< The amount of reference to this memory for IPC
|
||||||
|
Memory::Permission remote_permission; //!< The permission of any process except the owner process
|
||||||
|
Memory::Type type; //!< The type of this memory allocation
|
||||||
|
pid_t owner_pid; //!< The PID of the owner process, 0 means memory is owned by kernel process
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor of a shared memory object
|
||||||
|
* @param size The size of the allocation
|
||||||
|
* @param local_permission The permission of the owner process
|
||||||
|
* @param remote_permission The permission of any process except the owner process
|
||||||
|
* @param owner_pid The PID of the owner process, 0 means memory is owned by kernel process
|
||||||
|
*/
|
||||||
|
KSharedMemory(const device_state &state, size_t size, const Memory::Permission local_permission, const Memory::Permission remote_permission, Memory::Type type, handle_t handle, pid_t owner_pid = 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps the shared memory at an address
|
||||||
|
* @param address The address to map to (If NULL an arbitrary address is picked)
|
||||||
|
*/
|
||||||
|
void Map(u64 address);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor of shared memory, it deallocates the memory from all processes
|
||||||
|
*/
|
||||||
|
~KSharedMemory();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resize a chunk of memory as to change the size occupied by it
|
||||||
|
* @param new_size The new size of the memory
|
||||||
|
*/
|
||||||
|
void Resize(size_t new_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the permissions of a chunk of mapped memory
|
||||||
|
* @param local If true change local permissions else change remote permissions
|
||||||
|
* @param perms The new permissions to be set for the memory
|
||||||
|
*/
|
||||||
|
void UpdatePermission(bool local, Memory::Permission new_perms);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiates the instance of shared memory in a particular process
|
||||||
|
* @param pid The PID of the process
|
||||||
|
*/
|
||||||
|
void InitiateProcess(pid_t pid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param pid The PID of the requesting process
|
||||||
|
* @return A Memory::MemoryInfo struct based on attributes of the memory
|
||||||
|
*/
|
||||||
|
Memory::MemoryInfo GetInfo(pid_t pid);
|
||||||
|
};
|
||||||
|
}
|
@ -4,7 +4,7 @@
|
|||||||
#include "../../nce.h"
|
#include "../../nce.h"
|
||||||
|
|
||||||
namespace lightSwitch::kernel::type {
|
namespace lightSwitch::kernel::type {
|
||||||
KThread::KThread(handle_t handle, pid_t pid, uint64_t entry_point, uint64_t entry_arg, uint64_t stack_top, uint64_t tls, uint8_t priority, KProcess* parent, const device_state &state) : handle(handle), pid(pid), entry_point(entry_point), entry_arg(entry_arg), stack_top(stack_top), tls(tls), priority(priority), parent(parent), state(state), KObject(handle, KObjectType::KThread) {
|
KThread::KThread(handle_t handle, pid_t pid, u64 entry_point, u64 entry_arg, u64 stack_top, u64 tls, u8 priority, KProcess *parent, const device_state &state) : handle(handle), pid(pid), entry_point(entry_point), entry_arg(entry_arg), stack_top(stack_top), tls(tls), priority(priority), parent(parent), state(state), KObject(handle, KObjectType::KThread) {
|
||||||
UpdatePriority(priority);
|
UpdatePriority(priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -13,14 +13,14 @@ namespace lightSwitch::kernel::type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void KThread::Start() {
|
void KThread::Start() {
|
||||||
if(pid==parent->main_thread) parent->process_state = KProcess::process_state_t::Started;
|
if (pid == parent->main_thread) parent->process_state = KProcess::process_state_t::Started;
|
||||||
state.nce->StartProcess(entry_point, entry_arg, stack_top, handle, pid);
|
state.nce->StartProcess(entry_point, entry_arg, stack_top, handle, pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void KThread::UpdatePriority(uint8_t priority) {
|
void KThread::UpdatePriority(u8 priority) {
|
||||||
this->priority = priority;
|
this->priority = priority;
|
||||||
auto li_priority = static_cast<int8_t>(constant::priority_an.first + ((static_cast<float>(constant::priority_an.second - constant::priority_an.first) / static_cast<float>(constant::priority_nin.second - constant::priority_nin.first)) * (static_cast<float>(priority) - constant::priority_nin.first))); // Remap range priority_nin (Nintendo Priority) to priority_an (Android Priority)
|
auto li_priority = static_cast<int8_t>(constant::priority_an.first + ((static_cast<float>(constant::priority_an.second - constant::priority_an.first) / static_cast<float>(constant::priority_nin.second - constant::priority_nin.first)) * (static_cast<float>(priority) - constant::priority_nin.first))); // Resize range priority_nin (Nintendo Priority) to priority_an (Android Priority)
|
||||||
if(setpriority(PRIO_PROCESS, static_cast<id_t>(pid), li_priority) == -1)
|
if (setpriority(PRIO_PROCESS, static_cast<id_t>(pid), li_priority) == -1)
|
||||||
throw exception(fmt::format("Couldn't set process priority to {} for PID: {}", li_priority, pid));
|
throw exception(fmt::format("Couldn't set process priority to {} for PID: {}", li_priority, pid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,18 +7,18 @@ namespace lightSwitch::kernel::type {
|
|||||||
* KThread class is responsible for holding the state of a thread
|
* KThread class is responsible for holding the state of a thread
|
||||||
*/
|
*/
|
||||||
class KThread : public KObject {
|
class KThread : public KObject {
|
||||||
private:
|
private:
|
||||||
KProcess *parent; //!< The parent process of this thread
|
KProcess *parent; //!< The parent process of this thread
|
||||||
const device_state& state; //!< The state of the device
|
const device_state &state; //!< The state of the device
|
||||||
uint64_t entry_point; //!< The address to start execution at
|
u64 entry_point; //!< The address to start execution at
|
||||||
uint64_t entry_arg; //!< An argument to pass to the process on entry
|
u64 entry_arg; //!< An argument to pass to the process on entry
|
||||||
|
|
||||||
public:
|
public:
|
||||||
handle_t handle; //!< The handle of the current thread in it's parent process's handle table
|
handle_t handle; //!< The handle of the current thread in it's parent process's 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])
|
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])
|
||||||
uint64_t stack_top; //!< The top of the stack (Where it starts growing downwards from)
|
u64 stack_top; //!< The top of the stack (Where it starts growing downwards from)
|
||||||
uint64_t tls; //!< The address of TLS (Thread Local Storage) slot assigned to the current thread
|
u64 tls; //!< The address of TLS (Thread Local Storage) slot assigned to the current thread
|
||||||
uint8_t priority; //!< Hold the priority of a thread in Nintendo format
|
u8 priority; //!< Hold the priority of a thread in Nintendo format
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param handle The handle of the current thread
|
* @param handle The handle of the current thread
|
||||||
@ -31,7 +31,7 @@ namespace lightSwitch::kernel::type {
|
|||||||
* @param parent The parent process of this thread
|
* @param parent The parent process of this thread
|
||||||
* @param arg An optional argument to pass to the process
|
* @param arg An optional argument to pass to the process
|
||||||
*/
|
*/
|
||||||
KThread(handle_t handle, pid_t pid, uint64_t entry_point, uint64_t entry_arg, uint64_t stack_top, uint64_t tls, uint8_t priority, KProcess *parent, const device_state &state);
|
KThread(handle_t handle, pid_t pid, u64 entry_point, u64 entry_arg, u64 stack_top, u64 tls, u8 priority, KProcess *parent, const device_state &state);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Kills the thread and deallocates the memory allocated for stack.
|
* Kills the thread and deallocates the memory allocated for stack.
|
||||||
@ -48,6 +48,6 @@ namespace lightSwitch::kernel::type {
|
|||||||
* We rescale the priority from Nintendo scale to that of Android.
|
* We rescale the priority from Nintendo scale to that of Android.
|
||||||
* @param priority The priority of the thread in Nintendo format
|
* @param priority The priority of the thread in Nintendo format
|
||||||
*/
|
*/
|
||||||
void UpdatePriority(uint8_t priority);
|
void UpdatePriority(u8 priority);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
namespace lightSwitch::loader {
|
namespace lightSwitch::loader {
|
||||||
class Loader {
|
class Loader {
|
||||||
protected:
|
protected:
|
||||||
std::string file_path; //!< The path to the ROM file
|
std::string file_path; //!< The path to the ROM file
|
||||||
std::ifstream file; //!< An input stream from the file
|
std::ifstream file; //!< An input stream from the file
|
||||||
|
|
||||||
@ -17,13 +17,13 @@ namespace lightSwitch::loader {
|
|||||||
* @param size The amount to read in bytes
|
* @param size The amount to read in bytes
|
||||||
*/
|
*/
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void ReadOffset(T *output, uint32_t offset, size_t size) {
|
void ReadOffset(T *output, u32 offset, size_t size) {
|
||||||
file.seekg(offset, std::ios_base::beg);
|
file.seekg(offset, std::ios_base::beg);
|
||||||
file.read(reinterpret_cast<char *>(output), size);
|
file.read(reinterpret_cast<char *>(output), size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @param file_path_ The path to the ROM file
|
* @param file_path_ The path to the ROM file
|
||||||
*/
|
*/
|
||||||
|
@ -4,42 +4,40 @@
|
|||||||
namespace lightSwitch::loader {
|
namespace lightSwitch::loader {
|
||||||
NroLoader::NroLoader(std::string file_path, const device_state &state) : Loader(file_path) {
|
NroLoader::NroLoader(std::string file_path, const device_state &state) : Loader(file_path) {
|
||||||
NroHeader header{};
|
NroHeader header{};
|
||||||
ReadOffset((uint32_t *) &header, 0x0, sizeof(NroHeader));
|
ReadOffset((u32 *) &header, 0x0, sizeof(NroHeader));
|
||||||
if (header.magic != constant::nro_magic)
|
if (header.magic != constant::nro_magic)
|
||||||
throw exception(fmt::format("Invalid NRO magic! 0x{0:X}", header.magic));
|
throw exception(fmt::format("Invalid NRO magic! 0x{0:X}", header.magic));
|
||||||
|
|
||||||
state.nce->MapShared(constant::base_addr, header.text.size, {true, true, true}, memory::Region::text); // RWX
|
state.nce->MapSharedRegion(constant::base_addr, header.text.size, {true, true, true}, {true, true, true}, Memory::Type::CodeStatic, Memory::Region::text); // R-X
|
||||||
state.logger->Write(Logger::DEBUG, "Successfully mapped region .text @ 0x{0:X}, Size = 0x{1:X}", constant::base_addr, header.text.size);
|
state.logger->Write(Logger::DEBUG, "Successfully mapped region .text @ 0x{0:X}, Size = 0x{1:X}", constant::base_addr, header.text.size);
|
||||||
|
|
||||||
state.nce->MapShared(constant::base_addr + header.text.size, header.ro.size, {true, true, false}, memory::Region::rodata); // RW- but should be R--
|
auto rodata = state.nce->MapSharedRegion(constant::base_addr + header.text.size, header.ro.size, {true, true, false}, {true, false, false}, Memory::Type::CodeReadOnly, Memory::Region::rodata); // R--
|
||||||
state.logger->Write(Logger::DEBUG, "Successfully mapped region .ro @ 0x{0:X}, Size = 0x{1:X}", constant::base_addr + header.text.size, header.ro.size);
|
state.logger->Write(Logger::DEBUG, "Successfully mapped region .ro @ 0x{0:X}, Size = 0x{1:X}", constant::base_addr + header.text.size, header.ro.size);
|
||||||
|
|
||||||
state.nce->MapShared(constant::base_addr + header.text.size + header.ro.size, header.data.size, {true, true, false}, memory::Region::data); // RW-
|
state.nce->MapSharedRegion(constant::base_addr + header.text.size + header.ro.size, header.data.size, {true, true, false}, {true, true, false}, Memory::Type::CodeStatic, Memory::Region::data); // RW-
|
||||||
state.logger->Write(Logger::DEBUG, "Successfully mapped region .data @ 0x{0:X}, Size = 0x{1:X}", constant::base_addr + header.text.size + header.ro.size, header.data.size);
|
state.logger->Write(Logger::DEBUG, "Successfully mapped region .data @ 0x{0:X}, Size = 0x{1:X}", constant::base_addr + header.text.size + header.ro.size, header.data.size);
|
||||||
|
|
||||||
state.nce->MapShared(constant::base_addr + header.text.size + header.ro.size + header.data.size, header.bssSize, {true, true, false}, memory::Region::bss); // RW-
|
state.nce->MapSharedRegion(constant::base_addr + header.text.size + header.ro.size + header.data.size, header.bssSize, {true, true, true}, {true, true, true}, Memory::Type::CodeMutable, Memory::Region::bss); // RWX
|
||||||
state.logger->Write(Logger::DEBUG, "Successfully mapped region .bss @ 0x{0:X}, Size = 0x{1:X}", constant::base_addr + header.text.size + header.ro.size + header.data.size, header.bssSize);
|
state.logger->Write(Logger::DEBUG, "Successfully mapped region .bss @ 0x{0:X}, Size = 0x{1:X}", constant::base_addr + header.text.size + header.ro.size + header.data.size, header.bssSize);
|
||||||
|
|
||||||
ReadOffset(reinterpret_cast<uint8_t *>(constant::base_addr), header.text.offset, header.text.size);
|
ReadOffset(reinterpret_cast<u8 *>(constant::base_addr), header.text.offset, header.text.size);
|
||||||
ReadOffset(reinterpret_cast<uint8_t *>(constant::base_addr + header.text.size), header.ro.offset, header.ro.size);
|
ReadOffset(reinterpret_cast<u8 *>(constant::base_addr + header.text.size), header.ro.offset, header.ro.size);
|
||||||
ReadOffset(reinterpret_cast<uint8_t *>(constant::base_addr + header.text.size + header.ro.size), header.data.offset, header.data.size);
|
ReadOffset(reinterpret_cast<u8 *>(constant::base_addr + header.text.size + header.ro.size), header.data.offset, header.data.size);
|
||||||
|
|
||||||
// Make .ro read-only after writing to it
|
|
||||||
state.nce->UpdatePermissionShared(memory::Region::rodata, {true, false, false});
|
|
||||||
|
|
||||||
// Replace SVC & MRS with BRK
|
// Replace SVC & MRS with BRK
|
||||||
auto address = (uint32_t *) constant::base_addr + header.text.offset;
|
auto address = (u32 *) constant::base_addr + header.text.offset;
|
||||||
size_t text_size = header.text.size / sizeof(uint32_t);
|
size_t text_size = header.text.size / sizeof(u32);
|
||||||
for (size_t iter = 0; iter < text_size; iter++) {
|
for (size_t iter = 0; iter < text_size; iter++) {
|
||||||
auto instr_svc = reinterpret_cast<instr::svc *>(address + iter);
|
auto instr_svc = reinterpret_cast<instr::svc *>(address + iter);
|
||||||
auto instr_mrs = reinterpret_cast<instr::mrs *>(address + iter);
|
auto instr_mrs = reinterpret_cast<instr::mrs *>(address + iter);
|
||||||
|
|
||||||
if (instr_svc->verify()) {
|
if (instr_svc->verify()) {
|
||||||
instr::brk brk(static_cast<uint16_t>(instr_svc->value));
|
instr::brk brk(static_cast<u16>(instr_svc->value));
|
||||||
address[iter] = *reinterpret_cast<uint32_t *>(&brk);
|
address[iter] = *reinterpret_cast<u32 *>(&brk);
|
||||||
} else if (instr_mrs->verify() && instr_mrs->src_reg == constant::tpidrro_el0) {
|
} else if (instr_mrs->verify() && instr_mrs->src_reg == constant::tpidrro_el0) {
|
||||||
instr::brk brk(static_cast<uint16_t>(constant::svc_last + 1 + instr_mrs->dst_reg));
|
instr::brk brk(static_cast<u16>(constant::svc_last + 1 + instr_mrs->dst_reg));
|
||||||
address[iter] = *reinterpret_cast<uint32_t *>(&brk);
|
address[iter] = *reinterpret_cast<u32 *>(&brk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,37 +5,37 @@
|
|||||||
|
|
||||||
namespace lightSwitch::loader {
|
namespace lightSwitch::loader {
|
||||||
class NroLoader : public Loader {
|
class NroLoader : public Loader {
|
||||||
private:
|
private:
|
||||||
struct NroSegmentHeader {
|
struct NroSegmentHeader {
|
||||||
uint32_t offset;
|
u32 offset;
|
||||||
uint32_t size;
|
u32 size;
|
||||||
}; //!< The structure of a single Segment descriptor in the NRO's header
|
}; //!< The structure of a single Segment descriptor in the NRO's header
|
||||||
|
|
||||||
struct NroHeader {
|
struct NroHeader {
|
||||||
uint32_t : 32;
|
u32 : 32;
|
||||||
uint32_t mod_offset;
|
u32 mod_offset;
|
||||||
uint64_t : 64;
|
u64 : 64;
|
||||||
|
|
||||||
uint32_t magic;
|
u32 magic;
|
||||||
uint32_t version;
|
u32 version;
|
||||||
uint32_t size;
|
u32 size;
|
||||||
uint32_t flags;
|
u32 flags;
|
||||||
|
|
||||||
NroSegmentHeader text;
|
NroSegmentHeader text;
|
||||||
NroSegmentHeader ro;
|
NroSegmentHeader ro;
|
||||||
NroSegmentHeader data;
|
NroSegmentHeader data;
|
||||||
|
|
||||||
uint32_t bssSize;
|
u32 bssSize;
|
||||||
uint32_t : 32;
|
u32 : 32;
|
||||||
uint64_t build_id[4];
|
u64 build_id[4];
|
||||||
uint64_t : 64;
|
u64 : 64;
|
||||||
|
|
||||||
NroSegmentHeader api_info;
|
NroSegmentHeader api_info;
|
||||||
NroSegmentHeader dynstr;
|
NroSegmentHeader dynstr;
|
||||||
NroSegmentHeader dynsym;
|
NroSegmentHeader dynsym;
|
||||||
}; //!< A bit-field struct to read the header of an NRO directly
|
}; //!< A bit-field struct to read the header of an NRO directly
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @param file_path The path to the ROM file
|
* @param file_path The path to the ROM file
|
||||||
* @param state The state of the device
|
* @param state The state of the device
|
||||||
|
110
app/src/main/cpp/switch/memory.h
Normal file
110
app/src/main/cpp/switch/memory.h
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
namespace lightSwitch::Memory {
|
||||||
|
/**
|
||||||
|
* The Permission struct holds the permission of a particular chunk of memory
|
||||||
|
*/
|
||||||
|
struct Permission {
|
||||||
|
/**
|
||||||
|
* Initializes all values to false
|
||||||
|
*/
|
||||||
|
Permission() {
|
||||||
|
r = 0;
|
||||||
|
w = 0;
|
||||||
|
x = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param read If memory has read permission
|
||||||
|
* @param write If memory has write permission
|
||||||
|
* @param execute If memory has execute permission
|
||||||
|
*/
|
||||||
|
Permission(bool read, bool write, bool execute) {
|
||||||
|
r = read;
|
||||||
|
w = write;
|
||||||
|
x = execute;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Equality operator between two Permission objects
|
||||||
|
*/
|
||||||
|
bool operator==(const Permission &rhs) const { return (this->r == rhs.r && this->w == rhs.w && this->x == rhs.x); };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inequality operator between two Permission objects
|
||||||
|
*/
|
||||||
|
bool operator!=(const Permission &rhs) const { return !operator==(rhs); };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The value of the permission struct in mmap(2) format
|
||||||
|
*/
|
||||||
|
int get() const {
|
||||||
|
int perm = 0;
|
||||||
|
if (r) perm |= PROT_READ;
|
||||||
|
if (w) perm |= PROT_WRITE;
|
||||||
|
if (x) perm |= PROT_EXEC;
|
||||||
|
return perm;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool r : 1, w : 1, x : 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This holds certain attributes of a chunk of memory: https://switchbrew.org/wiki/SVC#MemoryAttribute
|
||||||
|
*/
|
||||||
|
struct MemoryAttribute {
|
||||||
|
bool IsBorrowed : 1;
|
||||||
|
bool IsIpcLocked : 1;
|
||||||
|
bool IsDeviceShared : 1;
|
||||||
|
bool IsUncached : 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This contains information about a chunk of memory: https://switchbrew.org/wiki/SVC#MemoryInfo
|
||||||
|
*/
|
||||||
|
struct MemoryInfo {
|
||||||
|
u64 base_address : 64;
|
||||||
|
u64 size : 64;
|
||||||
|
u64 type : 64;
|
||||||
|
MemoryAttribute memory_attribute;
|
||||||
|
Permission perms;
|
||||||
|
u32 ipc_ref_count : 32;
|
||||||
|
u32 device_ref_count : 32;
|
||||||
|
u32 : 32;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(MemoryInfo) == 0x28);
|
||||||
|
|
||||||
|
enum class Type : u32 {
|
||||||
|
Unmapped = 0x00000000,
|
||||||
|
Io = 0x00002001,
|
||||||
|
Normal = 0x00042002,
|
||||||
|
CodeStatic = 0x00DC7E03,
|
||||||
|
CodeMutable = 0x03FEBD04,
|
||||||
|
Heap = 0x037EBD05,
|
||||||
|
SharedMemory = 0x00402006,
|
||||||
|
Alias = 0x00482907,
|
||||||
|
ModuleCodeStatic = 0x00DD7E08,
|
||||||
|
ModuleCodeMutable = 0x03FFBD09,
|
||||||
|
Ipc = 0x005C3C0A,
|
||||||
|
Stack = 0x005C3C0B,
|
||||||
|
ThreadLocal = 0x0040200C,
|
||||||
|
TransferMemoryIsolated = 0x015C3C0D,
|
||||||
|
TransferMemory = 0x005C380E,
|
||||||
|
ProcessMemory = 0x0040380F,
|
||||||
|
Reserved = 0x00000010,
|
||||||
|
NonSecureIpc = 0x005C3811,
|
||||||
|
NonDeviceIpc = 0x004C2812,
|
||||||
|
KernelStack = 0x00002013,
|
||||||
|
CodeReadOnly = 0x00402214,
|
||||||
|
CodeWritable = 0x00402015
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Memory Regions that are mapped by the kernel
|
||||||
|
*/
|
||||||
|
enum class Region {
|
||||||
|
heap, text, rodata, data, bss
|
||||||
|
};
|
||||||
|
}
|
@ -1,9 +1,6 @@
|
|||||||
#include <sched.h>
|
#include <sched.h>
|
||||||
#include <linux/uio.h>
|
#include <linux/uio.h>
|
||||||
#include <linux/elf.h>
|
#include <linux/elf.h>
|
||||||
#include <fcntl.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <android/sharedmem.h>
|
|
||||||
#include "os.h"
|
#include "os.h"
|
||||||
#include "nce.h"
|
#include "nce.h"
|
||||||
|
|
||||||
@ -22,46 +19,41 @@ namespace lightSwitch {
|
|||||||
if (status == -1) throw exception(fmt::format("Cannot write registers, PID: {}, Error: {}", pid, strerror(errno)));
|
if (status == -1) throw exception(fmt::format("Cannot write registers, PID: {}, Error: {}", pid, strerror(errno)));
|
||||||
}
|
}
|
||||||
|
|
||||||
instr::brk NCE::ReadBrk(uint64_t address, pid_t pid) const {
|
instr::brk NCE::ReadBrk(u64 address, pid_t pid) const {
|
||||||
long status = ptrace(PTRACE_PEEKDATA, pid ? pid : curr_pid, address, NULL);
|
long status = ptrace(PTRACE_PEEKDATA, pid ? pid : curr_pid, address, NULL);
|
||||||
if (status == -1) throw exception(fmt::format("Cannot read instruction from memory, Address: {}, PID: {}, Error: {}", address, pid, strerror(errno)));
|
if (status == -1) throw exception(fmt::format("Cannot read instruction from memory, Address: {}, PID: {}, Error: {}", address, pid, strerror(errno)));
|
||||||
return *(reinterpret_cast<instr::brk *>(&status));
|
return *(reinterpret_cast<instr::brk *>(&status));
|
||||||
}
|
}
|
||||||
|
|
||||||
NCE::~NCE() {
|
void NCE::Initialize(const device_state &state) {
|
||||||
for (auto®ion : region_memory_map) {
|
this->state = &state;
|
||||||
munmap(reinterpret_cast<void *>(region.second.address), region.second.size);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NCE::Execute(const device_state &state) {
|
void NCE::Execute() {
|
||||||
this->state = const_cast<device_state *>(&state);
|
int status = 0;
|
||||||
int status;
|
while (!halt && !state->os->process_map.empty() && ((curr_pid = wait(&status)) != -1)) {
|
||||||
while (!halt && !state.os->process_map.empty() && ((curr_pid = wait(&status)) != -1)) {
|
|
||||||
if (WIFSTOPPED(status) && (WSTOPSIG(status) == SIGTRAP || WSTOPSIG(status) == SIGSTOP)) { // NOLINT(hicpp-signed-bitwise)
|
if (WIFSTOPPED(status) && (WSTOPSIG(status) == SIGTRAP || WSTOPSIG(status) == SIGSTOP)) { // NOLINT(hicpp-signed-bitwise)
|
||||||
auto& curr_regs = register_map[curr_pid];
|
auto &curr_regs = register_map[curr_pid];
|
||||||
ReadRegisters(curr_regs);
|
ReadRegisters(curr_regs);
|
||||||
auto instr = ReadBrk(curr_regs.pc);
|
auto instr = ReadBrk(curr_regs.pc);
|
||||||
if (instr.verify()) {
|
if (instr.verify()) {
|
||||||
// We store the instruction value as the immediate value in BRK. 0x0 to 0x7F are SVC, 0x80 to 0x9E is MRS for TPIDRRO_EL0.
|
// We store the instruction value as the immediate value in BRK. 0x0 to 0x7F are SVC, 0x80 to 0x9E is MRS for TPIDRRO_EL0.
|
||||||
if (instr.value <= constant::svc_last) {
|
if (instr.value <= constant::svc_last) {
|
||||||
state.os->SvcHandler(static_cast<uint16_t>(instr.value), curr_pid);
|
state->os->SvcHandler(static_cast<u16>(instr.value), curr_pid);
|
||||||
} else if (instr.value > constant::svc_last && instr.value <= constant::svc_last + constant::num_regs) {
|
} else if (instr.value > constant::svc_last && instr.value <= constant::svc_last + constant::num_regs) {
|
||||||
// Catch MRS that reads the value of TPIDRRO_EL0 (TLS)
|
// Catch MRS that reads the value of TPIDRRO_EL0 (TLS)
|
||||||
SetRegister(regs::xreg(instr.value - (constant::svc_last + 1)), state.os->process_map.at(curr_pid)->thread_map.at(curr_pid)->tls);
|
SetRegister(static_cast<xreg>(instr.value - (constant::svc_last + 1)), state->os->process_map.at(curr_pid)->thread_map.at(curr_pid)->tls);
|
||||||
state.logger->Write(Logger::DEBUG, "\"MRS X{}, TPIDRRO_EL0\" has been called", instr.value - (constant::svc_last + 1));
|
state->logger->Write(Logger::DEBUG, "\"MRS X{}, TPIDRRO_EL0\" has been called", instr.value - (constant::svc_last + 1));
|
||||||
} else if (instr.value == constant::brk_rdy)
|
} else if (instr.value == constant::brk_rdy)
|
||||||
continue;
|
continue;
|
||||||
else
|
else
|
||||||
throw exception(fmt::format("Received unhandled BRK: 0x{:X}", static_cast<uint64_t>(instr.value)));
|
throw exception(fmt::format("Received unhandled BRK: 0x{:X}", static_cast<u64>(instr.value)));
|
||||||
}
|
}
|
||||||
curr_regs.pc += 4; // Increment program counter by a single instruction (32 bits)
|
curr_regs.pc += 4; // Increment program counter by a single instruction (32 bits)
|
||||||
WriteRegisters(curr_regs);
|
WriteRegisters(curr_regs);
|
||||||
} else {
|
} else {
|
||||||
auto& curr_regs = register_map[curr_pid];
|
state->logger->Write(Logger::DEBUG, "Thread threw unknown signal, PID: {}, Stop Signal: {}", curr_pid, strsignal(WSTOPSIG(status))); // NOLINT(hicpp-signed-bitwise)
|
||||||
ReadRegisters(curr_regs);
|
state->os->KillThread(curr_pid);
|
||||||
state.logger->Write(Logger::DEBUG, "Thread threw unknown signal, PID: {}, Status: 0x{:X}, INSTR: 0x{:X}", curr_pid, status, *(uint32_t*)curr_regs.pc);
|
|
||||||
state.os->KillThread(curr_pid);
|
|
||||||
}
|
}
|
||||||
ResumeProcess();
|
ResumeProcess();
|
||||||
}
|
}
|
||||||
@ -76,9 +68,9 @@ namespace lightSwitch {
|
|||||||
bool was_running = PauseProcess(pid);
|
bool was_running = PauseProcess(pid);
|
||||||
user_pt_regs backup_regs{};
|
user_pt_regs backup_regs{};
|
||||||
ReadRegisters(backup_regs, pid);
|
ReadRegisters(backup_regs, pid);
|
||||||
func_regs.pc = reinterpret_cast<uint64_t>(func);
|
func_regs.pc = reinterpret_cast<u64>(func);
|
||||||
func_regs.sp = backup_regs.sp;
|
func_regs.sp = backup_regs.sp;
|
||||||
func_regs.regs[regs::x30] = reinterpret_cast<uint64_t>(brk_lr); // Set LR to 'brk_lr' so the application will hit a breakpoint after the function returns [LR is where the program goes after it returns from a function]
|
func_regs.regs[static_cast<uint>(xreg::x30)] = reinterpret_cast<u64>(brk_lr); // Set LR to 'brk_lr' so the application will hit a breakpoint after the function returns [LR is where the program goes after it returns from a function]
|
||||||
WriteRegisters(func_regs, pid);
|
WriteRegisters(func_regs, pid);
|
||||||
ResumeProcess(pid);
|
ResumeProcess(pid);
|
||||||
func_regs = WaitRdy(pid);
|
func_regs = WaitRdy(pid);
|
||||||
@ -99,7 +91,7 @@ namespace lightSwitch {
|
|||||||
WriteRegisters(regs, pid);
|
WriteRegisters(regs, pid);
|
||||||
return regs;
|
return regs;
|
||||||
} else
|
} else
|
||||||
throw exception(fmt::format("An unknown BRK was hit during WaitRdy, PID: {}, BRK value: {}", pid, static_cast<uint64_t>(instr.value)));
|
throw exception(fmt::format("An unknown BRK was hit during WaitRdy, PID: {}, BRK value: {}", pid, static_cast<u64>(instr.value)));
|
||||||
} else
|
} else
|
||||||
throw exception(fmt::format("An unknown signal was caused during WaitRdy, PID: {}, Status: 0x{:X}, Signal: {}", pid, status, strsignal(WSTOPSIG(status)))); // NOLINT(hicpp-signed-bitwise)
|
throw exception(fmt::format("An unknown signal was caused during WaitRdy, PID: {}, Status: 0x{:X}, Signal: {}", pid, status, strsignal(WSTOPSIG(status)))); // NOLINT(hicpp-signed-bitwise)
|
||||||
}
|
}
|
||||||
@ -120,7 +112,7 @@ namespace lightSwitch {
|
|||||||
if (status == -1) throw exception(fmt::format("Cannot resume process: {}, Error: {}", pid, strerror(errno)));
|
if (status == -1) throw exception(fmt::format("Cannot resume process: {}, Error: {}", pid, strerror(errno)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void NCE::StartProcess(uint64_t entry_point, uint64_t entry_arg, uint64_t stack_top, uint32_t handle, pid_t pid) const {
|
void NCE::StartProcess(u64 entry_point, u64 entry_arg, u64 stack_top, u32 handle, pid_t pid) const {
|
||||||
user_pt_regs regs{0};
|
user_pt_regs regs{0};
|
||||||
regs.sp = stack_top;
|
regs.sp = stack_top;
|
||||||
regs.pc = entry_point;
|
regs.pc = entry_point;
|
||||||
@ -130,165 +122,59 @@ namespace lightSwitch {
|
|||||||
ResumeProcess(pid);
|
ResumeProcess(pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t NCE::GetRegister(regs::xreg reg_id, pid_t pid) {
|
u64 NCE::GetRegister(xreg reg_id, pid_t pid) {
|
||||||
return register_map.at(pid ? pid : curr_pid).regs[reg_id];
|
return register_map.at(pid ? pid : curr_pid).regs[static_cast<uint>(reg_id)];
|
||||||
}
|
}
|
||||||
|
|
||||||
void NCE::SetRegister(regs::xreg reg_id, uint64_t value, pid_t pid) {
|
void NCE::SetRegister(xreg reg_id, u64 value, pid_t pid) {
|
||||||
register_map.at(pid ? pid : curr_pid).regs[reg_id] = value;
|
register_map.at(pid ? pid : curr_pid).regs[static_cast<uint>(reg_id)] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t NCE::GetRegister(regs::wreg reg_id, pid_t pid) {
|
u64 NCE::GetRegister(wreg reg_id, pid_t pid) {
|
||||||
return (reinterpret_cast<uint32_t *>(®ister_map.at(pid ? pid : curr_pid).regs))[reg_id * 2];
|
return (reinterpret_cast<u32 *>(®ister_map.at(pid ? pid : curr_pid).regs))[static_cast<uint>(reg_id) * 2];
|
||||||
}
|
}
|
||||||
|
|
||||||
void NCE::SetRegister(regs::wreg reg_id, uint32_t value, pid_t pid) {
|
void NCE::SetRegister(wreg reg_id, u32 value, pid_t pid) {
|
||||||
(reinterpret_cast<uint32_t *>(®ister_map.at(pid ? pid : curr_pid).regs))[reg_id * 2] = value;
|
(reinterpret_cast<u32 *>(®ister_map.at(pid ? pid : curr_pid).regs))[static_cast<uint>(reg_id) * 2] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t NCE::GetRegister(regs::sreg reg_id, pid_t pid) {
|
u64 NCE::GetRegister(sreg reg_id, pid_t pid) {
|
||||||
pid = pid ? pid : curr_pid;
|
pid = pid ? pid : curr_pid;
|
||||||
switch (reg_id) {
|
switch (reg_id) {
|
||||||
case regs::pc:
|
case sreg::pc:
|
||||||
return register_map.at(pid).pc;
|
return register_map.at(pid).pc;
|
||||||
case regs::sp:
|
case sreg::sp:
|
||||||
return register_map.at(pid).sp;
|
return register_map.at(pid).sp;
|
||||||
case regs::pstate:
|
case sreg::pstate:
|
||||||
return register_map.at(pid).pstate;
|
return register_map.at(pid).pstate;
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NCE::SetRegister(regs::sreg reg_id, uint32_t value, pid_t pid) {
|
void NCE::SetRegister(sreg reg_id, u32 value, pid_t pid) {
|
||||||
pid = pid ? pid : curr_pid;
|
pid = pid ? pid : curr_pid;
|
||||||
switch (reg_id) {
|
switch (reg_id) {
|
||||||
case regs::pc:
|
case sreg::pc:
|
||||||
register_map.at(pid).pc = value;
|
register_map.at(pid).pc = value;
|
||||||
case regs::sp:
|
case sreg::sp:
|
||||||
register_map.at(pid).sp = value;
|
register_map.at(pid).sp = value;
|
||||||
case regs::pstate:
|
case sreg::pstate:
|
||||||
register_map.at(pid).pstate = value;
|
register_map.at(pid).pstate = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t MapSharedFunc(uint64_t address, size_t size, uint64_t perms, uint64_t fd) {
|
std::shared_ptr<kernel::type::KSharedMemory> NCE::MapSharedRegion(const u64 address, const size_t size, const Memory::Permission local_permission, const Memory::Permission remote_permission, const Memory::Type type, const Memory::Region region) {
|
||||||
return reinterpret_cast<uint64_t>(mmap(reinterpret_cast<void *>(address), size, static_cast<int>(perms), MAP_PRIVATE | MAP_ANONYMOUS | ((address) ? MAP_FIXED : 0), static_cast<int>(fd), 0)); // NOLINT(hicpp-signed-bitwise)
|
auto item = std::make_shared<kernel::type::KSharedMemory>(*state, size, local_permission, remote_permission, type, 0, 0);
|
||||||
|
item->Map(address);
|
||||||
|
memory_map[item->address] = item;
|
||||||
|
memory_region_map[region] = item;
|
||||||
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t NCE::MapShared(uint64_t address, size_t size, const memory::Permission perms) {
|
size_t NCE::GetSharedSize() {
|
||||||
int fd = -1;
|
size_t shared_size = 0;
|
||||||
if(state && !state->os->process_map.empty()) {
|
for (auto ®ion : memory_map) {
|
||||||
fd = ASharedMemory_create("", size);
|
shared_size += region.second->size;
|
||||||
for(auto& process : state->os->process_map) {
|
|
||||||
user_pt_regs fregs = {0};
|
|
||||||
fregs.regs[0] = address;
|
|
||||||
fregs.regs[1] = size;
|
|
||||||
fregs.regs[2] = static_cast<uint64_t>(perms.get());
|
|
||||||
fregs.regs[3] = static_cast<uint64_t>(fd);
|
|
||||||
ExecuteFunction(reinterpret_cast<void *>(MapSharedFunc), fregs, process.second->main_thread);
|
|
||||||
if (reinterpret_cast<void *>(fregs.regs[0]) == MAP_FAILED)
|
|
||||||
throw exception("An error occurred while mapping shared region in child process");
|
|
||||||
address = fregs.regs[0]; // In case address was 0, this ensures all processes allocate the same address
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
void *ptr = mmap((void *) address, size, perms.get(), MAP_PRIVATE | MAP_ANONYMOUS | ((address) ? MAP_FIXED : 0), fd, 0); // NOLINT(hicpp-signed-bitwise)
|
return shared_size;
|
||||||
if (ptr == MAP_FAILED)
|
|
||||||
throw exception(fmt::format("An occurred while mapping shared region: {}", strerror(errno)));
|
|
||||||
addr_memory_map[address] = {address, size, perms, fd};
|
|
||||||
return reinterpret_cast<uint64_t>(ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t NCE::MapShared(uint64_t address, size_t size, const memory::Permission perms, const memory::Region region) {
|
|
||||||
uint64_t addr = MapShared(address,size, perms); // TODO: Change MapShared return type to RegionData
|
|
||||||
region_memory_map[region] = {addr, size, perms};
|
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t RemapSharedFunc(uint64_t address, size_t old_size, size_t size) {
|
|
||||||
return reinterpret_cast<uint64_t>(mremap(reinterpret_cast<void *>(address), old_size, size, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
void NCE::RemapShared(uint64_t address, size_t old_size, size_t size) {
|
|
||||||
if(state && !state->os->process_map.empty()) {
|
|
||||||
for(auto& process : state->os->process_map) {
|
|
||||||
user_pt_regs fregs = {0};
|
|
||||||
fregs.regs[0] = address;
|
|
||||||
fregs.regs[1] = old_size;
|
|
||||||
fregs.regs[2] = size;
|
|
||||||
ExecuteFunction(reinterpret_cast<void *>(RemapSharedFunc), fregs, process.second->main_thread);
|
|
||||||
if (reinterpret_cast<void *>(fregs.regs[0]) == MAP_FAILED)
|
|
||||||
throw exception("An error occurred while remapping shared region in child process");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void *ptr = mremap(reinterpret_cast<void *>(address), old_size, size, 0);
|
|
||||||
if (ptr == MAP_FAILED)
|
|
||||||
throw exception(fmt::format("An occurred while remapping shared region: {}", strerror(errno)));
|
|
||||||
addr_memory_map.at(address).size = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NCE::RemapShared(const memory::Region region, size_t size) {
|
|
||||||
memory::RegionData& region_data = region_memory_map.at(region);
|
|
||||||
RemapShared(region_data.address, region_data.size, size);
|
|
||||||
region_data.size = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t UpdatePermissionSharedFunc(uint64_t address, size_t size, uint64_t perms) {
|
|
||||||
return static_cast<uint64_t>(mprotect(reinterpret_cast<void *>(address), size, static_cast<int>(perms)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void NCE::UpdatePermissionShared(uint64_t address, size_t size, const memory::Permission perms) {
|
|
||||||
if(state && !state->os->process_map.empty()) {
|
|
||||||
for(auto& process : state->os->process_map) {
|
|
||||||
user_pt_regs fregs = {0};
|
|
||||||
fregs.regs[0] = address;
|
|
||||||
fregs.regs[1] = size;
|
|
||||||
fregs.regs[2] = static_cast<uint64_t>(perms.get());
|
|
||||||
ExecuteFunction(reinterpret_cast<void *>(UpdatePermissionSharedFunc), fregs, process.second->main_thread);
|
|
||||||
if (static_cast<int>(fregs.regs[0]) == -1)
|
|
||||||
throw exception("An error occurred while updating shared region's permissions in child process");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mprotect(reinterpret_cast<void *>(address), size, perms.get()) == -1)
|
|
||||||
throw exception(fmt::format("An occurred while updating shared region's permissions: {}", strerror(errno)));
|
|
||||||
addr_memory_map.at(address).perms = perms;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NCE::UpdatePermissionShared(const memory::Region region, const memory::Permission perms) {
|
|
||||||
memory::RegionData& region_data = region_memory_map.at(region);
|
|
||||||
if (region_data.perms != perms) {
|
|
||||||
UpdatePermissionShared(region_data.address, region_data.size, perms);
|
|
||||||
region_data.perms = perms;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t UnmapSharedFunc(uint64_t address, size_t size) {
|
|
||||||
return static_cast<uint64_t>(munmap(reinterpret_cast<void *>(address), size));
|
|
||||||
}
|
|
||||||
|
|
||||||
void NCE::UnmapShared(uint64_t address, size_t size) {
|
|
||||||
if(state && !state->os->process_map.empty()) {
|
|
||||||
for(auto& process : state->os->process_map) {
|
|
||||||
user_pt_regs fregs = {0};
|
|
||||||
fregs.regs[0] = address;
|
|
||||||
fregs.regs[1] = size;
|
|
||||||
ExecuteFunction(reinterpret_cast<void *>(UnmapSharedFunc), fregs, process.second->main_thread);
|
|
||||||
if (static_cast<int>(fregs.regs[0]) == -1)
|
|
||||||
throw exception("An error occurred while unmapping shared region in child process");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int err = munmap(reinterpret_cast<void *>(address), size);
|
|
||||||
if (err == -1)
|
|
||||||
throw exception(fmt::format("An occurred while unmapping shared region: {}", strerror(errno)));
|
|
||||||
if (addr_memory_map.at(address).fd!=-1) {
|
|
||||||
if (close(addr_memory_map.at(address).fd)==-1)
|
|
||||||
throw exception(fmt::format("An occurred while closing shared file descriptor: {}", strerror(errno)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NCE::UnmapShared(const memory::Region region) {
|
|
||||||
memory::RegionData& region_data = region_memory_map.at(region);
|
|
||||||
UnmapShared(region_data.address, region_data.size);
|
|
||||||
region_memory_map.erase(region);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,49 +6,50 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "kernel/types/KSharedMemory.h"
|
||||||
|
|
||||||
namespace lightSwitch {
|
namespace lightSwitch {
|
||||||
class NCE {
|
class NCE {
|
||||||
private:
|
private:
|
||||||
pid_t curr_pid = 0; //!< The PID of the process currently being handled, this is so the PID won't have to be passed into functions like ReadRegister redundantly
|
pid_t curr_pid = 0; //!< The PID of the process currently being handled, this is so the PID won't have to be passed into functions like ReadRegister redundantly
|
||||||
std::unordered_map<pid_t, user_pt_regs> register_map; //!< A map of all PIDs and their corresponding registers (Whenever they were last updated)
|
std::unordered_map<pid_t, user_pt_regs> register_map; //!< A map of all PIDs and their corresponding registers (Whenever they were last updated)
|
||||||
device_state *state;
|
const device_state *state; //!< The state of the device
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads process registers into the `registers` variable
|
* Reads process registers into the `registers` variable
|
||||||
* @param registers A set of registers to fill with values from the process
|
* @param registers A set of registers to fill with values from the process
|
||||||
* @param pid The PID of the process
|
* @param pid The PID of the process
|
||||||
*/
|
*/
|
||||||
void ReadRegisters(user_pt_regs ®isters, pid_t pid=0) const;
|
void ReadRegisters(user_pt_regs ®isters, pid_t pid = 0) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes process registers from the `registers` variable
|
* Writes process registers from the `registers` variable
|
||||||
* @param registers The registers to be written by the process
|
* @param registers The registers to be written by the process
|
||||||
* @param pid The PID of the process
|
* @param pid The PID of the process
|
||||||
*/
|
*/
|
||||||
void WriteRegisters(user_pt_regs ®isters, pid_t pid=0) const;
|
void WriteRegisters(user_pt_regs ®isters, pid_t pid = 0) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param address The address of the BRK instruction
|
* @param address The address of the BRK instruction
|
||||||
* @param pid The PID of the process
|
* @param pid The PID of the process
|
||||||
* @return An instance of BRK with the corresponding values
|
* @return An instance of BRK with the corresponding values
|
||||||
*/
|
*/
|
||||||
instr::brk ReadBrk(uint64_t address, pid_t pid=0) const;
|
instr::brk ReadBrk(u64 address, pid_t pid = 0) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
std::map<memory::Region, memory::RegionData> region_memory_map; //!< A mapping from every memory::Region to it's corresponding memory::RegionData which holds it's address, size and fd
|
std::map<Memory::Region, std::shared_ptr<kernel::type::KSharedMemory>> memory_region_map; //!< A mapping from every Memory::Region to a shared pointer to it's corresponding kernel::type::KSharedMemory
|
||||||
std::map<uint64_t, memory::RegionData> addr_memory_map; //!< A mapping from every address to it's corresponding memory::RegionData
|
std::map<u64, std::shared_ptr<kernel::type::KSharedMemory>> memory_map; //!< A mapping from every address to a shared pointer to it's corresponding kernel::type::KSharedMemory
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterates over shared_memory_vec unmapping all memory
|
* Initialize NCE by setting the device_state variable
|
||||||
|
* @param state The state of the device
|
||||||
*/
|
*/
|
||||||
~NCE();
|
void Initialize(const device_state &state);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start managing child processes
|
* Start managing child processes
|
||||||
* @param state The state of the device
|
|
||||||
*/
|
*/
|
||||||
void Execute(const device_state& state);
|
void Execute();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute any arbitrary function on a particular child process
|
* Execute any arbitrary function on a particular child process
|
||||||
@ -76,7 +77,7 @@ namespace lightSwitch {
|
|||||||
* Resumes a particular process, does nothing if it was already running
|
* Resumes a particular process, does nothing if it was already running
|
||||||
* @param pid The PID of the process
|
* @param pid The PID of the process
|
||||||
*/
|
*/
|
||||||
void ResumeProcess(pid_t pid=0) const;
|
void ResumeProcess(pid_t pid = 0) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts a particular process, sets the registers to their expected values and jumps to address
|
* Starts a particular process, sets the registers to their expected values and jumps to address
|
||||||
@ -86,7 +87,7 @@ namespace lightSwitch {
|
|||||||
* @param handle The handle of the main thread (Set to value of 1st register)
|
* @param handle The handle of the main thread (Set to value of 1st register)
|
||||||
* @param pid The PID of the process
|
* @param pid The PID of the process
|
||||||
*/
|
*/
|
||||||
void StartProcess(uint64_t address, uint64_t entry_arg, uint64_t stack_top, uint32_t handle, pid_t pid) const;
|
void StartProcess(u64 address, u64 entry_arg, u64 stack_top, u32 handle, pid_t pid) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the value of a Xn register
|
* Get the value of a Xn register
|
||||||
@ -94,7 +95,7 @@ namespace lightSwitch {
|
|||||||
* @param pid The PID of the process
|
* @param pid The PID of the process
|
||||||
* @return The value of the register
|
* @return The value of the register
|
||||||
*/
|
*/
|
||||||
uint64_t GetRegister(regs::xreg reg_id, pid_t pid=0);
|
u64 GetRegister(xreg reg_id, pid_t pid = 0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the value of a Xn register
|
* Set the value of a Xn register
|
||||||
@ -102,7 +103,7 @@ namespace lightSwitch {
|
|||||||
* @param value The value to set
|
* @param value The value to set
|
||||||
* @param pid The PID of the process
|
* @param pid The PID of the process
|
||||||
*/
|
*/
|
||||||
void SetRegister(regs::xreg reg_id, uint64_t value, pid_t pid=0);
|
void SetRegister(xreg reg_id, u64 value, pid_t pid = 0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the value of a Wn register
|
* Get the value of a Wn register
|
||||||
@ -110,7 +111,7 @@ namespace lightSwitch {
|
|||||||
* @param pid The PID of the process
|
* @param pid The PID of the process
|
||||||
* @return The value in the register
|
* @return The value in the register
|
||||||
*/
|
*/
|
||||||
uint64_t GetRegister(regs::wreg reg_id, pid_t pid=0);
|
u64 GetRegister(wreg reg_id, pid_t pid = 0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the value of a Wn register
|
* Set the value of a Wn register
|
||||||
@ -118,7 +119,7 @@ namespace lightSwitch {
|
|||||||
* @param value The value to set
|
* @param value The value to set
|
||||||
* @param pid The PID of the process
|
* @param pid The PID of the process
|
||||||
*/
|
*/
|
||||||
void SetRegister(regs::wreg reg_id, uint32_t value, pid_t pid=0);
|
void SetRegister(wreg reg_id, u32 value, pid_t pid = 0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the value of a special register
|
* Get the value of a special register
|
||||||
@ -126,7 +127,7 @@ namespace lightSwitch {
|
|||||||
* @param pid The PID of the process
|
* @param pid The PID of the process
|
||||||
* @return The value in the register
|
* @return The value in the register
|
||||||
*/
|
*/
|
||||||
uint64_t GetRegister(regs::sreg reg_id, pid_t pid=0);
|
u64 GetRegister(sreg reg_id, pid_t pid = 0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the value of a special register
|
* Set the value of a special register
|
||||||
@ -134,69 +135,22 @@ namespace lightSwitch {
|
|||||||
* @param value The value to set
|
* @param value The value to set
|
||||||
* @param pid The PID of the process
|
* @param pid The PID of the process
|
||||||
*/
|
*/
|
||||||
void SetRegister(regs::sreg reg_id, uint32_t value, pid_t pid=0);
|
void SetRegister(sreg reg_id, u32 value, pid_t pid = 0);
|
||||||
|
|
||||||
// TODO: Shared Memory mappings don't update after child process has been created
|
|
||||||
/**
|
|
||||||
* Map a chunk of shared memory
|
|
||||||
* @param address The address to map to (Can be 0 if address doesn't matter)
|
|
||||||
* @param size The size of the chunk of memory
|
|
||||||
* @param perms The permissions of the memory
|
|
||||||
* @return The address of the mapped chunk (Use when address is 0)
|
|
||||||
*/
|
|
||||||
uint64_t MapShared(uint64_t address, size_t size, const memory::Permission perms);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map a chunk of shared memory
|
* Map a chunk of shared memory
|
||||||
* @param address The address to map to (Can be 0 if address doesn't matter)
|
* @param address The address to map to (Can be 0 if address doesn't matter)
|
||||||
* @param size The size of the chunk of memory
|
* @param size The size of the chunk of memory
|
||||||
* @param perms The permissions of the memory
|
* @param perms The permissions of the memory
|
||||||
|
* @param type The type of the memory
|
||||||
* @param region The specific region this memory is mapped for
|
* @param region The specific region this memory is mapped for
|
||||||
* @return The address of the mapped chunk (Use when address is 0)
|
* @return A shared pointer to the kernel::type::KSharedMemory object
|
||||||
*/
|
*/
|
||||||
uint64_t MapShared(uint64_t address, size_t size, const memory::Permission perms, const memory::Region region);
|
std::shared_ptr<kernel::type::KSharedMemory> MapSharedRegion(const u64 address, const size_t size, const Memory::Permission local_permission, const Memory::Permission remote_permission, const Memory::Type type, const Memory::Region region);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remap a chunk of memory as to change the size occupied by it
|
* @return The total size of allocated shared memory
|
||||||
* @param address The address of the mapped memory
|
|
||||||
* @param old_size The current size of the memory
|
|
||||||
* @param size The new size of the memory
|
|
||||||
*/
|
*/
|
||||||
void RemapShared(uint64_t address, size_t old_size, size_t size);
|
size_t GetSharedSize();
|
||||||
|
|
||||||
/**
|
|
||||||
* Remap a chunk of memory as to change the size occupied by it
|
|
||||||
* @param region The region of memory that was mapped
|
|
||||||
* @param size The new size of the memory
|
|
||||||
*/
|
|
||||||
void RemapShared(memory::Region region, size_t size);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the permissions of a chunk of mapped memory
|
|
||||||
* @param address The address of the mapped memory
|
|
||||||
* @param size The size of the mapped memory
|
|
||||||
* @param perms The new permissions to be set for the memory
|
|
||||||
*/
|
|
||||||
void UpdatePermissionShared(uint64_t address, size_t size, const memory::Permission perms);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the permissions of a chunk of mapped memory
|
|
||||||
* @param region The region of memory that was mapped
|
|
||||||
* @param perms The new permissions to be set for the memory
|
|
||||||
*/
|
|
||||||
void UpdatePermissionShared(memory::Region region, memory::Permission perms);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unmap a particular chunk of mapped memory
|
|
||||||
* @param address The address of the mapped memory
|
|
||||||
* @param size The size of the mapped memory
|
|
||||||
*/
|
|
||||||
void UnmapShared(uint64_t address, size_t size);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unmap a particular chunk of mapped memory
|
|
||||||
* @param region The region of mapped memory
|
|
||||||
*/
|
|
||||||
void UnmapShared(const memory::Region region);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -9,15 +9,16 @@ namespace lightSwitch::kernel {
|
|||||||
OS::OS(std::shared_ptr<Logger> &logger, std::shared_ptr<Settings> &settings) : state(this, this_process, this_thread, std::make_shared<NCE>(), settings, logger) {}
|
OS::OS(std::shared_ptr<Logger> &logger, std::shared_ptr<Settings> &settings) : state(this, this_process, this_thread, std::make_shared<NCE>(), settings, logger) {}
|
||||||
|
|
||||||
void OS::Execute(std::string rom_file) {
|
void OS::Execute(std::string rom_file) {
|
||||||
|
state.nce->Initialize(state);
|
||||||
std::string rom_ext = rom_file.substr(rom_file.find_last_of('.') + 1);
|
std::string rom_ext = rom_file.substr(rom_file.find_last_of('.') + 1);
|
||||||
std::transform(rom_ext.begin(), rom_ext.end(), rom_ext.begin(), [](unsigned char c) { return std::tolower(c); });
|
std::transform(rom_ext.begin(), rom_ext.end(), rom_ext.begin(), [](unsigned char c) { return std::tolower(c); });
|
||||||
|
|
||||||
if (rom_ext == "nro") loader::NroLoader loader(rom_file, state);
|
if (rom_ext == "nro") loader::NroLoader loader(rom_file, state);
|
||||||
else throw exception("Unsupported ROM extension.");
|
else throw exception("Unsupported ROM extension.");
|
||||||
|
|
||||||
auto main_process = CreateProcess(state.nce->region_memory_map.at(memory::Region::text).address, constant::def_stack_size);
|
auto main_process = CreateProcess(state.nce->memory_region_map.at(Memory::Region::text)->address, constant::def_stack_size);
|
||||||
main_process->thread_map[main_process->main_thread]->Start(); // The kernel itself is responsible for starting the main thread
|
main_process->thread_map[main_process->main_thread]->Start(); // The kernel itself is responsible for starting the main thread
|
||||||
state.nce->Execute(state);
|
state.nce->Execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -29,9 +30,8 @@ namespace lightSwitch::kernel {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<type::KProcess> OS::CreateProcess(u64 address, size_t stack_size) {
|
||||||
std::shared_ptr<type::KProcess> OS::CreateProcess(uint64_t address, size_t stack_size) {
|
auto *stack = static_cast<u8 *>(mmap(nullptr, stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_NORESERVE | MAP_ANONYMOUS | MAP_STACK, -1, 0)); // NOLINT(hicpp-signed-bitwise)
|
||||||
auto *stack = static_cast<uint8_t *>(mmap(nullptr, stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_NORESERVE | MAP_ANONYMOUS | MAP_STACK, -1, 0)); // NOLINT(hicpp-signed-bitwise)
|
|
||||||
if (stack == MAP_FAILED)
|
if (stack == MAP_FAILED)
|
||||||
throw exception("Failed to allocate stack memory");
|
throw exception("Failed to allocate stack memory");
|
||||||
if (mprotect(stack, PAGE_SIZE, PROT_NONE)) {
|
if (mprotect(stack, PAGE_SIZE, PROT_NONE)) {
|
||||||
@ -39,22 +39,24 @@ namespace lightSwitch::kernel {
|
|||||||
throw exception("Failed to create guard pages");
|
throw exception("Failed to create guard pages");
|
||||||
}
|
}
|
||||||
pid_t pid = clone(&ExecuteChild, stack + stack_size, CLONE_FS | SIGCHLD, nullptr); // NOLINT(hicpp-signed-bitwise)
|
pid_t pid = clone(&ExecuteChild, stack + stack_size, CLONE_FS | SIGCHLD, nullptr); // NOLINT(hicpp-signed-bitwise)
|
||||||
if(pid==-1) throw exception(fmt::format("Call to clone() has failed: {}", strerror(errno)));
|
if (pid == -1) throw exception(fmt::format("Call to clone() has failed: {}", strerror(errno)));
|
||||||
std::shared_ptr<type::KProcess> process = std::make_shared<kernel::type::KProcess>(pid, address, reinterpret_cast<uint64_t>(stack), stack_size, state);
|
std::shared_ptr<type::KProcess> process = std::make_shared<kernel::type::KProcess>(pid, address, reinterpret_cast<u64>(stack), stack_size, state);
|
||||||
process_map[pid] = process;
|
process_map[pid] = process;
|
||||||
|
process_vec.push_back(pid);
|
||||||
state.logger->Write(Logger::DEBUG, "Successfully created process with PID: {}", pid);
|
state.logger->Write(Logger::DEBUG, "Successfully created process with PID: {}", pid);
|
||||||
return process;
|
return process;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OS::KillThread(pid_t pid) {
|
void OS::KillThread(pid_t pid) {
|
||||||
auto process = process_map.at(pid);
|
auto process = process_map.at(pid);
|
||||||
if(process->main_thread==pid) {
|
if (process->main_thread == pid) {
|
||||||
state.logger->Write(Logger::DEBUG, "Exiting process with PID: {}", pid);
|
state.logger->Write(Logger::DEBUG, "Exiting process with PID: {}", pid);
|
||||||
// Erasing all shared_ptr instances to the process will call the destructor
|
// Erasing all shared_ptr instances to the process will call the destructor
|
||||||
// However, in the case these are not all instances of it we wouldn't want to call the destructor
|
// However, in the case these are not all instances of it we wouldn't want to call the destructor
|
||||||
for (auto& [key, value]: process->thread_map) {
|
for (auto&[key, value]: process->thread_map) {
|
||||||
process_map.erase(key);
|
process_map.erase(key);
|
||||||
};
|
};
|
||||||
|
process_vec.erase(std::remove(process_vec.begin(), process_vec.end(), pid), process_vec.end());
|
||||||
} else {
|
} else {
|
||||||
state.logger->Write(Logger::DEBUG, "Exiting thread with TID: {}", pid);
|
state.logger->Write(Logger::DEBUG, "Exiting thread with TID: {}", pid);
|
||||||
process->handle_table.erase(process->thread_map[pid]->handle);
|
process->handle_table.erase(process->thread_map[pid]->handle);
|
||||||
@ -63,7 +65,7 @@ namespace lightSwitch::kernel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OS::SvcHandler(uint16_t svc, pid_t pid) {
|
void OS::SvcHandler(u16 svc, pid_t pid) {
|
||||||
this_process = process_map.at(pid);
|
this_process = process_map.at(pid);
|
||||||
this_thread = this_process->thread_map.at(pid);
|
this_thread = this_process->thread_map.at(pid);
|
||||||
if (svc::svcTable[svc]) {
|
if (svc::svcTable[svc]) {
|
||||||
@ -74,17 +76,15 @@ namespace lightSwitch::kernel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ipc::IpcResponse OS::IpcHandler(ipc::IpcRequest &request) {
|
ipc::IpcResponse OS::IpcHandler(handle_t handle) {
|
||||||
ipc::IpcResponse response;
|
ipc::IpcRequest request(false, state);
|
||||||
switch (request.req_info->type) {
|
ipc::IpcResponse response(false, state);
|
||||||
case 4: // Request
|
switch (request.header->type) {
|
||||||
response.SetError(0xDEADBEE5);
|
case static_cast<u16>(ipc::CommandType::Request):
|
||||||
response.MoveHandle(0xBAADBEEF);
|
case static_cast<u16>(ipc::CommandType::RequestWithContext):
|
||||||
response.MoveHandle(0xFACCF00D);
|
throw exception("Services are in-progress");
|
||||||
response.Generate(state);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
throw exception(fmt::format("Unimplemented IPC message type {0}", uint16_t(request.req_info->type)));
|
throw exception(fmt::format("Unimplemented IPC message type {0}", u16(request.header->type)));
|
||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
@ -12,11 +12,12 @@ namespace lightSwitch::kernel {
|
|||||||
* The OS class manages the interaction between OS components and the underlying hardware in NCE
|
* The OS class manages the interaction between OS components and the underlying hardware in NCE
|
||||||
*/
|
*/
|
||||||
class OS {
|
class OS {
|
||||||
private:
|
private:
|
||||||
device_state state; //!< The state of the device
|
device_state state; //!< The state of the device
|
||||||
|
|
||||||
public:
|
public:
|
||||||
std::unordered_map<pid_t, std::shared_ptr<type::KProcess>> process_map; //!< The state of the device
|
std::unordered_map<pid_t, std::shared_ptr<type::KProcess>> process_map; //!< A mapping from a process's PID to it's corresponding PID (Threads have their own PID too, so there are overlapping values)
|
||||||
|
std::vector<pid_t> process_vec; //!< A vector of all processes by their main thread's PID
|
||||||
std::shared_ptr<type::KProcess> this_process; //!< The corresponding KProcess object of the process that's called an SVC
|
std::shared_ptr<type::KProcess> this_process; //!< The corresponding KProcess object of the process that's called an SVC
|
||||||
std::shared_ptr<type::KThread> this_thread; //!< The corresponding KThread object of the thread that's called an SVC
|
std::shared_ptr<type::KThread> this_thread; //!< The corresponding KThread object of the thread that's called an SVC
|
||||||
|
|
||||||
@ -38,7 +39,7 @@ namespace lightSwitch::kernel {
|
|||||||
* @param stack_size The size of the main stack
|
* @param stack_size The size of the main stack
|
||||||
* @return An instance of the KProcess of the created process
|
* @return An instance of the KProcess of the created process
|
||||||
*/
|
*/
|
||||||
std::shared_ptr<type::KProcess> CreateProcess(uint64_t address, size_t stack_size);
|
std::shared_ptr<type::KProcess> CreateProcess(u64 address, size_t stack_size);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Kill a particular thread
|
* Kill a particular thread
|
||||||
@ -50,12 +51,12 @@ namespace lightSwitch::kernel {
|
|||||||
* @param svc The ID of the SVC to be called
|
* @param svc The ID of the SVC to be called
|
||||||
* @param pid The PID of the process/thread calling the SVC
|
* @param pid The PID of the process/thread calling the SVC
|
||||||
*/
|
*/
|
||||||
void SvcHandler(uint16_t svc, pid_t pid);
|
void SvcHandler(u16 svc, pid_t pid);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param req The IPC request to be handled
|
* @param handle The handle of the object
|
||||||
* @return The corresponding response returned by a service
|
* @return The corresponding response returned by a service
|
||||||
*/
|
*/
|
||||||
ipc::IpcResponse IpcHandler(ipc::IpcRequest &req);
|
ipc::IpcResponse IpcHandler(handle_t handle);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import android.view.Window;
|
|||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.RelativeLayout;
|
import android.widget.RelativeLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -8,7 +8,9 @@ import android.view.ViewGroup;
|
|||||||
import android.widget.BaseAdapter;
|
import android.widget.BaseAdapter;
|
||||||
import android.widget.Filter;
|
import android.widget.Filter;
|
||||||
import android.widget.Filterable;
|
import android.widget.Filterable;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import me.xdrop.fuzzywuzzy.FuzzySearch;
|
import me.xdrop.fuzzywuzzy.FuzzySearch;
|
||||||
import me.xdrop.fuzzywuzzy.model.ExtractedResult;
|
import me.xdrop.fuzzywuzzy.model.ExtractedResult;
|
||||||
|
|
||||||
@ -67,10 +69,10 @@ abstract class HeaderAdapter<ItemType extends BaseItem> extends BaseAdapter impl
|
|||||||
header_array.add((String) item);
|
header_array.add((String) item);
|
||||||
type_array_uf.add(new ContentType(header_array.size() - 1, ContentType.Header));
|
type_array_uf.add(new ContentType(header_array.size() - 1, ContentType.Header));
|
||||||
}
|
}
|
||||||
if(search_term.length()!=0)
|
if (search_term.length() != 0)
|
||||||
this.getFilter().filter(search_term);
|
this.getFilter().filter(search_term);
|
||||||
else
|
else
|
||||||
type_array=type_array_uf;
|
type_array = type_array_uf;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void save(File file) throws IOException {
|
public void save(File file) throws IOException {
|
||||||
|
@ -9,20 +9,36 @@ import android.view.Menu;
|
|||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.appcompat.widget.SearchView;
|
import androidx.appcompat.widget.SearchView;
|
||||||
import androidx.appcompat.widget.Toolbar;
|
import androidx.appcompat.widget.Toolbar;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.BufferedInputStream;
|
||||||
import java.net.HttpURLConnection;
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.BufferedWriter;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import javax.net.ssl.HttpsURLConnection;
|
||||||
|
|
||||||
import static java.lang.Thread.interrupted;
|
import static java.lang.Thread.interrupted;
|
||||||
|
|
||||||
public class LogActivity extends AppCompatActivity {
|
public class LogActivity extends AppCompatActivity {
|
||||||
@ -47,42 +63,42 @@ public class LogActivity extends AppCompatActivity {
|
|||||||
try {
|
try {
|
||||||
InputStream inputStream = new FileInputStream(log_file);
|
InputStream inputStream = new FileInputStream(log_file);
|
||||||
reader = new BufferedReader(new InputStreamReader(inputStream));
|
reader = new BufferedReader(new InputStreamReader(inputStream));
|
||||||
thread = new Thread(new Runnable() {
|
thread = new Thread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@SuppressWarnings("deprecation") // Required as FileObserver(File) is only on API level 29 also no AndroidX version present
|
@SuppressWarnings("deprecation") // Required as FileObserver(File) is only on API level 29 also no AndroidX version present
|
||||||
FileObserver observer = new FileObserver(log_file.getPath()) {
|
FileObserver observer = new FileObserver(log_file.getPath()) {
|
||||||
@Override
|
@Override
|
||||||
public void onEvent(int event, String path) {
|
public void onEvent(int event, String path) {
|
||||||
if (event == FileObserver.MODIFY) {
|
if (event == FileObserver.MODIFY) {
|
||||||
try {
|
try {
|
||||||
boolean done = false;
|
boolean done = false;
|
||||||
while (!done) {
|
while (!done) {
|
||||||
final String line = reader.readLine();
|
final String line = reader.readLine();
|
||||||
done = (line == null);
|
done = (line == null);
|
||||||
if (!done) {
|
if (!done) {
|
||||||
runOnUiThread(new Runnable() {
|
runOnUiThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
adapter.add(line);
|
adapter.add(line);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w("Logger", "IO Error during access of log file: " + e.getMessage());
|
||||||
|
Toast.makeText(getApplicationContext(), getString(R.string.io_error) + ": " + e.getMessage(), Toast.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
|
||||||
Log.w("Logger", "IO Error during access of log file: " + e.getMessage());
|
|
||||||
Toast.makeText(getApplicationContext(), getString(R.string.io_error) + ": " + e.getMessage(), Toast.LENGTH_LONG).show();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
};
|
observer.onEvent(FileObserver.MODIFY, log_file.getPath());
|
||||||
observer.onEvent(FileObserver.MODIFY, log_file.getPath());
|
observer.startWatching();
|
||||||
observer.startWatching();
|
while (!interrupted()) ;
|
||||||
while (!interrupted()) ;
|
observer.stopWatching();
|
||||||
observer.stopWatching();
|
}
|
||||||
}
|
});
|
||||||
});
|
thread.start();
|
||||||
thread.start();
|
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
Log.w("Logger", "IO Error during access of log file: " + e.getMessage());
|
Log.w("Logger", "IO Error during access of log file: " + e.getMessage());
|
||||||
Toast.makeText(getApplicationContext(), getString(R.string.file_missing), Toast.LENGTH_LONG).show();
|
Toast.makeText(getApplicationContext(), getString(R.string.file_missing), Toast.LENGTH_LONG).show();
|
||||||
@ -129,10 +145,15 @@ public class LogActivity extends AppCompatActivity {
|
|||||||
Thread share_thread = new Thread(new Runnable() {
|
Thread share_thread = new Thread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
HttpsURLConnection urlConnection = null;
|
||||||
try {
|
try {
|
||||||
URL url = new URL("https://hastebin.com/documents");
|
URL url = new URL("https://hastebin.com/documents");
|
||||||
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
|
urlConnection = (HttpsURLConnection) url.openConnection();
|
||||||
urlConnection.setRequestMethod("POST");
|
urlConnection.setRequestMethod("POST");
|
||||||
|
urlConnection.setRequestProperty("Host", "hastebin.com");
|
||||||
|
urlConnection.setRequestProperty("Content-Type", "application/json; charset=utf-8");
|
||||||
|
urlConnection.setRequestProperty("Referer", "https://hastebin.com/");
|
||||||
|
urlConnection.setRequestProperty("Connection", "keep-alive");
|
||||||
OutputStream outputStream = new BufferedOutputStream(urlConnection.getOutputStream());
|
OutputStream outputStream = new BufferedOutputStream(urlConnection.getOutputStream());
|
||||||
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8));
|
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8));
|
||||||
FileReader fileReader = new FileReader(log_file);
|
FileReader fileReader = new FileReader(log_file);
|
||||||
@ -143,25 +164,34 @@ public class LogActivity extends AppCompatActivity {
|
|||||||
bufferedWriter.flush();
|
bufferedWriter.flush();
|
||||||
bufferedWriter.close();
|
bufferedWriter.close();
|
||||||
outputStream.close();
|
outputStream.close();
|
||||||
urlConnection.connect();
|
if (urlConnection.getResponseCode() != 200) {
|
||||||
|
Log.e("LogUpload", "HTTPS Status Code: " + urlConnection.getResponseCode());
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
InputStream inputStream = new BufferedInputStream(urlConnection.getInputStream());
|
InputStream inputStream = new BufferedInputStream(urlConnection.getInputStream());
|
||||||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
|
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
|
||||||
String key = new JSONObject(bufferedReader.lines().collect(Collectors.joining())).getString("key");
|
String key = new JSONObject(bufferedReader.lines().collect(Collectors.joining())).getString("key");
|
||||||
bufferedReader.close();
|
bufferedReader.close();
|
||||||
inputStream.close();
|
inputStream.close();
|
||||||
urlConnection.disconnect();
|
|
||||||
String result = "https://hastebin.com/" + key;
|
String result = "https://hastebin.com/" + key;
|
||||||
Intent sharingIntent = new Intent(Intent.ACTION_SEND).setType("text/plain").putExtra(Intent.EXTRA_TEXT, result);
|
Intent sharingIntent = new Intent(Intent.ACTION_SEND).setType("text/plain").putExtra(Intent.EXTRA_TEXT, result);
|
||||||
startActivity(Intent.createChooser(sharingIntent, "Share log url with:"));
|
startActivity(Intent.createChooser(sharingIntent, "Share log url with:"));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
runOnUiThread(new Runnable() {@Override public void run() {Toast.makeText(getApplicationContext(), getString(R.string.share_error), Toast.LENGTH_LONG).show();}});
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {Toast.makeText(getApplicationContext(), getString(R.string.share_error), Toast.LENGTH_LONG).show();}
|
||||||
|
});
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
assert urlConnection != null;
|
||||||
|
urlConnection.disconnect();
|
||||||
}
|
}
|
||||||
}});
|
}
|
||||||
|
});
|
||||||
share_thread.start();
|
share_thread.start();
|
||||||
try {
|
try {
|
||||||
share_thread.join(1000);
|
share_thread.join(1000);
|
||||||
} catch (InterruptedException e) {
|
} catch (Exception e) {
|
||||||
Toast.makeText(getApplicationContext(), getString(R.string.share_error), Toast.LENGTH_LONG).show();
|
Toast.makeText(getApplicationContext(), getString(R.string.share_error), Toast.LENGTH_LONG).show();
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,8 @@ import android.view.View;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import com.google.android.material.snackbar.Snackbar;
|
|
||||||
|
|
||||||
class LogItem extends BaseItem {
|
class LogItem extends BaseItem {
|
||||||
private String content;
|
private String content;
|
||||||
@ -60,8 +60,8 @@ public class LogAdapter extends HeaderAdapter<LogItem> implements View.OnLongCli
|
|||||||
@Override
|
@Override
|
||||||
public boolean onLongClick(View view) {
|
public boolean onLongClick(View view) {
|
||||||
LogItem item = (LogItem) getItem(((ViewHolder) view.getTag()).position);
|
LogItem item = (LogItem) getItem(((ViewHolder) view.getTag()).position);
|
||||||
clipboard.setPrimaryClip(ClipData.newPlainText("Log Message", item.getLevel() + ": " + item.getMessage()));
|
clipboard.setPrimaryClip(ClipData.newPlainText("Log Message", item.getMessage() + " (" + item.getLevel() + ")"));
|
||||||
Toast.makeText(view.getContext(), "Copied to clipboard", Snackbar.LENGTH_LONG).show();
|
Toast.makeText(view.getContext(), "Copied to clipboard", Toast.LENGTH_LONG).show();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ import android.view.MenuItem;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.AdapterView;
|
import android.widget.AdapterView;
|
||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.appcompat.widget.SearchView;
|
import androidx.appcompat.widget.SearchView;
|
||||||
@ -18,6 +19,7 @@ import androidx.appcompat.widget.Toolbar;
|
|||||||
import androidx.core.app.ActivityCompat;
|
import androidx.core.app.ActivityCompat;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
import com.google.android.material.snackbar.Snackbar;
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package emu.lightswitch;
|
package emu.lightswitch;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
import androidx.appcompat.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.appcompat.widget.Toolbar;
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
@ -1,34 +1,34 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:aapt="http://schemas.android.com/aapt"
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
android:width="108dp"
|
android:width="108dp"
|
||||||
android:height="108dp"
|
android:height="108dp"
|
||||||
android:viewportWidth="108"
|
android:viewportWidth="108"
|
||||||
android:viewportHeight="108">
|
android:viewportHeight="108">
|
||||||
<path
|
<path
|
||||||
android:fillType="evenOdd"
|
android:fillType="evenOdd"
|
||||||
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
|
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
|
||||||
android:strokeWidth="1"
|
android:strokeWidth="1"
|
||||||
android:strokeColor="#00000000">
|
android:strokeColor="#00000000">
|
||||||
<aapt:attr name="android:fillColor">
|
<aapt:attr name="android:fillColor">
|
||||||
<gradient
|
<gradient
|
||||||
android:endX="78.5885"
|
android:endX="78.5885"
|
||||||
android:endY="90.9159"
|
android:endY="90.9159"
|
||||||
android:startX="48.7653"
|
android:startX="48.7653"
|
||||||
android:startY="61.0927"
|
android:startY="61.0927"
|
||||||
android:type="linear">
|
android:type="linear">
|
||||||
<item
|
<item
|
||||||
android:color="#44000000"
|
android:color="#44000000"
|
||||||
android:offset="0.0" />
|
android:offset="0.0" />
|
||||||
<item
|
<item
|
||||||
android:color="#00000000"
|
android:color="#00000000"
|
||||||
android:offset="1.0" />
|
android:offset="1.0" />
|
||||||
</gradient>
|
</gradient>
|
||||||
</aapt:attr>
|
</aapt:attr>
|
||||||
</path>
|
</path>
|
||||||
<path
|
<path
|
||||||
android:fillColor="#FFFFFF"
|
android:fillColor="#FFFFFF"
|
||||||
android:fillType="nonZero"
|
android:fillType="nonZero"
|
||||||
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
|
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
|
||||||
android:strokeWidth="1"
|
android:strokeWidth="1"
|
||||||
android:strokeColor="#00000000" />
|
android:strokeColor="#00000000" />
|
||||||
</vector>
|
</vector>
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
<vector android:alpha="0.85" android:height="24dp"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
android:width="24dp"
|
||||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
android:height="24dp"
|
||||||
<path android:fillColor="#FFF" android:pathData="M5,13h14v-2L5,11v2zM3,17h14v-2L3,15v2zM7,7v2h14L21,7L7,7z"/>
|
android:alpha="0.85"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFF"
|
||||||
|
android:pathData="M5,13h14v-2L5,11v2zM3,17h14v-2L3,15v2zM7,7v2h14L21,7L7,7z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
@ -1,74 +1,170 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<vector
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:height="108dp"
|
android:width="108dp"
|
||||||
android:width="108dp"
|
android:height="108dp"
|
||||||
android:viewportHeight="108"
|
android:viewportWidth="108"
|
||||||
android:viewportWidth="108"
|
android:viewportHeight="108">
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
<path
|
||||||
<path android:fillColor="#008577"
|
android:fillColor="#008577"
|
||||||
android:pathData="M0,0h108v108h-108z"/>
|
android:pathData="M0,0h108v108h-108z" />
|
||||||
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
|
<path
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
android:fillColor="#00000000"
|
||||||
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
|
android:pathData="M9,0L9,108"
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
android:strokeWidth="0.8"
|
||||||
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
|
android:strokeColor="#33FFFFFF" />
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
<path
|
||||||
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
|
android:fillColor="#00000000"
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
android:pathData="M19,0L19,108"
|
||||||
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
|
android:strokeWidth="0.8"
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
android:strokeColor="#33FFFFFF" />
|
||||||
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
|
<path
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
android:fillColor="#00000000"
|
||||||
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
|
android:pathData="M29,0L29,108"
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
android:strokeWidth="0.8"
|
||||||
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
|
android:strokeColor="#33FFFFFF" />
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
<path
|
||||||
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
|
android:fillColor="#00000000"
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
android:pathData="M39,0L39,108"
|
||||||
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
|
android:strokeWidth="0.8"
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
android:strokeColor="#33FFFFFF" />
|
||||||
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
|
<path
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
android:fillColor="#00000000"
|
||||||
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
|
android:pathData="M49,0L49,108"
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
android:strokeWidth="0.8"
|
||||||
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
|
android:strokeColor="#33FFFFFF" />
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
<path
|
||||||
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
|
android:fillColor="#00000000"
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
android:pathData="M59,0L59,108"
|
||||||
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
|
android:strokeWidth="0.8"
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
android:strokeColor="#33FFFFFF" />
|
||||||
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
|
<path
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
android:fillColor="#00000000"
|
||||||
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
|
android:pathData="M69,0L69,108"
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
android:strokeWidth="0.8"
|
||||||
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
|
android:strokeColor="#33FFFFFF" />
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
<path
|
||||||
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
|
android:fillColor="#00000000"
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
android:pathData="M79,0L79,108"
|
||||||
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
|
android:strokeWidth="0.8"
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
android:strokeColor="#33FFFFFF" />
|
||||||
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
|
<path
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
android:fillColor="#00000000"
|
||||||
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
|
android:pathData="M89,0L89,108"
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
android:strokeWidth="0.8"
|
||||||
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
|
android:strokeColor="#33FFFFFF" />
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
<path
|
||||||
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
|
android:fillColor="#00000000"
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
android:pathData="M99,0L99,108"
|
||||||
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
|
android:strokeWidth="0.8"
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
android:strokeColor="#33FFFFFF" />
|
||||||
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
|
<path
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
android:fillColor="#00000000"
|
||||||
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
|
android:pathData="M0,9L108,9"
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
android:strokeWidth="0.8"
|
||||||
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
|
android:strokeColor="#33FFFFFF" />
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
<path
|
||||||
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
|
android:fillColor="#00000000"
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
android:pathData="M0,19L108,19"
|
||||||
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
|
android:strokeWidth="0.8"
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
android:strokeColor="#33FFFFFF" />
|
||||||
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
|
<path
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
android:fillColor="#00000000"
|
||||||
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
|
android:pathData="M0,29L108,29"
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,39L108,39"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,49L108,49"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,59L108,59"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,69L108,69"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,79L108,79"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,89L108,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,99L108,99"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,29L89,29"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,39L89,39"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,49L89,49"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,59L89,59"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,69L89,69"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,79L89,79"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M29,19L29,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M39,19L39,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M49,19L49,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M59,19L59,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M69,19L69,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M79,19L79,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
</vector>
|
</vector>
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
<vector android:alpha="0.85" android:height="24dp"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
android:width="24dp"
|
||||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
android:height="24dp"
|
||||||
<path android:fillColor="#FFFF" android:pathData="M7,5h10v2h2L19,3c0,-1.1 -0.9,-1.99 -2,-1.99L7,1c-1.1,0 -2,0.9 -2,2v4h2L7,5zM15.41,16.59L20,12l-4.59,-4.59L14,8.83 17.17,12 14,15.17l1.41,1.42zM10,15.17L6.83,12 10,8.83 8.59,7.41 4,12l4.59,4.59L10,15.17zM17,19L7,19v-2L5,17v4c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2v-4h-2v2z"/>
|
android:alpha="0.85"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFF"
|
||||||
|
android:pathData="M7,5h10v2h2L19,3c0,-1.1 -0.9,-1.99 -2,-1.99L7,1c-1.1,0 -2,0.9 -2,2v4h2L7,5zM15.41,16.59L20,12l-4.59,-4.59L14,8.83 17.17,12 14,15.17l1.41,1.42zM10,15.17L6.83,12 10,8.83 8.59,7.41 4,12l4.59,4.59L10,15.17zM17,19L7,19v-2L5,17v4c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2v-4h-2v2z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
<vector android:alpha="0.6" android:height="24dp"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
android:width="24dp"
|
||||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
android:height="24dp"
|
||||||
<path android:fillColor="#FF000000" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM17,13L7,13v-2h10v2z"/>
|
android:alpha="0.6"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM17,13L7,13v-2h10v2z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:viewportWidth="24.0"
|
android:viewportWidth="24.0"
|
||||||
android:viewportHeight="24.0">
|
android:viewportHeight="24.0">
|
||||||
<path
|
<path
|
||||||
android:fillColor="#FFFF"
|
android:fillColor="#FFFF"
|
||||||
android:pathData="M12,4L12,1L8,5l4,4L12,6c3.31,0 6,2.69 6,6 0,1.01 -0.25,1.97 -0.7,2.8l1.46,1.46C19.54,15.03 20,13.57 20,12c0,-4.42 -3.58,-8 -8,-8zM12,18c-3.31,0 -6,-2.69 -6,-6 0,-1.01 0.25,-1.97 0.7,-2.8L5.24,7.74C4.46,8.97 4,10.43 4,12c0,4.42 3.58,8 8,8v3l4,-4 -4,-4v3z" />
|
android:pathData="M12,4L12,1L8,5l4,4L12,6c3.31,0 6,2.69 6,6 0,1.01 -0.25,1.97 -0.7,2.8l1.46,1.46C19.54,15.03 20,13.57 20,12c0,-4.42 -3.58,-8 -8,-8zM12,18c-3.31,0 -6,-2.69 -6,-6 0,-1.01 0.25,-1.97 0.7,-2.8L5.24,7.74C4.46,8.97 4,10.43 4,12c0,4.42 3.58,8 8,8v3l4,-4 -4,-4v3z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
<vector android:alpha="0.85" android:height="24dp"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:tint="#FFFFFF" android:viewportHeight="24.0"
|
android:width="24dp"
|
||||||
android:viewportWidth="24.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
android:height="24dp"
|
||||||
<path android:fillColor="#FF000000" android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
|
android:alpha="0.85"
|
||||||
|
android:tint="#FFFFFF"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
<vector android:alpha="0.85" android:height="24dp"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:viewportHeight="24" android:viewportWidth="24"
|
android:width="24dp"
|
||||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
android:height="24dp"
|
||||||
<path android:fillColor="#FFFF" android:pathData="M19.1,12.9a2.8,2.8 0,0 0,0.1 -0.9,2.8 2.8,0 0,0 -0.1,-0.9l2.1,-1.6a0.7,0.7 0,0 0,0.1 -0.6L19.4,5.5a0.7,0.7 0,0 0,-0.6 -0.2l-2.4,1a6.5,6.5 0,0 0,-1.6 -0.9l-0.4,-2.6a0.5,0.5 0,0 0,-0.5 -0.4H10.1a0.5,0.5 0,0 0,-0.5 0.4L9.3,5.4a5.6,5.6 0,0 0,-1.7 0.9l-2.4,-1a0.4,0.4 0,0 0,-0.5 0.2l-2,3.4c-0.1,0.2 0,0.4 0.2,0.6l2,1.6a2.8,2.8 0,0 0,-0.1 0.9,2.8 2.8,0 0,0 0.1,0.9L2.8,14.5a0.7,0.7 0,0 0,-0.1 0.6l1.9,3.4a0.7,0.7 0,0 0,0.6 0.2l2.4,-1a6.5,6.5 0,0 0,1.6 0.9l0.4,2.6a0.5,0.5 0,0 0,0.5 0.4h3.8a0.5,0.5 0,0 0,0.5 -0.4l0.3,-2.6a5.6,5.6 0,0 0,1.7 -0.9l2.4,1a0.4,0.4 0,0 0,0.5 -0.2l2,-3.4c0.1,-0.2 0,-0.4 -0.2,-0.6ZM12,15.6A3.6,3.6 0,1 1,15.6 12,3.6 3.6,0 0,1 12,15.6Z"/>
|
android:alpha="0.85"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFF"
|
||||||
|
android:pathData="M19.1,12.9a2.8,2.8 0,0 0,0.1 -0.9,2.8 2.8,0 0,0 -0.1,-0.9l2.1,-1.6a0.7,0.7 0,0 0,0.1 -0.6L19.4,5.5a0.7,0.7 0,0 0,-0.6 -0.2l-2.4,1a6.5,6.5 0,0 0,-1.6 -0.9l-0.4,-2.6a0.5,0.5 0,0 0,-0.5 -0.4H10.1a0.5,0.5 0,0 0,-0.5 0.4L9.3,5.4a5.6,5.6 0,0 0,-1.7 0.9l-2.4,-1a0.4,0.4 0,0 0,-0.5 0.2l-2,3.4c-0.1,0.2 0,0.4 0.2,0.6l2,1.6a2.8,2.8 0,0 0,-0.1 0.9,2.8 2.8,0 0,0 0.1,0.9L2.8,14.5a0.7,0.7 0,0 0,-0.1 0.6l1.9,3.4a0.7,0.7 0,0 0,0.6 0.2l2.4,-1a6.5,6.5 0,0 0,1.6 0.9l0.4,2.6a0.5,0.5 0,0 0,0.5 0.4h3.8a0.5,0.5 0,0 0,0.5 -0.4l0.3,-2.6a5.6,5.6 0,0 0,1.7 -0.9l2.4,1a0.4,0.4 0,0 0,0.5 -0.2l2,-3.4c0.1,-0.2 0,-0.4 -0.2,-0.6ZM12,15.6A3.6,3.6 0,1 1,15.6 12,3.6 3.6,0 0,1 12,15.6Z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
<vector android:alpha="0.85" android:height="24dp"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:tint="#FFFFFF" android:viewportHeight="24.0"
|
android:width="24dp"
|
||||||
android:viewportWidth="24.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
android:height="24dp"
|
||||||
<path android:fillColor="#FF000000" android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"/>
|
android:alpha="0.85"
|
||||||
|
android:tint="#FFFFFF"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
@ -1,34 +1,34 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:padding="15dp">
|
android:padding="15dp">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/icon"
|
android:id="@+id/icon"
|
||||||
android:layout_width="50dp"
|
android:layout_width="50dp"
|
||||||
android:layout_height="50dp"
|
android:layout_height="50dp"
|
||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_alignParentTop="false"
|
android:layout_alignParentTop="false"
|
||||||
android:layout_centerVertical="true"
|
android:layout_centerVertical="true"
|
||||||
android:contentDescription="@string/icon"
|
android:contentDescription="@string/icon"
|
||||||
android:src="@drawable/ic_missing_icon" />
|
android:src="@drawable/ic_missing_icon" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/text_title"
|
android:id="@+id/text_title"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="60dp"
|
android:layout_marginStart="60dp"
|
||||||
android:layout_marginTop="5dp"
|
android:layout_marginTop="5dp"
|
||||||
android:textAppearance="?android:attr/textAppearanceListItem" />
|
android:textAppearance="?android:attr/textAppearanceListItem" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/text_subtitle"
|
android:id="@+id/text_subtitle"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@id/text_title"
|
android:layout_below="@id/text_title"
|
||||||
android:layout_alignStart="@id/text_title"
|
android:layout_alignStart="@id/text_title"
|
||||||
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
|
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
|
||||||
android:textColor="@android:color/tertiary_text_light" />
|
android:textColor="@android:color/tertiary_text_light" />
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
@ -1,29 +1,29 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context=".LogActivity">
|
tools:context=".LogActivity">
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
<androidx.appcompat.widget.Toolbar
|
||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="?attr/colorPrimary"
|
android:background="?attr/colorPrimary"
|
||||||
android:minHeight="?attr/actionBarSize"
|
android:minHeight="?attr/actionBarSize"
|
||||||
android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar"
|
android:popupTheme="@style/ThemeOverlay.MaterialComponents.Light"
|
||||||
android:popupTheme="@style/ThemeOverlay.MaterialComponents.Light"
|
android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"/>
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<ListView
|
<ListView
|
||||||
android:id="@+id/log_list"
|
android:id="@+id/log_list"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:transcriptMode="normal"
|
android:fastScrollEnabled="true"
|
||||||
android:fastScrollEnabled="true"
|
android:transcriptMode="normal"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/toolbar"/>
|
app:layout_constraintTop_toBottomOf="@+id/toolbar" />
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -1,26 +1,27 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="15dp"
|
|
||||||
android:background="?attr/selectableItemBackground">
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/text_title"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="5dp"
|
android:background="?attr/selectableItemBackground"
|
||||||
android:layout_marginTop="2dp"
|
android:orientation="vertical"
|
||||||
android:textSize="15sp"
|
android:padding="15dp">
|
||||||
android:textAppearance="?android:attr/textAppearanceListItem" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/text_subtitle"
|
android:id="@+id/text_title"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@id/text_title"
|
android:layout_marginStart="5dp"
|
||||||
android:layout_alignStart="@id/text_title"
|
android:layout_marginTop="2dp"
|
||||||
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
|
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||||
android:textSize="12sp"
|
android:textSize="15sp" />
|
||||||
android:textColor="@android:color/tertiary_text_light" />
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_subtitle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/text_title"
|
||||||
|
android:layout_alignStart="@id/text_title"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
|
||||||
|
android:textColor="@android:color/tertiary_text_light"
|
||||||
|
android:textSize="12sp" />
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
@ -1,39 +1,39 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context=".MainActivity">
|
tools:context=".MainActivity">
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
<androidx.appcompat.widget.Toolbar
|
||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="?attr/colorPrimary"
|
android:background="?attr/colorPrimary"
|
||||||
android:minHeight="?attr/actionBarSize"
|
android:minHeight="?attr/actionBarSize"
|
||||||
android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar"
|
android:popupTheme="@style/ThemeOverlay.MaterialComponents.Light"
|
||||||
android:popupTheme="@style/ThemeOverlay.MaterialComponents.Light"
|
android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"/>
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<ListView
|
<ListView
|
||||||
android:id="@+id/game_list"
|
android:id="@+id/game_list"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/toolbar" />
|
app:layout_constraintTop_toBottomOf="@+id/toolbar" />
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
android:id="@+id/log_fab"
|
android:id="@+id/log_fab"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
android:layout_marginBottom="16dp"
|
android:layout_marginBottom="16dp"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:srcCompat="@drawable/ic_log" />
|
app:srcCompat="@drawable/ic_log" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -1,18 +1,18 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:clickable="false"
|
|
||||||
android:padding="12dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/text_title"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textColor="@color/colorPrimary"
|
android:clickable="false"
|
||||||
android:textSize="13sp"
|
android:orientation="vertical"
|
||||||
android:layout_marginTop="2dp"
|
android:padding="12dp">
|
||||||
android:layout_marginStart="5dp"/>
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
android:textColor="@color/colorPrimary"
|
||||||
|
android:textSize="13sp" />
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
@ -1,22 +1,22 @@
|
|||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
|
||||||
android:id="@+id/toolbar"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="?attr/colorPrimary"
|
android:orientation="vertical">
|
||||||
android:minHeight="?attr/actionBarSize"
|
|
||||||
android:theme="@style/ThemeOverlay.AppCompat.Dark"
|
<androidx.appcompat.widget.Toolbar
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
android:id="@+id/toolbar"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
android:layout_width="match_parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/colorPrimary"
|
||||||
|
android:minHeight="?attr/actionBarSize"
|
||||||
|
android:theme="@style/ThemeOverlay.AppCompat.Dark"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/settings"
|
android:id="@+id/settings"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent" />
|
android:layout_height="match_parent" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
@ -1,20 +1,20 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_search_log"
|
android:id="@+id/action_search_log"
|
||||||
android:icon="@drawable/ic_search"
|
android:icon="@drawable/ic_search"
|
||||||
android:title="@string/search"
|
android:title="@string/search"
|
||||||
app:showAsAction="ifRoom|withText"
|
app:actionViewClass="androidx.appcompat.widget.SearchView"
|
||||||
app:actionViewClass="androidx.appcompat.widget.SearchView"/>
|
app:showAsAction="ifRoom|withText" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_share_log"
|
android:id="@+id/action_share_log"
|
||||||
android:icon="@drawable/ic_share"
|
android:icon="@drawable/ic_share"
|
||||||
android:title="@string/share"
|
android:title="@string/share"
|
||||||
app:showAsAction="ifRoom"/>
|
app:showAsAction="ifRoom" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_clear"
|
android:id="@+id/action_clear"
|
||||||
android:icon="@drawable/ic_clear"
|
android:icon="@drawable/ic_clear"
|
||||||
android:title="@string/clear"
|
android:title="@string/clear"
|
||||||
app:showAsAction="ifRoom"/>
|
app:showAsAction="ifRoom" />
|
||||||
</menu>
|
</menu>
|
@ -1,20 +1,20 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_search_main"
|
android:id="@+id/action_search_main"
|
||||||
android:icon="@drawable/ic_search"
|
android:icon="@drawable/ic_search"
|
||||||
android:title="@string/search"
|
android:title="@string/search"
|
||||||
app:showAsAction="ifRoom|withText"
|
app:actionViewClass="androidx.appcompat.widget.SearchView"
|
||||||
app:actionViewClass="androidx.appcompat.widget.SearchView"/>
|
app:showAsAction="ifRoom|withText" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_settings"
|
android:id="@+id/action_settings"
|
||||||
android:icon="@drawable/ic_settings"
|
android:icon="@drawable/ic_settings"
|
||||||
android:title="@string/settings"
|
android:title="@string/settings"
|
||||||
app:showAsAction="ifRoom"/>
|
app:showAsAction="ifRoom" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_refresh"
|
android:id="@+id/action_refresh"
|
||||||
android:icon="@drawable/ic_refresh"
|
android:icon="@drawable/ic_refresh"
|
||||||
android:title="@string/refresh"
|
android:title="@string/refresh"
|
||||||
app:showAsAction="ifRoom"/>
|
app:showAsAction="ifRoom" />
|
||||||
</menu>
|
</menu>
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@drawable/ic_launcher_background"/>
|
<background android:drawable="@drawable/ic_launcher_background" />
|
||||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
<foreground android:drawable="@mipmap/ic_launcher_foreground" />
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@drawable/ic_launcher_background"/>
|
<background android:drawable="@drawable/ic_launcher_background" />
|
||||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
<foreground android:drawable="@mipmap/ic_launcher_foreground" />
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
@ -6,6 +6,7 @@
|
|||||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||||
<item name="colorAccent">@color/colorAccent</item>
|
<item name="colorAccent">@color/colorAccent</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="ToolbarTheme">
|
<style name="ToolbarTheme">
|
||||||
<!-- Query Text Color -->
|
<!-- Query Text Color -->
|
||||||
<item name="android:textColorPrimary">@android:color/white</item>
|
<item name="android:textColorPrimary">@android:color/white</item>
|
||||||
|
@ -15,36 +15,36 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
android:key="category_search"
|
android:key="category_search"
|
||||||
android:title="@string/search">
|
android:title="@string/search">
|
||||||
<EditTextPreference
|
<EditTextPreference
|
||||||
android:defaultValue="/sdcard/"
|
android:defaultValue="/sdcard/"
|
||||||
app:key="search_location"
|
app:key="search_location"
|
||||||
app:title="@string/search_location"
|
app:title="@string/search_location"
|
||||||
app:useSimpleSummaryProvider="true" />
|
app:useSimpleSummaryProvider="true" />
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
android:key="category_log"
|
android:key="category_log"
|
||||||
android:title="@string/logging">
|
android:title="@string/logging">
|
||||||
<ListPreference
|
<ListPreference
|
||||||
android:defaultValue="2"
|
android:defaultValue="2"
|
||||||
android:entries="@array/log_level"
|
android:entries="@array/log_level"
|
||||||
android:entryValues="@array/log_level_val"
|
android:entryValues="@array/log_level_val"
|
||||||
app:key="log_level"
|
app:key="log_level"
|
||||||
app:title="@string/log_level"
|
app:title="@string/log_level"
|
||||||
app:useSimpleSummaryProvider="true" />
|
app:useSimpleSummaryProvider="true" />
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
android:key="category_localization"
|
android:key="category_localization"
|
||||||
android:title="@string/localization">
|
android:title="@string/localization">
|
||||||
<ListPreference
|
<ListPreference
|
||||||
android:defaultValue="sys"
|
android:defaultValue="sys"
|
||||||
android:entries="@array/language_names"
|
android:entries="@array/language_names"
|
||||||
android:entryValues="@array/language_values"
|
android:entryValues="@array/language_values"
|
||||||
app:key="localization_language"
|
app:key="localization_language"
|
||||||
app:title="@string/localization_language"
|
app:title="@string/localization_language"
|
||||||
app:useSimpleSummaryProvider="true" />
|
app:useSimpleSummaryProvider="true" />
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
</androidx.preference.PreferenceScreen>
|
</androidx.preference.PreferenceScreen>
|
||||||
|
Loading…
Reference in New Issue
Block a user