2024-03-07 22:05:16 +01:00
# include "ns.hpp"
# include "os.hpp"
# include <loader/gamecard.hpp>
# include <processes/pxi_fs.hpp> // TODO: Get rid of this dependency..
# include <platform/file_formats/ncch.hpp>
# include "../platform/fs.hpp"
# include "../platform/ns.hpp"
# include "../platform/sm.hpp"
# include "../platform/file_formats/bcfnt.hpp"
# include "processes/pxi.hpp"
# include <framework/exceptions.hpp>
# include <boost/algorithm/cxx11/any_of.hpp>
# include <boost/endian/arithmetic.hpp>
# include <boost/range/adaptor/reversed.hpp>
# include <boost/range/adaptor/sliced.hpp>
# include <boost/range/algorithm/copy.hpp>
# include <boost/range/algorithm/fill.hpp>
# include <boost/scope_exit.hpp>
# include <range/v3/algorithm/equal.hpp>
# include <range/v3/algorithm/search.hpp>
# include <bitset>
# include <fstream>
# include <functional>
# include <sstream>
# include <iomanip>
namespace FS = Platform : : FS ;
namespace SM = Platform : : SM ;
namespace HLE {
namespace OS {
using Platform : : NS : : AppId ;
using Platform : : NS : : AppletAttr ;
using Platform : : NS : : AppletCommand ;
using Platform : : NS : : AppletPos ;
using Platform : : PXI : : PM : : ProgramInfo ;
template < typename RangeIn >
static uint32_t GetDecompressedLZSSDataSize ( const RangeIn & compressed_reverse ) {
// Reverse the input buffer
auto compressed = compressed_reverse | boost : : adaptors : : reversed ;
// Get difference between compressed and uncompressed size from the last word in the input buffer
uint32_t size_delta = compressed [ 3 ] | ( static_cast < uint32_t > ( compressed [ 2 ] ) < < 8 )
| ( static_cast < uint32_t > ( compressed [ 1 ] ) < < 16 ) | ( static_cast < uint32_t > ( compressed [ 0 ] ) < < 24 ) ;
return boost : : size ( compressed ) + size_delta ;
}
template < typename RangeIn , typename RangeOut >
static void DecompressLZSSData ( const RangeIn & compressed_reverse , const RangeOut & decompressed_reverse ) {
// Sanity checks
if ( boost : : size ( compressed_reverse ) < 8 )
std : : runtime_error ( " Invalid input: Smaller than 8 bytes " ) ;
if ( boost : : size ( decompressed_reverse ) < boost : : size ( compressed_reverse ) )
throw std : : runtime_error ( " Decompressed buffer is smaller than compressed one! " ) ;
// Copy the raw compressed buffer into the output buffer
boost : : fill ( decompressed_reverse , 0 ) ;
boost : : copy ( compressed_reverse , boost : : begin ( decompressed_reverse ) ) ;
// Reverse the input buffer
auto compressed = compressed_reverse | boost : : adaptors : : reversed ;
const uint32_t begin = compressed [ 4 ] ;
const uint32_t end = compressed [ 7 ] | ( compressed [ 6 ] < < 8 ) | ( compressed [ 5 ] < < 16 ) ;
if ( end > boost : : size ( compressed ) )
throw std : : runtime_error ( " Invalid size given " ) ;
auto working_set = compressed | boost : : adaptors : : sliced ( begin , end ) ; // "begin" and "end" inclusive
auto input_it = std : : begin ( working_set ) ;
// TODO: Working with the reversed decompressed buffer would be more readable, but I couldn't really get it working...
// auto decompressed = decompressed_reverse | boost::adaptors::reversed;
// auto output_it = boost::begin(decompressed);
auto output_it = boost : : end ( decompressed_reverse ) ;
for ( ; ; ) {
auto input_bytes_left = std : : distance ( input_it , boost : : end ( working_set ) ) ;
if ( input_bytes_left < = 1 )
break ;
const auto compression_mask = * input_it + + ;
for ( unsigned i = 0 ; i < 8 ; + + i ) {
input_bytes_left = std : : distance ( input_it , boost : : end ( working_set ) ) ;
auto output_bytes_left = std : : distance ( boost : : begin ( decompressed_reverse ) , output_it ) ;
if ( compression_mask & ( 0x80 > > i ) ) {
// compressed section
// TODO: Figure out if this could actually be a valid input
if ( input_bytes_left < 2 ) {
// Make sure to terminate the loop if we haven't already reached the end
input_it = boost : : end ( working_set ) ;
throw std : : runtime_error ( " Unexpected end of file " ) ;
}
auto offset = 2 + ( ( ( input_it [ 0 ] & 0xF ) < < 8 ) | input_it [ 1 ] ) ;
auto size = 3 + ( input_it [ 0 ] > > 4 ) ;
input_it + = 2 ;
if ( output_bytes_left < size )
throw std : : runtime_error ( " No output bytes left " ) ;
// NOTE: These ranges may overlap, hence using <algorithm> may yield undefined behavior.
// TODO: Double-check the guarantees given by std::copy/copy_backward to see whether we may use it here
auto end = output_it + offset - size ;
for ( auto temp_it = output_it + offset ; temp_it ! = end ; temp_it - - )
* - - output_it = * temp_it ;
} else {
// uncompressed byte
// NOTE: This is not an error; there is no explicit "EOF" indicator
if ( input_bytes_left < 1 )
break ;
if ( output_bytes_left < 1 )
throw std : : runtime_error ( " No output bytes left " ) ;
* - - output_it = * input_it + + ;
}
}
}
}
HandleTable : : Entry < Process > LoadProcessFromFile ( FakeThread & source ,
bool from_firm ,
const FileFormat : : ExHeader & exheader ,
std : : unique_ptr < HLE : : PXI : : FS : : File > file , bool is_exefs ) {
HLE : : PXI : : FS : : FileContext file_context { * source . GetLogger ( ) } ;
if ( ! is_exefs ) {
// Read ExeFS
uint8_t code [ 8 ] = { ' . ' , ' c ' , ' o ' , ' d ' , ' e ' } ;
auto input_file = PXI : : FS : : NCCHOpenExeFSSection ( * source . GetLogger ( ) , file_context ,
source . GetParentProcess ( ) . interpreter_setup . keydb ,
std : : move ( file ) , 1 , std : : basic_string_view < uint8_t > ( code , sizeof ( code ) ) ) ;
2024-03-23 17:05:55 +01:00
if ( std : : get < 0 > ( input_file - > OpenReadOnly ( file_context ) ) ! = RESULT_OK ) {
2024-03-07 22:05:16 +01:00
// TODO: Better error message
throw std : : runtime_error ( " Could not launch title from emulated NAND. " ) ;
}
file = std : : move ( input_file ) ;
}
auto & input_file = file ;
// Load process data into CodeSet based on contents of the ExeFs ".code" section.
// The actual program binary may be compressed, so decompress it along the way.
HandleTable : : Entry < CodeSet > codeset ;
source . GetLogger ( ) - > info ( " {}: {} " , __FUNCTION__ , __LINE__ ) ;
const uint32_t page_size = 0x1000 ;
uint32_t total_size_aligned = ( exheader . section_text . size_pages + exheader . section_ro . size_pages + exheader . section_data . size_pages ) * page_size ;
// TODO: Check if we still need the following line. It used to be necessary for loading SM, but I never figured out why!
// auto code_buffer = parent_process.AllocateBuffer(decompressed_size + /*0x300*/0);
auto & parent_process = source . GetParentProcess ( ) ;
auto code_buffer = parent_process . AllocateBuffer ( total_size_aligned ) ;
auto [ result , code_size ] = input_file - > GetSize ( file_context ) ;
if ( exheader . flags . compress_exefs_code ( ) ( ) ) {
auto compressed_code_buffer = parent_process . AllocateBuffer ( code_size ) ;
uint32_t bytes_read ;
std : : tie ( result , bytes_read ) = input_file - > Read ( file_context , 0 , code_size , HLE : : PXI : : FS : : FileBufferInHostMemory ( compressed_code_buffer . second , code_size ) ) ;
if ( result ! = RESULT_OK | | bytes_read ! = code_size ) {
throw std : : runtime_error ( " Failed to read code section from ExeFS " ) ;
}
// The last word in the compressed stream denotes the difference between compressed and decompressed buffer size
auto compressed_buffer_range = boost : : iterator_range < uint8_t * > ( compressed_code_buffer . second , compressed_code_buffer . second + code_size ) ;
uint32_t decompressed_size = GetDecompressedLZSSDataSize ( compressed_buffer_range ) ;
if ( decompressed_size > total_size_aligned )
throw std : : runtime_error ( " Decompressed size exceeds virtual memory size. Corrupt ROM? " ) ;
// TODO: Handle exceptions thrown by this function!
DecompressLZSSData ( compressed_buffer_range ,
boost : : iterator_range < uint8_t * > ( code_buffer . second , code_buffer . second + decompressed_size ) ) ;
parent_process . FreeBuffer ( compressed_code_buffer . first ) ;
} else {
if ( code_size > total_size_aligned ) {
throw std : : runtime_error ( " Code section size exceeds virtual memory size. Corrupt ROM? " ) ;
}
uint32_t bytes_read ;
std : : tie ( result , bytes_read ) = input_file - > Read ( file_context , 0 , code_size , HLE : : PXI : : FS : : FileBufferInHostMemory ( code_buffer . second , code_size ) ) ;
if ( result ! = RESULT_OK | | bytes_read ! = code_size ) {
throw std : : runtime_error ( " Failed to read code section from ExeFS " ) ;
}
}
auto kernel_flags = Meta : : invoke ( [ & ] ( ) {
for ( auto cap : exheader . aci . arm11_kernel_capabilities ) {
auto flags = FileFormat : : ExHeader : : ARM11KernelCapabilityDescriptor : : KernelFlags { cap . storage } ;
if ( flags . id_field ( ) = = flags . id_field_ref ( ) ) {
return flags ;
}
}
throw Mikage : : Exceptions : : Invalid ( " ExHeader did not specify kernel flags " ) ;
} ) ;
uint32_t code_buffer2 ;
std : : tie ( result , code_buffer2 ) = source . CallSVC ( & OS : : SVCControlMemory , 0 , 0 , total_size_aligned , 3 /* COMMIT */ | ( kernel_flags . memory_type ( ) < < 8 ) , 3 /* RW */ ) ;
if ( result ! = RESULT_OK ) {
throw std : : runtime_error ( " Failed to allocate memory for program " ) ;
}
for ( uint32_t offset = 0 ; offset < total_size_aligned ; + + offset ) {
source . WriteMemory ( code_buffer2 + offset , * ( code_buffer . second + offset ) ) ;
}
parent_process . FreeBuffer ( code_buffer . first ) ;
// NOTE: The decompressed buffer need not be aligned to page size, hence the distinction between size_bytes and size_pages is critical!
VAddr text_vaddr = code_buffer2 ;
VAddr ro_vaddr ;
VAddr data_vaddr ;
VAddr data_vaddr_end ;
if ( from_firm ) {
// Apparently, only modules loaded from FIRM take the true size in bytes (TODOTEST)
ro_vaddr = text_vaddr + exheader . section_text . size_bytes ;
data_vaddr = ro_vaddr + exheader . section_ro . size_bytes ;
data_vaddr_end = data_vaddr + exheader . section_data . size_bytes ;
} else {
// Modules loaded "normally" align each source section to pages.
ro_vaddr = text_vaddr + exheader . section_text . size_pages * page_size ;
data_vaddr = ro_vaddr + exheader . section_ro . size_pages * page_size ;
data_vaddr_end = data_vaddr + exheader . section_data . size_pages * page_size ;
}
if ( data_vaddr_end - text_vaddr > total_size_aligned )
throw std : : runtime_error ( " Text size exceeds virtual memory size. Corrupt ROM? " ) ;
// TODO: Chances are the "size_bytes" fields are actually page sizes!
CodeSetInfo codesetinfo { } ;
codesetinfo . text_start = exheader . section_text . address ;
codesetinfo . text_pages = ( exheader . section_text . size_bytes + page_size - 1 ) > > 12 ;
codesetinfo . text_size = exheader . section_text . size_pages ;
codesetinfo . ro_start = exheader . section_ro . address ;
codesetinfo . ro_pages = ( exheader . section_ro . size_bytes + page_size - 1 ) > > 12 ;
codesetinfo . ro_size = exheader . section_ro . size_pages ;
codesetinfo . data_start = exheader . section_data . address ;
// NOTE: The data size in the exheader is exclusive of the bss region, while the kernel expects an inclusive size.
// Hence, we just add the two together and round the result up to the page size.
// TODO: Should data_pages be inclusive or exclusive of bss?
codesetinfo . data_pages = ( exheader . section_data . size_bytes + page_size - 1 ) > > 12 ;
codesetinfo . data_size = exheader . section_data . size_pages + ( exheader . bss_size + page_size - 1 ) / page_size ;
memcpy ( codesetinfo . app_name . data ( ) , exheader . application_title . data ( ) , sizeof ( exheader . application_title ) ) ;
std : : tie ( result , codeset ) = source . CallSVC ( & OS : : SVCCreateCodeSet , codesetinfo , text_vaddr , ro_vaddr , data_vaddr ) ;
if ( result ! = RESULT_OK )
throw std : : runtime_error ( " LaunchTitle failed to SVCCreateCodeSet " ) ;
// Create the actual process
HandleTable : : Entry < Process > process ;
// TODO: Avoid cast...
std : : tie ( result , process ) = source . CallSVC ( & OS : : SVCCreateProcess , codeset . first , ( HLE : : OS : : OS : : KernelCapability * ) exheader . aci . arm11_kernel_capabilities . data ( ) , std : : size ( exheader . aci . arm11_kernel_capabilities ) ) ;
if ( result ! = RESULT_OK )
throw std : : runtime_error ( " LaunchTitle failed to SVCCreateProcess " ) ;
// Clean up
codeset . second . reset ( ) ;
source . CallSVC ( & OS : : SVCCloseHandle , codeset . first ) ;
return process ;
}
// TODO: Replace LaunchTitleInternal with (LaunchTitleFromFile + this)
OS : : ResultAnd < ProcessId > LaunchTitleInternal3 ( FakeThread & source , const FileFormat : : ExHeader & exheader , HandleTable : : Entry < Process > & process , uint8_t media_type ) {
auto [ result , process_id ] = source . CallSVC ( & OS : : SVCGetProcessId , * process . second ) ;
if ( result ! = RESULT_OK )
throw std : : runtime_error ( " LaunchTitle failed to SVCGetProcessId " ) ;
HandleTable : : Entry < ClientSession > srv_session = { HANDLE_INVALID , nullptr } ;
bool needs_services = true ;
if ( needs_services ) {
std : : tie ( result , srv_session ) = source . CallSVC ( & OS : : SVCConnectToPort , " srv: " ) ;
if ( result ! = RESULT_OK )
throw std : : runtime_error ( " LaunchTitle failed to SVCConnectToPort " ) ;
IPC : : SendIPCRequest < SM : : SRV : : RegisterClient > ( source , srv_session . first , IPC : : EmptyValue { } ) ;
}
// Register the created process to srv:pm
// TODO: Enable this code...
// TODO: Now that we fixed SM HLE, WE REALLY NEED TO DO THIS PROPERLY INSTEAD. I.e. have loader do this, and have loader register these via pm
auto not_null = [ ] ( auto letter ) { return letter ! = 0 ; } ;
auto nonempty_entry = [ & ] ( const auto & service ) { return boost : : algorithm : : any_of ( service , not_null ) ; } ;
// bool needs_services = boost::algorithm::any_of(exheader.service_access_list, nonempty_entry);
if ( false & & needs_services ) {
// Register process through "srv:pm"
// TODO: srv:pm is a service for firmwares higher than 7.0.0
// Handle srvpm_handle = IPC::SendIPCRequest<SM::SRV::GetServiceHandle>(source, srv_session.first,
// SM::PortName("srv:pm"), 0);
HandleTable : : Entry < ClientSession > srvpm_session ;
// TODO: On system version 7.0.0 and up, srv:pm is a service!
std : : tie ( result , srvpm_session ) = source . CallSVC ( & OS : : SVCConnectToPort , " srv:pm " ) ;
if ( result ! = RESULT_OK ) {
throw std : : runtime_error ( " LaunchTitle failed to connect to srv:pm " ) ;
}
auto & parent_process = source . GetParentProcess ( ) ;
// srvpm RegisterProcess
source . WriteTLS ( 0x80 , IPC : : CommandHeader : : Make ( 0x403 , 2 , 2 ) . raw ) ;
source . WriteTLS ( 0x84 , process_id ) ;
auto service_acl_entry_size = sizeof ( exheader . aci . service_access_list [ 0 ] ) ;
auto service_acl_size = ( sizeof ( exheader . aci . service_access_list ) + service_acl_entry_size - 1 ) / service_acl_entry_size * service_acl_entry_size ;
auto service_acl_addr = parent_process . AllocateStaticBuffer ( service_acl_size ) ;
auto service_acl_numentries = service_acl_size / service_acl_entry_size ;
for ( auto off = 0 ; off < sizeof ( exheader . aci . service_access_list ) ; + + off ) {
auto entry = off / service_acl_entry_size ;
auto byte = off % service_acl_entry_size ;
source . WriteMemory ( service_acl_addr + off , exheader . aci . service_access_list [ entry ] [ byte ] ) ;
}
source . WriteTLS ( 0x88 , service_acl_numentries ) ;
source . WriteTLS ( 0x8c , IPC : : TranslationDescriptor : : MakeStaticBuffer ( 0 , service_acl_size ) . raw ) ;
source . WriteTLS ( 0x90 , service_acl_addr ) ;
std : : tie ( result ) = source . CallSVC ( & OS : : SVCSendSyncRequest , srvpm_session . first ) ;
if ( result ! = RESULT_OK & & source . ReadTLS ( 0x84 ) ! = RESULT_OK )
throw std : : runtime_error ( " LaunchTitle failed to RegisterProcess to srv:pm " ) ;
parent_process . FreeStaticBuffer ( service_acl_addr ) ;
source . CallSVC ( & OS : : SVCCloseHandle , srvpm_session . first ) ;
}
FS : : ProgramInfo program_info { } ;
program_info . program_id = exheader . aci . program_id ;
program_info . media_type = media_type ;
// Connect to fs:REG and register the new process
// NOTE: Currently, we do this for all non-FIRM processes. There probably
// is an exheader flag that determines when to do this, though!
{
Handle fsreg_handle = IPC : : SendIPCRequest < SM : : SRV : : GetServiceHandle > ( source , srv_session . first ,
SM : : PortName ( " fs:REG " ) , 0 ) ;
source . GetLogger ( ) - > info ( " Registering to fs:REG with program id {:#018x} " , program_info . program_id ) ;
// TODO: This program handle is supposed to be retrieved from
// PxiPM::RegisterProgram, which returns the Process9-internal
// handle for the registered application. Since we don't emulate
// PxiPM currently, we just dummy-initialize this program handle
// with an incorrect (but unique) value for now.
Platform : : PXI : : PM : : ProgramHandle program_handle = { process_id } ;
IPC : : SendIPCRequest < FS : : Reg : : Register > ( source , fsreg_handle ,
process_id , program_handle ,
program_info , FS : : StorageInfo { } ) ;
source . CallSVC ( & OS : : SVCCloseHandle , fsreg_handle ) ;
}
source . CallSVC ( & OS : : SVCCloseHandle , srv_session . first ) ;
source . GetLogger ( ) - > info ( " {} {}EXHEADER STACK SIZE: {:#x} " , ThreadPrinter { source } , __LINE__ , exheader . stack_size ) ;
// Run main thread
OS : : StartupInfo startup { } ;
startup . stack_size = exheader . stack_size ;
startup . priority = exheader . aci . flags . priority ( ) ;
source . GetLogger ( ) - > info ( " {}about to start title main thread " , ThreadPrinter { source } ) ;
std : : tie ( result ) = source . CallSVC ( & OS : : SVCRun , process . first , startup ) ;
// Clean up
source . CallSVC ( & OS : : SVCCloseHandle , std : : move ( process ) . first ) ;
return std : : make_tuple ( RESULT_OK , process_id ) ;
}
OS : : ResultAnd < ProcessId > LaunchTitleInternal ( FakeThread & source , bool from_firm , uint64_t title_id , uint32_t flags /* (currently unused) */ ) {
// TODO: This is fairly ad-hoc currently, i.e. all logic in here is
// just done so that we can get any system processes up and running
// at all for now.
// Normally, this would go through filesystem, loader, and other
// services instead.
// Read ExHeader
ProgramInfo info { title_id , ! title_id ? uint8_t { 2 } : uint8_t { 0 } } ;
auto exheader = HLE : : PXI : : GetExtendedHeader ( source , info ) ;
// Read ExeFS
uint8_t code [ 8 ] = { ' . ' , ' c ' , ' o ' , ' d ' , ' e ' } ;
auto input_file = PXI : : FS : : OpenNCCHSubFile ( source , info , 0 , 1 , std : : basic_string_view < uint8_t > ( code , sizeof ( code ) ) , source . GetOS ( ) . setup . gamecard . get ( ) ) ;
HLE : : PXI : : FS : : FileContext file_context { * source . GetLogger ( ) } ;
2024-03-23 17:05:55 +01:00
if ( std : : get < 0 > ( input_file - > OpenReadOnly ( file_context ) ) ! = RESULT_OK ) {
2024-03-07 22:05:16 +01:00
throw std : : runtime_error ( fmt : : format ( " Tried to launch non-existing title {:#x} from emulated NAND. \n \n Please dump the title from your 3DS and install it manually to this path: \n {} " ,
title_id , " filename " /* TODO */ ) ) ;
}
OS : : Result result ;
auto process = LoadProcessFromFile ( source , from_firm , exheader , std : : move ( input_file ) , true ) ;
auto & parent_process = source . GetParentProcess ( ) ;
ProcessId process_id ;
std : : tie ( result , process_id ) = source . CallSVC ( & OS : : SVCGetProcessId , * process . second ) ;
if ( result ! = RESULT_OK )
throw std : : runtime_error ( " LaunchTitle failed to SVCGetProcessId " ) ;
HandleTable : : Entry < ClientSession > srv_session = { HANDLE_INVALID , nullptr } ;
bool needs_services = ! from_firm ;
if ( needs_services ) {
std : : tie ( result , srv_session ) = source . CallSVC ( & OS : : SVCConnectToPort , " srv: " ) ;
if ( result ! = RESULT_OK )
throw std : : runtime_error ( " LaunchTitle failed to SVCConnectToPort " ) ;
IPC : : SendIPCRequest < SM : : SRV : : RegisterClient > ( source , srv_session . first , IPC : : EmptyValue { } ) ;
}
// Register the created process to srv:pm
// TODO: Enable this code...
// TODO: Now that we fixed SM HLE, WE REALLY NEED TO DO THIS PROPERLY INSTEAD. I.e. have loader do this, and have loader register these via pm
auto not_null = [ ] ( auto letter ) { return letter ! = 0 ; } ;
auto nonempty_entry = [ & ] ( const auto & service ) { return boost : : algorithm : : any_of ( service , not_null ) ; } ;
// bool needs_services = boost::algorithm::any_of(exheader.service_access_list, nonempty_entry);
if ( false & & needs_services ) {
// Register process through "srv:pm"
// TODO: srv:pm is a service for firmwares higher than 7.0.0
// Handle srvpm_handle = IPC::SendIPCRequest<SM::SRV::GetServiceHandle>(source, srv_session.first,
// SM::PortName("srv:pm"), 0);
HandleTable : : Entry < ClientSession > srvpm_session ;
// TODO: On system version 7.0.0 and up, srv:pm is a service!
std : : tie ( result , srvpm_session ) = source . CallSVC ( & OS : : SVCConnectToPort , " srv:pm " ) ;
if ( result ! = RESULT_OK ) {
throw std : : runtime_error ( " LaunchTitle failed to connect to srv:pm " ) ;
}
// srvpm RegisterProcess
source . WriteTLS ( 0x80 , IPC : : CommandHeader : : Make ( 0x403 , 2 , 2 ) . raw ) ;
source . WriteTLS ( 0x84 , process_id ) ;
auto service_acl_entry_size = sizeof ( exheader . aci . service_access_list [ 0 ] ) ;
auto service_acl_size = ( sizeof ( exheader . aci . service_access_list ) + service_acl_entry_size - 1 ) / service_acl_entry_size * service_acl_entry_size ;
auto service_acl_addr = parent_process . AllocateStaticBuffer ( service_acl_size ) ;
auto service_acl_numentries = service_acl_size / service_acl_entry_size ;
for ( auto off = 0 ; off < sizeof ( exheader . aci . service_access_list ) ; + + off ) {
auto entry = off / service_acl_entry_size ;
auto byte = off % service_acl_entry_size ;
source . WriteMemory ( service_acl_addr + off , exheader . aci . service_access_list [ entry ] [ byte ] ) ;
}
source . WriteTLS ( 0x88 , service_acl_numentries ) ;
source . WriteTLS ( 0x8c , IPC : : TranslationDescriptor : : MakeStaticBuffer ( 0 , service_acl_size ) . raw ) ;
source . WriteTLS ( 0x90 , service_acl_addr ) ;
std : : tie ( result ) = source . CallSVC ( & OS : : SVCSendSyncRequest , srvpm_session . first ) ;
if ( result ! = RESULT_OK & & source . ReadTLS ( 0x84 ) ! = RESULT_OK )
throw std : : runtime_error ( " LaunchTitle failed to RegisterProcess to srv:pm " ) ;
parent_process . FreeStaticBuffer ( service_acl_addr ) ;
source . CallSVC ( & OS : : SVCCloseHandle , srvpm_session . first ) ;
}
FS : : ProgramInfo program_info { } ;
program_info . program_id = exheader . aci . program_id ;
program_info . media_type = ( title_id = = 0 ) ? 2 : 0 ; // NAND or game card
// TODO: SD titles may be launched through this function too, and supposedly the title id indicates whether a title is from NAND or SD...
// Connect to fs:REG and register the new process
// NOTE: Currently, we do this for all processes other than the FS module
// itself. There probably is an exheader flag that determines when to
// do this, though!
// NOTE: Disabled for now, reflecting the recent changes in the boot
// process, due to which actual process launching will be handled in
// low-level emulated system modules anyway. TODO: NOT ANYMORE!
if ( ! from_firm ) {
Handle fsreg_handle = IPC : : SendIPCRequest < SM : : SRV : : GetServiceHandle > ( source , srv_session . first ,
SM : : PortName ( " fs:REG " ) , 0 ) ;
source . GetLogger ( ) - > info ( " Registering to fs:REG with program id {:#018x} " , program_info . program_id ) ;
// TODO: This program handle is supposed to be retrieved from
// PxiPM::RegisterProgram, which returns the Process9-internal
// handle for the registered application. Since we don't emulate
// PxiPM currently, we just dummy-initialize this program handle
// with an incorrect (but unique) value for now.
Platform : : PXI : : PM : : ProgramHandle program_handle = { process_id } ;
IPC : : SendIPCRequest < FS : : Reg : : Register > ( source , fsreg_handle ,
process_id , program_handle ,
program_info , FS : : StorageInfo { } ) ;
source . CallSVC ( & OS : : SVCCloseHandle , fsreg_handle ) ;
}
if ( ! from_firm )
source . CallSVC ( & OS : : SVCCloseHandle , srv_session . first ) ;
source . GetLogger ( ) - > info ( " {} {}EXHEADER STACK SIZE: {:#x} " , ThreadPrinter { source } , __LINE__ , exheader . stack_size ) ;
// Run main thread
OS : : StartupInfo startup { } ;
startup . stack_size = exheader . stack_size ;
startup . priority = exheader . aci . flags . priority ( ) ;
source . GetLogger ( ) - > info ( " {}about to start title main thread " , ThreadPrinter { source } ) ;
std : : tie ( result ) = source . CallSVC ( & OS : : SVCRun , process . first , startup ) ;
// Clean up
source . CallSVC ( & OS : : SVCCloseHandle , std : : move ( process ) . first ) ;
return std : : make_tuple ( RESULT_OK , process_id ) ;
}
} // namespace OS
} // namespace HLE