Files
dolphin/Source/Core/Core/IOS/IPC.cpp
Léo Lam c4136d0365 IOS/ES: Handle contexts properly
This changes the IOS code to handle ES contexts inside of ES, instead
of leaking out implementation details into the IPC request dispatcher.

The intent is to clarify what's shared between every single ES context,
and what is specific to an ES context. (Not much.) This should reduce
the number of static members in the ES class.

The other changes are there just because we now keep track of the
IPC FD inside of ES.

Future plans:

* After the WAD direct launch hack is dropped, the title context
  will be made a class member.

* Have proper function prototypes, instead of having every single one
  of them take ioctlv requests. This will allow reusing IOS code in
  other parts of the Dolphin codebase without having to construct
  ioctlv requests.
2017-04-28 20:20:51 +02:00

1042 lines
36 KiB
C++

// Copyright 2008 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
// This is the main Wii IPC file that handles all incoming IPC requests and directs them
// to the right function.
//
// IPC basics (IOS' usage):
// All IPC request handlers will write a return value to 0x04.
// Open: Device file descriptor or error code
// Close: IPC_SUCCESS
// Read: Bytes read
// Write: Bytes written
// Seek: Seek position
// Ioctl: Depends on the handler
// Ioctlv: Depends on the handler
// Replies may be sent immediately or asynchronously for ioctls and ioctlvs.
#include "Core/IOS/IPC.h"
#include <algorithm>
#include <array>
#include <cinttypes>
#include <deque>
#include <map>
#include <memory>
#include <mutex>
#include <string>
#include <utility>
#include "Common/Assert.h"
#include "Common/ChunkFile.h"
#include "Common/CommonTypes.h"
#include "Common/Logging/Log.h"
#include "Core/Boot/Boot_DOL.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/CoreTiming.h"
#include "Core/HW/Memmap.h"
#include "Core/HW/WII_IPC.h"
#include "Core/IOS/DI/DI.h"
#include "Core/IOS/Device.h"
#include "Core/IOS/DeviceStub.h"
#include "Core/IOS/ES/ES.h"
#include "Core/IOS/FS/FS.h"
#include "Core/IOS/FS/FileIO.h"
#include "Core/IOS/MIOS.h"
#include "Core/IOS/Network/IP/Top.h"
#include "Core/IOS/Network/KD/NetKDRequest.h"
#include "Core/IOS/Network/KD/NetKDTime.h"
#include "Core/IOS/Network/NCD/Manage.h"
#include "Core/IOS/Network/SSL.h"
#include "Core/IOS/Network/Socket.h"
#include "Core/IOS/Network/WD/Command.h"
#include "Core/IOS/SDIO/SDIOSlot0.h"
#include "Core/IOS/STM/STM.h"
#include "Core/IOS/USB/Bluetooth/BTEmu.h"
#include "Core/IOS/USB/Bluetooth/BTReal.h"
#include "Core/IOS/USB/OH0/OH0.h"
#include "Core/IOS/USB/OH0/OH0Device.h"
#include "Core/IOS/USB/USB_HID/HIDv4.h"
#include "Core/IOS/USB/USB_KBD.h"
#include "Core/IOS/USB/USB_VEN/VEN.h"
#include "Core/IOS/WFS/WFSI.h"
#include "Core/IOS/WFS/WFSSRV.h"
#include "Core/PowerPC/PowerPC.h"
#include "DiscIO/NANDContentLoader.h"
namespace IOS
{
namespace HLE
{
static std::map<u32, std::shared_ptr<Device::Device>> s_device_map;
static std::mutex s_device_map_mutex;
// STATE_TO_SAVE
constexpr u8 IPC_MAX_FDS = 0x18;
static std::shared_ptr<Device::Device> s_fdmap[IPC_MAX_FDS];
using IPCMsgQueue = std::deque<u32>;
static IPCMsgQueue s_request_queue; // ppc -> arm
static IPCMsgQueue s_reply_queue; // arm -> ppc
static IPCMsgQueue s_ack_queue; // arm -> ppc
static CoreTiming::EventType* s_event_enqueue;
static CoreTiming::EventType* s_event_sdio_notify;
static u64 s_last_reply_time;
static u64 s_active_title_id;
static u32 s_ppc_uid;
static u16 s_ppc_gid;
static constexpr u64 ENQUEUE_REQUEST_FLAG = 0x100000000ULL;
static constexpr u64 ENQUEUE_ACKNOWLEDGEMENT_FLAG = 0x200000000ULL;
enum class MemorySetupType
{
IOSReload,
Full,
};
struct IosMemoryValues
{
u16 ios_number;
u32 ios_version;
u32 ios_date;
u32 mem1_physical_size;
u32 mem1_simulated_size;
u32 mem1_end;
u32 mem1_arena_begin;
u32 mem1_arena_end;
u32 mem2_physical_size;
u32 mem2_simulated_size;
u32 mem2_end;
u32 mem2_arena_begin;
u32 mem2_arena_end;
u32 ipc_buffer_begin;
u32 ipc_buffer_end;
u32 hollywood_revision;
u32 ram_vendor;
u32 unknown_begin;
u32 unknown_end;
u32 sysmenu_sync;
};
constexpr u32 ADDR_MEM1_SIZE = 0x3100;
constexpr u32 ADDR_MEM1_SIM_SIZE = 0x3104;
constexpr u32 ADDR_MEM1_END = 0x3108;
constexpr u32 ADDR_MEM1_ARENA_BEGIN = 0x310c;
constexpr u32 ADDR_MEM1_ARENA_END = 0x3110;
constexpr u32 ADDR_PH1 = 0x3114;
constexpr u32 ADDR_MEM2_SIZE = 0x3118;
constexpr u32 ADDR_MEM2_SIM_SIZE = 0x311c;
constexpr u32 ADDR_MEM2_END = 0x3120;
constexpr u32 ADDR_MEM2_ARENA_BEGIN = 0x3124;
constexpr u32 ADDR_MEM2_ARENA_END = 0x3128;
constexpr u32 ADDR_PH2 = 0x312c;
constexpr u32 ADDR_IPC_BUFFER_BEGIN = 0x3130;
constexpr u32 ADDR_IPC_BUFFER_END = 0x3134;
constexpr u32 ADDR_HOLLYWOOD_REVISION = 0x3138;
constexpr u32 ADDR_PH3 = 0x313c;
constexpr u32 ADDR_IOS_VERSION = 0x3140;
constexpr u32 ADDR_IOS_DATE = 0x3144;
constexpr u32 ADDR_UNKNOWN_BEGIN = 0x3148;
constexpr u32 ADDR_UNKNOWN_END = 0x314c;
constexpr u32 ADDR_PH4 = 0x3150;
constexpr u32 ADDR_PH5 = 0x3154;
constexpr u32 ADDR_RAM_VENDOR = 0x3158;
constexpr u32 ADDR_BOOT_FLAG = 0x315c;
constexpr u32 ADDR_APPLOADER_FLAG = 0x315d;
constexpr u32 ADDR_DEVKIT_BOOT_PROGRAM_VERSION = 0x315e;
constexpr u32 ADDR_SYSMENU_SYNC = 0x3160;
constexpr u32 MEM1_SIZE = 0x01800000;
constexpr u32 MEM1_END = 0x81800000;
constexpr u32 MEM1_ARENA_BEGIN = 0x00000000;
constexpr u32 MEM1_ARENA_END = 0x81800000;
constexpr u32 MEM2_SIZE = 0x4000000;
constexpr u32 MEM2_ARENA_BEGIN = 0x90000800;
constexpr u32 HOLLYWOOD_REVISION = 0x00000011;
constexpr u32 PLACEHOLDER = 0xDEADBEEF;
constexpr u32 RAM_VENDOR = 0x0000FF01;
constexpr u32 RAM_VENDOR_MIOS = 0xCAFEBABE;
// These values were manually extracted from the relevant IOS binaries.
// The writes are usually contained in a single function that
// mostly writes raw literals to the relevant locations.
// e.g. IOS9, version 1034, content id 0x00000006, function at 0xffff6884
constexpr std::array<IosMemoryValues, 40> ios_memory_values = {
{{
9, 0x9040a, 0x030110, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93400000, MEM2_ARENA_BEGIN,
0x933E0000, 0x933E0000, 0x93400000, HOLLYWOOD_REVISION,
RAM_VENDOR, PLACEHOLDER, PLACEHOLDER, 0,
},
{
11, 0xb000a, 0x102506, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93400000, MEM2_ARENA_BEGIN,
0x933E0000, 0x933E0000, 0x93400000, HOLLYWOOD_REVISION,
RAM_VENDOR, PLACEHOLDER, PLACEHOLDER, 0,
},
{
12, 0xc020e, 0x030110, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93400000, MEM2_ARENA_BEGIN,
0x933E0000, 0x933E0000, 0x93400000, HOLLYWOOD_REVISION,
RAM_VENDOR, PLACEHOLDER, PLACEHOLDER, 0,
},
{
13, 0xd0408, 0x030110, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93400000, MEM2_ARENA_BEGIN,
0x933E0000, 0x933E0000, 0x93400000, HOLLYWOOD_REVISION,
RAM_VENDOR, PLACEHOLDER, PLACEHOLDER, 0,
},
{
14, 0xe0408, 0x030110, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93400000, MEM2_ARENA_BEGIN,
0x933E0000, 0x933E0000, 0x93400000, HOLLYWOOD_REVISION,
RAM_VENDOR, PLACEHOLDER, PLACEHOLDER, 0,
},
{
15, 0xf0408, 0x030110, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93400000, MEM2_ARENA_BEGIN,
0x933E0000, 0x933E0000, 0x93400000, HOLLYWOOD_REVISION,
RAM_VENDOR, PLACEHOLDER, PLACEHOLDER, 0,
},
{
17, 0x110408, 0x030110, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93400000, MEM2_ARENA_BEGIN,
0x933E0000, 0x933E0000, 0x93400000, HOLLYWOOD_REVISION,
RAM_VENDOR, PLACEHOLDER, PLACEHOLDER, 0,
},
{
20, 0x14000c, 0x102506, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93400000, MEM2_ARENA_BEGIN,
0x933E0000, 0x933E0000, 0x93400000, HOLLYWOOD_REVISION,
RAM_VENDOR, PLACEHOLDER, PLACEHOLDER, 0,
},
{
21, 0x15040f, 0x030110, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93400000, MEM2_ARENA_BEGIN,
0x933E0000, 0x933E0000, 0x93400000, HOLLYWOOD_REVISION,
RAM_VENDOR, PLACEHOLDER, PLACEHOLDER, 0,
},
{
22, 0x16050e, 0x030110, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93400000, MEM2_ARENA_BEGIN,
0x933E0000, 0x933E0000, 0x93400000, HOLLYWOOD_REVISION,
RAM_VENDOR, PLACEHOLDER, PLACEHOLDER, 0,
},
{
28, 0x1c070f, 0x030110, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93800000, MEM2_ARENA_BEGIN,
0x937E0000, 0x937E0000, 0x93800000, HOLLYWOOD_REVISION,
RAM_VENDOR, 0x93800000, 0x93820000, 0,
},
{
30, 0x1e0a10, 0x40308, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93600000, MEM2_ARENA_BEGIN,
0x935E0000, 0x935E0000, 0x93600000, HOLLYWOOD_REVISION,
RAM_VENDOR, 0x93600000, 0x93620000, 0,
},
{
31, 0x1f0e18, 0x030110, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93600000, MEM2_ARENA_BEGIN,
0x935E0000, 0x935E0000, 0x93600000, HOLLYWOOD_REVISION,
RAM_VENDOR, 0x93600000, 0x93620000, 0,
},
{
33, 0x210e18, 0x030110, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93600000, MEM2_ARENA_BEGIN,
0x935E0000, 0x935E0000, 0x93600000, HOLLYWOOD_REVISION,
RAM_VENDOR, 0x93600000, 0x93620000, 0,
},
{
34, 0x220e18, 0x030110, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93600000, MEM2_ARENA_BEGIN,
0x935E0000, 0x935E0000, 0x93600000, HOLLYWOOD_REVISION,
RAM_VENDOR, 0x93600000, 0x93620000, 0,
},
{
35, 0x230e18, 0x030110, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93600000, MEM2_ARENA_BEGIN,
0x935E0000, 0x935E0000, 0x93600000, HOLLYWOOD_REVISION,
RAM_VENDOR, 0x93600000, 0x93620000, 0,
},
{
36, 0x240e18, 0x030110, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93600000, MEM2_ARENA_BEGIN,
0x935E0000, 0x935E0000, 0x93600000, HOLLYWOOD_REVISION,
RAM_VENDOR, 0x93600000, 0x93620000, 0,
},
{
37, 0x25161f, 0x030110, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93600000, MEM2_ARENA_BEGIN,
0x935E0000, 0x935E0000, 0x93600000, HOLLYWOOD_REVISION,
RAM_VENDOR, 0x93600000, 0x93620000, 0,
},
{
38, 0x26101c, 0x030110, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93600000, MEM2_ARENA_BEGIN,
0x935E0000, 0x935E0000, 0x93600000, HOLLYWOOD_REVISION,
RAM_VENDOR, 0x93600000, 0x93620000, 0,
},
{
40, 0x280911, 0x022308, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93600000, MEM2_ARENA_BEGIN,
0x935E0000, 0x935E0000, 0x93600000, HOLLYWOOD_REVISION,
RAM_VENDOR, 0x93600000, 0x93620000, 0,
},
{
41, 0x290e17, 0x030110, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93600000, MEM2_ARENA_BEGIN,
0x935E0000, 0x935E0000, 0x93600000, HOLLYWOOD_REVISION,
RAM_VENDOR, 0x93600000, 0x93620000, 0,
},
{
43, 0x2b0e17, 0x030110, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93600000, MEM2_ARENA_BEGIN,
0x935E0000, 0x935E0000, 0x93600000, HOLLYWOOD_REVISION,
RAM_VENDOR, 0x93600000, 0x93620000, 0,
},
{
45, 0x2d0e17, 0x030110, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93600000, MEM2_ARENA_BEGIN,
0x935E0000, 0x935E0000, 0x93600000, HOLLYWOOD_REVISION,
RAM_VENDOR, 0x93600000, 0x93620000, 0,
},
{
46, 0x2e0e17, 0x030110, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93600000, MEM2_ARENA_BEGIN,
0x935E0000, 0x935E0000, 0x93600000, HOLLYWOOD_REVISION,
RAM_VENDOR, 0x93600000, 0x93620000, 0,
},
{
48, 0x30101c, 0x030110, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93600000, MEM2_ARENA_BEGIN,
0x935E0000, 0x935E0000, 0x93600000, HOLLYWOOD_REVISION,
RAM_VENDOR, 0x93600000, 0x93620000, 0,
},
{
50, 0x321319, 0x101008, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93600000, MEM2_ARENA_BEGIN,
0x935E0000, 0x935E0000, 0x93600000, HOLLYWOOD_REVISION,
RAM_VENDOR, 0x93600000, 0x93620000, 0,
},
{
51, 0x331219, 0x071108, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93600000, MEM2_ARENA_BEGIN,
0x935E0000, 0x935E0000, 0x93600000, HOLLYWOOD_REVISION,
RAM_VENDOR, 0x93600000, 0x93620000, 0,
},
{
52, 0x34161d, 0x101008, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93600000, MEM2_ARENA_BEGIN,
0x935E0000, 0x935E0000, 0x93600000, HOLLYWOOD_REVISION,
RAM_VENDOR, 0x93600000, 0x93620000, 0,
},
{
53, 0x35161f, 0x030110, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93600000, MEM2_ARENA_BEGIN,
0x935E0000, 0x935E0000, 0x93600000, HOLLYWOOD_REVISION,
RAM_VENDOR, 0x93600000, 0x93620000, 0,
},
{
55, 0x37161f, 0x030110, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93600000, MEM2_ARENA_BEGIN,
0x935E0000, 0x935E0000, 0x93600000, HOLLYWOOD_REVISION,
RAM_VENDOR, 0x93600000, 0x93620000, 0,
},
{
56, 0x38161e, 0x030110, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93600000, MEM2_ARENA_BEGIN,
0x935E0000, 0x935E0000, 0x93600000, HOLLYWOOD_REVISION,
RAM_VENDOR, 0x93600000, 0x93620000, 0,
},
{
57, 0x39171f, 0x030110, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93600000, MEM2_ARENA_BEGIN,
0x935E0000, 0x935E0000, 0x93600000, HOLLYWOOD_REVISION,
RAM_VENDOR, 0x93600000, 0x93620000, 0,
},
{
58, 0x3a1820, 0x030110, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93600000, MEM2_ARENA_BEGIN,
0x935E0000, 0x935E0000, 0x93600000, HOLLYWOOD_REVISION,
RAM_VENDOR, 0x93600000, 0x93620000, 0,
},
{
59, 0x3b1c21, 0x101811, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93600000, MEM2_ARENA_BEGIN,
0x935E0000, 0x935E0000, 0x93600000, HOLLYWOOD_REVISION,
RAM_VENDOR, 0x93600000, 0x93620000, 0,
},
{
60, 0x3c181e, 0x112408, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93600000, MEM2_ARENA_BEGIN,
0x935E0000, 0x935E0000, 0x93600000, HOLLYWOOD_REVISION,
RAM_VENDOR, 0x93600000, 0x93620000, 0,
},
{
61, 0x3d161e, 0x030110, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93600000, MEM2_ARENA_BEGIN,
0x935E0000, 0x935E0000, 0x93600000, HOLLYWOOD_REVISION,
RAM_VENDOR, 0x93600000, 0x93620000, 0,
},
{
62, 0x3e191e, 0x022712, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93600000, MEM2_ARENA_BEGIN,
0x935E0000, 0x935E0000, 0x93600000, HOLLYWOOD_REVISION,
RAM_VENDOR, 0x93600000, 0x93620000, 0,
},
{
70, 0x461a1f, 0x060309, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93600000, MEM2_ARENA_BEGIN,
0x935E0000, 0x935E0000, 0x93600000, HOLLYWOOD_REVISION,
RAM_VENDOR, 0x93600000, 0x93620000, 0,
},
{
80, 0x501b20, 0x030310, MEM1_SIZE,
MEM1_SIZE, MEM1_END, MEM1_ARENA_BEGIN, MEM1_ARENA_END,
MEM2_SIZE, MEM2_SIZE, 0x93600000, MEM2_ARENA_BEGIN,
0x935E0000, 0x935E0000, 0x93600000, HOLLYWOOD_REVISION,
RAM_VENDOR, 0x93600000, 0x93620000, 0,
},
{
257,
0x707,
0x82209,
MEM1_SIZE,
MEM1_SIZE,
MEM1_END,
MEM1_ARENA_BEGIN,
MEM1_ARENA_END,
MEM2_SIZE,
MEM2_SIZE,
0x93600000,
MEM2_ARENA_BEGIN,
0x935E0000,
0x935E0000,
0x93600000,
HOLLYWOOD_REVISION,
RAM_VENDOR_MIOS,
PLACEHOLDER,
PLACEHOLDER,
PLACEHOLDER,
}}};
static void EnqueueEvent(u64 userdata, s64 cycles_late = 0)
{
if (userdata & ENQUEUE_ACKNOWLEDGEMENT_FLAG)
{
s_ack_queue.push_back(static_cast<u32>(userdata));
}
else if (userdata & ENQUEUE_REQUEST_FLAG)
{
s_request_queue.push_back(static_cast<u32>(userdata));
}
else
{
s_reply_queue.push_back(static_cast<u32>(userdata));
}
Update();
}
static void SDIO_EventNotify_CPUThread(u64 userdata, s64 cycles_late)
{
auto device = static_cast<Device::SDIOSlot0*>(GetDeviceByName("/dev/sdio/slot0").get());
if (device)
device->EventNotify();
}
// The title ID is a u64 where the first 32 bits are used for the title type.
// For IOS title IDs, the type will always be 00000001 (system), and the lower 32 bits
// are used for the IOS major version -- which is what we want here.
u32 GetVersion()
{
return static_cast<u32>(s_active_title_id);
}
static bool SetupMemory(u64 ios_title_id, MemorySetupType setup_type)
{
auto target_imv = std::find_if(
ios_memory_values.begin(), ios_memory_values.end(),
[&](const IosMemoryValues& imv) { return imv.ios_number == (ios_title_id & 0xffff); });
if (target_imv == ios_memory_values.end())
{
ERROR_LOG(IOS, "Unknown IOS version: %016" PRIx64, ios_title_id);
return false;
}
if (setup_type == MemorySetupType::IOSReload)
{
Memory::Write_U32(target_imv->ios_version, ADDR_IOS_VERSION);
// These values are written by the IOS kernel as part of its boot process (for IOS28 and newer).
//
// This works in a slightly different way on a real console: older IOS versions (< IOS28) all
// have the same range (933E0000 - 93400000), thus they don't write it at boot and just inherit
// all values. However, the range has changed since IOS28. To make things work properly
// after a reload, newer IOSes always write the legacy range before loading an IOS kernel;
// the new IOS either updates the range (>= IOS28) or inherits it (< IOS28).
//
// We can skip this convoluted process and just write the correct range directly.
Memory::Write_U32(target_imv->mem2_physical_size, ADDR_MEM2_SIZE);
Memory::Write_U32(target_imv->mem2_simulated_size, ADDR_MEM2_SIM_SIZE);
Memory::Write_U32(target_imv->mem2_end, ADDR_MEM2_END);
Memory::Write_U32(target_imv->mem2_arena_begin, ADDR_MEM2_ARENA_BEGIN);
Memory::Write_U32(target_imv->mem2_arena_end, ADDR_MEM2_ARENA_END);
Memory::Write_U32(target_imv->ipc_buffer_begin, ADDR_IPC_BUFFER_BEGIN);
Memory::Write_U32(target_imv->ipc_buffer_end, ADDR_IPC_BUFFER_END);
Memory::Write_U32(target_imv->unknown_begin, ADDR_UNKNOWN_BEGIN);
Memory::Write_U32(target_imv->unknown_end, ADDR_UNKNOWN_END);
return true;
}
Memory::Write_U32(target_imv->mem1_physical_size, ADDR_MEM1_SIZE);
Memory::Write_U32(target_imv->mem1_simulated_size, ADDR_MEM1_SIM_SIZE);
Memory::Write_U32(target_imv->mem1_end, ADDR_MEM1_END);
Memory::Write_U32(target_imv->mem1_arena_begin, ADDR_MEM1_ARENA_BEGIN);
Memory::Write_U32(target_imv->mem1_arena_end, ADDR_MEM1_ARENA_END);
Memory::Write_U32(PLACEHOLDER, ADDR_PH1);
Memory::Write_U32(target_imv->mem2_physical_size, ADDR_MEM2_SIZE);
Memory::Write_U32(target_imv->mem2_simulated_size, ADDR_MEM2_SIM_SIZE);
Memory::Write_U32(target_imv->mem2_end, ADDR_MEM2_END);
Memory::Write_U32(target_imv->mem2_arena_begin, ADDR_MEM2_ARENA_BEGIN);
Memory::Write_U32(target_imv->mem2_arena_end, ADDR_MEM2_ARENA_END);
Memory::Write_U32(PLACEHOLDER, ADDR_PH2);
Memory::Write_U32(target_imv->ipc_buffer_begin, ADDR_IPC_BUFFER_BEGIN);
Memory::Write_U32(target_imv->ipc_buffer_end, ADDR_IPC_BUFFER_END);
Memory::Write_U32(target_imv->hollywood_revision, ADDR_HOLLYWOOD_REVISION);
Memory::Write_U32(PLACEHOLDER, ADDR_PH3);
Memory::Write_U32(target_imv->ios_version, ADDR_IOS_VERSION);
Memory::Write_U32(target_imv->ios_date, ADDR_IOS_DATE);
Memory::Write_U32(target_imv->unknown_begin, ADDR_UNKNOWN_BEGIN);
Memory::Write_U32(target_imv->unknown_end, ADDR_UNKNOWN_END);
Memory::Write_U32(PLACEHOLDER, ADDR_PH4);
Memory::Write_U32(PLACEHOLDER, ADDR_PH5);
Memory::Write_U32(target_imv->ram_vendor, ADDR_RAM_VENDOR);
Memory::Write_U8(0xDE, ADDR_BOOT_FLAG);
Memory::Write_U8(0xAD, ADDR_APPLOADER_FLAG);
Memory::Write_U16(0xBEEF, ADDR_DEVKIT_BOOT_PROGRAM_VERSION);
Memory::Write_U32(target_imv->sysmenu_sync, ADDR_SYSMENU_SYNC);
return true;
}
static u32 num_devices;
template <typename T>
std::shared_ptr<T> AddDevice(const char* device_name)
{
auto device = std::make_shared<T>(num_devices, device_name);
_assert_(device->GetDeviceType() == Device::Device::DeviceType::Static);
s_device_map[num_devices] = device;
num_devices++;
return device;
}
static void AddStaticDevices()
{
std::lock_guard<std::mutex> lock(s_device_map_mutex);
_assert_msg_(IOS, s_device_map.empty(), "Reinit called while already initialized");
num_devices = 0;
// Build hardware devices
if (!SConfig::GetInstance().m_bt_passthrough_enabled)
AddDevice<Device::BluetoothEmu>("/dev/usb/oh1/57e/305");
else
AddDevice<Device::BluetoothReal>("/dev/usb/oh1/57e/305");
AddDevice<Device::STMImmediate>("/dev/stm/immediate");
AddDevice<Device::STMEventHook>("/dev/stm/eventhook");
AddDevice<Device::FS>("/dev/fs");
AddDevice<Device::ES>("/dev/es");
AddDevice<Device::DI>("/dev/di");
AddDevice<Device::NetKDRequest>("/dev/net/kd/request");
AddDevice<Device::NetKDTime>("/dev/net/kd/time");
AddDevice<Device::NetNCDManage>("/dev/net/ncd/manage");
AddDevice<Device::NetWDCommand>("/dev/net/wd/command");
AddDevice<Device::NetIPTop>("/dev/net/ip/top");
AddDevice<Device::NetSSL>("/dev/net/ssl");
AddDevice<Device::USB_KBD>("/dev/usb/kbd");
AddDevice<Device::SDIOSlot0>("/dev/sdio/slot0");
AddDevice<Device::Stub>("/dev/sdio/slot1");
AddDevice<Device::USB_HIDv4>("/dev/usb/hid");
AddDevice<Device::OH0>("/dev/usb/oh0");
AddDevice<Device::Stub>("/dev/usb/oh1");
AddDevice<Device::USB_VEN>("/dev/usb/ven");
AddDevice<Device::WFSSRV>("/dev/usb/wfssrv");
AddDevice<Device::WFSI>("/dev/wfsi");
}
// IOS used by the latest System Menu (4.3).
constexpr u64 IOS80_TITLE_ID = 0x0000000100000050;
void Init()
{
s_event_enqueue = CoreTiming::RegisterEvent("IPCEvent", EnqueueEvent);
s_event_sdio_notify = CoreTiming::RegisterEvent("SDIO_EventNotify", SDIO_EventNotify_CPUThread);
// On a Wii, boot2 launches the system menu IOS, which then launches the system menu
// (which bootstraps the PPC). This means that after a normal boot process, the constants
// in the 0x3100 region will always have been set up.
// This is necessary because booting games from the game list skips a significant part
// of a Wii's boot process.
SetupMemory(IOS80_TITLE_ID, MemorySetupType::Full);
}
static void Reset()
{
CoreTiming::RemoveAllEvents(s_event_enqueue);
// Close all devices that were opened and delete their resources
for (auto& device : s_fdmap)
{
if (!device)
continue;
device->Close(0);
device.reset();
}
{
std::lock_guard<std::mutex> lock(s_device_map_mutex);
s_device_map.clear();
}
s_request_queue.clear();
s_reply_queue.clear();
s_last_reply_time = 0;
}
void Shutdown()
{
Reset();
}
constexpr u64 BC_TITLE_ID = 0x0000000100000100;
constexpr u64 MIOS_TITLE_ID = 0x0000000100000101;
// Similar to syscall 0x42 (ios_boot); this is used to change the current active IOS.
// IOS writes the new version to 0x3140 before restarting, but it does *not* poke any
// of the other constants to the memory.
bool Reload(const u64 ios_title_id)
{
// A real Wii goes through several steps before getting to MIOS.
//
// * The System Menu detects a GameCube disc and launches BC (1-100) instead of the game.
// * BC (similar to boot1) lowers the clock speed to the Flipper's and then launches boot2.
// * boot2 sees the lowered clock speed and launches MIOS (1-101) instead of the System Menu.
//
// Because we currently don't have boot1 and boot2, and BC is only ever used to launch MIOS
// (indirectly via boot2), we can just launch MIOS when BC is launched.
if (ios_title_id == BC_TITLE_ID)
{
NOTICE_LOG(IOS, "BC: Launching MIOS...");
return Reload(MIOS_TITLE_ID);
}
if (!SetupMemory(ios_title_id, MemorySetupType::IOSReload))
return false;
s_active_title_id = ios_title_id;
Reset();
if (ios_title_id == MIOS_TITLE_ID)
{
// MIOS is a special case. It does not have the same syscalls as regular IOSes
// and writes the magic values at a different time (here) in the boot process.
SetupMemory(ios_title_id, MemorySetupType::Full);
return MIOS::Load();
}
AddStaticDevices();
return true;
}
// Since we don't have actual processes, we keep track of only the PPC's UID/GID.
// These functions roughly correspond to syscalls 0x2b, 0x2c, 0x2d, 0x2e (though only for the PPC).
void SetUIDForPPC(u32 uid)
{
s_ppc_uid = uid;
}
u32 GetUIDForPPC()
{
return s_ppc_uid;
}
void SetGIDForPPC(u16 gid)
{
s_ppc_gid = gid;
}
u16 GetGIDForPPC()
{
return s_ppc_gid;
}
// This corresponds to syscall 0x41, which loads a binary from the NAND and bootstraps the PPC.
// Unlike 0x42, IOS will set up some constants in memory before booting the PPC.
bool BootstrapPPC(const DiscIO::CNANDContentLoader& content_loader)
{
if (!content_loader.IsValid())
return false;
const auto* content = content_loader.GetContentByIndex(content_loader.GetTMD().GetBootIndex());
if (!content)
return false;
const auto dol_loader = std::make_unique<CDolLoader>(content->m_Data->Get());
if (!dol_loader->IsValid())
return false;
if (!SetupMemory(s_active_title_id, MemorySetupType::Full))
return false;
dol_loader->Load();
// NAND titles start with address translation off at 0x3400 (via the PPC bootstub)
// The state of other CPU registers (like the BAT registers) doesn't matter much
// because the realmode code at 0x3400 initializes everything itself anyway.
MSR = 0;
PC = 0x3400;
return true;
}
void SDIO_EventNotify()
{
// TODO: Potential race condition: If IsRunning() becomes false after
// it's checked, an event may be scheduled after CoreTiming shuts down.
if (SConfig::GetInstance().bWii && Core::IsRunning())
CoreTiming::ScheduleEvent(0, s_event_sdio_notify, 0, CoreTiming::FromThread::NON_CPU);
}
static int GetFreeDeviceID()
{
for (u32 i = 0; i < IPC_MAX_FDS; i++)
{
if (s_fdmap[i] == nullptr)
{
return i;
}
}
return -1;
}
std::shared_ptr<Device::Device> GetDeviceByName(const std::string& device_name)
{
std::lock_guard<std::mutex> lock(s_device_map_mutex);
for (const auto& entry : s_device_map)
{
if (entry.second && entry.second->GetDeviceName() == device_name)
{
return entry.second;
}
}
return nullptr;
}
std::shared_ptr<Device::Device> AccessDeviceByID(u32 id)
{
std::lock_guard<std::mutex> lock(s_device_map_mutex);
if (s_device_map.find(id) != s_device_map.end())
{
return s_device_map[id];
}
return nullptr;
}
void DoState(PointerWrap& p)
{
p.Do(s_request_queue);
p.Do(s_reply_queue);
p.Do(s_last_reply_time);
p.Do(s_active_title_id);
p.Do(s_ppc_uid);
p.Do(s_ppc_gid);
if (s_active_title_id == MIOS_TITLE_ID)
return;
// We need to make sure all file handles are closed so IOS::HLE::Device::FS::DoState can
// successfully save or re-create /tmp
for (auto& descriptor : s_fdmap)
{
if (descriptor)
descriptor->PrepareForState(p.GetMode());
}
for (const auto& entry : s_device_map)
entry.second->DoState(p);
if (p.GetMode() == PointerWrap::MODE_READ)
{
for (u32 i = 0; i < IPC_MAX_FDS; i++)
{
u32 exists = 0;
p.Do(exists);
if (exists)
{
auto device_type = Device::Device::DeviceType::Static;
p.Do(device_type);
switch (device_type)
{
case Device::Device::DeviceType::Static:
{
u32 device_id = 0;
p.Do(device_id);
s_fdmap[i] = AccessDeviceByID(device_id);
break;
}
case Device::Device::DeviceType::FileIO:
s_fdmap[i] = std::make_shared<Device::FileIO>(i, "");
s_fdmap[i]->DoState(p);
break;
case Device::Device::DeviceType::OH0:
s_fdmap[i] = std::make_shared<Device::OH0Device>(i, "");
s_fdmap[i]->DoState(p);
break;
}
}
}
}
else
{
for (auto& descriptor : s_fdmap)
{
u32 exists = descriptor ? 1 : 0;
p.Do(exists);
if (exists)
{
auto device_type = descriptor->GetDeviceType();
p.Do(device_type);
if (device_type == Device::Device::DeviceType::Static)
{
u32 hwId = descriptor->GetDeviceID();
p.Do(hwId);
}
else
{
descriptor->DoState(p);
}
}
}
}
}
// Returns the FD for the newly opened device (on success) or an error code.
static s32 OpenDevice(OpenRequest& request)
{
const s32 new_fd = GetFreeDeviceID();
INFO_LOG(IOS, "Opening %s (mode %d, fd %d)", request.path.c_str(), request.flags, new_fd);
if (new_fd < 0 || new_fd >= IPC_MAX_FDS)
{
ERROR_LOG(IOS, "Couldn't get a free fd, too many open files");
return FS_EFDEXHAUSTED;
}
request.fd = new_fd;
std::shared_ptr<Device::Device> device;
if (request.path.find("/dev/usb/oh0/") == 0 && !GetDeviceByName(request.path))
{
device = std::make_shared<Device::OH0Device>(new_fd, request.path);
}
else if (request.path.find("/dev/") == 0)
{
device = GetDeviceByName(request.path);
}
else if (request.path.find('/') == 0)
{
device = std::make_shared<Device::FileIO>(new_fd, request.path);
}
if (!device)
{
ERROR_LOG(IOS, "Unknown device: %s", request.path.c_str());
return IPC_ENOENT;
}
const ReturnCode code = device->Open(request);
if (code < IPC_SUCCESS)
return code;
s_fdmap[new_fd] = device;
return new_fd;
}
static IPCCommandResult HandleCommand(const Request& request)
{
if (request.command == IPC_CMD_OPEN)
{
OpenRequest open_request{request.address};
const s32 new_fd = OpenDevice(open_request);
return Device::Device::GetDefaultReply(new_fd);
}
const auto device = (request.fd < IPC_MAX_FDS) ? s_fdmap[request.fd] : nullptr;
if (!device)
return Device::Device::GetDefaultReply(IPC_EINVAL);
switch (request.command)
{
case IPC_CMD_CLOSE:
s_fdmap[request.fd].reset();
return Device::Device::GetDefaultReply(device->Close(request.fd));
case IPC_CMD_READ:
return device->Read(ReadWriteRequest{request.address});
case IPC_CMD_WRITE:
return device->Write(ReadWriteRequest{request.address});
case IPC_CMD_SEEK:
return device->Seek(SeekRequest{request.address});
case IPC_CMD_IOCTL:
return device->IOCtl(IOCtlRequest{request.address});
case IPC_CMD_IOCTLV:
return device->IOCtlV(IOCtlVRequest{request.address});
default:
_assert_msg_(IOS, false, "Unexpected command: %x", request.command);
return Device::Device::GetDefaultReply(IPC_EINVAL);
}
}
void ExecuteCommand(const u32 address)
{
Request request{address};
IPCCommandResult result = HandleCommand(request);
// Ensure replies happen in order
const s64 ticks_until_last_reply = s_last_reply_time - CoreTiming::GetTicks();
if (ticks_until_last_reply > 0)
result.reply_delay_ticks += ticks_until_last_reply;
s_last_reply_time = CoreTiming::GetTicks() + result.reply_delay_ticks;
if (result.send_reply)
EnqueueReply(request, result.return_value, static_cast<int>(result.reply_delay_ticks));
}
// Happens AS SOON AS IPC gets a new pointer!
void EnqueueRequest(u32 address)
{
CoreTiming::ScheduleEvent(1000, s_event_enqueue, address | ENQUEUE_REQUEST_FLAG);
}
// Called to send a reply to an IOS syscall
void EnqueueReply(const Request& request, const s32 return_value, int cycles_in_future,
CoreTiming::FromThread from)
{
Memory::Write_U32(static_cast<u32>(return_value), request.address + 4);
// IOS writes back the command that was responded to in the FD field.
Memory::Write_U32(request.command, request.address + 8);
// IOS also overwrites the command type with the reply type.
Memory::Write_U32(IPC_REPLY, request.address);
CoreTiming::ScheduleEvent(cycles_in_future, s_event_enqueue, request.address, from);
}
void EnqueueCommandAcknowledgement(u32 address, int cycles_in_future)
{
CoreTiming::ScheduleEvent(cycles_in_future, s_event_enqueue,
address | ENQUEUE_ACKNOWLEDGEMENT_FLAG);
}
// This is called every IPC_HLE_PERIOD from SystemTimers.cpp
// Takes care of routing ipc <-> ipc HLE
void Update()
{
if (!IsReady())
return;
if (s_request_queue.size())
{
GenerateAck(s_request_queue.front());
DEBUG_LOG(IOS, "||-- Acknowledge IPC Request @ 0x%08x", s_request_queue.front());
u32 command = s_request_queue.front();
s_request_queue.pop_front();
ExecuteCommand(command);
return;
}
if (s_reply_queue.size())
{
GenerateReply(s_reply_queue.front());
DEBUG_LOG(IOS, "<<-- Reply to IPC Request @ 0x%08x", s_reply_queue.front());
s_reply_queue.pop_front();
return;
}
if (s_ack_queue.size())
{
GenerateAck(s_ack_queue.front());
WARN_LOG(IOS, "<<-- Double-ack to IPC Request @ 0x%08x", s_ack_queue.front());
s_ack_queue.pop_front();
return;
}
}
void UpdateDevices()
{
// Check if a hardware device must be updated
for (const auto& entry : s_device_map)
{
if (entry.second->IsOpened())
{
entry.second->Update();
}
}
}
void UpdateWantDeterminism(const bool new_want_determinism)
{
WiiSockMan::GetInstance().UpdateWantDeterminism(new_want_determinism);
for (const auto& device : s_device_map)
device.second->UpdateWantDeterminism(new_want_determinism);
}
} // namespace HLE
} // namespace IOS