2019-07-24 22:19:43 +02:00
# pragma once
# include <map>
2019-09-05 14:42:19 +02:00
# include <unordered_map>
# include <vector>
2019-07-24 22:19:43 +02:00
# include <fstream>
# include <syslog.h>
# include <string>
# include <sstream>
# include <memory>
# include <fmt/format.h>
2019-09-05 14:42:19 +02:00
# include <sys/mman.h>
# include <sys/ptrace.h>
# include <cstdint>
# include <stdexcept>
# include <string>
2019-07-24 22:19:43 +02:00
namespace lightSwitch {
2019-09-05 14:42:19 +02:00
// Global typedefs
2019-09-14 14:41:00 +02:00
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 ;
2019-09-05 14:42:19 +02:00
typedef std : : runtime_error exception ; //!< This is used as the default exception
2019-09-14 14:41:00 +02:00
typedef u32 handle_t ; //!< The type of an handle
2019-09-05 14:42:19 +02:00
namespace constant {
// Memory
2019-09-14 14:41:00 +02:00
constexpr u64 base_addr = 0x8000000 ; //!< The address space base
constexpr u64 map_addr = base_addr + 0x80000000 ; //!< The address of the map region
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
2019-09-05 14:42:19 +02:00
constexpr size_t def_stack_size = 0x1E8480 ; //!< The default amount of stack: 2 MB
2019-09-14 14:41:00 +02:00
constexpr size_t def_heap_size = PAGE_SIZE ; //!< The default amount of heap
2019-09-05 14:42:19 +02:00
constexpr size_t tls_slot_size = 0x200 ; //!< The size of a single TLS slot
2019-09-14 14:41:00 +02:00
constexpr u8 tls_slots = PAGE_SIZE / tls_slot_size ; //!< The amount of TLS slots in a single page
2019-09-05 14:42:19 +02:00
// Loader
2019-09-14 14:41:00 +02:00
constexpr u32 nro_magic = 0x304F524E ; //!< "NRO0" in reverse, this is written at the start of every NRO file
2019-09-05 14:42:19 +02:00
// NCE
2019-09-14 14:41:00 +02:00
constexpr u8 num_regs = 31 ; //!< The amount of registers that ARMv8 has
constexpr u16 svc_last = 0x7F ; //!< The index of the last SVC
constexpr u16 brk_rdy = 0xFF ; //!< This is reserved for our kernel's to know when a process/thread is ready
constexpr u32 tpidrro_el0 = 0x5E83 ; //!< ID of tpidrro_el0 in MRS
2019-09-05 14:42:19 +02:00
// IPC
constexpr size_t tls_ipc_size = 0x100 ; //!< The size of the IPC command buffer in a TLS slot
2019-09-14 14:41:00 +02:00
constexpr handle_t sm_handle = 0xD000 ; //!< sm:'s handle
constexpr u8 port_size = 0x8 ; //!< The size of a port name string
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
2019-09-05 14:42:19 +02:00
// Process
2019-09-14 14:41:00 +02:00
constexpr handle_t base_handle_index = sm_handle + 1 ; // The index of the base handle
constexpr u8 default_priority = 31 ; //!< The default priority of a process
2019-09-05 14:42:19 +02:00
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
2019-09-14 14:41:00 +02:00
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"
}
2019-09-05 14:42:19 +02:00
} ;
namespace instr {
/**
2019-09-14 14:41:00 +02:00
* 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.
2019-09-05 14:42:19 +02:00
*/
struct brk {
/**
* Creates a BRK instruction with a specific immediate value , used for generating BRK opcodes
* @ param val The immediate value of the instruction
*/
2019-09-14 14:41:00 +02:00
brk ( u16 val ) {
2019-09-05 14:42:19 +02:00
start = 0x0 ; // First 5 bits of an BRK instruction are 0
value = val ;
2019-09-14 14:41:00 +02:00
end = 0x6A1 ; // Last 11 bits of an BRK instruction stored as u16
2019-09-05 14:42:19 +02:00
}
/**
* @ return If the opcode represents a valid BRK instruction
*/
bool verify ( ) {
return ( start = = 0x0 & & end = = 0x6A1 ) ;
}
2019-09-14 14:41:00 +02:00
u8 start : 5 ;
u32 value : 16 ;
u16 end : 11 ;
2019-09-05 14:42:19 +02:00
} ;
2019-09-14 14:41:00 +02:00
static_assert ( sizeof ( brk ) = = sizeof ( u32 ) ) ;
2019-09-05 14:42:19 +02:00
/**
2019-09-14 14:41:00 +02:00
* A bit - field struct that encapsulates a SVC instruction . See https : //developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/svc-supervisor-call.
2019-09-05 14:42:19 +02:00
*/
struct svc {
/**
* @ return If the opcode represents a valid SVC instruction
*/
bool verify ( ) {
return ( start = = 0x1 & & end = = 0x6A0 ) ;
}
2019-09-14 14:41:00 +02:00
u8 start : 5 ;
u32 value : 16 ;
u16 end : 11 ;
2019-09-05 14:42:19 +02:00
} ;
2019-09-14 14:41:00 +02:00
static_assert ( sizeof ( svc ) = = sizeof ( u32 ) ) ;
2019-09-05 14:42:19 +02:00
/**
2019-09-14 14:41:00 +02:00
* 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.
2019-09-05 14:42:19 +02:00
*/
struct mrs {
/**
* @ return If the opcode represents a valid MRS instruction
*/
bool verify ( ) {
return ( end = = 0xD53 ) ;
}
2019-09-14 14:41:00 +02:00
u8 dst_reg : 5 ;
u32 src_reg : 15 ;
u16 end : 12 ;
2019-09-05 14:42:19 +02:00
} ;
2019-09-14 14:41:00 +02:00
static_assert ( sizeof ( mrs ) = = sizeof ( u32 ) ) ;
2019-09-05 14:42:19 +02:00
} ;
/**
* Read about ARMv8 registers here : https : //developer.arm.com/docs/100878/latest/registers
*/
2019-09-14 14:41:00 +02:00
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 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 class sreg { sp , pc , pstate } ;
2019-09-05 14:42:19 +02:00
/**
* The Settings class is used to access the parameters set in the Java component of the application
*/
2019-07-24 22:19:43 +02:00
class Settings {
2019-09-14 14:41:00 +02:00
private :
2019-07-24 22:19:43 +02:00
struct KeyCompare {
bool operator ( ) ( char const * a , char const * b ) const {
return std : : strcmp ( a , b ) < 0 ;
}
2019-09-05 14:42:19 +02:00
} ; //!< This is a comparision operator between strings, implemented to store strings in a std::map
2019-07-24 22:19:43 +02:00
2019-09-05 14:42:19 +02:00
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
2019-07-24 22:19:43 +02:00
2019-09-14 14:41:00 +02:00
public :
2019-09-05 14:42:19 +02:00
/**
* @ param pref_xml The path to the preference XML file
*/
2019-09-14 14:41:00 +02:00
Settings ( std : : string & pref_xml ) ;
2019-07-24 22:19:43 +02:00
2019-09-05 14:42:19 +02:00
/**
* @ param key The key of the setting
* @ return The string value of the setting
*/
2019-07-24 22:19:43 +02:00
char * GetString ( char * key ) ;
2019-09-05 14:42:19 +02:00
/**
* @ param key The key of the setting
* @ return The boolean value of the setting
*/
2019-07-24 22:19:43 +02:00
bool GetBool ( char * key ) ;
2019-09-05 14:42:19 +02:00
/**
* Writes all settings keys and values to syslog . This function is for development purposes .
*/
2019-07-24 22:19:43 +02:00
void List ( ) ;
} ;
2019-09-05 14:42:19 +02:00
/**
* The Logger class is to generate a log of the program
*/
2019-07-24 22:19:43 +02:00
class Logger {
2019-09-14 14:41:00 +02:00
private :
2019-09-05 14:42:19 +02:00
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
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
2019-07-24 22:19:43 +02:00
2019-09-14 14:41:00 +02:00
public :
enum LogLevel { ERROR , WARN , INFO , DEBUG } ; //!< The level of a particular log
2019-07-24 22:19:43 +02:00
2019-09-05 14:42:19 +02:00
/**
* @ param log_path The path to the log file
*/
Logger ( const std : : string & log_path ) ;
2019-07-24 22:19:43 +02:00
2019-09-05 14:42:19 +02:00
/**
* Writes " Logging ended " to as a header
*/
2019-07-24 22:19:43 +02:00
~ Logger ( ) ;
2019-09-05 14:42:19 +02:00
/**
* Writes a header , should only be used for emulation starting and ending
* @ param str The value to be written
*/
void WriteHeader ( const std : : string & str ) ;
2019-07-24 22:19:43 +02:00
2019-09-05 14:42:19 +02:00
/**
* Write a log to the log file
* @ param level The level of the log
* @ param str The value to be written
*/
void Write ( const LogLevel level , const std : : string & str ) ;
2019-07-24 22:19:43 +02:00
2019-09-05 14:42:19 +02:00
/**
* Write a log to the log file with libfmt formatting
* @ param level The level of the log
* @ param format_str The value to be written , with libfmt formatting
* @ param args The arguments based on format_str
*/
2019-07-24 22:19:43 +02:00
template < typename S , typename . . . Args >
2019-09-05 14:42:19 +02:00
void Write ( Logger : : LogLevel level , const S & format_str , Args & & . . . args ) {
# ifdef NDEBUG
if ( level = = DEBUG ) return ;
# endif
Write ( level , fmt : : format ( format_str , args . . . ) ) ;
2019-07-24 22:19:43 +02:00
}
} ;
2019-09-05 14:42:19 +02:00
// Predeclare some classes here as we use them in device_state
class NCE ;
namespace kernel {
namespace type {
class KProcess ;
2019-09-14 14:41:00 +02:00
2019-09-05 14:42:19 +02:00
class KThread ;
}
class OS ;
}
/**
* This struct is used to hold the state of a device
*/
2019-07-24 22:19:43 +02:00
struct device_state {
2019-09-05 14:42:19 +02:00
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
2019-09-14 14:41:00 +02:00
std : : shared_ptr < kernel : : type : : KProcess > & this_process ;
std : : shared_ptr < kernel : : type : : KThread > & this_thread ;
2019-09-05 14:42:19 +02:00
std : : shared_ptr < NCE > nce ;
2019-07-24 22:19:43 +02:00
std : : shared_ptr < Settings > settings ;
std : : shared_ptr < Logger > logger ;
} ;
2019-09-05 14:42:19 +02:00
}