From 13b17a584802157b1ee410da4b603d54ac838960 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 7 Feb 2021 16:29:38 -0800 Subject: [PATCH 001/280] usb: add ds client api --- .../libstratosphere/include/stratosphere.hpp | 1 + .../include/stratosphere/usb.hpp | 21 + .../stratosphere/usb/ds/usb_i_ds_endpoint.hpp | 33 + .../usb/ds/usb_i_ds_interface.hpp | 41 + .../stratosphere/usb/ds/usb_i_ds_service.hpp | 44 + .../include/stratosphere/usb/usb_device.hpp | 145 ++++ .../stratosphere/usb/usb_device_types.hpp | 60 ++ .../include/stratosphere/usb/usb_limits.hpp | 34 + .../include/stratosphere/usb/usb_types.hpp | 214 +++++ .../source/usb/impl/usb_util.hpp | 45 + .../libstratosphere/source/usb/usb_device.cpp | 803 ++++++++++++++++++ .../source/usb/usb_remote_ds_endpoint.cpp | 61 ++ .../source/usb/usb_remote_ds_endpoint.hpp | 37 + .../source/usb/usb_remote_ds_interface.cpp | 139 +++ .../source/usb/usb_remote_ds_interface.hpp | 48 ++ .../source/usb/usb_remote_ds_root_service.cpp | 33 + .../source/usb/usb_remote_ds_root_service.hpp | 36 + .../source/usb/usb_remote_ds_service.cpp | 114 +++ .../source/usb/usb_remote_ds_service.hpp | 46 + .../libvapours/include/vapours/results.hpp | 1 + .../include/vapours/results/usb_results.hpp | 37 + 21 files changed, 1993 insertions(+) create mode 100644 libraries/libstratosphere/include/stratosphere/usb.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/usb/ds/usb_i_ds_endpoint.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/usb/ds/usb_i_ds_interface.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/usb/ds/usb_i_ds_service.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/usb/usb_device.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/usb/usb_device_types.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/usb/usb_limits.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/usb/usb_types.hpp create mode 100644 libraries/libstratosphere/source/usb/impl/usb_util.hpp create mode 100644 libraries/libstratosphere/source/usb/usb_device.cpp create mode 100644 libraries/libstratosphere/source/usb/usb_remote_ds_endpoint.cpp create mode 100644 libraries/libstratosphere/source/usb/usb_remote_ds_endpoint.hpp create mode 100644 libraries/libstratosphere/source/usb/usb_remote_ds_interface.cpp create mode 100644 libraries/libstratosphere/source/usb/usb_remote_ds_interface.hpp create mode 100644 libraries/libstratosphere/source/usb/usb_remote_ds_root_service.cpp create mode 100644 libraries/libstratosphere/source/usb/usb_remote_ds_root_service.hpp create mode 100644 libraries/libstratosphere/source/usb/usb_remote_ds_service.cpp create mode 100644 libraries/libstratosphere/source/usb/usb_remote_ds_service.hpp create mode 100644 libraries/libvapours/include/vapours/results/usb_results.hpp diff --git a/libraries/libstratosphere/include/stratosphere.hpp b/libraries/libstratosphere/include/stratosphere.hpp index c6bbe862b..f9c66eb31 100644 --- a/libraries/libstratosphere/include/stratosphere.hpp +++ b/libraries/libstratosphere/include/stratosphere.hpp @@ -79,6 +79,7 @@ #include #include #include +#include #include /* Include FS last. */ diff --git a/libraries/libstratosphere/include/stratosphere/usb.hpp b/libraries/libstratosphere/include/stratosphere/usb.hpp new file mode 100644 index 000000000..8939f64b5 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/usb.hpp @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include +#include +#include +#include diff --git a/libraries/libstratosphere/include/stratosphere/usb/ds/usb_i_ds_endpoint.hpp b/libraries/libstratosphere/include/stratosphere/usb/ds/usb_i_ds_endpoint.hpp new file mode 100644 index 000000000..f4c72a0a5 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/usb/ds/usb_i_ds_endpoint.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include + +#define AMS_USB_I_DS_ENDPOINT_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 0, Result, PostBufferAsync, (sf::Out out_urb_id, u64 address, u32 size), (out_urb_id, address, size)) \ + AMS_SF_METHOD_INFO(C, H, 1, Result, Cancel, (), ()) \ + AMS_SF_METHOD_INFO(C, H, 2, Result, GetCompletionEvent, (sf::OutCopyHandle out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 3, Result, GetUrbReport, (sf::Out out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 4, Result, Stall, (), ()) \ + AMS_SF_METHOD_INFO(C, H, 5, Result, SetZlt, (bool zlt), (zlt)) + +/* TODO: Deprecated interface? */ + +AMS_SF_DEFINE_INTERFACE(ams::usb::ds, IDsEndpoint, AMS_USB_I_DS_ENDPOINT_INTERFACE_INFO) + diff --git a/libraries/libstratosphere/include/stratosphere/usb/ds/usb_i_ds_interface.hpp b/libraries/libstratosphere/include/stratosphere/usb/ds/usb_i_ds_interface.hpp new file mode 100644 index 000000000..ceb30c184 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/usb/ds/usb_i_ds_interface.hpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include +#include + +#define AMS_USB_I_DS_INTERFACE_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 0, Result, RegisterEndpoint, (u8 endpoint_address, sf::Out> out), (endpoint_address, out)) \ + AMS_SF_METHOD_INFO(C, H, 1, Result, GetSetupEvent, (sf::OutCopyHandle out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 2, Result, GetSetupPacket, (const sf::OutBuffer &out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 3, Result, CtrlInAsync, (sf::Out out_urb_id, u64 address, u32 size), (out_urb_id, address, size)) \ + AMS_SF_METHOD_INFO(C, H, 4, Result, CtrlOutAsync, (sf::Out out_urb_id, u64 address, u32 size), (out_urb_id, address, size)) \ + AMS_SF_METHOD_INFO(C, H, 5, Result, GetCtrlInCompletionEvent, (sf::OutCopyHandle out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 6, Result, GetCtrlInUrbReport, (sf::Out out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 7, Result, GetCtrlOutCompletionEvent, (sf::OutCopyHandle out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 8, Result, GetCtrlOutUrbReport, (sf::Out out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 9, Result, CtrlStall, (), ()) \ + AMS_SF_METHOD_INFO(C, H, 10, Result, AppendConfigurationData, (u8 bInterfaceNumber, usb::UsbDeviceSpeed device_speed, const sf::InBuffer &data), (bInterfaceNumber, device_speed, data)) \ + AMS_SF_METHOD_INFO(C, H, 65000, Result, Enable, (), ()) \ + AMS_SF_METHOD_INFO(C, H, 65001, Result, Disable, (), ()) + +/* TODO: Deprecated interface? */ + +AMS_SF_DEFINE_INTERFACE(ams::usb::ds, IDsInterface, AMS_USB_I_DS_INTERFACE_INTERFACE_INFO) + diff --git a/libraries/libstratosphere/include/stratosphere/usb/ds/usb_i_ds_service.hpp b/libraries/libstratosphere/include/stratosphere/usb/ds/usb_i_ds_service.hpp new file mode 100644 index 000000000..32ec155b9 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/usb/ds/usb_i_ds_service.hpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include +#include + +#define AMS_USB_I_DS_SERVICE_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 0, Result, Bind, (usb::ComplexId complex_id, sf::CopyHandle process_h), (complex_id, process_h)) \ + AMS_SF_METHOD_INFO(C, H, 1, Result, RegisterInterface, (sf::Out> out, u8 bInterfaceNumber), (out, bInterfaceNumber)) \ + AMS_SF_METHOD_INFO(C, H, 2, Result, GetStateChangeEvent, (sf::OutCopyHandle out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 3, Result, GetState, (sf::Out out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 4, Result, ClearDeviceData, (), ()) \ + AMS_SF_METHOD_INFO(C, H, 5, Result, AddUsbStringDescriptor, (sf::Out out, const sf::InBuffer &desc), (out, desc)) \ + AMS_SF_METHOD_INFO(C, H, 6, Result, DeleteUsbStringDescriptor, (u8 index), (index)) \ + AMS_SF_METHOD_INFO(C, H, 7, Result, SetUsbDeviceDescriptor, (const sf::InBuffer &desc, usb::UsbDeviceSpeed speed), (desc, speed)) \ + AMS_SF_METHOD_INFO(C, H, 8, Result, SetBinaryObjectStore, (const sf::InBuffer &bos), (bos)) \ + AMS_SF_METHOD_INFO(C, H, 9, Result, Enable, (), ()) \ + AMS_SF_METHOD_INFO(C, H, 10, Result, Disable, (), ()) + +/* TODO: Deprecated interface? */ + +AMS_SF_DEFINE_INTERFACE(ams::usb::ds, IDsService, AMS_USB_I_DS_SERVICE_INTERFACE_INFO) + +#define AMS_USB_I_DS_ROOT_SERVICE_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 0, Result, GetService, (sf::Out> out), (out)) + +AMS_SF_DEFINE_INTERFACE(ams::usb::ds, IDsRootService, AMS_USB_I_DS_ROOT_SERVICE_INTERFACE_INFO) + diff --git a/libraries/libstratosphere/include/stratosphere/usb/usb_device.hpp b/libraries/libstratosphere/include/stratosphere/usb/usb_device.hpp new file mode 100644 index 000000000..0549eb4e1 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/usb/usb_device.hpp @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace ams::usb { + + class DsInterface; + class DsEndpoint; + + class DsClient { + friend class DsInterface; + friend class DsEndpoint; + private: + /* NOTE: Nintendo uses a UnitHeap here on newer firmware versions. */ + /* For now, we'll use an ExpHeap and do it the old way. */ + sf::ExpHeapAllocator m_allocator{}; + u8 m_heap_buffer[32_KB]; + lmem::HeapHandle m_heap_handle{}; + sf::SharedPointer m_root_service{}; + sf::SharedPointer m_ds_service{}; + bool m_is_initialized{false}; + std::atomic m_reference_count{0}; + os::SystemEventType m_state_change_event{}; + DsInterface *m_interfaces[DsLimitMaxInterfacesPerConfigurationCount]{}; + bool m_is_enabled{false}; + public: + DsClient() = default; + ~DsClient() { /* ... */ } + public: + Result Initialize(ComplexId complex_id); + Result Finalize(); + + bool IsInitialized(); + + Result EnableDevice(); + Result DisableDevice(); + + os::SystemEventType *GetStateChangeEvent(); + Result GetState(UsbState *out); + + Result ClearDeviceData(); + + Result AddUsbStringDescriptor(u8 *out_index, UsbStringDescriptor *desc); + Result DeleteUsbStringDescriptor(u8 index); + + Result SetUsbDeviceDescriptor(UsbDeviceDescriptor *desc, UsbDeviceSpeed speed); + + Result SetBinaryObjectStore(u8 *data, int size); + private: + Result AddInterface(DsInterface *intf, sf::SharedPointer *out_srv, uint8_t bInterfaceNumber); + Result DeleteInterface(uint8_t bInterfaceNumber); + }; + + class DsInterface { + friend class DsEndpoint; + private: + DsClient *m_client; + sf::SharedPointer m_interface; + bool m_is_initialized; + std::atomic m_reference_count; + os::SystemEventType m_setup_event; + os::SystemEventType m_ctrl_in_completion_event; + os::SystemEventType m_ctrl_out_completion_event; + UrbReport m_report; + u8 m_interface_num; + DsEndpoint *m_endpoints[UsbLimitMaxEndpointsCount]; + public: + DsInterface() : m_client(nullptr), m_is_initialized(false), m_reference_count(0) { /* ... */ } + ~DsInterface() { /* ... */ } + public: + Result Initialize(DsClient *client, u8 bInterfaceNumber); + Result Finalize(); + + Result AppendConfigurationData(UsbDeviceSpeed speed, void *data, u32 size); + + bool IsInitialized(); + + os::SystemEventType *GetSetupEvent(); + Result GetSetupPacket(UsbCtrlRequest *out); + + Result Enable(); + Result Disable(); + + Result CtrlRead(u32 *out_transferred, void *dst, u32 size); + Result CtrlWrite(u32 *out_transferred, void *dst, u32 size); + Result CtrlDone(); + Result CtrlStall(); + private: + Result AddEndpoint(DsEndpoint *ep, u8 bEndpointAddress, sf::SharedPointer *out); + Result DeleteEndpoint(u8 bEndpointAddress); + + Result CtrlIn(u32 *out_transferred, void *dst, u32 size); + Result CtrlOut(u32 *out_transferred, void *dst, u32 size); + }; + + class DsEndpoint { + private: + bool m_is_initialized; + bool m_is_new_format; + std::atomic m_reference_count; + DsInterface *m_interface; + sf::SharedPointer m_endpoint; + u8 m_address; + os::SystemEventType m_completion_event; + os::SystemEventType m_unknown_event; + public: + DsEndpoint() : m_is_initialized(false), m_is_new_format(false), m_reference_count(0) { /* ... */ } + public: + Result Initialize(DsInterface *interface, u8 bEndpointAddress); + Result Finalize(); + + bool IsInitialized(); + + Result PostBuffer(u32 *out_transferred, void *buf, u32 size); + Result PostBufferAsync(u32 *out_urb_id, void *buf, u32 size); + + os::SystemEventType *GetCompletionEvent(); + + Result GetUrbReport(UrbReport *out); + + Result Cancel(); + + Result SetZeroLengthTransfer(bool zlt); + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/usb/usb_device_types.hpp b/libraries/libstratosphere/include/stratosphere/usb/usb_device_types.hpp new file mode 100644 index 000000000..26c67f595 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/usb/usb_device_types.hpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include + +namespace ams::usb { + + constexpr inline u8 InterfaceNumberAuto = DsLimitMaxInterfacesPerConfigurationCount; + constexpr inline u8 EndpointAddressAutoIn = UsbEndpointAddressMask_DirDevicetoHost; + constexpr inline u8 EndpointAddressAutoOut = UsbEndpointAddressMask_DirHostToDevice; + + enum UrbStatus { + UrbStatus_Invalid = 0, + UrbStatus_Pending = 1, + UrbStatus_Running = 2, + UrbStatus_Finished = 3, + UrbStatus_Cancelled = 4, + UrbStatus_Failed = 5, + }; + + struct UrbReport { + struct Report { + u32 id; + u32 requested_size; + u32 transferred_size; + UrbStatus status; + } reports[DsLimitRingSize]; + u32 count; + }; + + enum DsString { + DsString_Max = 0x20, + }; + + struct DsVidPidBcd { + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; + + char manufacturer[DsString_Max]; + char product[DsString_Max]; + char serial_number[DsString_Max]; + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/usb/usb_limits.hpp b/libraries/libstratosphere/include/stratosphere/usb/usb_limits.hpp new file mode 100644 index 000000000..f1b0ac8fb --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/usb/usb_limits.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::usb { + + constexpr inline int HwLimitDmaBufferAlignmentSize = dd::DeviceAddressSpaceMemoryRegionAlignment; + constexpr inline int HwLimitDataCacheLineSize = 0x40; + constexpr inline int HwLimitMaxPortCount = 0x4; + + constexpr inline int UsbLimitMaxEndpointsCount = 0x20; + constexpr inline int UsbLimitMaxEndpointPairCount = 0x10; + + constexpr inline int DsLimitMaxConfigurationsPerDeviceCount = 1; + constexpr inline int DsLimitMaxInterfacesPerConfigurationCount = 4; + constexpr inline int DsLimitMaxNameSize = 0x40; + constexpr inline int DsLimitRingSize = 8; + +} diff --git a/libraries/libstratosphere/include/stratosphere/usb/usb_types.hpp b/libraries/libstratosphere/include/stratosphere/usb/usb_types.hpp new file mode 100644 index 000000000..26ccecad6 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/usb/usb_types.hpp @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::usb { + + constexpr ALWAYS_INLINE bool IsDmaAligned(u64 address) { + return util::IsAligned(address, static_cast(HwLimitDmaBufferAlignmentSize)); + } + + enum ComplexId { + ComplexId_Tegra21x = 2, + }; + + enum UsbDescriptorType { + UsbDescriptorType_Device = 1, + UsbDescriptorType_Config = 2, + UsbDescriptorType_String = 3, + UsbDescriptorType_Interface = 4, + UsbDescriptorType_Endpoint = 5, + UsbDescriptorType_DeviceQualifier = 6, + UsbDescriptorType_OtherSpeedConfig = 7, + UsbDescriptorType_InterfacePower = 8, + UsbDescriptorType_Otg = 9, + UsbDescriptorType_Debug = 10, + UsbDescriptorType_InterfaceAssociation = 11, + UsbDescriptorType_Bos = 15, + UsbDescriptorType_DeviceCapability = 16, + + UsbDescriptorType_Hid = 33, + UsbDescriptorType_Report = 34, + UsbDescriptorType_Physical = 35, + + UsbDescriptorType_Hub = 41, + + UsbDescriptorType_EndpointCompanion = 48, + UsbDescriptorType_IsocEndpointCompanion = 49, + }; + + struct UsbDescriptorHeader { + uint8_t bLength; + uint8_t bDescriptorType; + } PACKED; + + struct UsbInterfaceDescriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bInterfaceNumber; + uint8_t bAlternateSetting; + uint8_t bNumEndpoints; + uint8_t bInterfaceClass; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; + uint8_t iInterface; + } PACKED; + + struct UsbEndpointDescriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bEndpointAddress; + uint8_t bmAttributes; + uint16_t wMaxPacketSize; + uint8_t bInterval; + } PACKED; + + struct UsbDeviceDescriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bcdUSB; + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + uint8_t bMaxPacketSize0; + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; + uint8_t iManufacturer; + uint8_t iProduct; + uint8_t iSerialNumber; + uint8_t bNumConfigurations; + } PACKED; + + struct UsbConfigDescriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wTotalLength; + uint8_t bNumInterfaces; + uint8_t bConfigurationValue; + uint8_t iConfiguration; + uint8_t bmAttributes; + uint8_t bMaxPower; + } PACKED; + + struct UsbEndpointCompanionDescriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bMaxBurst; + uint8_t bmAttributes; + uint16_t wBytesPerInterval; + } PACKED; + + struct UsbStringDescriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wData[DsLimitMaxNameSize]; + } PACKED; + + struct UsbCtrlRequest { + uint8_t bmRequestType; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; + } PACKED; + + enum UsbState { + UsbState_Detached = 0, + UsbState_Attached = 1, + UsbState_Powered = 2, + UsbState_Default = 3, + UsbState_Address = 4, + UsbState_Configured = 5, + UsbState_Suspended = 6, + }; + + enum UsbDescriptorSize { + UsbDescriptorSize_Interface = sizeof(UsbInterfaceDescriptor), + UsbDescriptorSize_Endpoint = sizeof(UsbEndpointDescriptor), + UsbDescriptorSize_Device = sizeof(UsbDeviceDescriptor), + UsbDescriptorSize_Config = sizeof(UsbConfigDescriptor), + UsbDescriptorSize_EndpointCompanion = sizeof(UsbEndpointCompanionDescriptor), + }; + + enum UsbDeviceSpeed { + UsbDeviceSpeed_Invalid = 0, + UsbDeviceSpeed_Low = 1, + UsbDeviceSpeed_Full = 2, + UsbDeviceSpeed_High = 3, + UsbDeviceSpeed_Super = 4, + UsbDeviceSpeed_SuperPlus = 5, + }; + + + enum UsbEndpointAddressMask { + UsbEndpointAddressMask_EndpointNumber = (0xF << 0), + + UsbEndpointAddressMask_Dir = (0x1 << 7), + + UsbEndpointAddressMask_DirHostToDevice = (0x0 << 7), + UsbEndpointAddressMask_DirDevicetoHost = (0x1 << 7), + }; + + enum UsbEndpointDirection { + UsbEndpointDirection_Invalid = 0, + UsbEndpointDirection_ToDevice = 1, + UsbEndpointDirection_ToHost = 2, + UsbEndpointDirection_Control = 3, + }; + + constexpr inline u8 UsbGetEndpointNumber(const UsbEndpointDescriptor *desc) { + return desc->bEndpointAddress & UsbEndpointAddressMask_EndpointNumber; + } + + constexpr inline bool UsbEndpointIsHostToDevice(const UsbEndpointDescriptor *desc) { + return (desc->bEndpointAddress & UsbEndpointAddressMask_Dir) == UsbEndpointAddressMask_DirHostToDevice; + } + + constexpr inline bool UsbEndpointIsDeviceToHost(const UsbEndpointDescriptor *desc) { + return (desc->bEndpointAddress & UsbEndpointAddressMask_Dir) == UsbEndpointAddressMask_DirDevicetoHost; + } + + constexpr inline u8 UsbGetEndpointAddress(u8 number, UsbEndpointDirection dir) { + u8 val = static_cast(number & UsbEndpointAddressMask_EndpointNumber); + if (dir == UsbEndpointDirection_ToHost) { + val |= UsbEndpointAddressMask_DirDevicetoHost; + } else { + val |= UsbEndpointAddressMask_DirHostToDevice; + } + return val; + } + + constexpr inline UsbEndpointDirection GetUsbEndpointDirection(const UsbEndpointDescriptor *desc) { + if (UsbEndpointIsDeviceToHost(desc)) { + return UsbEndpointDirection_ToHost; + } else { + return UsbEndpointDirection_ToDevice; + } + } + + constexpr inline bool UsbEndpointIsValid(const UsbEndpointDescriptor *desc) { + return desc != nullptr && desc->bLength >= UsbDescriptorSize_Endpoint && desc->bEndpointAddress != 0; + } + + constexpr inline void UsbMarkEndpointInvalid(UsbEndpointDescriptor *desc) { + desc->bLength = 0; + desc->bEndpointAddress = 0; + } + +} diff --git a/libraries/libstratosphere/source/usb/impl/usb_util.hpp b/libraries/libstratosphere/source/usb/impl/usb_util.hpp new file mode 100644 index 000000000..e9ac43514 --- /dev/null +++ b/libraries/libstratosphere/source/usb/impl/usb_util.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::usb::impl { + + constexpr int GetEndpointIndex(u8 address) { + int idx = address & UsbEndpointAddressMask_EndpointNumber; + if ((address & UsbEndpointAddressMask_Dir) == UsbEndpointAddressMask_DirDevicetoHost) { + idx += 0x10; + } + return idx; + } + + template + class ScopedRefCount { + NON_COPYABLE(ScopedRefCount); + NON_MOVEABLE(ScopedRefCount); + private: + T &m_obj; + public: + ALWAYS_INLINE ScopedRefCount(T &o) : m_obj(o) { + ++m_obj; + } + + ALWAYS_INLINE ~ScopedRefCount() { + --m_obj; + } + }; + +} diff --git a/libraries/libstratosphere/source/usb/usb_device.cpp b/libraries/libstratosphere/source/usb/usb_device.cpp new file mode 100644 index 000000000..cbc52439c --- /dev/null +++ b/libraries/libstratosphere/source/usb/usb_device.cpp @@ -0,0 +1,803 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "usb_remote_ds_root_service.hpp" +#include "usb_remote_ds_service.hpp" +#include "impl/usb_util.hpp" + +namespace ams::usb { + + Result DsClient::Initialize(ComplexId complex_id) { + /* Clear interfaces. */ + for (size_t i = 0; i < util::size(m_interfaces); ++i) { + m_interfaces[i] = nullptr; + } + + /* Initialize heap. */ + m_heap_handle = lmem::CreateExpHeap(m_heap_buffer, sizeof(m_heap_buffer), lmem::CreateOption_None); + R_UNLESS(m_heap_handle != nullptr, usb::ResultMemAllocFailure()); + + /* Attach our allocator. */ + m_allocator.Attach(m_heap_handle); + + /* Connect to usb:ds. */ + /* NOTE: Here, Nintendo does m_domain.InitializeByDomain<...>(...); m_domain.SetSessionCount(1); */ + { + Service srv; + R_TRY(sm::GetService(std::addressof(srv), sm::ServiceName::Encode("usb:ds"))); + + R_ABORT_UNLESS(serviceConvertToDomain(std::addressof(srv))); + + using Allocator = decltype(m_allocator); + using ObjectFactory = sf::ObjectFactory; + + if (hos::GetVersion() >= hos::Version_11_0_0) { + m_root_service = ObjectFactory::CreateSharedEmplaced(std::addressof(m_allocator), srv, std::addressof(m_allocator)); + + R_TRY(m_root_service->GetService(std::addressof(m_ds_service))); + } else { + m_ds_service = ObjectFactory::CreateSharedEmplaced(std::addressof(m_allocator), srv, std::addressof(m_allocator)); + } + } + + /* Bind the client process. */ + R_TRY(m_ds_service->Bind(complex_id, dd::GetCurrentProcessHandle())); + + /* Get the state change event. */ + sf::CopyHandle event_handle; + R_TRY(m_ds_service->GetStateChangeEvent(std::addressof(event_handle))); + + /* Attach the state change event handle to our event. */ + os::AttachReadableHandleToSystemEvent(std::addressof(m_state_change_event), event_handle.GetValue(), true, os::EventClearMode_ManualClear); + + /* Mark ourselves as initialized. */ + m_is_initialized = true; + + return ResultSuccess(); + } + + Result DsClient::Finalize() { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Disable and finalize all interfaces. */ + R_TRY(this->DisableDevice()); + for (size_t i = 0; i < util::size(m_interfaces); ++i) { + if (m_interfaces[i] != nullptr) { + R_TRY(m_interfaces[i]->Finalize()); + } + } + + /* Check our reference count .*/ + R_UNLESS(m_reference_count <= 1, usb::ResultResourceBusy()); + + /* Finalize members. */ + m_is_initialized = false; + os::DestroySystemEvent(std::addressof(m_state_change_event)); + lmem::DestroyExpHeap(m_heap_handle); + m_heap_handle = nullptr; + + /* Destroy interface objects. */ + m_ds_service = nullptr; + m_root_service = nullptr; + + return ResultSuccess(); + } + + bool DsClient::IsInitialized() { + return m_is_initialized; + } + + Result DsClient::EnableDevice() { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Enable all interfaces. */ + if (hos::GetVersion() < hos::Version_11_0_0) { + for (size_t i = 0; i < util::size(m_interfaces); ++i) { + if (m_interfaces[i] != nullptr) { + R_TRY(m_interfaces[i]->Enable()); + } + } + } + + /* Enable the device. */ + R_TRY(m_ds_service->Enable()); + + /* Mark disabled. */ + m_is_enabled = true; + return ResultSuccess(); + } + + Result DsClient::DisableDevice() { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Disable the device. */ + R_TRY(m_ds_service->Disable()); + + /* Disable all interfaces. */ + if (hos::GetVersion() < hos::Version_11_0_0) { + for (size_t i = 0; i < util::size(m_interfaces); ++i) { + if (m_interfaces[i] != nullptr) { + R_TRY(m_interfaces[i]->Disable()); + } + } + } + + /* Mark disabled. */ + m_is_enabled = false; + return ResultSuccess(); + } + + os::SystemEventType *DsClient::GetStateChangeEvent() { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + return m_is_initialized ? std::addressof(m_state_change_event) : nullptr; + } + + Result DsClient::GetState(UsbState *out) { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Check that we have a service. */ + AMS_ABORT_UNLESS(m_ds_service != nullptr); + + return m_ds_service->GetState(out); + } + + Result DsClient::ClearDeviceData() { + return m_ds_service->ClearDeviceData(); + } + + Result DsClient::AddUsbStringDescriptor(u8 *out_index, UsbStringDescriptor *desc) { + return m_ds_service->AddUsbStringDescriptor(out_index, sf::InBuffer(reinterpret_cast(desc), sizeof(*desc))); + } + + Result DsClient::DeleteUsbStringDescriptor(u8 index) { + return m_ds_service->DeleteUsbStringDescriptor(index); + } + + Result DsClient::SetUsbDeviceDescriptor(UsbDeviceDescriptor *desc, UsbDeviceSpeed speed) { + return m_ds_service->SetUsbDeviceDescriptor(sf::InBuffer(reinterpret_cast(desc), sizeof(*desc)), speed); + } + + Result DsClient::SetBinaryObjectStore(u8 *data, int size) { + return m_ds_service->SetBinaryObjectStore(sf::InBuffer(reinterpret_cast(data), size)); + } + + Result DsClient::AddInterface(DsInterface *intf, sf::SharedPointer *out_srv, uint8_t bInterfaceNumber) { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Check that we have a service. */ + AMS_ABORT_UNLESS(m_ds_service != nullptr); + + /* Register the interface. */ + R_TRY(m_ds_service->RegisterInterface(out_srv, bInterfaceNumber)); + + /* Set interface. */ + m_interfaces[bInterfaceNumber] = intf; + + return ResultSuccess(); + } + + Result DsClient::DeleteInterface(uint8_t bInterfaceNumber) { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Check that we have the interface. */ + R_UNLESS(m_interfaces[bInterfaceNumber] != nullptr, usb::ResultOperationDenied()); + + /* Clear the interface. */ + m_interfaces[bInterfaceNumber] = nullptr; + + return ResultSuccess(); + } + + Result DsInterface::Initialize(DsClient *client, u8 bInterfaceNumber) { + /* Check that we haven't already initialized. */ + R_UNLESS(!m_is_initialized, usb::ResultAlreadyInitialized()); + + /* Set our client. */ + m_client = client; + + /* Clear all endpoints. */ + for (size_t i = 0; i < util::size(m_endpoints); ++i) { + m_endpoints[i] = nullptr; + } + + /* Set our interface number. */ + R_UNLESS(bInterfaceNumber < util::size(m_client->m_interfaces), usb::ResultInvalidParameter()); + m_interface_num = bInterfaceNumber; + + /* Add the interface. */ + R_TRY(m_client->AddInterface(this, std::addressof(m_interface), m_interface_num)); + + /* Ensure we cleanup if we fail after this. */ + auto intf_guard = SCOPE_GUARD { m_client->DeleteInterface(m_interface_num); m_interface = nullptr; }; + + /* Get events. */ + sf::CopyHandle setup_event_handle; + sf::CopyHandle ctrl_in_event_handle; + sf::CopyHandle ctrl_out_event_handle; + R_TRY(m_interface->GetSetupEvent(std::addressof(setup_event_handle))); + R_TRY(m_interface->GetCtrlInCompletionEvent(std::addressof(ctrl_in_event_handle))); + R_TRY(m_interface->GetCtrlOutCompletionEvent(std::addressof(ctrl_out_event_handle))); + + /* Attach events. */ + os::AttachReadableHandleToSystemEvent(std::addressof(m_setup_event), setup_event_handle.GetValue(), true, os::EventClearMode_ManualClear); + os::AttachReadableHandleToSystemEvent(std::addressof(m_ctrl_in_completion_event), ctrl_in_event_handle.GetValue(), true, os::EventClearMode_ManualClear); + os::AttachReadableHandleToSystemEvent(std::addressof(m_ctrl_out_completion_event), ctrl_out_event_handle.GetValue(), true, os::EventClearMode_ManualClear); + + /* Increment our client's reference count. */ + ++m_client->m_reference_count; + + /* Set ourselves as initialized. */ + m_is_initialized = true; + + intf_guard.Cancel(); + return ResultSuccess(); + } + + Result DsInterface::Finalize() { + /* Validate that we have a service. */ + R_ABORT_UNLESS(m_interface != nullptr); + + /* We must be disabled. */ + R_UNLESS(!m_client->m_is_enabled, usb::ResultResourceBusy()); + + /* Finalize all endpoints. */ + for (size_t i = 0; i < util::size(m_endpoints); ++i) { + if (m_endpoints[i] != nullptr) { + R_TRY(m_endpoints[i]->Finalize()); + } + } + + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Check our reference count .*/ + R_UNLESS(m_reference_count <= 1, usb::ResultResourceBusy()); + + /* Finalize members. */ + m_is_initialized = false; + os::DestroySystemEvent(std::addressof(m_setup_event)); + os::DestroySystemEvent(std::addressof(m_ctrl_in_completion_event)); + os::DestroySystemEvent(std::addressof(m_ctrl_out_completion_event)); + + /* Delete ourselves from our cleint. */ + m_client->DeleteInterface(m_interface_num); + + /* Destroy our service. */ + m_interface = nullptr; + + /* Close our reference to our client. */ + --m_client->m_reference_count; + m_client = nullptr; + + return ResultSuccess(); + } + + Result DsInterface::AppendConfigurationData(UsbDeviceSpeed speed, void *data, u32 size) { + return m_interface->AppendConfigurationData(m_interface_num, speed, sf::InBuffer(data, size)); + } + + bool DsInterface::IsInitialized() { + return m_is_initialized; + } + + os::SystemEventType *DsInterface::GetSetupEvent() { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + return m_is_initialized ? std::addressof(m_setup_event) : nullptr; + } + + Result DsInterface::GetSetupPacket(UsbCtrlRequest *out) { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Check that we have a service. */ + AMS_ABORT_UNLESS(m_interface != nullptr); + + return m_interface->GetSetupPacket(sf::OutBuffer(out, sizeof(*out))); + } + + Result DsInterface::Enable() { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* If we're already enabled, nothing to do. */ + R_SUCCEED_IF(m_client->m_is_enabled); + + /* Check that we have a service. */ + AMS_ABORT_UNLESS(m_interface != nullptr); + + /* Perform the enable. */ + R_TRY(m_interface->Enable()); + + return ResultSuccess(); + } + + Result DsInterface::Disable() { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* If we're already disabled, nothing to do. */ + R_SUCCEED_IF(!m_client->m_is_enabled); + + /* Check that we have a service. */ + AMS_ABORT_UNLESS(m_interface != nullptr); + + /* Perform the disable. */ + R_TRY(m_interface->Disable()); + + return ResultSuccess(); + } + + Result DsInterface::AddEndpoint(DsEndpoint *ep, u8 bEndpointAddress, sf::SharedPointer *out) { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Check that we're not already enabled. */ + R_UNLESS(!m_client->m_is_enabled, usb::ResultOperationDenied()); + + /* Check that we have a service. */ + AMS_ABORT_UNLESS(m_interface != nullptr); + + /* Register the endpoint. */ + R_TRY(m_interface->RegisterEndpoint(bEndpointAddress, out)); + + /* Set the endpoint. */ + m_endpoints[impl::GetEndpointIndex(bEndpointAddress)] = ep; + + return ResultSuccess(); + } + + Result DsInterface::DeleteEndpoint(u8 bEndpointAddress) { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Check that we're disabled and have the endpoint. */ + const auto index = impl::GetEndpointIndex(bEndpointAddress); + R_UNLESS(!m_client->m_is_enabled, usb::ResultOperationDenied()); + R_UNLESS(m_endpoints[index] != nullptr, usb::ResultOperationDenied()); + + /* Clear the endpoint. */ + m_endpoints[index] = nullptr; + + return ResultSuccess(); + } + + Result DsInterface::CtrlIn(u32 *out_transferred, void *dst, u32 size) { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Check that we're enabled. */ + R_UNLESS(m_client->m_is_enabled, usb::ResultOperationDenied()); + + /* Check that the data is aligned. */ + R_UNLESS(usb::IsDmaAligned(reinterpret_cast(dst)), usb::ResultAlignmentError()); + + /* If we should, flush cache. */ + if (size != 0) { + dd::FlushDataCache(dst, size); + } + + /* Check that we have a service. */ + AMS_ABORT_UNLESS(m_interface != nullptr); + + /* Perform the transfer. */ + u32 urb_id; + R_TRY(m_interface->CtrlInAsync(std::addressof(urb_id), reinterpret_cast(dst), size)); + + /* Wait for control to finish. */ + os::WaitSystemEvent(std::addressof(m_ctrl_in_completion_event)); + os::ClearSystemEvent(std::addressof(m_ctrl_in_completion_event)); + + /* Get the urb report. */ + R_ABORT_UNLESS(m_interface->GetCtrlInUrbReport(std::addressof(m_report))); + + /* Check the report is for our urb. */ + R_UNLESS(m_report.count == 1, usb::ResultInternalStateError()); + R_UNLESS(m_report.reports[0].id == urb_id, usb::ResultInternalStateError()); + + /* Set output bytes. */ + if (out_transferred != nullptr) { + *out_transferred = m_report.reports[0].transferred_size; + } + + /* Handle the report. */ + switch (m_report.reports[0].status) { + case UrbStatus_Cancelled: + return usb::ResultInterrupted(); + case UrbStatus_Failed: + return usb::ResultTransactionError(); + case UrbStatus_Finished: + return ResultSuccess(); + default: + return usb::ResultInternalStateError(); + } + } + + Result DsInterface::CtrlOut(u32 *out_transferred, void *dst, u32 size) { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Check that we're enabled. */ + R_UNLESS(m_client->m_is_enabled, usb::ResultOperationDenied()); + + /* Check that the data is aligned. */ + R_UNLESS(usb::IsDmaAligned(reinterpret_cast(dst)), usb::ResultAlignmentError()); + + /* If we should, invalidate cache. */ + if (size != 0) { + dd::InvalidateDataCache(dst, size); + } + + /* Check that we have a service. */ + AMS_ABORT_UNLESS(m_interface != nullptr); + + /* Perform the transfer. */ + u32 urb_id; + R_TRY(m_interface->CtrlOutAsync(std::addressof(urb_id), reinterpret_cast(dst), size)); + + /* Wait for control to finish. */ + os::WaitSystemEvent(std::addressof(m_ctrl_out_completion_event)); + os::ClearSystemEvent(std::addressof(m_ctrl_out_completion_event)); + + /* Ensure that cache remains consistent. */ + ON_SCOPE_EXIT { + if (size != 0) { + dd::InvalidateDataCache(dst, size); + } + }; + + /* Get the urb report. */ + R_ABORT_UNLESS(m_interface->GetCtrlOutUrbReport(std::addressof(m_report))); + + /* Check the report is for our urb. */ + R_UNLESS(m_report.count == 1, usb::ResultInternalStateError()); + R_UNLESS(m_report.reports[0].id == urb_id, usb::ResultInternalStateError()); + + /* Set output bytes. */ + if (out_transferred != nullptr) { + *out_transferred = m_report.reports[0].transferred_size; + } + + /* Handle the report. */ + switch (m_report.reports[0].status) { + case UrbStatus_Cancelled: + return usb::ResultInterrupted(); + case UrbStatus_Failed: + return usb::ResultTransactionError(); + case UrbStatus_Finished: + return ResultSuccess(); + default: + return usb::ResultInternalStateError(); + } + } + + Result DsInterface::CtrlRead(u32 *out_transferred, void *dst, u32 size) { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Do the data transfer. */ + Result result = this->CtrlOut(out_transferred, dst, size); + + /* Do the status transfer. */ + if (R_SUCCEEDED(result)) { + result = this->CtrlIn(nullptr, nullptr, 0); + } + + /* If we failed, stall. */ + if (R_FAILED(result)) { + result = this->CtrlStall(); + } + + return result; + } + + Result DsInterface::CtrlWrite(u32 *out_transferred, void *dst, u32 size) { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Do the data transfer. */ + Result result = this->CtrlIn(out_transferred, dst, size); + + /* Do the status transfer. */ + if (R_SUCCEEDED(result)) { + result = this->CtrlOut(nullptr, nullptr, 0); + } + + /* If we failed, stall. */ + if (R_FAILED(result)) { + result = this->CtrlStall(); + } + + return result; + } + + Result DsInterface::CtrlDone() { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Do the status transfer. */ + Result result = this->CtrlIn(nullptr, nullptr, 0); + + /* If we failed, stall. */ + if (R_FAILED(result)) { + result = this->CtrlStall(); + } + + return result; + } + + Result DsInterface::CtrlStall() { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Check that we're enabled. */ + R_UNLESS(m_client->m_is_enabled, usb::ResultOperationDenied()); + + /* Check that we have a service. */ + AMS_ABORT_UNLESS(m_interface != nullptr); + + return m_interface->CtrlStall(); + } + + Result DsEndpoint::Initialize(DsInterface *interface, u8 bEndpointAddress) { + /* Check that the interface is valid. */ + AMS_ABORT_UNLESS(interface != nullptr); + + /* Check that we're not already initialized. */ + R_UNLESS(!m_is_initialized, usb::ResultAlreadyInitialized()); + + /* Set our interface. */ + m_interface = interface; + + /* Add the endpoint. */ + R_TRY(m_interface->AddEndpoint(this, bEndpointAddress, std::addressof(m_endpoint))); + + /* Set our address. */ + m_address = bEndpointAddress; + + /* Ensure we clean up if we fail after this. */ + auto ep_guard = SCOPE_GUARD { m_interface->DeleteEndpoint(m_address); m_endpoint = nullptr; }; + + /* Get completion event. */ + sf::CopyHandle event_handle; + R_TRY(m_endpoint->GetCompletionEvent(std::addressof(event_handle))); + + /* Increment our interface's reference count. */ + ++m_interface->m_reference_count; + ++m_interface->m_client->m_reference_count; + + /* Attach our event. */ + os::AttachReadableHandleToSystemEvent(std::addressof(m_completion_event), event_handle, true, os::EventClearMode_ManualClear); + + /* Mark initialized. */ + m_is_initialized = true; + + ep_guard.Cancel(); + return ResultSuccess(); + } + + Result DsEndpoint::Finalize() { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Cancel any pending transactions. */ + m_endpoint->Cancel(); + + /* Wait for us to be at one reference count. */ + while (m_reference_count > 1) { + os::SleepThread(TimeSpan::FromMilliSeconds(25)); + } + + /* Destroy our event. */ + os::DestroySystemEvent(std::addressof(m_completion_event)); + + /* Decrement our interface's reference count. */ + --m_interface->m_reference_count; + --m_interface->m_client->m_reference_count; + + /* Delete ourselves. */ + R_TRY(m_interface->DeleteEndpoint(m_address)); + + /* Clear ourselves. */ + m_interface = nullptr; + m_endpoint = nullptr; + + /* Mark uninitialized. */ + m_is_initialized = false; + + return ResultSuccess(); + } + + bool DsEndpoint::IsInitialized() { + return m_is_initialized; + } + + Result DsEndpoint::PostBuffer(u32 *out_transferred, void *buf, u32 size) { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Post buffer. */ + u32 urb_id; + R_TRY(this->PostBufferAsync(std::addressof(urb_id), buf, size)); + + /* Wait for completion. */ + os::WaitSystemEvent(std::addressof(m_completion_event)); + os::ClearSystemEvent(std::addressof(m_completion_event)); + + /* Get URB report. */ + UrbReport report; + AMS_ABORT_UNLESS(m_endpoint != nullptr); + R_ABORT_UNLESS(m_endpoint->GetUrbReport(std::addressof(report))); + + /* Check the report is for our urb. */ + R_UNLESS(report.count == 1, usb::ResultInternalStateError()); + R_UNLESS(report.reports[0].id == urb_id, usb::ResultInternalStateError()); + + /* Set output bytes. */ + if (out_transferred != nullptr) { + *out_transferred = report.reports[0].transferred_size; + } + + /* Handle the report. */ + switch (report.reports[0].status) { + case UrbStatus_Cancelled: + return usb::ResultInterrupted(); + case UrbStatus_Failed: + return usb::ResultTransactionError(); + case UrbStatus_Finished: + return ResultSuccess(); + default: + return usb::ResultInternalStateError(); + } + } + + Result DsEndpoint::PostBufferAsync(u32 *out_urb_id, void *buf, u32 size) { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Check that the buffer is DMA aligned. */ + R_UNLESS(usb::IsDmaAligned(reinterpret_cast(buf)), usb::ResultAlignmentError()); + + /* Check that we have a service. */ + AMS_ABORT_UNLESS(m_endpoint != nullptr); + + /* Post */ + u32 urb_id = 0; + R_TRY(m_endpoint->PostBufferAsync(std::addressof(urb_id), reinterpret_cast(buf), size)); + + *out_urb_id = urb_id; + return ResultSuccess(); + } + + os::SystemEventType *DsEndpoint::GetCompletionEvent() { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + return m_is_initialized ? std::addressof(m_completion_event) : nullptr; + } + + Result DsEndpoint::GetUrbReport(UrbReport *out) { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Check that we have a service. */ + AMS_ABORT_UNLESS(m_endpoint != nullptr); + + return m_endpoint->GetUrbReport(out); + } + + Result DsEndpoint::Cancel() { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Check that we have a service. */ + AMS_ABORT_UNLESS(m_endpoint != nullptr); + + return m_endpoint->Cancel(); + } + + Result DsEndpoint::SetZeroLengthTransfer(bool zlt) { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Check that we have a service. */ + AMS_ABORT_UNLESS(m_endpoint != nullptr); + + return m_endpoint->SetZlt(zlt); + } + +} diff --git a/libraries/libstratosphere/source/usb/usb_remote_ds_endpoint.cpp b/libraries/libstratosphere/source/usb/usb_remote_ds_endpoint.cpp new file mode 100644 index 000000000..9c75a125c --- /dev/null +++ b/libraries/libstratosphere/source/usb/usb_remote_ds_endpoint.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "usb_remote_ds_endpoint.hpp" + +namespace ams::usb { + + Result RemoteDsEndpoint::PostBufferAsync(sf::Out out_urb_id, u64 address, u32 size) { + const struct { + u32 size; + u64 address; + } in = { size, address }; + + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatchInOut(std::addressof(m_srv), 0, in, *out_urb_id); + } + + Result RemoteDsEndpoint::Cancel() { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatch(std::addressof(m_srv), 1); + } + + Result RemoteDsEndpoint::GetCompletionEvent(sf::OutCopyHandle out) { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatch(std::addressof(m_srv), 2, + .out_handle_attrs = { SfOutHandleAttr_HipcCopy }, + .out_handles = out.GetHandlePointer(), + ); + } + + Result RemoteDsEndpoint::GetUrbReport(sf::Out out) { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatchOut(std::addressof(m_srv), 3, *out); + } + + Result RemoteDsEndpoint::Stall() { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatch(std::addressof(m_srv), 4); + } + + Result RemoteDsEndpoint::SetZlt(bool zlt) { + const u8 in = zlt ? 1 : 0; + + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatchIn(std::addressof(m_srv), 5, in); + } + +} diff --git a/libraries/libstratosphere/source/usb/usb_remote_ds_endpoint.hpp b/libraries/libstratosphere/source/usb/usb_remote_ds_endpoint.hpp new file mode 100644 index 000000000..79e0e2051 --- /dev/null +++ b/libraries/libstratosphere/source/usb/usb_remote_ds_endpoint.hpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::usb { + + class RemoteDsEndpoint { + private: + Service m_srv; + public: + RemoteDsEndpoint(Service &srv) : m_srv(srv) { /* ... */ } + virtual ~RemoteDsEndpoint() { serviceClose(std::addressof(m_srv)); } + public: + Result PostBufferAsync(sf::Out out_urb_id, u64 address, u32 size); + Result Cancel(); + Result GetCompletionEvent(sf::OutCopyHandle out); + Result GetUrbReport(sf::Out out); + Result Stall(); + Result SetZlt(bool zlt); + }; + static_assert(ds::IsIDsEndpoint); + +} diff --git a/libraries/libstratosphere/source/usb/usb_remote_ds_interface.cpp b/libraries/libstratosphere/source/usb/usb_remote_ds_interface.cpp new file mode 100644 index 000000000..bbe8b1c8c --- /dev/null +++ b/libraries/libstratosphere/source/usb/usb_remote_ds_interface.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "usb_remote_ds_interface.hpp" +#include "usb_remote_ds_endpoint.hpp" + +namespace ams::usb { + + Result RemoteDsInterface::RegisterEndpoint(u8 endpoint_address, sf::Out> out) { + Service srv; + + serviceAssumeDomain(std::addressof(m_srv)); + R_TRY(serviceDispatchIn(std::addressof(m_srv), 0, endpoint_address, + .out_num_objects = 1, + .out_objects = std::addressof(srv), + )); + + *out = ObjectFactory::CreateSharedEmplaced(m_allocator, srv); + + return ResultSuccess(); + } + + Result RemoteDsInterface::GetSetupEvent(sf::OutCopyHandle out) { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatch(std::addressof(m_srv), 1, + .out_handle_attrs = { SfOutHandleAttr_HipcCopy }, + .out_handles = out.GetHandlePointer(), + ); + } + + Result RemoteDsInterface::GetSetupPacket(const sf::OutBuffer &out) { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatch(std::addressof(m_srv), 2, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out }, + .buffers = { { out.GetPointer(), out.GetSize() } }, + ); + } + + Result RemoteDsInterface::CtrlInAsync(sf::Out out_urb_id, u64 address, u32 size) { + const struct { + u32 size; + u64 address; + } in = { size, address }; + + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatchInOut(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 3 : 5, in, *out_urb_id); + } + + Result RemoteDsInterface::CtrlOutAsync(sf::Out out_urb_id, u64 address, u32 size) { + const struct { + u32 size; + u64 address; + } in = { size, address }; + + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatchInOut(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 4 : 6, in, *out_urb_id); + } + + Result RemoteDsInterface::GetCtrlInCompletionEvent(sf::OutCopyHandle out) { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 5 : 7, + .out_handle_attrs = { SfOutHandleAttr_HipcCopy }, + .out_handles = out.GetHandlePointer(), + ); + } + + Result RemoteDsInterface::GetCtrlInUrbReport(sf::Out out) { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatchOut(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 6 : 8, *out); + } + + Result RemoteDsInterface::GetCtrlOutCompletionEvent(sf::OutCopyHandle out) { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 7 : 9, + .out_handle_attrs = { SfOutHandleAttr_HipcCopy }, + .out_handles = out.GetHandlePointer(), + ); + } + + Result RemoteDsInterface::GetCtrlOutUrbReport(sf::Out out) { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatchOut(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 8 : 10, *out); + } + + Result RemoteDsInterface::CtrlStall() { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 9 : 11); + } + + Result RemoteDsInterface::AppendConfigurationData(u8 bInterfaceNumber, usb::UsbDeviceSpeed device_speed, const sf::InBuffer &data) { + if (hos::GetVersion() >= hos::Version_11_0_0) { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatchIn(std::addressof(m_srv), 12, device_speed, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, + .buffers = { { data.GetPointer(), data.GetSize() } }, + ); + } else { + const struct { + u8 bInterfaceNumber; + usb::UsbDeviceSpeed device_speed; + } in = { bInterfaceNumber, device_speed }; + + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatchIn(std::addressof(m_srv), 10, in, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, + .buffers = { { data.GetPointer(), data.GetSize() } }, + ); + } + } + + Result RemoteDsInterface::Enable() { + R_SUCCEED_IF(hos::GetVersion() >= hos::Version_11_0_0); + + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatch(std::addressof(m_srv), 3); + } + + Result RemoteDsInterface::Disable() { + R_SUCCEED_IF(hos::GetVersion() >= hos::Version_11_0_0); + + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatch(std::addressof(m_srv), 3); + } + + +} diff --git a/libraries/libstratosphere/source/usb/usb_remote_ds_interface.hpp b/libraries/libstratosphere/source/usb/usb_remote_ds_interface.hpp new file mode 100644 index 000000000..300241524 --- /dev/null +++ b/libraries/libstratosphere/source/usb/usb_remote_ds_interface.hpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::usb { + + class RemoteDsInterface { + private: + using Allocator = sf::ExpHeapAllocator; + using ObjectFactory = sf::ObjectFactory; + private: + Service m_srv; + Allocator *m_allocator; + public: + RemoteDsInterface(Service &srv, sf::ExpHeapAllocator *allocator) : m_srv(srv), m_allocator(allocator) { /* ... */ } + virtual ~RemoteDsInterface() { serviceClose(std::addressof(m_srv)); } + public: + Result RegisterEndpoint(u8 endpoint_address, sf::Out> out); + Result GetSetupEvent(sf::OutCopyHandle out); + Result GetSetupPacket(const sf::OutBuffer & out); + Result CtrlInAsync(sf::Out out_urb_id, u64 address, u32 size); + Result CtrlOutAsync(sf::Out out_urb_id, u64 address, u32 size); + Result GetCtrlInCompletionEvent(sf::OutCopyHandle out); + Result GetCtrlInUrbReport(sf::Out out); + Result GetCtrlOutCompletionEvent(sf::OutCopyHandle out); + Result GetCtrlOutUrbReport(sf::Out out); + Result CtrlStall(); + Result AppendConfigurationData(u8 bInterfaceNumber, usb::UsbDeviceSpeed device_speed, const sf::InBuffer &data); + Result Enable(); + Result Disable(); + }; + static_assert(ds::IsIDsInterface); + +} diff --git a/libraries/libstratosphere/source/usb/usb_remote_ds_root_service.cpp b/libraries/libstratosphere/source/usb/usb_remote_ds_root_service.cpp new file mode 100644 index 000000000..9bf16c3dc --- /dev/null +++ b/libraries/libstratosphere/source/usb/usb_remote_ds_root_service.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "usb_remote_ds_root_service.hpp" +#include "usb_remote_ds_service.hpp" + +namespace ams::usb { + + Result RemoteDsRootService::GetService(sf::Out> out) { + Service srv; + + serviceAssumeDomain(std::addressof(m_srv)); + R_TRY(serviceDispatch(std::addressof(m_srv), 0, .out_num_objects = 1, .out_objects = std::addressof(srv))); + + *out = ObjectFactory::CreateSharedEmplaced(m_allocator, srv, m_allocator); + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/usb/usb_remote_ds_root_service.hpp b/libraries/libstratosphere/source/usb/usb_remote_ds_root_service.hpp new file mode 100644 index 000000000..16df3b8e1 --- /dev/null +++ b/libraries/libstratosphere/source/usb/usb_remote_ds_root_service.hpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::usb { + + class RemoteDsRootService { + private: + using Allocator = sf::ExpHeapAllocator; + using ObjectFactory = sf::ObjectFactory; + private: + Service m_srv; + Allocator *m_allocator; + public: + RemoteDsRootService(Service &srv, sf::ExpHeapAllocator *allocator) : m_srv(srv), m_allocator(allocator) { /* ... */ } + virtual ~RemoteDsRootService() { serviceClose(std::addressof(m_srv)); } + public: + Result GetService(sf::Out> out); + }; + static_assert(ds::IsIDsRootService); + +} diff --git a/libraries/libstratosphere/source/usb/usb_remote_ds_service.cpp b/libraries/libstratosphere/source/usb/usb_remote_ds_service.cpp new file mode 100644 index 000000000..04b72e358 --- /dev/null +++ b/libraries/libstratosphere/source/usb/usb_remote_ds_service.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "usb_remote_ds_service.hpp" +#include "usb_remote_ds_interface.hpp" + +namespace ams::usb { + + Result RemoteDsService::Bind(usb::ComplexId complex_id, sf::CopyHandle process_h) { + if (hos::GetVersion() >= hos::Version_11_0_0) { + serviceAssumeDomain(std::addressof(m_srv)); + R_TRY(serviceDispatchIn(std::addressof(m_srv), 0, complex_id, + .in_num_handles = 1, + .in_handles = { process_h.GetValue() } + )); + } else { + serviceAssumeDomain(std::addressof(m_srv)); + R_TRY(serviceDispatchIn(std::addressof(m_srv), 0, complex_id)); + + serviceAssumeDomain(std::addressof(m_srv)); + R_TRY(serviceDispatch(std::addressof(m_srv), 1, + .in_num_handles = 1, + .in_handles = { process_h.GetValue() }) + ); + } + + return ResultSuccess(); + } + + Result RemoteDsService::RegisterInterface(sf::Out> out, u8 bInterfaceNumber) { + Service srv; + + serviceAssumeDomain(std::addressof(m_srv)); + R_TRY(serviceDispatchIn(std::addressof(m_srv), (hos::GetVersion() >= hos::Version_11_0_0 ? 1 : 2), bInterfaceNumber, + .out_num_objects = 1, + .out_objects = std::addressof(srv), + )); + + *out = ObjectFactory::CreateSharedEmplaced(m_allocator, srv, m_allocator); + + return ResultSuccess(); + } + + Result RemoteDsService::GetStateChangeEvent(sf::OutCopyHandle out) { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 2 : 3, + .out_handle_attrs = { SfOutHandleAttr_HipcCopy }, + .out_handles = out.GetHandlePointer(), + ); + } + + Result RemoteDsService::GetState(sf::Out out) { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatchOut(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 3 : 4, *out); + } + + Result RemoteDsService::ClearDeviceData() { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 4 : 5); + } + + Result RemoteDsService::AddUsbStringDescriptor(sf::Out out, const sf::InBuffer &desc) { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatchOut(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 5 : 6, *out, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, + .buffers = { { desc.GetPointer(), desc.GetSize() } }, + ); + } + + Result RemoteDsService::DeleteUsbStringDescriptor(u8 index) { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatchIn(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 6 : 7, index); + } + + Result RemoteDsService::SetUsbDeviceDescriptor(const sf::InBuffer &desc, usb::UsbDeviceSpeed speed) { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatchIn(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 7 : 8, speed, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, + .buffers = { { desc.GetPointer(), desc.GetSize() } }, + ); + } + + Result RemoteDsService::SetBinaryObjectStore(const sf::InBuffer &bos) { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 8 : 9, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, + .buffers = { { bos.GetPointer(), bos.GetSize() } }, + ); + } + + Result RemoteDsService::Enable() { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 9 : 10); + } + + Result RemoteDsService::Disable() { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 10 : 11); + } + +} diff --git a/libraries/libstratosphere/source/usb/usb_remote_ds_service.hpp b/libraries/libstratosphere/source/usb/usb_remote_ds_service.hpp new file mode 100644 index 000000000..68d93763e --- /dev/null +++ b/libraries/libstratosphere/source/usb/usb_remote_ds_service.hpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::usb { + + class RemoteDsService { + private: + using Allocator = sf::ExpHeapAllocator; + using ObjectFactory = sf::ObjectFactory; + private: + Service m_srv; + Allocator *m_allocator; + public: + RemoteDsService(Service &srv, sf::ExpHeapAllocator *allocator) : m_srv(srv), m_allocator(allocator) { /* ... */ } + virtual ~RemoteDsService() { serviceClose(std::addressof(m_srv)); } + public: + Result Bind(usb::ComplexId complex_id, sf::CopyHandle process_h); + Result RegisterInterface(sf::Out> out, u8 bInterfaceNumber); + Result GetStateChangeEvent(sf::OutCopyHandle out); + Result GetState(sf::Out out); + Result ClearDeviceData(); + Result AddUsbStringDescriptor(sf::Out out, const sf::InBuffer &desc); + Result DeleteUsbStringDescriptor(u8 index); + Result SetUsbDeviceDescriptor(const sf::InBuffer &desc, usb::UsbDeviceSpeed speed); + Result SetBinaryObjectStore(const sf::InBuffer &bos); + Result Enable(); + Result Disable(); + }; + static_assert(ds::IsIDsService); + +} diff --git a/libraries/libvapours/include/vapours/results.hpp b/libraries/libvapours/include/vapours/results.hpp index f10e5af32..0781b5d9e 100644 --- a/libraries/libvapours/include/vapours/results.hpp +++ b/libraries/libvapours/include/vapours/results.hpp @@ -59,6 +59,7 @@ #include #include #include +#include #include /* Unofficial. */ diff --git a/libraries/libvapours/include/vapours/results/usb_results.hpp b/libraries/libvapours/include/vapours/results/usb_results.hpp new file mode 100644 index 000000000..6aed0d46e --- /dev/null +++ b/libraries/libvapours/include/vapours/results/usb_results.hpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::usb { + + R_DEFINE_NAMESPACE_RESULT_MODULE(140); + + R_DEFINE_ERROR_RESULT(NotInitialized, 0); + R_DEFINE_ERROR_RESULT(AlreadyInitialized, 1); + + R_DEFINE_ERROR_RANGE(InvalidParameter, 100, 199); + R_DEFINE_ERROR_RESULT(AlignmentError, 103); + + R_DEFINE_ERROR_RESULT(OperationDenied, 201); + R_DEFINE_ERROR_RESULT(MemAllocFailure, 202); + R_DEFINE_ERROR_RESULT(ResourceBusy, 206); + R_DEFINE_ERROR_RESULT(InternalStateError, 207); + + R_DEFINE_ERROR_RESULT(TransactionError, 401); + R_DEFINE_ERROR_RESULT(Interrupted, 409); + +} From 2b825d56dc58083092cf7de81e08d4899b87c74d Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 7 Feb 2021 17:14:48 -0800 Subject: [PATCH 002/280] usb: fix wrong command id for AppendConfigurationData --- .../include/stratosphere/usb/usb_types.hpp | 9 +++++++++ .../source/usb/usb_remote_ds_interface.cpp | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/libraries/libstratosphere/include/stratosphere/usb/usb_types.hpp b/libraries/libstratosphere/include/stratosphere/usb/usb_types.hpp index 26ccecad6..f9abca21d 100644 --- a/libraries/libstratosphere/include/stratosphere/usb/usb_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/usb/usb_types.hpp @@ -165,6 +165,15 @@ namespace ams::usb { UsbEndpointAddressMask_DirDevicetoHost = (0x1 << 7), }; + enum UsbEndpointAttributeMask { + UsbEndpointAttributeMask_XferType = (0x3 << 0), + + UsbEndpointAttributeMask_XferTypeControl = (0x0 << 0), + UsbEndpointAttributeMask_XferTypeIsoc = (0x1 << 0), + UsbEndpointAttributeMask_XferTypeBulk = (0x2 << 0), + UsbEndpointAttributeMask_XferTypeInt = (0x3 << 0), + }; + enum UsbEndpointDirection { UsbEndpointDirection_Invalid = 0, UsbEndpointDirection_ToDevice = 1, diff --git a/libraries/libstratosphere/source/usb/usb_remote_ds_interface.cpp b/libraries/libstratosphere/source/usb/usb_remote_ds_interface.cpp index bbe8b1c8c..5b7e32ab4 100644 --- a/libraries/libstratosphere/source/usb/usb_remote_ds_interface.cpp +++ b/libraries/libstratosphere/source/usb/usb_remote_ds_interface.cpp @@ -103,7 +103,7 @@ namespace ams::usb { Result RemoteDsInterface::AppendConfigurationData(u8 bInterfaceNumber, usb::UsbDeviceSpeed device_speed, const sf::InBuffer &data) { if (hos::GetVersion() >= hos::Version_11_0_0) { serviceAssumeDomain(std::addressof(m_srv)); - return serviceDispatchIn(std::addressof(m_srv), 12, device_speed, + return serviceDispatchIn(std::addressof(m_srv), 10, device_speed, .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, .buffers = { { data.GetPointer(), data.GetSize() } }, ); @@ -114,7 +114,7 @@ namespace ams::usb { } in = { bInterfaceNumber, device_speed }; serviceAssumeDomain(std::addressof(m_srv)); - return serviceDispatchIn(std::addressof(m_srv), 10, in, + return serviceDispatchIn(std::addressof(m_srv), 12, in, .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, .buffers = { { data.GetPointer(), data.GetSize() } }, ); From fe5c850e69911d0916379af54f3b5cd67c2492c5 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 10 Feb 2021 02:57:22 -0800 Subject: [PATCH 003/280] psc: fix pm module init --- .../include/stratosphere/psc/psc_pm_module_id.hpp | 2 +- .../libstratosphere/source/psc/psc_pm_module.os.horizon.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/libstratosphere/include/stratosphere/psc/psc_pm_module_id.hpp b/libraries/libstratosphere/include/stratosphere/psc/psc_pm_module_id.hpp index 61196a79c..079ceb73e 100644 --- a/libraries/libstratosphere/include/stratosphere/psc/psc_pm_module_id.hpp +++ b/libraries/libstratosphere/include/stratosphere/psc/psc_pm_module_id.hpp @@ -19,7 +19,7 @@ namespace ams::psc { - enum PmModuleId : u16 { + enum PmModuleId : u32 { PmModuleId_Usb = 4, PmModuleId_Ethernet = 5, PmModuleId_Fgm = 6, diff --git a/libraries/libstratosphere/source/psc/psc_pm_module.os.horizon.cpp b/libraries/libstratosphere/source/psc/psc_pm_module.os.horizon.cpp index e808dbf2b..89ee7d03f 100644 --- a/libraries/libstratosphere/source/psc/psc_pm_module.os.horizon.cpp +++ b/libraries/libstratosphere/source/psc/psc_pm_module.os.horizon.cpp @@ -46,9 +46,9 @@ namespace ams::psc { Result PmModule::Initialize(const PmModuleId mid, const PmModuleId *dependencies, u32 dependency_count, os::EventClearMode clear_mode) { R_UNLESS(!this->initialized, psc::ResultAlreadyInitialized()); - static_assert(sizeof(*dependencies) == sizeof(u16)); + static_assert(sizeof(*dependencies) == sizeof(u32)); ::PscPmModule module; - R_TRY(::pscmGetPmModule(std::addressof(module), static_cast<::PscPmModuleId>(mid), reinterpret_cast(dependencies), dependency_count, clear_mode == os::EventClearMode_AutoClear)); + R_TRY(::pscmGetPmModule(std::addressof(module), static_cast<::PscPmModuleId>(mid), reinterpret_cast(dependencies), dependency_count, clear_mode == os::EventClearMode_AutoClear)); this->intf = RemoteObjectFactory::CreateSharedEmplaced(module); this->system_event.AttachReadableHandle(module.event.revent, false, clear_mode); From 26b6216fa02e9fee1f8f78a79e8d8f4aa68ef3c3 Mon Sep 17 00:00:00 2001 From: znxDomain <47802543+znxDomain@users.noreply.github.com> Date: Thu, 11 Feb 2021 08:43:47 -0500 Subject: [PATCH 004/280] Correct ams_mitm.md formatting Move set_mitm H3's under correct H2 element --- docs/components/modules/ams_mitm.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/components/modules/ams_mitm.md b/docs/components/modules/ams_mitm.md index 425e31b33..743f45426 100644 --- a/docs/components/modules/ams_mitm.md +++ b/docs/components/modules/ams_mitm.md @@ -25,12 +25,6 @@ set_mitm enables intercepting requests to the system settings service. It curren + `ns` system module and games (to allow for overriding game locales) + All firmware debug settings requests (to allow modification of system settings not directly exposed to the user) -## dns_mitm - -dns_mitm enables intercepting requests to dns resolution services, to enable redirecting requests for specified hostnames. - -For documentation, see [here](../../features/dns_mitm.md). - ### Firmware Version set_mitm intercepts the `GetFirmwareVersion` command, if the requester is `qlaunch` or `maintenance`. It modifies the `display_version` field of the returned system version, causing the version to display @@ -39,3 +33,8 @@ in settings as `#.#.#|AMS #.#.#|?` with `? = S` when running under system eMMC o ### System Settings set_mitm intercepts the `GetSettingsItemValueSize` and `GetSettingsItemValue` commands for all requesters. It does so in order to enable user configuration of system settings, which are parsed from `/atmosphere/system_settings.ini` on boot. See [here](../../features/configurations.md) for more information on the system settings format. + +## dns_mitm +dns_mitm enables intercepting requests to dns resolution services, to enable redirecting requests for specified hostnames. + +For documentation, see [here](../../features/dns_mitm.md). From 74e4e70053a30b1b71cabacff15aedf479af592d Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 15 Feb 2021 19:38:28 -0800 Subject: [PATCH 005/280] fs.mitm: fix cache of non-current-process data storages (closes #1371) --- stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp b/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp index 2c2fdeca3..27d170bb7 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp @@ -380,7 +380,7 @@ namespace ams::mitm::fs { /* Try to get a storage from the cache. */ { - std::shared_ptr cached_storage = GetStorageCacheEntry(this->client_info.program_id); + std::shared_ptr cached_storage = GetStorageCacheEntry(data_id); if (cached_storage != nullptr) { out.SetValue(MakeSharedStorage(cached_storage), target_object_id); return ResultSuccess(); @@ -403,7 +403,7 @@ namespace ams::mitm::fs { new_storage = std::move(layered_storage); } - SetStorageCacheEntry(this->client_info.program_id, &new_storage); + SetStorageCacheEntry(data_id, &new_storage); out.SetValue(MakeSharedStorage(new_storage), target_object_id); } From a96786fd2cb5901e4d15973f8e5ae93f479aa198 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 15 Feb 2021 19:40:00 -0800 Subject: [PATCH 006/280] ams: add kernel to debug elf zip --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index b4177e682..d1db70423 100644 --- a/Makefile +++ b/Makefile @@ -135,6 +135,7 @@ dist: dist-no-debug cp exosphere/program/sc7fw/sc7fw.elf atmosphere-$(AMSVER)-debug/exosphere-sc7fw.elf cp exosphere/program/rebootstub/rebootstub.elf atmosphere-$(AMSVER)-debug/exosphere-rebootstub.elf cp mesosphere/kernel_ldr/kernel_ldr.elf atmosphere-$(AMSVER)-debug/kernel_ldr.elf + cp mesosphere/kernel/kernel.elf atmosphere-$(AMSVER)-debug/kernel.elf cp stratosphere/ams_mitm/ams_mitm.elf atmosphere-$(AMSVER)-debug/ams_mitm.elf cp stratosphere/boot/boot.elf atmosphere-$(AMSVER)-debug/boot.elf cp stratosphere/boot2/boot2.elf atmosphere-$(AMSVER)-debug/boot2.elf From 71add1add8521e0c2115ec612c514400ac7ba688 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 15 Feb 2021 19:51:48 -0800 Subject: [PATCH 007/280] ams: bump version to 0.18.1 --- docs/changelog.md | 11 +++++++++++ .../libvapours/include/vapours/ams/ams_api_version.h | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 0fa705e14..472284450 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,4 +1,15 @@ # Changelog +## 0.18.1 ++ A number of minor issues were fixed, including: + + The new `dns.mitm` module added in 0.18.0 no longer fatal errors when receiving port=nullptr. + + This fixes youtube ad-blocking, and possibly other usecases. + + A bug was fixed that caused ams.mitm to incorrectly cache data storages. + + This potentially broke DLC when using romfs mods, and could have caused other issues (e.g. with custom themes, and maybe other cases). + + A bug was fixed in power state control module registration. + + This might fix a weird edge case with system module dependencies on sleep/wake, but probably nobody should notice any differences. + + A bug was fixed where mesosphere sometimes treated virtual core IDs as though they were physical core IDs. + + This had zero impact, because for Switch virtual core == physical core, but it could have affected future platforms if it had remained unresolved. ++ Several issues were fixed, and usability and stability were improved. ## 0.18.0 + A new mitm module was added (`dns.mitm`). + This provides a highly configurable mechanism for redirecting DNS resolution requests. diff --git a/libraries/libvapours/include/vapours/ams/ams_api_version.h b/libraries/libvapours/include/vapours/ams/ams_api_version.h index 73750f8fa..a2fef79b1 100644 --- a/libraries/libvapours/include/vapours/ams/ams_api_version.h +++ b/libraries/libvapours/include/vapours/ams/ams_api_version.h @@ -17,7 +17,7 @@ #define ATMOSPHERE_RELEASE_VERSION_MAJOR 0 #define ATMOSPHERE_RELEASE_VERSION_MINOR 18 -#define ATMOSPHERE_RELEASE_VERSION_MICRO 0 +#define ATMOSPHERE_RELEASE_VERSION_MICRO 1 #define ATMOSPHERE_RELEASE_VERSION ATMOSPHERE_RELEASE_VERSION_MAJOR, ATMOSPHERE_RELEASE_VERSION_MINOR, ATMOSPHERE_RELEASE_VERSION_MICRO From 1545fa9d44f5f0074db6ef2abbb8e7fb738cba6d Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 15 Feb 2021 19:52:28 -0800 Subject: [PATCH 008/280] git subrepo push libraries subrepo: subdir: "libraries" merged: "bc08912d" upstream: origin: "https://github.com/Atmosphere-NX/Atmosphere-libs" branch: "master" commit: "bc08912d" git-subrepo: version: "0.4.1" origin: "???" commit: "???" --- libraries/.gitrepo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/.gitrepo b/libraries/.gitrepo index d525598ae..3707a58e1 100644 --- a/libraries/.gitrepo +++ b/libraries/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/Atmosphere-NX/Atmosphere-libs branch = master - commit = 17960517bad5d2d07effb28b744ac8d907d571e0 - parent = ee2e9d50fd93721b365daf0eae1eef17c8ba62e8 + commit = bc08912dd31bb172467add8e24b4f0adac431939 + parent = 71add1add8521e0c2115ec612c514400ac7ba688 method = merge cmdver = 0.4.1 From 95a6b0828ffc09b9661e43ac5003dd1e985ca0c8 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 20 Feb 2021 17:37:59 -0800 Subject: [PATCH 009/280] dmnt: set the debug process handle slightly more carefully --- stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp index 365f249e0..79f3a8b84 100644 --- a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp +++ b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp @@ -834,7 +834,11 @@ namespace ams::dmnt::cheat::impl { } /* Open a debug handle. */ - R_ABORT_UNLESS_IF_NEW_PROCESS(svcDebugActiveProcess(&this->cheat_process_debug_handle, static_cast(this->cheat_process_metadata.process_id))); + svc::Handle debug_handle = svc::InvalidHandle; + R_ABORT_UNLESS_IF_NEW_PROCESS(svc::DebugActiveProcess(std::addressof(debug_handle), this->cheat_process_metadata.process_id.value)); + + /* Set our debug handle. */ + this->cheat_process_debug_handle = debug_handle; /* Cancel process guard. */ proc_guard.Cancel(); From cbf3ba9b754e866c4b11251b001e5cfc9b1fa49b Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 21 Feb 2021 23:50:01 -0800 Subject: [PATCH 010/280] General system stability improvements to enhance the user's experience. --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 472284450..939f4235c 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -41,7 +41,7 @@ + This also substantially improves power drain when the system is shut off; consoles powered off from Atmosphere should now drain battery at the same reduced rate as original firmware. + A number of minor changes were made, including: + A number of inconsistencies in the build system were fixed. - + Fow those building atmosphère at home, the `boot` sysmodule will no longer rebuild every time make is invoked. + + For those building atmosphère at home, the `boot` sysmodule will no longer rebuild every time make is invoked. + This substantially improves build times during development iteration. + `sm` was updated to more accurately reflect how official code manages request deferral. + `mesosphère` was updated to more accurately reflect official kernel management of the trace buffer. From 287f4e6fa108d51ca09cc4c21e350fef7b85afa1 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 23 Feb 2021 14:21:36 -0800 Subject: [PATCH 011/280] sm: fix abort on RegisterService while at port limit closes #1382 --- stratosphere/sm/source/impl/sm_service_manager.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stratosphere/sm/source/impl/sm_service_manager.cpp b/stratosphere/sm/source/impl/sm_service_manager.cpp index 48e1de943..ea2fae307 100644 --- a/stratosphere/sm/source/impl/sm_service_manager.cpp +++ b/stratosphere/sm/source/impl/sm_service_manager.cpp @@ -409,13 +409,15 @@ namespace ams::sm::impl { /* Create the new service. */ *out = INVALID_HANDLE; - R_TRY(svcCreatePort(out, free_service->port_h.GetPointerAndClear(), max_sessions, is_light, free_service->name.name)); + Handle server_hnd = INVALID_HANDLE; + R_TRY(svcCreatePort(out, std::addressof(server_hnd), max_sessions, is_light, free_service->name.name)); /* Save info. */ free_service->name = service; free_service->owner_process_id = process_id; free_service->max_sessions = max_sessions; free_service->is_light = is_light; + *free_service->port_h.GetPointerAndClear() = server_hnd; /* This might undefer some requests. */ TriggerResume(service); From eb50e99748fb14d60b9c4e2367be974b02bc7a20 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 23 Feb 2021 14:31:24 -0800 Subject: [PATCH 012/280] kern: alleviate a little KPort pressure. --- libraries/libmesosphere/source/init/kern_init_slab_setup.cpp | 2 +- stratosphere/sm/source/impl/sm_service_manager.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/libmesosphere/source/init/kern_init_slab_setup.cpp b/libraries/libmesosphere/source/init/kern_init_slab_setup.cpp index 804ab0a1e..dcff4c205 100644 --- a/libraries/libmesosphere/source/init/kern_init_slab_setup.cpp +++ b/libraries/libmesosphere/source/init/kern_init_slab_setup.cpp @@ -60,7 +60,7 @@ namespace ams::kern::init { constexpr size_t SlabCountKThread = 800; constexpr size_t SlabCountKEvent = 700; constexpr size_t SlabCountKInterruptEvent = 100; - constexpr size_t SlabCountKPort = 256; + constexpr size_t SlabCountKPort = 256 + 0x20 /* Extra 0x20 ports over Nintendo for homebrew. */; constexpr size_t SlabCountKSharedMemory = 80; constexpr size_t SlabCountKTransferMemory = 200; constexpr size_t SlabCountKCodeMemory = 10; diff --git a/stratosphere/sm/source/impl/sm_service_manager.cpp b/stratosphere/sm/source/impl/sm_service_manager.cpp index ea2fae307..7b480c4fe 100644 --- a/stratosphere/sm/source/impl/sm_service_manager.cpp +++ b/stratosphere/sm/source/impl/sm_service_manager.cpp @@ -23,7 +23,7 @@ namespace ams::sm::impl { /* Constexpr definitions. */ static constexpr size_t ProcessCountMax = 0x40; - static constexpr size_t ServiceCountMax = 0x100; + static constexpr size_t ServiceCountMax = 0x100 + 0x10; /* Extra 0x10 services over Nintendo for homebrew. */ static constexpr size_t FutureMitmCountMax = 0x20; static constexpr size_t AccessControlSizeMax = 0x200; From fc060d377767f209d6bffb9a30b3d3087708c857 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 7 Feb 2021 18:08:20 -0800 Subject: [PATCH 013/280] htc: skeleton main file/sysmodule dir --- .../impl/ams_system_thread_definitions.hpp | 21 +++ .../ncm/ncm_system_content_meta_id.hpp | 6 + stratosphere/htc/Makefile | 113 ++++++++++++++ stratosphere/htc/htc.json | 106 +++++++++++++ stratosphere/htc/source/htc_main.cpp | 146 ++++++++++++++++++ 5 files changed, 392 insertions(+) create mode 100644 stratosphere/htc/Makefile create mode 100644 stratosphere/htc/htc.json create mode 100644 stratosphere/htc/source/htc_main.cpp diff --git a/libraries/libstratosphere/include/stratosphere/ams/impl/ams_system_thread_definitions.hpp b/libraries/libstratosphere/include/stratosphere/ams/impl/ams_system_thread_definitions.hpp index ff789a78d..f08fa246f 100644 --- a/libraries/libstratosphere/include/stratosphere/ams/impl/ams_system_thread_definitions.hpp +++ b/libraries/libstratosphere/include/stratosphere/ams/impl/ams_system_thread_definitions.hpp @@ -123,6 +123,27 @@ namespace ams::impl { AMS_DEFINE_SYSTEM_THREAD(21, pgl, Main); AMS_DEFINE_SYSTEM_THREAD(21, pgl, ProcessControlTask); + /* htc. */ + AMS_DEFINE_SYSTEM_THREAD(10, htc, Main); + AMS_DEFINE_SYSTEM_THREAD(10, htc, HtcIpc); + AMS_DEFINE_SYSTEM_THREAD(10, htc, HtcsIpc); + AMS_DEFINE_SYSTEM_THREAD(10, htc, HtcsMonitor); + AMS_DEFINE_SYSTEM_THREAD(10, htc, HtcfsIpc); + AMS_DEFINE_SYSTEM_THREAD(10, htc, HtcfsMonitor); + AMS_DEFINE_SYSTEM_THREAD(10, htc, HtclowDiscovery); + AMS_DEFINE_SYSTEM_THREAD(10, htc, HtclowTcpServer); + AMS_DEFINE_SYSTEM_THREAD(10, htc, HtclowUsbIndication); + AMS_DEFINE_SYSTEM_THREAD(10, htc, HtclowListen); + AMS_DEFINE_SYSTEM_THREAD(10, htc, HtclowObserver); + AMS_DEFINE_SYSTEM_THREAD(10, htc, HtclowSend); + AMS_DEFINE_SYSTEM_THREAD(10, htc, HtclowReceive); + AMS_DEFINE_SYSTEM_THREAD(10, htc, Htcmisc); + AMS_DEFINE_SYSTEM_THREAD(10, htc, HtcmiscReceive); + AMS_DEFINE_SYSTEM_THREAD(10, htc, HtcmiscSend); + + AMS_DEFINE_SYSTEM_THREAD(10, tma, BridgePcieDriver); + + #undef AMS_DEFINE_SYSTEM_THREAD } diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_system_content_meta_id.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_system_content_meta_id.hpp index 7154243cb..894c556fe 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_system_content_meta_id.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_system_content_meta_id.hpp @@ -98,6 +98,9 @@ namespace ams::ncm { static const SystemProgramId Pgl; static const SystemProgramId End; + + static const SystemProgramId Manu; + static const SystemProgramId Htc; }; struct AtmosphereProgramId { @@ -197,6 +200,9 @@ namespace ams::ncm { inline constexpr const SystemProgramId SystemProgramId::End = { 0x01000000000007FFul }; + inline constexpr const SystemProgramId SystemProgramId::Manu = { 0x010000000000B14Aul }; + inline constexpr const SystemProgramId SystemProgramId::Htc = { 0x010000000000B240ul }; + inline constexpr bool IsSystemProgramId(const ProgramId &program_id) { return (SystemProgramId::Start <= program_id && program_id <= SystemProgramId::End) || IsAtmosphereProgramId(program_id); } diff --git a/stratosphere/htc/Makefile b/stratosphere/htc/Makefile new file mode 100644 index 000000000..aab86a63a --- /dev/null +++ b/stratosphere/htc/Makefile @@ -0,0 +1,113 @@ +#--------------------------------------------------------------------------------- +# pull in common stratosphere sysmodule configuration +#--------------------------------------------------------------------------------- +include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../libraries/config/templates/stratosphere.mk + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(call FIND_SOURCE_FILES,$(SOURCES),c) +CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp) +SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),s) + +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC) + +ifeq ($(strip $(CONFIG_JSON)),) + jsons := $(wildcard *.json) + ifneq (,$(findstring $(TARGET).json,$(jsons))) + export APP_JSON := $(TOPDIR)/$(TARGET).json + else + ifneq (,$(findstring config.json,$(jsons))) + export APP_JSON := $(TOPDIR)/config.json + endif + endif +else + export APP_JSON := $(TOPDIR)/$(CONFIG_JSON) +endif + +.PHONY: $(BUILD) clean all + +#--------------------------------------------------------------------------------- +all: $(BUILD) + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).nsp $(TARGET).npdm $(TARGET).nso $(TARGET).elf + + +#--------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +all : $(OUTPUT).nsp + +ifeq ($(strip $(APP_JSON)),) +$(OUTPUT).nsp : $(OUTPUT).nso +else +$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm +endif + +$(OUTPUT).nso : $(OUTPUT).elf + +$(OUTPUT).elf : $(OFILES) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/stratosphere/htc/htc.json b/stratosphere/htc/htc.json new file mode 100644 index 000000000..95d24bfdd --- /dev/null +++ b/stratosphere/htc/htc.json @@ -0,0 +1,106 @@ +{ + "name": "htc", + "title_id": "0x010000000000b240", + "title_id_range_min": "0x010000000000b240", + "title_id_range_max": "0x010000000000b240", + "main_thread_stack_size": "0x00004000", + "main_thread_priority": 38, + "default_cpu_id": 3, + "process_category": 0, + "is_retail": true, + "pool_partition": 2, + "is_64_bit": true, + "address_space_type": 3, + "disable_device_address_space_merge": true, + "filesystem_access": { + "permissions": "0xFFFFFFFFFFFFFFFF" + }, + "service_access": ["pcie", "psc:m", "set:cal", "set:fd", "set:sys", "usb:ds", "fsp-srv"], + "service_host": ["file_io", "htc", "htcs"], + "kernel_capabilities": [{ + "type": "kernel_flags", + "value": { + "highest_thread_priority": 63, + "lowest_thread_priority": 20, + "lowest_cpu_id": 3, + "highest_cpu_id": 3 + } + }, { + "type": "syscalls", + "value": { + "svcSetHeapSize": "0x01", + "svcSetMemoryPermission": "0x02", + "svcSetMemoryAttribute": "0x03", + "svcMapMemory": "0x04", + "svcUnmapMemory": "0x05", + "svcQueryMemory": "0x06", + "svcExitProcess": "0x07", + "svcCreateThread": "0x08", + "svcStartThread": "0x09", + "svcExitThread": "0x0a", + "svcSleepThread": "0x0b", + "svcGetThreadPriority": "0x0c", + "svcSetThreadPriority": "0x0d", + "svcGetThreadCoreMask": "0x0e", + "svcSetThreadCoreMask": "0x0f", + "svcGetCurrentProcessorNumber": "0x10", + "svcSignalEvent": "0x11", + "svcClearEvent": "0x12", + "svcMapSharedMemory": "0x13", + "svcUnmapSharedMemory": "0x14", + "svcCreateTransferMemory": "0x15", + "svcCloseHandle": "0x16", + "svcResetSignal": "0x17", + "svcWaitSynchronization": "0x18", + "svcCancelSynchronization": "0x19", + "svcArbitrateLock": "0x1a", + "svcArbitrateUnlock": "0x1b", + "svcWaitProcessWideKeyAtomic": "0x1c", + "svcSignalProcessWideKey": "0x1d", + "svcGetSystemTick": "0x1e", + "svcConnectToNamedPort": "0x1f", + "svcSendSyncRequestLight": "0x20", + "svcSendSyncRequest": "0x21", + "svcSendSyncRequestWithUserBuffer": "0x22", + "svcSendAsyncRequestWithUserBuffer": "0x23", + "svcGetProcessId": "0x24", + "svcGetThreadId": "0x25", + "svcBreak": "0x26", + "svcOutputDebugString": "0x27", + "svcReturnFromException": "0x28", + "svcGetInfo": "0x29", + "svcWaitForAddress": "0x34", + "svcSignalToAddress": "0x35", + "svcCreateSession": "0x40", + "svcAcceptSession": "0x41", + "svcReplyAndReceiveLight": "0x42", + "svcReplyAndReceive": "0x43", + "svcReplyAndReceiveWithUserBuffer": "0x44", + "svcCreateEvent": "0x45", + "svcMapTransferMemory": "0x51", + "svcUnmapTransferMemory": "0x52", + "svcQueryIoMapping": "0x55", + "svcCallSecureMonitor": "0x7f" + } + }, { + "type": "map", + "value": { + "address": "0x12000000", + "is_ro": false, + "size": "0x04010000", + "is_io": true + } + }, { + "type": "irq_pair", + "value": [130, null] + }, { + "type": "irq_pair", + "value": [131, 132] + }, { + "type": "min_kernel_version", + "value": "0x0030" + }, { + "type": "handle_table_size", + "value": 0 + }] +} \ No newline at end of file diff --git a/stratosphere/htc/source/htc_main.cpp b/stratosphere/htc/source/htc_main.cpp new file mode 100644 index 000000000..c0d83289b --- /dev/null +++ b/stratosphere/htc/source/htc_main.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +extern "C" { + extern u32 __start__; + + u32 __nx_applet_type = AppletType_None; + u32 __nx_fs_num_sessions = 1; + + #define INNER_HEAP_SIZE 0x0 + size_t nx_inner_heap_size = INNER_HEAP_SIZE; + char nx_inner_heap[INNER_HEAP_SIZE]; + + void __libnx_initheap(void); + void __appInit(void); + void __appExit(void); + + void *__libnx_alloc(size_t size); + void *__libnx_aligned_alloc(size_t alignment, size_t size); + void __libnx_free(void *mem); +} + +namespace ams { + + ncm::ProgramId CurrentProgramId = ncm::SystemProgramId::Htc; + +} + +using namespace ams; + +namespace ams::htc { + + namespace { + + alignas(0x40) constinit u8 g_heap_buffer[4_KB]; + lmem::HeapHandle g_heap_handle; + + void *Allocate(size_t size) { + return lmem::AllocateFromExpHeap(g_heap_handle, size); + } + + void Deallocate(void *p, size_t size) { + return lmem::FreeToExpHeap(g_heap_handle, p); + } + + void InitializeHeap() { + /* Setup server allocator. */ + g_heap_handle = lmem::CreateExpHeap(g_heap_buffer, sizeof(g_heap_buffer), lmem::CreateOption_ThreadSafe); + } + + } + +} + +void __libnx_initheap(void) { + void* addr = nx_inner_heap; + size_t size = nx_inner_heap_size; + + /* Newlib */ + extern char* fake_heap_start; + extern char* fake_heap_end; + + fake_heap_start = (char*)addr; + fake_heap_end = (char*)addr + size; + + ams::htc::InitializeHeap(); +} + +void __appInit(void) { + hos::InitializeForStratosphere(); + + fs::SetAllocator(htc::Allocate, htc::Deallocate); + + sm::DoWithSession([&]() { + R_ABORT_UNLESS(setsysInitialize()); + R_ABORT_UNLESS(fsInitialize()); + }); + + R_ABORT_UNLESS(fs::MountSdCard("sdmc")); + + ams::CheckApiVersion(); +} + +void __appExit(void) { + fsExit(); + setsysExit(); +} + +namespace ams { + + void *Malloc(size_t size) { + AMS_ABORT("ams::Malloc was called"); + } + + void Free(void *ptr) { + AMS_ABORT("ams::Free was called"); + } + +} + +void *operator new(size_t size) { + AMS_ABORT("operator new(size_t) was called"); +} + +void operator delete(void *p) { + AMS_ABORT("operator delete(void *) was called"); +} + +void *__libnx_alloc(size_t size) { + AMS_ABORT("__libnx_alloc was called"); +} + +void *__libnx_aligned_alloc(size_t alignment, size_t size) { + AMS_ABORT("__libnx_aligned_alloc was called"); +} + +void __libnx_free(void *mem) { + AMS_ABORT("__libnx_free was called"); +} + +int main(int argc, char **argv) +{ + /* Set thread name. */ + os::SetThreadNamePointer(os::GetCurrentThread(), AMS_GET_SYSTEM_THREAD_NAME(htc, Main)); + AMS_ASSERT(os::GetThreadPriority(os::GetCurrentThread()) == AMS_GET_SYSTEM_THREAD_PRIORITY(htc, Main)); + + /* TODO */ + + /* Cleanup */ + return 0; +} + From 83c1c175ba594fccf7f70e56e7507b3840a2bf61 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 7 Feb 2021 18:34:12 -0800 Subject: [PATCH 014/280] htc: skeleton some more of main --- .../libstratosphere/include/stratosphere.hpp | 4 + .../include/stratosphere/htc.hpp | 17 ++++ .../include/stratosphere/htcfs.hpp | 17 ++++ .../include/stratosphere/htclow.hpp | 18 ++++ .../htclow/htclow_manager_holder.hpp | 35 ++++++++ .../stratosphere/htclow/htclow_types.hpp | 33 +++++++ .../include/stratosphere/htcs.hpp | 17 ++++ .../source/htclow/htclow_manager_holder.cpp | 85 +++++++++++++++++++ stratosphere/htc/source/htc_main.cpp | 42 +++++++++ 9 files changed, 268 insertions(+) create mode 100644 libraries/libstratosphere/include/stratosphere/htc.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/htcfs.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/htclow.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/htclow/htclow_manager_holder.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/htclow/htclow_types.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/htcs.hpp create mode 100644 libraries/libstratosphere/source/htclow/htclow_manager_holder.cpp diff --git a/libraries/libstratosphere/include/stratosphere.hpp b/libraries/libstratosphere/include/stratosphere.hpp index f9c66eb31..2b083f991 100644 --- a/libraries/libstratosphere/include/stratosphere.hpp +++ b/libraries/libstratosphere/include/stratosphere.hpp @@ -53,6 +53,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include diff --git a/libraries/libstratosphere/include/stratosphere/htc.hpp b/libraries/libstratosphere/include/stratosphere/htc.hpp new file mode 100644 index 000000000..999d1a35e --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htc.hpp @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + diff --git a/libraries/libstratosphere/include/stratosphere/htcfs.hpp b/libraries/libstratosphere/include/stratosphere/htcfs.hpp new file mode 100644 index 000000000..999d1a35e --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htcfs.hpp @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + diff --git a/libraries/libstratosphere/include/stratosphere/htclow.hpp b/libraries/libstratosphere/include/stratosphere/htclow.hpp new file mode 100644 index 000000000..4d21e65f7 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htclow.hpp @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include diff --git a/libraries/libstratosphere/include/stratosphere/htclow/htclow_manager_holder.hpp b/libraries/libstratosphere/include/stratosphere/htclow/htclow_manager_holder.hpp new file mode 100644 index 000000000..54ac1f924 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htclow/htclow_manager_holder.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::htclow { + + class HtclowManager; + + namespace HtclowManagerHolder { + + void AddReference(); + void Release(); + + HtclowManager *GetHtclowManager(); + + void SetDefaultDriver(htclow::impl::DriverType driver_type); + + } + +} diff --git a/libraries/libstratosphere/include/stratosphere/htclow/htclow_types.hpp b/libraries/libstratosphere/include/stratosphere/htclow/htclow_types.hpp new file mode 100644 index 000000000..b05857437 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htclow/htclow_types.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htclow { + + namespace impl { + + enum class DriverType { + Socket = 2, + Usb = 3, + HostBridge = 4, + PlainChannel = 5, + }; + + } + + +} diff --git a/libraries/libstratosphere/include/stratosphere/htcs.hpp b/libraries/libstratosphere/include/stratosphere/htcs.hpp new file mode 100644 index 000000000..999d1a35e --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htcs.hpp @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + diff --git a/libraries/libstratosphere/source/htclow/htclow_manager_holder.cpp b/libraries/libstratosphere/source/htclow/htclow_manager_holder.cpp new file mode 100644 index 000000000..07ce8894c --- /dev/null +++ b/libraries/libstratosphere/source/htclow/htclow_manager_holder.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::htclow::HtclowManagerHolder { + + namespace { + + constinit os::SdkMutex g_holder_mutex; + constinit int g_holder_reference_count = 0; + + mem::StandardAllocator g_allocator; + + constinit HtclowManager *g_manager = nullptr; + + constinit htclow::impl::DriverType g_default_driver_type = htclow::impl::DriverType::Socket; + + alignas(os::MemoryPageSize) u8 g_heap_buffer[928_KB]; + + } + + void AddReference() { + std::scoped_lock lk(g_holder_mutex); + + if ((g_holder_reference_count++) == 0) { + /* Initialize the allocator for the manager. */ + g_allocator.Initialize(g_heap_buffer, sizeof(g_heap_buffer)); + + /* TODO: Allocate the manager. */ + /* g_manager = g_allocator.Allocate(sizeof(HtclowManager), alignof(HtclowManager)); */ + + /* TODO: Construct the manager. */ + /* std::construct_at(g_manager, std::addressof(g_allocator)); */ + + /* TODO: Open the driver. */ + /* R_ABORT_UNLESS(g_manager->OpenDriver(g_default_driver_type)); */ + } + + AMS_ASSERT(g_holder_reference_count > 0); + } + + void Release() { + std::scoped_lock lk(g_holder_mutex); + + AMS_ASSERT(g_holder_reference_count > 0); + + if ((--g_holder_reference_count) == 0) { + /* TODO: Disconnect. */ + /* g_manager->Disconnect(); */ + + /* TODO: Close the driver. */ + /* g_manager->CloseDriver(); */ + + /* TODO: Destroy the manager. */ + /* std::destroy_at(g_manager); */ + /* g_allocator.Free(g_manager); */ + /* g_manager = nullptr; */ + + /* Finalize the allocator. */ + g_allocator.Finalize(); + } + } + + HtclowManager *GetHtclowManager() { + return g_manager; + } + + void SetDefaultDriver(htclow::impl::DriverType driver_type) { + g_default_driver_type = driver_type; + } + +} diff --git a/stratosphere/htc/source/htc_main.cpp b/stratosphere/htc/source/htc_main.cpp index c0d83289b..775dff411 100644 --- a/stratosphere/htc/source/htc_main.cpp +++ b/stratosphere/htc/source/htc_main.cpp @@ -132,12 +132,54 @@ void __libnx_free(void *mem) { AMS_ABORT("__libnx_free was called"); } +namespace ams::htc { + + namespace { + + constexpr htclow::impl::DriverType DefaultHtclowDriverType = htclow::impl::DriverType::Usb; + + htclow::impl::DriverType GetHtclowDriverType() { + /* Get the transport type. */ + char transport[0x10]; + if (settings::fwdbg::GetSettingsItemValue(transport, sizeof(transport), "bsp0", "tm_transport") == 0) { + return DefaultHtclowDriverType; + } + + /* Make the transport type case insensitive. */ + transport[util::size(transport) - 1] = '\x00'; + for (size_t i = 0; i < util::size(transport); ++i) { + transport[i] = std::tolower(static_cast(transport[i])); + } + + /* Select the transport. */ + if (std::strstr(transport, "usb")) { + return htclow::impl::DriverType::Usb; + } else if (std::strstr(transport, "hb")) { + return htclow::impl::DriverType::HostBridge; + } else if (std::strstr(transport, "plainchannel")) { + return htclow::impl::DriverType::PlainChannel; + } else { + return DefaultHtclowDriverType; + } + } + + } + +} + int main(int argc, char **argv) { /* Set thread name. */ os::SetThreadNamePointer(os::GetCurrentThread(), AMS_GET_SYSTEM_THREAD_NAME(htc, Main)); AMS_ASSERT(os::GetThreadPriority(os::GetCurrentThread()) == AMS_GET_SYSTEM_THREAD_PRIORITY(htc, Main)); + /* Get and set the default driver type. */ + const auto driver_type = htc::GetHtclowDriverType(); + htclow::HtclowManagerHolder::SetDefaultDriver(driver_type); + + /* Initialize the htclow manager. */ + htclow::HtclowManagerHolder::AddReference(); + /* TODO */ /* Cleanup */ From cf99f54a34654e575de377e993a1e607164835d8 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 7 Feb 2021 23:13:04 -0800 Subject: [PATCH 015/280] htc: skeleton much of the type hierarchy for htclow manager --- .../include/stratosphere/htclow.hpp | 1 + .../htclow/htclow_channel_types.hpp | 30 +++++++++ .../htclow/htclow_module_types.hpp | 31 ++++++++++ .../htclow/impl/htclow_internal_types.hpp | 61 +++++++++++++++++++ .../source/htclow/ctrl/htclow_ctrl_packet.hpp | 25 ++++++++ .../ctrl/htclow_ctrl_packet_factory.hpp | 40 ++++++++++++ .../htclow/ctrl/htclow_ctrl_send_buffer.hpp | 34 +++++++++++ .../htclow/ctrl/htclow_ctrl_service.hpp | 58 ++++++++++++++++++ .../ctrl/htclow_ctrl_settings_holder.hpp | 31 ++++++++++ .../source/htclow/ctrl/htclow_ctrl_state.hpp | 25 ++++++++ .../htclow/ctrl/htclow_ctrl_state_machine.hpp | 44 +++++++++++++ .../htclow/driver/htclow_driver_manager.hpp | 34 +++++++++++ .../source/htclow/driver/htclow_i_driver.hpp | 34 +++++++++++ .../source/htclow/htclow_listener.hpp | 39 ++++++++++++ .../source/htclow/htclow_manager.cpp | 43 +++++++++++++ .../source/htclow/htclow_manager.hpp | 37 +++++++++++ .../source/htclow/htclow_manager_holder.cpp | 31 +++++----- .../source/htclow/htclow_manager_impl.cpp | 40 ++++++++++++ .../source/htclow/htclow_manager_impl.hpp | 50 +++++++++++++++ .../source/htclow/htclow_packet.hpp | 25 ++++++++ .../source/htclow/htclow_packet_factory.hpp | 28 +++++++++ .../source/htclow/htclow_worker.hpp | 42 +++++++++++++ .../source/htclow/mux/htclow_mux.hpp | 39 ++++++++++++ .../mux/htclow_mux_channel_impl_map.hpp | 56 +++++++++++++++++ .../mux/htclow_mux_global_send_buffer.hpp | 38 ++++++++++++ .../htclow/mux/htclow_mux_task_manager.hpp | 40 ++++++++++++ 26 files changed, 942 insertions(+), 14 deletions(-) create mode 100644 libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/htclow/htclow_module_types.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/htclow/impl/htclow_internal_types.hpp create mode 100644 libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet.hpp create mode 100644 libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet_factory.hpp create mode 100644 libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_send_buffer.hpp create mode 100644 libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp create mode 100644 libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_settings_holder.hpp create mode 100644 libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state.hpp create mode 100644 libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp create mode 100644 libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp create mode 100644 libraries/libstratosphere/source/htclow/driver/htclow_i_driver.hpp create mode 100644 libraries/libstratosphere/source/htclow/htclow_listener.hpp create mode 100644 libraries/libstratosphere/source/htclow/htclow_manager.cpp create mode 100644 libraries/libstratosphere/source/htclow/htclow_manager.hpp create mode 100644 libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp create mode 100644 libraries/libstratosphere/source/htclow/htclow_manager_impl.hpp create mode 100644 libraries/libstratosphere/source/htclow/htclow_packet.hpp create mode 100644 libraries/libstratosphere/source/htclow/htclow_packet_factory.hpp create mode 100644 libraries/libstratosphere/source/htclow/htclow_worker.hpp create mode 100644 libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp create mode 100644 libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp create mode 100644 libraries/libstratosphere/source/htclow/mux/htclow_mux_global_send_buffer.hpp create mode 100644 libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp diff --git a/libraries/libstratosphere/include/stratosphere/htclow.hpp b/libraries/libstratosphere/include/stratosphere/htclow.hpp index 4d21e65f7..07be9c057 100644 --- a/libraries/libstratosphere/include/stratosphere/htclow.hpp +++ b/libraries/libstratosphere/include/stratosphere/htclow.hpp @@ -15,4 +15,5 @@ */ #pragma once #include +#include #include diff --git a/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp b/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp new file mode 100644 index 000000000..c72487fdb --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::htclow { + + using ChannelId = u16; + + struct ChannelType { + bool _is_initialized; + ModuleId _module_id; + ChannelId _channel_id; + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/htclow/htclow_module_types.hpp b/libraries/libstratosphere/include/stratosphere/htclow/htclow_module_types.hpp new file mode 100644 index 000000000..ef8c92e3f --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htclow/htclow_module_types.hpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::htclow { + + enum class ModuleId : u8 { + /* ... */ + }; + + struct ModuleType { + bool _is_initialized; + ModuleId _id; + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/htclow/impl/htclow_internal_types.hpp b/libraries/libstratosphere/include/stratosphere/htclow/impl/htclow_internal_types.hpp new file mode 100644 index 000000000..30bcb5860 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htclow/impl/htclow_internal_types.hpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::htclow::impl { + + struct ChannelInternalType { + ChannelId channel_id; + s8 reserved; + ModuleId module_id; + }; + static_assert(sizeof(ChannelInternalType) == 4); + + ALWAYS_INLINE ChannelInternalType ConvertChannelType(ChannelType channel) { + return { + .channel_id = channel._channel_id, + .reserved = 0, + .module_id = channel._module_id, + }; + } + + ALWAYS_INLINE bool operator==(const ChannelInternalType &lhs, const ChannelInternalType &rhs) { + return lhs.module_id == rhs.module_id && lhs.reserved == rhs.reserved && lhs.channel_id == rhs.channel_id; + } + + ALWAYS_INLINE bool operator!=(const ChannelInternalType &lhs, const ChannelInternalType &rhs) { + return !(lhs == rhs); + } + + ALWAYS_INLINE bool operator<(const ChannelInternalType &lhs, const ChannelInternalType &rhs) { + return lhs.module_id < rhs.module_id || lhs.reserved < rhs.reserved || lhs.channel_id < rhs.channel_id; + } + + ALWAYS_INLINE bool operator>(const ChannelInternalType &lhs, const ChannelInternalType &rhs) { + return rhs < lhs; + } + + ALWAYS_INLINE bool operator<=(const ChannelInternalType &lhs, const ChannelInternalType &rhs) { + return !(lhs > rhs); + } + + ALWAYS_INLINE bool operator>=(const ChannelInternalType &lhs, const ChannelInternalType &rhs) { + return !(lhs < rhs); + } + +} diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet.hpp new file mode 100644 index 000000000..630b8ede1 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htclow::ctrl { + + class HtcctrlPacket : public util::IntrusiveListBaseNode { + /* TODO */ + }; + +} diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet_factory.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet_factory.hpp new file mode 100644 index 000000000..d370ee39c --- /dev/null +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet_factory.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htclow::ctrl { + + class HtcctrlPacketFactory { + private: + mem::StandardAllocator *m_allocator; + u32 m_seed; + public: + HtcctrlPacketFactory(mem::StandardAllocator *allocator) : m_allocator(allocator) { + /* Get the current time. */ + const u64 time = os::GetSystemTick().GetInt64Value(); + + /* Set the random seed. */ + { + util::TinyMT rng; + rng.Initialize(reinterpret_cast(std::addressof(time)), sizeof(time) / sizeof(u32)); + + m_seed = rng.GenerateRandomU32(); + } + } + }; + +} diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_send_buffer.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_send_buffer.hpp new file mode 100644 index 000000000..5bf7ff413 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_send_buffer.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "htclow_ctrl_packet.hpp" + +namespace ams::htclow::ctrl { + + class HtcctrlPacketFactory; + + class HtcctrlSendBuffer { + private: + using PacketList = util::IntrusiveListBaseTraits::ListType; + private: + HtcctrlPacketFactory *m_packet_factory; + PacketList m_packet_list; + public: + HtcctrlSendBuffer(HtcctrlPacketFactory *pf) : m_packet_factory(pf), m_packet_list() { /* ... */ } + }; + +} diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp new file mode 100644 index 000000000..df0988614 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "htclow_ctrl_settings_holder.hpp" +#include "htclow_ctrl_send_buffer.hpp" + +namespace ams::htclow { + + namespace ctrl { + + class HtcctrlPacketFactory; + class HtcctrlStateMachine; + + } + + namespace mux { + + class Mux; + + } + +} + +namespace ams::htclow::ctrl { + + class HtcctrlService { + private: + SettingsHolder m_settings_holder; + u8 m_beacon_response[0x1000]; + u8 m_1100[0x1000]; + HtcctrlPacketFactory *m_packet_factory; + HtcctrlStateMachine *m_state_machine; + mux::Mux *m_mux; + os::EventType m_event; + HtcctrlSendBuffer m_send_buffer; + os::SdkMutex m_mutex; + os::SdkConditionVariable m_condvar; + u8 m_2170[0x1000]; + u16 m_version; + public: + HtcctrlService(HtcctrlPacketFactory *pf, HtcctrlStateMachine *sm, mux::Mux *mux); + }; + +} diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_settings_holder.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_settings_holder.hpp new file mode 100644 index 000000000..762f57808 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_settings_holder.hpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htclow::ctrl { + + class SettingsHolder { + private: + char m_hardware_type[0x40]; + char m_target_name[0x40]; + char m_serial_number[0x40]; + char m_firmware_version[0x40]; + public: + SettingsHolder() { /* ... */ } + }; + +} diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state.hpp new file mode 100644 index 000000000..0df654f61 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htclow::ctrl { + + enum HtcctrlState : u32 { + /* ... */ + }; + +} diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp new file mode 100644 index 000000000..a662950ea --- /dev/null +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "htclow_ctrl_state.hpp" + +namespace ams::htclow::ctrl { + + class HtcctrlStateMachine { + private: + struct ServiceChannelState { + u32 _00; + u32 _04; + }; + + static constexpr int MaxChannelCount = 10; + + using MapType = util::FixedMap; + + static constexpr size_t MapRequiredMemorySize = MapType::GetRequiredMemorySize(MaxChannelCount); + private: + u8 m_map_buffer[MapRequiredMemorySize]; + MapType m_map; + HtcctrlState m_state; + HtcctrlState m_prev_state; + os::SdkMutex m_mutex; + public: + HtcctrlStateMachine(); + }; + +} diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp new file mode 100644 index 000000000..caf909ee9 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "htclow_i_driver.hpp" + +namespace ams::htclow::driver { + + class DriverManager { + private: + std::optional m_driver_type{}; + /* TODO: SocketDriver m_socket_driver; */ + /* TODO: UsbDriver m_usb_driver; */ + /* TODO: PlainChannelDriver m_plain_channel_driver; */ + os::SdkMutex m_mutex{}; + IDriver *m_open_driver{}; + public: + DriverManager() = default; + }; + +} diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_i_driver.hpp b/libraries/libstratosphere/source/htclow/driver/htclow_i_driver.hpp new file mode 100644 index 000000000..7ed703854 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/driver/htclow_i_driver.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htclow::driver { + + class IDriver { + public: + virtual Result Open() = 0; + virtual void Close() = 0; + virtual Result Connect(os::EventType *event) = 0; + virtual void Shutdown() = 0; + virtual Result Send(const void *src, int src_size) = 0; + virtual Result Receive(void *dst, int dst_size) = 0; + virtual void CancelSendReceive() = 0; + virtual void Suspend() = 0; + virtual void Resume() = 0; + }; + +} diff --git a/libraries/libstratosphere/source/htclow/htclow_listener.hpp b/libraries/libstratosphere/source/htclow/htclow_listener.hpp new file mode 100644 index 000000000..0526aea50 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/htclow_listener.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "htclow_worker.hpp" + +namespace ams::htclow { + + class Listener { + private: + u32 m_thread_stack_size; + mem::StandardAllocator *m_allocator; + mux::Mux *m_mux; + ctrl::HtcctrlService *m_service; + Worker *m_worker; + os::EventType m_event; + os::ThreadType m_listen_thread; + void *m_listen_thread_stack; + driver::IDriver *m_driver; + bool m_thread_running; + bool m_cancelled; + public: + Listener(mem::StandardAllocator *allocator, mux::Mux *mux, ctrl::HtcctrlService *ctrl_srv, Worker *worker); + }; + +} diff --git a/libraries/libstratosphere/source/htclow/htclow_manager.cpp b/libraries/libstratosphere/source/htclow/htclow_manager.cpp new file mode 100644 index 000000000..527e4d50d --- /dev/null +++ b/libraries/libstratosphere/source/htclow/htclow_manager.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htclow_manager.hpp" +#include "htclow_manager_impl.hpp" + +namespace ams::htclow { + + HtclowManager::HtclowManager(mem::StandardAllocator *allocator) : m_allocator(allocator), m_impl(static_cast(allocator->Allocate(sizeof(HtclowManagerImpl), alignof(HtclowManagerImpl)))) { + std::construct_at(m_impl, m_allocator); + } + + HtclowManager::~HtclowManager() { + std::destroy_at(m_impl); + m_allocator->Free(m_impl); + } + + Result HtclowManager::OpenDriver(impl::DriverType driver_type) { + return m_impl->OpenDriver(driver_type); + } + + void HtclowManager::CloseDriver() { + return m_impl->CloseDriver(); + } + + void HtclowManager::Disconnect() { + return m_impl->Disconnect(); + } + +} diff --git a/libraries/libstratosphere/source/htclow/htclow_manager.hpp b/libraries/libstratosphere/source/htclow/htclow_manager.hpp new file mode 100644 index 000000000..c4160426b --- /dev/null +++ b/libraries/libstratosphere/source/htclow/htclow_manager.hpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htclow { + + class HtclowManagerImpl; + + class HtclowManager { + private: + mem::StandardAllocator *m_allocator; + HtclowManagerImpl *m_impl; + public: + HtclowManager(mem::StandardAllocator *allocator); + ~HtclowManager(); + public: + Result OpenDriver(impl::DriverType driver_type); + void CloseDriver(); + + void Disconnect(); + }; + +} diff --git a/libraries/libstratosphere/source/htclow/htclow_manager_holder.cpp b/libraries/libstratosphere/source/htclow/htclow_manager_holder.cpp index 07ce8894c..d97854b94 100644 --- a/libraries/libstratosphere/source/htclow/htclow_manager_holder.cpp +++ b/libraries/libstratosphere/source/htclow/htclow_manager_holder.cpp @@ -14,6 +14,7 @@ * along with this program. If not, see . */ #include +#include "htclow_manager.hpp" namespace ams::htclow::HtclowManagerHolder { @@ -39,14 +40,14 @@ namespace ams::htclow::HtclowManagerHolder { /* Initialize the allocator for the manager. */ g_allocator.Initialize(g_heap_buffer, sizeof(g_heap_buffer)); - /* TODO: Allocate the manager. */ - /* g_manager = g_allocator.Allocate(sizeof(HtclowManager), alignof(HtclowManager)); */ + /* Allocate the manager. */ + g_manager = static_cast(g_allocator.Allocate(sizeof(HtclowManager), alignof(HtclowManager))); - /* TODO: Construct the manager. */ - /* std::construct_at(g_manager, std::addressof(g_allocator)); */ + /* Construct the manager. */ + std::construct_at(g_manager, std::addressof(g_allocator)); - /* TODO: Open the driver. */ - /* R_ABORT_UNLESS(g_manager->OpenDriver(g_default_driver_type)); */ + /* Open the driver. */ + R_ABORT_UNLESS(g_manager->OpenDriver(g_default_driver_type)); } AMS_ASSERT(g_holder_reference_count > 0); @@ -58,16 +59,16 @@ namespace ams::htclow::HtclowManagerHolder { AMS_ASSERT(g_holder_reference_count > 0); if ((--g_holder_reference_count) == 0) { - /* TODO: Disconnect. */ - /* g_manager->Disconnect(); */ + /* Disconnect. */ + g_manager->Disconnect(); - /* TODO: Close the driver. */ - /* g_manager->CloseDriver(); */ + /* Close the driver. */ + g_manager->CloseDriver(); - /* TODO: Destroy the manager. */ - /* std::destroy_at(g_manager); */ - /* g_allocator.Free(g_manager); */ - /* g_manager = nullptr; */ + /* Destroy the manager. */ + std::destroy_at(g_manager); + g_allocator.Free(g_manager); + g_manager = nullptr; /* Finalize the allocator. */ g_allocator.Finalize(); @@ -75,6 +76,8 @@ namespace ams::htclow::HtclowManagerHolder { } HtclowManager *GetHtclowManager() { + std::scoped_lock lk(g_holder_mutex); + return g_manager; } diff --git a/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp b/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp new file mode 100644 index 000000000..e65f5c538 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htclow_manager_impl.hpp" + +namespace ams::htclow { + + HtclowManagerImpl::HtclowManagerImpl(mem::StandardAllocator *allocator) + : m_packet_factory(allocator), m_driver_manager(), m_mux(std::addressof(m_packet_factory), std::addressof(m_ctrl_state_machine)), + m_ctrl_packet_factory(allocator), m_ctrl_state_machine(), m_ctrl_service(std::addressof(m_ctrl_packet_factory), std::addressof(m_ctrl_state_machine), std::addressof(m_mux)), + m_worker(allocator, std::addressof(m_mux), std::addressof(m_ctrl_service)), + m_listener(allocator, std::addressof(m_mux), std::addressof(m_ctrl_service), std::addressof(m_worker)) + { + /* ... */ + } + + HtclowManagerImpl::~HtclowManagerImpl() { + /* ... */ + } + + //Result HtclowManagerImpl::OpenDriver(impl::DriverType driver_type); + // + //void HtclowManagerImpl::CloseDriver(); + // + //void HtclowManagerImpl::Disconnect(); + +} diff --git a/libraries/libstratosphere/source/htclow/htclow_manager_impl.hpp b/libraries/libstratosphere/source/htclow/htclow_manager_impl.hpp new file mode 100644 index 000000000..8bdac84b1 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/htclow_manager_impl.hpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "htclow_packet_factory.hpp" +#include "driver/htclow_driver_manager.hpp" +#include "mux/htclow_mux.hpp" +#include "ctrl/htclow_ctrl_packet_factory.hpp" +#include "ctrl/htclow_ctrl_state_machine.hpp" +#include "ctrl/htclow_ctrl_service.hpp" +#include "htclow_worker.hpp" +#include "htclow_listener.hpp" + +namespace ams::htclow { + + class HtclowManagerImpl { + private: + PacketFactory m_packet_factory; + driver::DriverManager m_driver_manager; + mux::Mux m_mux; + ctrl::HtcctrlPacketFactory m_ctrl_packet_factory; + ctrl::HtcctrlStateMachine m_ctrl_state_machine; + ctrl::HtcctrlService m_ctrl_service; + Worker m_worker; + Listener m_listener; + bool m_is_driver_open; + public: + HtclowManagerImpl(mem::StandardAllocator *allocator); + ~HtclowManagerImpl(); + public: + Result OpenDriver(impl::DriverType driver_type); + void CloseDriver(); + + void Disconnect(); + }; + +} diff --git a/libraries/libstratosphere/source/htclow/htclow_packet.hpp b/libraries/libstratosphere/source/htclow/htclow_packet.hpp new file mode 100644 index 000000000..89241c4be --- /dev/null +++ b/libraries/libstratosphere/source/htclow/htclow_packet.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htclow { + + class Packet : public util::IntrusiveListBaseNode { + /* TODO */ + }; + +} diff --git a/libraries/libstratosphere/source/htclow/htclow_packet_factory.hpp b/libraries/libstratosphere/source/htclow/htclow_packet_factory.hpp new file mode 100644 index 000000000..2a4f44093 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/htclow_packet_factory.hpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htclow { + + class PacketFactory { + private: + mem::StandardAllocator *m_allocator; + public: + PacketFactory(mem::StandardAllocator *allocator) : m_allocator(allocator) { /* ... */ } + }; + +} diff --git a/libraries/libstratosphere/source/htclow/htclow_worker.hpp b/libraries/libstratosphere/source/htclow/htclow_worker.hpp new file mode 100644 index 000000000..cc7b5fd8e --- /dev/null +++ b/libraries/libstratosphere/source/htclow/htclow_worker.hpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "driver/htclow_i_driver.hpp" +#include "ctrl/htclow_ctrl_service.hpp" +#include "mux/htclow_mux.hpp" + +namespace ams::htclow { + + class Worker { + private: + u32 m_thread_stack_size; + u8 m_04[0x7C024]; /* TODO... not knowing what an almost 128 KB field is is embarassing. */ + mem::StandardAllocator *m_allocator; + mux::Mux *m_mux; + ctrl::HtcctrlService *m_service; + driver::IDriver *m_driver; + os::ThreadType m_receive_thread; + os::ThreadType m_send_thread; + os::EventType m_event; + void *m_receive_thread_stack; + void *m_send_thread_stack; + u8 m_7C400; + public: + Worker(mem::StandardAllocator *allocator, mux::Mux *mux, ctrl::HtcctrlService *ctrl_srv); + }; + +} diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp new file mode 100644 index 000000000..03e4c0152 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "htclow_mux_task_manager.hpp" +#include "htclow_mux_channel_impl_map.hpp" +#include "htclow_mux_global_send_buffer.hpp" + +namespace ams::htclow::mux { + + class Mux { + private: + PacketFactory *m_packet_factory; + ctrl::HtcctrlStateMachine *m_state_machine; + TaskManager m_task_manager; + os::EventType m_wake_event; + ChannelImplMap m_channel_impl_map; + GlobalSendBuffer m_global_send_buffer; + os::SdkMutex m_mutex; + bool m_is_sleeping; + u16 m_version; + public: + Mux(PacketFactory *pf, ctrl::HtcctrlStateMachine *sm); + }; + +} diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp new file mode 100644 index 000000000..e5acfdcc2 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "htclow_mux_task_manager.hpp" + +namespace ams::htclow { + + class PacketFactory; + + namespace ctrl { + + class HtcctrlStateMachine; + + } + +} + +namespace ams::htclow::mux { + + class ChannelImplMap { + NON_COPYABLE(ChannelImplMap); + NON_MOVEABLE(ChannelImplMap); + public: + static constexpr int MaxChannelCount = 64; + + using MapType = util::FixedMap; + + static constexpr size_t MapRequiredMemorySize = MapType::GetRequiredMemorySize(MaxChannelCount); + private: + PacketFactory *m_packet_factory; + ctrl::HtcctrlStateMachine *m_state_machine; + TaskManager *m_task_manager; + os::EventType *m_event; + u8 m_map_buffer[MapRequiredMemorySize]; + MapType m_map; + u8 m_storage[0x5200]; /* TODO */ + bool m_storage_valid[MaxChannelCount]; + public: + ChannelImplMap(PacketFactory *pf, ctrl::HtcctrlStateMachine *sm, TaskManager *tm, os::EventType *ev); + }; + +} diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_global_send_buffer.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_global_send_buffer.hpp new file mode 100644 index 000000000..c9273c4fd --- /dev/null +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_global_send_buffer.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "../htclow_packet.hpp" + +namespace ams::htclow { + + class PacketFactory; + +} + +namespace ams::htclow::mux { + + class GlobalSendBuffer { + private: + using PacketList = util::IntrusiveListBaseTraits::ListType; + private: + PacketFactory *m_packet_factory; + PacketList m_packet_list; + public: + GlobalSendBuffer(PacketFactory *pf) : m_packet_factory(pf), m_packet_list() { /* ... */ } + }; + +} diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp new file mode 100644 index 000000000..6e9033c30 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htclow::mux { + + constexpr inline int MaxTaskCount = 0x80; + + class TaskManager { + private: + struct Task { + impl::ChannelInternalType channel; + os::EventType event; + u8 _30; + u8 _31; + u8 _32; + u64 _38; + }; + private: + bool m_valid[MaxTaskCount]; + Task m_tasks[MaxTaskCount]; + public: + TaskManager() : m_valid() { /* ... */ } + }; + +} From 1687bf2e07b0a4eb0f03c4e766dbd9a500ee99e1 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 7 Feb 2021 23:37:16 -0800 Subject: [PATCH 016/280] htclow: fix copyright headers, skeleton more manager types --- .../htclow/impl/htclow_internal_types.hpp | 14 ++--- .../source/htclow/ctrl/htclow_ctrl_packet.hpp | 2 +- .../ctrl/htclow_ctrl_packet_factory.hpp | 2 +- .../htclow/ctrl/htclow_ctrl_send_buffer.hpp | 2 +- .../htclow/ctrl/htclow_ctrl_service.hpp | 2 +- .../ctrl/htclow_ctrl_settings_holder.hpp | 2 +- .../source/htclow/ctrl/htclow_ctrl_state.hpp | 4 +- .../htclow/ctrl/htclow_ctrl_state_machine.cpp | 57 +++++++++++++++++++ .../htclow/ctrl/htclow_ctrl_state_machine.hpp | 2 +- .../htclow/driver/htclow_driver_manager.hpp | 2 +- .../source/htclow/driver/htclow_i_driver.hpp | 2 +- .../source/htclow/htclow_listener.hpp | 2 +- .../source/htclow/htclow_manager.cpp | 2 +- .../source/htclow/htclow_manager.hpp | 2 +- .../source/htclow/htclow_manager_holder.cpp | 2 +- .../source/htclow/htclow_manager_impl.cpp | 2 +- .../source/htclow/htclow_manager_impl.hpp | 2 +- .../source/htclow/htclow_packet.hpp | 2 +- .../source/htclow/htclow_packet_factory.hpp | 2 +- .../source/htclow/htclow_worker.hpp | 2 +- .../source/htclow/mux/htclow_mux.cpp | 29 ++++++++++ .../source/htclow/mux/htclow_mux.hpp | 4 +- .../mux/htclow_mux_channel_impl_map.cpp | 33 +++++++++++ .../mux/htclow_mux_channel_impl_map.hpp | 6 +- .../mux/htclow_mux_global_send_buffer.hpp | 2 +- .../htclow/mux/htclow_mux_task_manager.hpp | 2 +- 26 files changed, 152 insertions(+), 33 deletions(-) create mode 100644 libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp create mode 100644 libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp create mode 100644 libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.cpp diff --git a/libraries/libstratosphere/include/stratosphere/htclow/impl/htclow_internal_types.hpp b/libraries/libstratosphere/include/stratosphere/htclow/impl/htclow_internal_types.hpp index 30bcb5860..bee584a85 100644 --- a/libraries/libstratosphere/include/stratosphere/htclow/impl/htclow_internal_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/htclow/impl/htclow_internal_types.hpp @@ -26,7 +26,7 @@ namespace ams::htclow::impl { }; static_assert(sizeof(ChannelInternalType) == 4); - ALWAYS_INLINE ChannelInternalType ConvertChannelType(ChannelType channel) { + constexpr ALWAYS_INLINE ChannelInternalType ConvertChannelType(ChannelType channel) { return { .channel_id = channel._channel_id, .reserved = 0, @@ -34,27 +34,27 @@ namespace ams::htclow::impl { }; } - ALWAYS_INLINE bool operator==(const ChannelInternalType &lhs, const ChannelInternalType &rhs) { + constexpr ALWAYS_INLINE bool operator==(const ChannelInternalType &lhs, const ChannelInternalType &rhs) { return lhs.module_id == rhs.module_id && lhs.reserved == rhs.reserved && lhs.channel_id == rhs.channel_id; } - ALWAYS_INLINE bool operator!=(const ChannelInternalType &lhs, const ChannelInternalType &rhs) { + constexpr ALWAYS_INLINE bool operator!=(const ChannelInternalType &lhs, const ChannelInternalType &rhs) { return !(lhs == rhs); } - ALWAYS_INLINE bool operator<(const ChannelInternalType &lhs, const ChannelInternalType &rhs) { + constexpr ALWAYS_INLINE bool operator<(const ChannelInternalType &lhs, const ChannelInternalType &rhs) { return lhs.module_id < rhs.module_id || lhs.reserved < rhs.reserved || lhs.channel_id < rhs.channel_id; } - ALWAYS_INLINE bool operator>(const ChannelInternalType &lhs, const ChannelInternalType &rhs) { + constexpr ALWAYS_INLINE bool operator>(const ChannelInternalType &lhs, const ChannelInternalType &rhs) { return rhs < lhs; } - ALWAYS_INLINE bool operator<=(const ChannelInternalType &lhs, const ChannelInternalType &rhs) { + constexpr ALWAYS_INLINE bool operator<=(const ChannelInternalType &lhs, const ChannelInternalType &rhs) { return !(lhs > rhs); } - ALWAYS_INLINE bool operator>=(const ChannelInternalType &lhs, const ChannelInternalType &rhs) { + constexpr ALWAYS_INLINE bool operator>=(const ChannelInternalType &lhs, const ChannelInternalType &rhs) { return !(lhs < rhs); } diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet.hpp index 630b8ede1..574dcf845 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * Copyright (c) 2018-2020 Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet_factory.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet_factory.hpp index d370ee39c..8670672d7 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet_factory.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet_factory.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * Copyright (c) 2018-2020 Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_send_buffer.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_send_buffer.hpp index 5bf7ff413..abe1cf239 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_send_buffer.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_send_buffer.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * Copyright (c) 2018-2020 Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp index df0988614..9c42fd258 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * Copyright (c) 2018-2020 Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_settings_holder.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_settings_holder.hpp index 762f57808..df956e832 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_settings_holder.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_settings_holder.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * Copyright (c) 2018-2020 Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state.hpp index 0df654f61..9d739758a 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * Copyright (c) 2018-2020 Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -19,7 +19,7 @@ namespace ams::htclow::ctrl { enum HtcctrlState : u32 { - /* ... */ + HtcctrlState_Eleven = 11, }; } diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp new file mode 100644 index 000000000..655a1f8c1 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htclow_ctrl_state_machine.hpp" + +namespace ams::htclow::ctrl { + + namespace { + + constexpr const impl::ChannelInternalType ServiceChannels[] = { + { + .channel_id = 0, + .module_id = static_cast(1), + }, + { + .channel_id = 1, + .module_id = static_cast(3), + }, + { + .channel_id = 2, + .module_id = static_cast(3), + }, + { + .channel_id = 0, + .module_id = static_cast(4), + }, + }; + + } + + HtcctrlStateMachine::HtcctrlStateMachine() : m_map(), m_state(HtcctrlState_Eleven), m_prev_state(HtcctrlState_Eleven), m_mutex() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our map. */ + m_map.Initialize(MaxChannelCount, m_map_buffer, sizeof(m_map_buffer)); + + /* Insert each service channel the map. */ + for (const auto &channel : ServiceChannels) { + m_map.insert(std::make_pair(impl::ChannelInternalType{channel}, ServiceChannelState{})); + } + } + +} diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp index a662950ea..10053e793 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * Copyright (c) 2018-2020 Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp index caf909ee9..5eb99553e 100644 --- a/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp +++ b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * Copyright (c) 2018-2020 Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_i_driver.hpp b/libraries/libstratosphere/source/htclow/driver/htclow_i_driver.hpp index 7ed703854..ef13c635e 100644 --- a/libraries/libstratosphere/source/htclow/driver/htclow_i_driver.hpp +++ b/libraries/libstratosphere/source/htclow/driver/htclow_i_driver.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * Copyright (c) 2018-2020 Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, diff --git a/libraries/libstratosphere/source/htclow/htclow_listener.hpp b/libraries/libstratosphere/source/htclow/htclow_listener.hpp index 0526aea50..b6771cf87 100644 --- a/libraries/libstratosphere/source/htclow/htclow_listener.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_listener.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * Copyright (c) 2018-2020 Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, diff --git a/libraries/libstratosphere/source/htclow/htclow_manager.cpp b/libraries/libstratosphere/source/htclow/htclow_manager.cpp index 527e4d50d..44c670d29 100644 --- a/libraries/libstratosphere/source/htclow/htclow_manager.cpp +++ b/libraries/libstratosphere/source/htclow/htclow_manager.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * Copyright (c) 2018-2020 Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, diff --git a/libraries/libstratosphere/source/htclow/htclow_manager.hpp b/libraries/libstratosphere/source/htclow/htclow_manager.hpp index c4160426b..727422a25 100644 --- a/libraries/libstratosphere/source/htclow/htclow_manager.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_manager.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * Copyright (c) 2018-2020 Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, diff --git a/libraries/libstratosphere/source/htclow/htclow_manager_holder.cpp b/libraries/libstratosphere/source/htclow/htclow_manager_holder.cpp index d97854b94..b33bc3600 100644 --- a/libraries/libstratosphere/source/htclow/htclow_manager_holder.cpp +++ b/libraries/libstratosphere/source/htclow/htclow_manager_holder.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * Copyright (c) 2018-2020 Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, diff --git a/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp b/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp index e65f5c538..2bf71ffa0 100644 --- a/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp +++ b/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * Copyright (c) 2018-2020 Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, diff --git a/libraries/libstratosphere/source/htclow/htclow_manager_impl.hpp b/libraries/libstratosphere/source/htclow/htclow_manager_impl.hpp index 8bdac84b1..8a6f05d11 100644 --- a/libraries/libstratosphere/source/htclow/htclow_manager_impl.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_manager_impl.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * Copyright (c) 2018-2020 Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, diff --git a/libraries/libstratosphere/source/htclow/htclow_packet.hpp b/libraries/libstratosphere/source/htclow/htclow_packet.hpp index 89241c4be..11a53b4bd 100644 --- a/libraries/libstratosphere/source/htclow/htclow_packet.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_packet.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * Copyright (c) 2018-2020 Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, diff --git a/libraries/libstratosphere/source/htclow/htclow_packet_factory.hpp b/libraries/libstratosphere/source/htclow/htclow_packet_factory.hpp index 2a4f44093..5c0f85dd3 100644 --- a/libraries/libstratosphere/source/htclow/htclow_packet_factory.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_packet_factory.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * Copyright (c) 2018-2020 Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, diff --git a/libraries/libstratosphere/source/htclow/htclow_worker.hpp b/libraries/libstratosphere/source/htclow/htclow_worker.hpp index cc7b5fd8e..d4b046e76 100644 --- a/libraries/libstratosphere/source/htclow/htclow_worker.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_worker.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * Copyright (c) 2018-2020 Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp new file mode 100644 index 000000000..d5a7a3e46 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htclow_mux.hpp" + +namespace ams::htclow::mux { + + Mux::Mux(PacketFactory *pf, ctrl::HtcctrlStateMachine *sm) + : m_packet_factory(pf), m_state_machine(sm), m_task_manager(), m_wake_event(os::EventClearMode_ManualClear), + m_channel_impl_map(pf, sm, std::addressof(m_task_manager), std::addressof(m_wake_event)), m_global_send_buffer(pf), + m_mutex(), m_is_sleeping(false), m_version(5) + { + /* ... */ + } + +} diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp index 03e4c0152..dbe9b856b 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * Copyright (c) 2018-2020 Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -26,7 +26,7 @@ namespace ams::htclow::mux { PacketFactory *m_packet_factory; ctrl::HtcctrlStateMachine *m_state_machine; TaskManager m_task_manager; - os::EventType m_wake_event; + os::Event m_wake_event; ChannelImplMap m_channel_impl_map; GlobalSendBuffer m_global_send_buffer; os::SdkMutex m_mutex; diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.cpp new file mode 100644 index 000000000..3e5e23119 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htclow_mux_channel_impl_map.hpp" + +namespace ams::htclow::mux { + + ChannelImplMap::ChannelImplMap(PacketFactory *pf, ctrl::HtcctrlStateMachine *sm, TaskManager *tm, os::Event *ev) + : m_packet_factory(pf), m_state_machine(sm), m_task_manager(tm), m_event(ev), m_map() + { + /* Initialize the map. */ + m_map.Initialize(MaxChannelCount, m_map_buffer, sizeof(m_map_buffer)); + + /* Set all storages as invalid. */ + for (auto i = 0; i < MaxChannelCount; ++i) { + m_storage_valid[i] = false; + } + } + +} diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp index e5acfdcc2..1dcdc36c4 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * Copyright (c) 2018-2020 Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -44,13 +44,13 @@ namespace ams::htclow::mux { PacketFactory *m_packet_factory; ctrl::HtcctrlStateMachine *m_state_machine; TaskManager *m_task_manager; - os::EventType *m_event; + os::Event *m_event; u8 m_map_buffer[MapRequiredMemorySize]; MapType m_map; u8 m_storage[0x5200]; /* TODO */ bool m_storage_valid[MaxChannelCount]; public: - ChannelImplMap(PacketFactory *pf, ctrl::HtcctrlStateMachine *sm, TaskManager *tm, os::EventType *ev); + ChannelImplMap(PacketFactory *pf, ctrl::HtcctrlStateMachine *sm, TaskManager *tm, os::Event *ev); }; } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_global_send_buffer.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_global_send_buffer.hpp index c9273c4fd..ce32dca3c 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_global_send_buffer.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_global_send_buffer.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * Copyright (c) 2018-2020 Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp index 6e9033c30..b0f7d30a8 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * Copyright (c) 2018-2020 Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, From c8781232748b53a642ce166a5917681412786cf8 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 8 Feb 2021 01:25:10 -0800 Subject: [PATCH 017/280] htc: implement (fixing linker errors) through HtclowManagerImpl::OpenDriver --- .../stratosphere/htclow/htclow_types.hpp | 3 + .../include/stratosphere/settings.hpp | 1 + .../factory/settings_configuration_id.hpp | 29 +++++++ .../factory/settings_serial_number.hpp | 2 + .../htclow/ctrl/htclow_ctrl_service.cpp | 77 +++++++++++++++++++ .../htclow/ctrl/htclow_ctrl_service.hpp | 10 ++- .../ctrl/htclow_ctrl_settings_holder.cpp | 59 ++++++++++++++ .../ctrl/htclow_ctrl_settings_holder.hpp | 8 ++ .../htclow/ctrl/htclow_ctrl_state_machine.cpp | 1 + .../source/htclow/htclow_listener.cpp | 28 +++++++ .../source/htclow/htclow_listener.hpp | 2 +- .../source/htclow/htclow_manager_impl.cpp | 6 +- .../source/htclow/htclow_worker.cpp | 29 +++++++ .../source/htclow/htclow_worker.hpp | 2 +- .../source/htclow/mux/htclow_mux.cpp | 13 +++- .../source/htclow/mux/htclow_mux.hpp | 2 + .../htclow/mux/htclow_mux_channel_impl.cpp | 30 ++++++++ .../htclow/mux/htclow_mux_channel_impl.hpp | 56 ++++++++++++++ .../mux/htclow_mux_channel_impl_map.cpp | 13 ++++ .../mux/htclow_mux_channel_impl_map.hpp | 28 +++---- .../htclow/mux/htclow_mux_ring_buffer.hpp | 34 ++++++++ .../htclow/mux/htclow_mux_send_buffer.cpp | 26 +++++++ .../htclow/mux/htclow_mux_send_buffer.hpp | 46 +++++++++++ .../impl/settings_configuration_id_impl.cpp | 26 +++++++ .../impl/settings_configuration_id_impl.hpp | 23 ++++++ .../impl/settings_serial_number_impl.cpp | 5 ++ .../impl/settings_serial_number_impl.hpp | 1 + .../settings/settings_configuration_id.cpp | 25 ++++++ .../settings/settings_serial_number.cpp | 14 ++++ .../vapours/results/settings_results.hpp | 5 ++ 30 files changed, 582 insertions(+), 22 deletions(-) create mode 100644 libraries/libstratosphere/include/stratosphere/settings/factory/settings_configuration_id.hpp create mode 100644 libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp create mode 100644 libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_settings_holder.cpp create mode 100644 libraries/libstratosphere/source/htclow/htclow_listener.cpp create mode 100644 libraries/libstratosphere/source/htclow/htclow_worker.cpp create mode 100644 libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp create mode 100644 libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp create mode 100644 libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.hpp create mode 100644 libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp create mode 100644 libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp create mode 100644 libraries/libstratosphere/source/settings/impl/settings_configuration_id_impl.cpp create mode 100644 libraries/libstratosphere/source/settings/impl/settings_configuration_id_impl.hpp create mode 100644 libraries/libstratosphere/source/settings/settings_configuration_id.cpp diff --git a/libraries/libstratosphere/include/stratosphere/htclow/htclow_types.hpp b/libraries/libstratosphere/include/stratosphere/htclow/htclow_types.hpp index b05857437..8abd164a8 100644 --- a/libraries/libstratosphere/include/stratosphere/htclow/htclow_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/htclow/htclow_types.hpp @@ -21,6 +21,8 @@ namespace ams::htclow { namespace impl { enum class DriverType { + Unknown = 0, + Debug = 1, Socket = 2, Usb = 3, HostBridge = 4, @@ -29,5 +31,6 @@ namespace ams::htclow { } + constexpr inline s16 ProtocolVersion = 5; } diff --git a/libraries/libstratosphere/include/stratosphere/settings.hpp b/libraries/libstratosphere/include/stratosphere/settings.hpp index 5b995f595..5f0419aaa 100644 --- a/libraries/libstratosphere/include/stratosphere/settings.hpp +++ b/libraries/libstratosphere/include/stratosphere/settings.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libstratosphere/include/stratosphere/settings/factory/settings_configuration_id.hpp b/libraries/libstratosphere/include/stratosphere/settings/factory/settings_configuration_id.hpp new file mode 100644 index 000000000..4f46473e0 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/settings/factory/settings_configuration_id.hpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::settings::factory { + + struct ConfigurationId1 { + char str[30]; + }; + static_assert(sizeof(ConfigurationId1) == 30); + static_assert(util::is_pod::value); + + void GetConfigurationId1(ConfigurationId1 *out); + +} diff --git a/libraries/libstratosphere/include/stratosphere/settings/factory/settings_serial_number.hpp b/libraries/libstratosphere/include/stratosphere/settings/factory/settings_serial_number.hpp index 301139cca..cfb096004 100644 --- a/libraries/libstratosphere/include/stratosphere/settings/factory/settings_serial_number.hpp +++ b/libraries/libstratosphere/include/stratosphere/settings/factory/settings_serial_number.hpp @@ -24,4 +24,6 @@ namespace ams::settings::factory { static_assert(sizeof(SerialNumber) == 0x18); static_assert(util::is_pod::value); + Result GetSerialNumber(SerialNumber *out); + } diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp new file mode 100644 index 000000000..df719e155 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htclow_ctrl_service.hpp" +#include "../mux/htclow_mux.hpp" + +namespace ams::htclow::ctrl { + + namespace { + + constexpr const char BeaconPacketResponseTemplate[] = + "{\r\n" + " \"Spec\" : \"%s\",\r\n" + " \"Conn\" : \"%s\",\r\n" + " \"HW\" : \"%s\",\r\n" + " \"Name\" : \"%s\",\r\n" + " \"SN\" : \"%s\",\r\n" + " \"FW\" : \"%s\",\r\n" + " \"Prot\" : \"%d\"\r\n" + "}\r\n"; + + } + + HtcctrlService::HtcctrlService(HtcctrlPacketFactory *pf, HtcctrlStateMachine *sm, mux::Mux *mux) + : m_settings_holder(), m_beacon_response(), m_1100(), m_packet_factory(pf), m_state_machine(sm), m_mux(mux), m_event(os::EventClearMode_ManualClear), + m_send_buffer(pf), m_mutex(), m_condvar(), m_2170(), m_version(ProtocolVersion) + { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Set the mux version. */ + m_mux->SetVersion(m_version); + + /* Update our beacon response. */ + this->UpdateBeaconResponse(this->GetConnectionType(impl::DriverType::Unknown)); + } + + const char *HtcctrlService::GetConnectionType(impl::DriverType driver_type) const { + switch (driver_type) { + case impl::DriverType::Socket: return "TCP"; + case impl::DriverType::Usb: return "USB-gen2"; + case impl::DriverType::PlainChannel: return "HBPC-gen2"; + default: return "Unknown"; + } + } + + void HtcctrlService::UpdateBeaconResponse(const char *connection) { + /* Load settings into the holder. */ + m_settings_holder.LoadSettings(); + + /* Print our beacon response. */ + util::SNPrintf(m_beacon_response, sizeof(m_beacon_response), BeaconPacketResponseTemplate, + m_settings_holder.GetSpec(), + connection, + m_settings_holder.GetHardwareType(), + m_settings_holder.GetTargetName(), + m_settings_holder.GetSerialNumber(), + m_settings_holder.GetFirmwareVersion(), + ProtocolVersion + ); + } + + +} diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp index 9c42fd258..39b0a0274 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp @@ -40,17 +40,21 @@ namespace ams::htclow::ctrl { class HtcctrlService { private: SettingsHolder m_settings_holder; - u8 m_beacon_response[0x1000]; + char m_beacon_response[0x1000]; u8 m_1100[0x1000]; HtcctrlPacketFactory *m_packet_factory; HtcctrlStateMachine *m_state_machine; mux::Mux *m_mux; - os::EventType m_event; + os::Event m_event; HtcctrlSendBuffer m_send_buffer; os::SdkMutex m_mutex; os::SdkConditionVariable m_condvar; u8 m_2170[0x1000]; - u16 m_version; + s16 m_version; + private: + const char *GetConnectionType(impl::DriverType driver_type) const; + + void UpdateBeaconResponse(const char *connection); public: HtcctrlService(HtcctrlPacketFactory *pf, HtcctrlStateMachine *sm, mux::Mux *mux); }; diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_settings_holder.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_settings_holder.cpp new file mode 100644 index 000000000..6d17dde77 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_settings_holder.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htclow_ctrl_settings_holder.hpp" + +namespace ams::htclow::ctrl { + + void SettingsHolder::LoadSettings() { + /* Load configuration id. */ + { + settings::factory::ConfigurationId1 cfg_id; + settings::factory::GetConfigurationId1(std::addressof(cfg_id)); + + if (cfg_id.str[0]) { + util::Strlcpy(m_hardware_type, cfg_id.str, sizeof(m_hardware_type)); + } else { + util::Strlcpy(m_hardware_type, "Unknown", sizeof(m_hardware_type)); + } + } + + /* Load device name. */ + { + char device_name[0x40]; + settings::fwdbg::GetSettingsItemValue(device_name, sizeof(device_name), "target_manager", "device_name"); + util::Strlcpy(m_target_name, device_name, sizeof(m_target_name)); + } + + /* Load serial number. */ + { + settings::factory::SerialNumber sn; + if (R_SUCCEEDED(settings::factory::GetSerialNumber(std::addressof(sn)))) { + util::Strlcpy(m_serial_number, sn.str, sizeof(m_serial_number)); + } else { + m_serial_number[0] = '\x00'; + } + } + + /* Load firmware version. */ + { + settings::system::FirmwareVersion fw_ver; + settings::system::GetFirmwareVersion(std::addressof(fw_ver)); + util::Strlcpy(m_firmware_version, fw_ver.display_name, sizeof(m_firmware_version)); + } + } + +} diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_settings_holder.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_settings_holder.hpp index df956e832..ca4561008 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_settings_holder.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_settings_holder.hpp @@ -26,6 +26,14 @@ namespace ams::htclow::ctrl { char m_firmware_version[0x40]; public: SettingsHolder() { /* ... */ } + + void LoadSettings(); + + const char *GetSpec() { return "NX"; } + const char *GetHardwareType() { return m_hardware_type; } + const char *GetTargetName() { return m_target_name; } + const char *GetSerialNumber() { return m_serial_number; } + const char *GetFirmwareVersion() { return m_firmware_version; } }; } diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp index 655a1f8c1..b740a4bad 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp @@ -20,6 +20,7 @@ namespace ams::htclow::ctrl { namespace { + /* TODO: Real module id names */ constexpr const impl::ChannelInternalType ServiceChannels[] = { { .channel_id = 0, diff --git a/libraries/libstratosphere/source/htclow/htclow_listener.cpp b/libraries/libstratosphere/source/htclow/htclow_listener.cpp new file mode 100644 index 000000000..cb2a2d7bc --- /dev/null +++ b/libraries/libstratosphere/source/htclow/htclow_listener.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htclow_listener.hpp" + +namespace ams::htclow { + + Listener::Listener(mem::StandardAllocator *allocator, mux::Mux *mux, ctrl::HtcctrlService *ctrl_srv, Worker *worker) + : m_thread_stack_size(4_KB), m_allocator(allocator), m_mux(mux), m_service(ctrl_srv), m_worker(worker), m_event(os::EventClearMode_ManualClear), m_driver(nullptr), m_thread_running(false), m_cancelled(false) + { + /* Allocate stack. */ + m_listen_thread_stack = m_allocator->Allocate(m_thread_stack_size, os::ThreadStackAlignment); + } + +} diff --git a/libraries/libstratosphere/source/htclow/htclow_listener.hpp b/libraries/libstratosphere/source/htclow/htclow_listener.hpp index b6771cf87..0567a176f 100644 --- a/libraries/libstratosphere/source/htclow/htclow_listener.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_listener.hpp @@ -26,7 +26,7 @@ namespace ams::htclow { mux::Mux *m_mux; ctrl::HtcctrlService *m_service; Worker *m_worker; - os::EventType m_event; + os::Event m_event; os::ThreadType m_listen_thread; void *m_listen_thread_stack; driver::IDriver *m_driver; diff --git a/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp b/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp index 2bf71ffa0..e7154f25c 100644 --- a/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp +++ b/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp @@ -31,8 +31,10 @@ namespace ams::htclow { /* ... */ } - //Result HtclowManagerImpl::OpenDriver(impl::DriverType driver_type); - // + Result HtclowManagerImpl::OpenDriver(impl::DriverType driver_type) { + AMS_ABORT("TODO"); + } + //void HtclowManagerImpl::CloseDriver(); // //void HtclowManagerImpl::Disconnect(); diff --git a/libraries/libstratosphere/source/htclow/htclow_worker.cpp b/libraries/libstratosphere/source/htclow/htclow_worker.cpp new file mode 100644 index 000000000..bbe6ab99b --- /dev/null +++ b/libraries/libstratosphere/source/htclow/htclow_worker.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htclow_worker.hpp" + +namespace ams::htclow { + + Worker::Worker(mem::StandardAllocator *allocator, mux::Mux *mux, ctrl::HtcctrlService *ctrl_srv) + : m_thread_stack_size(4_KB), m_allocator(allocator), m_mux(mux), m_service(ctrl_srv), m_driver(nullptr), m_event(os::EventClearMode_ManualClear), m_7C400() + { + /* Allocate stacks. */ + m_receive_thread_stack = m_allocator->Allocate(m_thread_stack_size, os::ThreadStackAlignment); + m_send_thread_stack = m_allocator->Allocate(m_thread_stack_size, os::ThreadStackAlignment); + } + +} diff --git a/libraries/libstratosphere/source/htclow/htclow_worker.hpp b/libraries/libstratosphere/source/htclow/htclow_worker.hpp index d4b046e76..7c3c9f34f 100644 --- a/libraries/libstratosphere/source/htclow/htclow_worker.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_worker.hpp @@ -31,7 +31,7 @@ namespace ams::htclow { driver::IDriver *m_driver; os::ThreadType m_receive_thread; os::ThreadType m_send_thread; - os::EventType m_event; + os::Event m_event; void *m_receive_thread_stack; void *m_send_thread_stack; u8 m_7C400; diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp index d5a7a3e46..474470dbf 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp @@ -21,9 +21,20 @@ namespace ams::htclow::mux { Mux::Mux(PacketFactory *pf, ctrl::HtcctrlStateMachine *sm) : m_packet_factory(pf), m_state_machine(sm), m_task_manager(), m_wake_event(os::EventClearMode_ManualClear), m_channel_impl_map(pf, sm, std::addressof(m_task_manager), std::addressof(m_wake_event)), m_global_send_buffer(pf), - m_mutex(), m_is_sleeping(false), m_version(5) + m_mutex(), m_is_sleeping(false), m_version(ProtocolVersion) { /* ... */ } + void Mux::SetVersion(u16 version) { + /* Set our version. */ + m_version = version; + + /* Set all entries in our map. */ + /* NOTE: Nintendo does this highly inefficiently... */ + for (auto &pair : m_channel_impl_map.GetMap()) { + m_channel_impl_map[pair.first].SetVersion(m_version); + } + } + } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp index dbe9b856b..af8c27ae5 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp @@ -34,6 +34,8 @@ namespace ams::htclow::mux { u16 m_version; public: Mux(PacketFactory *pf, ctrl::HtcctrlStateMachine *sm); + + void SetVersion(u16 version); }; } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp new file mode 100644 index 000000000..f10d81a2f --- /dev/null +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htclow_mux_channel_impl.hpp" + +namespace ams::htclow::mux { + + void ChannelImpl::SetVersion(s16 version) { + /* Sanity check the version. */ + AMS_ASSERT(version <= ProtocolVersion); + + /* Set version. */ + m_version = version; + m_send_buffer.SetVersion(version); + } + +} diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp new file mode 100644 index 000000000..e014cd05a --- /dev/null +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "htclow_mux_task_manager.hpp" +#include "htclow_mux_send_buffer.hpp" + +namespace ams::htclow { + + class PacketFactory; + + namespace ctrl { + + class HtcctrlStateMachine; + + } + +} + +namespace ams::htclow::mux { + + class ChannelImpl { + private: + impl::ChannelInternalType m_channel; + PacketFactory *m_packet_factory; + ctrl::HtcctrlStateMachine *m_state_machine; + TaskManager *m_task_manager; + os::Event *m_event; + SendBuffer m_send_buffer; + RingBuffer m_receive_buffer; + s16 m_version; + /* TODO: Channel config */ + /* TODO: tracking variables. */ + std::optional m_108; + os::Event m_send_packet_event; + /* TODO: Channel state. */ + public: + ChannelImpl(impl::ChannelInternalType channel, PacketFactory *pf, ctrl::HtcctrlStateMachine *sm, TaskManager *tm, os::Event *ev); + + void SetVersion(s16 version); + }; + +} diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.cpp index 3e5e23119..cd3ccf955 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.cpp @@ -30,4 +30,17 @@ namespace ams::htclow::mux { } } + ChannelImpl &ChannelImplMap::GetChannelImpl(int index) { + return GetReference(m_channel_storage[index]); + } + + ChannelImpl &ChannelImplMap::GetChannelImpl(impl::ChannelInternalType channel) { + /* Find the channel. */ + auto it = m_map.find(channel); + AMS_ASSERT(it != m_map.end()); + + /* Return the implementation object. */ + return this->GetChannelImpl(it->second); + } + } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp index 1dcdc36c4..14886b152 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp @@ -15,19 +15,7 @@ */ #pragma once #include -#include "htclow_mux_task_manager.hpp" - -namespace ams::htclow { - - class PacketFactory; - - namespace ctrl { - - class HtcctrlStateMachine; - - } - -} +#include "htclow_mux_channel_impl.hpp" namespace ams::htclow::mux { @@ -47,10 +35,22 @@ namespace ams::htclow::mux { os::Event *m_event; u8 m_map_buffer[MapRequiredMemorySize]; MapType m_map; - u8 m_storage[0x5200]; /* TODO */ + TYPED_STORAGE(ChannelImpl) m_channel_storage[MaxChannelCount]; bool m_storage_valid[MaxChannelCount]; public: ChannelImplMap(PacketFactory *pf, ctrl::HtcctrlStateMachine *sm, TaskManager *tm, os::Event *ev); + + ChannelImpl &GetChannelImpl(impl::ChannelInternalType channel); + private: + ChannelImpl &GetChannelImpl(int index); + public: + MapType &GetMap() { + return m_map; + } + + ChannelImpl &operator[](impl::ChannelInternalType channel) { + return this->GetChannelImpl(channel); + } }; } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.hpp new file mode 100644 index 000000000..02557ea68 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htclow::mux { + + class RingBuffer { + private: + void *m_buffer; + void *m_read_only_buffer; + bool m_is_read_only; + size_t m_buffer_size; + size_t m_data_size; + size_t m_offset; + bool m_has_copied; + public: + RingBuffer() : m_buffer(), m_read_only_buffer(), m_is_read_only(true), m_buffer_size(), m_data_size(), m_offset(), m_has_copied(false) { /* ... */ } + }; + +} diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp new file mode 100644 index 000000000..2d3693428 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htclow_mux_channel_impl.hpp" + +namespace ams::htclow::mux { + + void SendBuffer::SetVersion(s16 version) { + /* Set version. */ + m_version = version; + } + +} diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp new file mode 100644 index 000000000..cfc5695be --- /dev/null +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "../htclow_packet.hpp" +#include "htclow_mux_ring_buffer.hpp" + +namespace ams::htclow { + + class PacketFactory; + +} + +namespace ams::htclow::mux { + + class SendBuffer { + private: + using PacketList = util::IntrusiveListBaseTraits::ListType; + private: + impl::ChannelInternalType m_channel; + PacketFactory *m_packet_factory; + RingBuffer m_ring_buffer; + PacketList m_packet_list; + s16 m_version; + bool m_flow_control_enabled; + size_t m_max_packet_size; + public: + SendBuffer(impl::ChannelInternalType channel, PacketFactory *pf); + + void SetVersion(s16 version); + }; + +} diff --git a/libraries/libstratosphere/source/settings/impl/settings_configuration_id_impl.cpp b/libraries/libstratosphere/source/settings/impl/settings_configuration_id_impl.cpp new file mode 100644 index 000000000..48b3ecd3e --- /dev/null +++ b/libraries/libstratosphere/source/settings/impl/settings_configuration_id_impl.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "settings_serial_number_impl.hpp" + +namespace ams::settings::impl { + + Result GetConfigurationId1(settings::factory::ConfigurationId1 *out) { + static_assert(sizeof(*out) == sizeof(::SetCalConfigurationId1)); + return ::setcalGetConfigurationId1(reinterpret_cast<::SetCalConfigurationId1 *>(out)); + } + +} diff --git a/libraries/libstratosphere/source/settings/impl/settings_configuration_id_impl.hpp b/libraries/libstratosphere/source/settings/impl/settings_configuration_id_impl.hpp new file mode 100644 index 000000000..3d5624831 --- /dev/null +++ b/libraries/libstratosphere/source/settings/impl/settings_configuration_id_impl.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::settings::impl { + + Result GetConfigurationId1(settings::factory::ConfigurationId1 *out); + +} diff --git a/libraries/libstratosphere/source/settings/impl/settings_serial_number_impl.cpp b/libraries/libstratosphere/source/settings/impl/settings_serial_number_impl.cpp index 56a073c02..93fe6facb 100644 --- a/libraries/libstratosphere/source/settings/impl/settings_serial_number_impl.cpp +++ b/libraries/libstratosphere/source/settings/impl/settings_serial_number_impl.cpp @@ -18,6 +18,11 @@ namespace ams::settings::impl { + Result GetSerialNumber(settings::factory::SerialNumber *out) { + static_assert(sizeof(*out) == sizeof(::SetCalSerialNumber)); + return ::setcalGetSerialNumber(reinterpret_cast<::SetCalSerialNumber *>(out)); + } + Result GetSerialNumber(settings::system::SerialNumber *out) { static_assert(sizeof(*out) == sizeof(::SetSysSerialNumber)); return ::setsysGetSerialNumber(reinterpret_cast<::SetSysSerialNumber *>(out)); diff --git a/libraries/libstratosphere/source/settings/impl/settings_serial_number_impl.hpp b/libraries/libstratosphere/source/settings/impl/settings_serial_number_impl.hpp index 87df5e90a..475ea27af 100644 --- a/libraries/libstratosphere/source/settings/impl/settings_serial_number_impl.hpp +++ b/libraries/libstratosphere/source/settings/impl/settings_serial_number_impl.hpp @@ -18,6 +18,7 @@ namespace ams::settings::impl { + Result GetSerialNumber(settings::factory::SerialNumber *out); Result GetSerialNumber(settings::system::SerialNumber *out); } diff --git a/libraries/libstratosphere/source/settings/settings_configuration_id.cpp b/libraries/libstratosphere/source/settings/settings_configuration_id.cpp new file mode 100644 index 000000000..f4baa6316 --- /dev/null +++ b/libraries/libstratosphere/source/settings/settings_configuration_id.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "impl/settings_configuration_id_impl.hpp" + +namespace ams::settings::factory { + + void GetConfigurationId1(ConfigurationId1 *out) { + R_ABORT_UNLESS(settings::impl::GetConfigurationId1(out)); + } + +} diff --git a/libraries/libstratosphere/source/settings/settings_serial_number.cpp b/libraries/libstratosphere/source/settings/settings_serial_number.cpp index 05e17c545..083dbb623 100644 --- a/libraries/libstratosphere/source/settings/settings_serial_number.cpp +++ b/libraries/libstratosphere/source/settings/settings_serial_number.cpp @@ -16,6 +16,20 @@ #include #include "impl/settings_serial_number_impl.hpp" +namespace ams::settings::factory { + + Result GetSerialNumber(SerialNumber *out) { + const Result result = settings::impl::GetSerialNumber(out); + + if (!settings::ResultCalibrationDataFileSystemCorrupted::Includes(result) && !settings::ResultCalibrationDataCrcError::Includes(result)) { + R_ABORT_UNLESS(result); + } + + return result; + } + +} + namespace ams::settings::system { void GetSerialNumber(SerialNumber *out) { diff --git a/libraries/libvapours/include/vapours/results/settings_results.hpp b/libraries/libvapours/include/vapours/results/settings_results.hpp index e704371b7..d01e41178 100644 --- a/libraries/libvapours/include/vapours/results/settings_results.hpp +++ b/libraries/libvapours/include/vapours/results/settings_results.hpp @@ -44,4 +44,9 @@ namespace ams::settings { R_DEFINE_ERROR_RESULT(SettingsItemKeyInvalidFormat, 262); R_DEFINE_ERROR_RESULT(SettingsItemValueInvalidFormat, 263); + R_DEFINE_ERROR_RANGE(CalibrationDataError, 580, 599); + R_DEFINE_ERROR_RESULT(CalibrationDataFileSystemCorrupted, 581); + R_DEFINE_ERROR_RESULT(CalibrationDataCrcError, 582); + R_DEFINE_ERROR_RESULT(CalibrationDataShaError, 583); + } From c59388caf1a20a657e853e9c92c235e031cfb31f Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 8 Feb 2021 03:37:30 -0800 Subject: [PATCH 018/280] htc: implement complete usb driver --- .../stratosphere/os/os_waitable_utils.hpp | 81 +++ .../htclow/ctrl/htclow_ctrl_service.cpp | 7 + .../htclow/ctrl/htclow_ctrl_service.hpp | 2 + .../htclow/driver/htclow_driver_manager.cpp | 63 +++ .../htclow/driver/htclow_driver_manager.hpp | 8 +- .../htclow/driver/htclow_usb_driver.cpp | 125 +++++ .../htclow/driver/htclow_usb_driver.hpp | 43 ++ .../source/htclow/driver/htclow_usb_impl.cpp | 460 ++++++++++++++++++ .../source/htclow/driver/htclow_usb_impl.hpp | 41 ++ .../source/htclow/htclow_listener.cpp | 37 +- .../source/htclow/htclow_listener.hpp | 8 + .../source/htclow/htclow_manager_impl.cpp | 21 +- .../source/htclow/mux/htclow_mux.hpp | 2 +- .../libvapours/include/vapours/results.hpp | 1 + .../vapours/results/htclow_results.hpp | 39 ++ 15 files changed, 933 insertions(+), 5 deletions(-) create mode 100644 libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp create mode 100644 libraries/libstratosphere/source/htclow/driver/htclow_usb_driver.cpp create mode 100644 libraries/libstratosphere/source/htclow/driver/htclow_usb_driver.hpp create mode 100644 libraries/libstratosphere/source/htclow/driver/htclow_usb_impl.cpp create mode 100644 libraries/libstratosphere/source/htclow/driver/htclow_usb_impl.hpp create mode 100644 libraries/libvapours/include/vapours/results/htclow_results.hpp diff --git a/libraries/libstratosphere/include/stratosphere/os/os_waitable_utils.hpp b/libraries/libstratosphere/include/stratosphere/os/os_waitable_utils.hpp index 7119d4ec7..5a25dd429 100644 --- a/libraries/libstratosphere/include/stratosphere/os/os_waitable_utils.hpp +++ b/libraries/libstratosphere/include/stratosphere/os/os_waitable_utils.hpp @@ -16,7 +16,88 @@ #pragma once #include #include +#include +#include namespace ams::os { + namespace impl { + + class AutoWaitableHolder { + private: + WaitableHolderType m_holder; + public: + template + ALWAYS_INLINE explicit AutoWaitableHolder(WaitableManagerType *manager, T &&arg) { + InitializeWaitableHolder(std::addressof(m_holder), std::forward(arg)); + LinkWaitableHolder(manager, std::addressof(m_holder)); + } + + ALWAYS_INLINE ~AutoWaitableHolder() { + UnlinkWaitableHolder(std::addressof(m_holder)); + FinalizeWaitableHolder(std::addressof(m_holder)); + } + + ALWAYS_INLINE std::pair ConvertResult(const std::pair result, int index) { + if (result.first == std::addressof(m_holder)) { + return std::make_pair(static_cast(nullptr), index); + } else { + return result; + } + } + }; + + template + inline std::pair WaitAnyImpl(F &&func, WaitableManagerType *manager, int) { + return std::pair(func(manager), -1); + } + + template + inline std::pair WaitAnyImpl(F &&func, WaitableManagerType *manager, int index, T &&x, Args &&... args) { + AutoWaitableHolder holder(manager, std::forward(x)); + return holder.ConvertResult(WaitAnyImpl(std::forward(func), manager, index + 1, std::forward(args)...), index); + } + + template + inline std::pair WaitAnyImpl(F &&func, WaitableManagerType *manager, Args &&... args) { + return WaitAnyImpl(std::forward(func), manager, 0, std::forward(args)...); + } + + class TempWaitableManager { + private: + WaitableManagerType m_manager; + public: + ALWAYS_INLINE TempWaitableManager() { + os::InitializeWaitableManager(std::addressof(m_manager)); + } + + ALWAYS_INLINE ~TempWaitableManager() { + os::FinalizeWaitableManager(std::addressof(m_manager)); + } + + WaitableManagerType *Get() { + return std::addressof(m_manager); + } + }; + + template + inline std::pair WaitAnyImpl(F &&func, Args &&... args) { + TempWaitableManager temp_manager; + return WaitAnyImpl(std::forward(func), temp_manager.Get(), 0, std::forward(args)...); + } + + using WaitAnyFunction = WaitableHolderType * (*)(WaitableManagerType *); + + } + + template requires (sizeof...(Args) > 0) + inline std::pair WaitAny(WaitableManagerType *manager, Args &&... args) { + return impl::WaitAnyImpl(static_cast(&::ams::os::WaitAny), manager, std::forward(args)...); + } + + template requires (sizeof...(Args) > 0) + inline int WaitAny(Args &&... args) { + return impl::WaitAnyImpl(static_cast(&::ams::os::WaitAny), std::forward(args)...).second; + } + } diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp index df719e155..546f9fb1b 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp @@ -73,5 +73,12 @@ namespace ams::htclow::ctrl { ); } + void HtcctrlService::SetDriverType(impl::DriverType driver_type) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Update our beacon response. */ + this->UpdateBeaconResponse(this->GetConnectionType(driver_type)); + } } diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp index 39b0a0274..c6d208325 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp @@ -57,6 +57,8 @@ namespace ams::htclow::ctrl { void UpdateBeaconResponse(const char *connection); public: HtcctrlService(HtcctrlPacketFactory *pf, HtcctrlStateMachine *sm, mux::Mux *mux); + + void SetDriverType(impl::DriverType driver_type); }; } diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp new file mode 100644 index 000000000..ea16c765d --- /dev/null +++ b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htclow_driver_manager.hpp" + +namespace ams::htclow::driver { + + Result DriverManager::OpenDriver(impl::DriverType driver_type) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Check that we're not already open. */ + R_UNLESS(m_open_driver == nullptr, htclow::ResultDriverOpened()); + + /* Open the driver. */ + switch (driver_type) { + case impl::DriverType::Debug: + R_TRY(m_debug_driver->Open()); + m_open_driver = m_debug_driver; + break; + case impl::DriverType::Socket: + //m_socket_driver.Open(); + //m_open_driver = std::addressof(m_socket_driver); + //break; + return htclow::ResultUnknownDriverType(); + case impl::DriverType::Usb: + //m_usb_driver.Open(); + //m_open_driver = std::addressof(m_usb_driver); + //break; + return htclow::ResultUnknownDriverType(); + case impl::DriverType::PlainChannel: + //m_plain_channel_driver.Open(); + //m_open_driver = std::addressof(m_plain_channel_driver); + //break; + return htclow::ResultUnknownDriverType(); + default: + return htclow::ResultUnknownDriverType(); + } + + return ResultSuccess(); + } + + IDriver *DriverManager::GetCurrentDriver() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + return m_open_driver; + } + +} diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp index 5eb99553e..6a2e80aff 100644 --- a/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp +++ b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp @@ -16,19 +16,25 @@ #pragma once #include #include "htclow_i_driver.hpp" +#include "htclow_usb_driver.hpp" namespace ams::htclow::driver { class DriverManager { private: std::optional m_driver_type{}; + IDriver *m_debug_driver; /* TODO: SocketDriver m_socket_driver; */ - /* TODO: UsbDriver m_usb_driver; */ + UsbDriver m_usb_driver{}; /* TODO: PlainChannelDriver m_plain_channel_driver; */ os::SdkMutex m_mutex{}; IDriver *m_open_driver{}; public: DriverManager() = default; + + Result OpenDriver(impl::DriverType driver_type); + + IDriver *GetCurrentDriver(); }; } diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_usb_driver.cpp b/libraries/libstratosphere/source/htclow/driver/htclow_usb_driver.cpp new file mode 100644 index 000000000..d52c07ea0 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/driver/htclow_usb_driver.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htclow_usb_driver.hpp" +#include "htclow_usb_impl.hpp" + +namespace ams::htclow::driver { + + void UsbDriver::OnUsbAvailabilityChange(UsbAvailability availability, void *param) { + /* Convert the argument to a driver. */ + UsbDriver *driver = static_cast(param); + + /* Handle the change. */ + switch (availability) { + case UsbAvailability_Unavailable: + CancelUsbSendReceive(); + break; + case UsbAvailability_Available: + driver->m_event.Signal(); + break; + case UsbAvailability_Unknown: + driver->CancelSendReceive(); + break; + } + } + + Result UsbDriver::Open() { + /* Clear our event. */ + m_event.Clear(); + + /* Set the availability change callback. */ + SetUsbAvailabilityChangeCallback(OnUsbAvailabilityChange, this); + + /* Initialize the interface. */ + return InitializeUsbInterface(); + } + + void UsbDriver::Close() { + /* Finalize the interface. */ + FinalizeUsbInterface(); + + /* Clear the availability callback. */ + ClearUsbAvailabilityChangeCallback(); + } + + Result UsbDriver::Connect(os::EventType *event) { + /* We must not already be connected. */ + AMS_ABORT_UNLESS(!m_connected); + + /* Perform a wait on our event. */ + const int idx = os::WaitAny(m_event.GetBase(), event); + R_UNLESS(idx == 0, htclow::ResultCancelled()); + + /* Clear our event. */ + m_event.Clear(); + + /* We're connected. */ + m_connected = true; + return ResultSuccess(); + } + + void UsbDriver::Shutdown() { + /* If we're connected, cancel anything we're doing. */ + if (m_connected) { + this->CancelSendReceive(); + m_connected = false; + } + } + + Result UsbDriver::Send(const void *src, int src_size) { + /* Check size. */ + R_UNLESS(src_size >= 0, htclow::ResultInvalidArgument()); + + /* Send until we've sent everything. */ + for (auto transferred = 0; transferred < src_size; /* ... */) { + int cur; + R_TRY(SendUsb(std::addressof(cur), reinterpret_cast(reinterpret_cast(src) + transferred), src_size - transferred)); + + transferred += cur; + } + + return ResultSuccess(); + } + + Result UsbDriver::Receive(void *dst, int dst_size) { + /* Check size. */ + R_UNLESS(dst_size >= 0, htclow::ResultInvalidArgument()); + + /* Send until we've sent everything. */ + for (auto transferred = 0; transferred < dst_size; /* ... */) { + int cur; + R_TRY(SendUsb(std::addressof(cur), reinterpret_cast(reinterpret_cast(dst) + transferred), dst_size - transferred)); + + transferred += cur; + } + + return ResultSuccess(); + } + + void UsbDriver::CancelSendReceive() { + CancelUsbSendReceive(); + } + + void UsbDriver::Suspend() { + this->Close(); + } + + void UsbDriver::Resume() { + R_ABORT_UNLESS(this->Open()); + } + +} diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_usb_driver.hpp b/libraries/libstratosphere/source/htclow/driver/htclow_usb_driver.hpp new file mode 100644 index 000000000..ba4be55ab --- /dev/null +++ b/libraries/libstratosphere/source/htclow/driver/htclow_usb_driver.hpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "htclow_i_driver.hpp" +#include "htclow_usb_impl.hpp" + +namespace ams::htclow::driver { + + class UsbDriver final : public IDriver { + private: + bool m_connected; + os::Event m_event; + public: + UsbDriver() : m_connected(false), m_event(os::EventClearMode_ManualClear) { /* ... */ } + public: + static void OnUsbAvailabilityChange(UsbAvailability availability, void *param); + public: + virtual Result Open() override; + virtual void Close() override; + virtual Result Connect(os::EventType *event) override; + virtual void Shutdown() override; + virtual Result Send(const void *src, int src_size) override; + virtual Result Receive(void *dst, int dst_size) override; + virtual void CancelSendReceive() override; + virtual void Suspend() override; + virtual void Resume() override; + }; + +} diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_usb_impl.cpp b/libraries/libstratosphere/source/htclow/driver/htclow_usb_impl.cpp new file mode 100644 index 000000000..76b55f1ce --- /dev/null +++ b/libraries/libstratosphere/source/htclow/driver/htclow_usb_impl.cpp @@ -0,0 +1,460 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htclow_usb_impl.hpp" + +namespace ams::htclow::driver { + + namespace { + + /* TODO: Should we identify differently than Nintendo does? */ + /* It's kind of silly to identify as "NintendoSDK DevKit", but it's also kind of amusing. */ + /* TBD */ + + constinit usb::UsbStringDescriptor LanguageStringDescriptor = { 4, usb::UsbDescriptorType_String, {0x0409}}; + constinit usb::UsbStringDescriptor ManufacturerStringDescriptor = {18, usb::UsbDescriptorType_String, {'N', 'i', 'n', 't', 'e', 'n', 'd', 'o'}}; + constinit usb::UsbStringDescriptor ProductStringFullSpeedDescriptor = {38, usb::UsbDescriptorType_String, {'N', 'i', 'n', 't', 'e', 'n', 'd', 'o', 'S', 'D', 'K', ' ', 'D', 'e', 'v', 'K', 'i', 't'}}; + constinit usb::UsbStringDescriptor SerialNumberStringDescriptor = { 0, usb::UsbDescriptorType_String, {}}; + constinit usb::UsbStringDescriptor InterfaceStringDescriptor = {16, usb::UsbDescriptorType_String, {'h', 't', 'c', ' ', 'u', 's', 'b'}}; + + constinit usb::UsbDeviceDescriptor UsbDeviceDescriptorFullSpeed = { + .bLength = usb::UsbDescriptorSize_Device, + .bDescriptorType = usb::UsbDescriptorType_Device, + .bcdUSB = 0x0110, + .bDeviceClass = 0x00, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize0 = 0x40, + .idVendor = 0x057E, + .idProduct = 0x3005, + .bcdDevice = 0x0100, + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + .bNumConfigurations = 0x01, + }; + + constinit usb::UsbDeviceDescriptor UsbDeviceDescriptorHighSpeed = { + .bLength = usb::UsbDescriptorSize_Device, + .bDescriptorType = usb::UsbDescriptorType_Device, + .bcdUSB = 0x0200, + .bDeviceClass = 0x00, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize0 = 0x40, + .idVendor = 0x057E, + .idProduct = 0x3005, + .bcdDevice = 0x0100, + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + .bNumConfigurations = 0x01, + }; + + constinit usb::UsbDeviceDescriptor UsbDeviceDescriptorSuperSpeed = { + .bLength = usb::UsbDescriptorSize_Device, + .bDescriptorType = usb::UsbDescriptorType_Device, + .bcdUSB = 0x0300, + .bDeviceClass = 0x00, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize0 = 0x09, + .idVendor = 0x057E, + .idProduct = 0x3005, + .bcdDevice = 0x0100, + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + .bNumConfigurations = 0x01, + }; + + constinit u8 BinaryObjectStore[] = { + 0x05, + usb::UsbDescriptorType_Bos, + 0x16, 0x00, + 0x02, + + 0x07, + usb::UsbDescriptorType_DeviceCapability, + 0x02, + 0x02, 0x00, 0x00, 0x00, + + 0x0A, + usb::UsbDescriptorType_DeviceCapability, + 0x03, + 0x00, + 0x0E, 0x00, + 0x03, + 0x00, + 0x00, 0x00, + }; + + constinit usb::UsbInterfaceDescriptor UsbInterfaceDescriptor = { + .bLength = usb::UsbDescriptorSize_Interface, + .bDescriptorType = usb::UsbDescriptorType_Interface, + .bInterfaceNumber = 0x00, + .bAlternateSetting = 0x00, + .bNumEndpoints = 0x02, + .bInterfaceClass = 0xFF, + .bInterfaceSubClass = 0xFF, + .bInterfaceProtocol = 0xFF, + .iInterface = 0x04, + }; + + constinit usb::UsbEndpointDescriptor UsbEndpointDescriptorsFullSpeed[2] = { + { + .bLength = usb::UsbDescriptorSize_Endpoint, + .bDescriptorType = usb::UsbDescriptorType_Endpoint, + .bEndpointAddress = 0x81, + .bmAttributes = usb::UsbEndpointAttributeMask_XferTypeBulk, + .wMaxPacketSize = 0x40, + .bInterval = 0x00, + }, + { + .bLength = usb::UsbDescriptorSize_Endpoint, + .bDescriptorType = usb::UsbDescriptorType_Endpoint, + .bEndpointAddress = 0x01, + .bmAttributes = usb::UsbEndpointAttributeMask_XferTypeBulk, + .wMaxPacketSize = 0x40, + .bInterval = 0x00, + } + }; + + constinit usb::UsbEndpointDescriptor UsbEndpointDescriptorsHighSpeed[2] = { + { + .bLength = usb::UsbDescriptorSize_Endpoint, + .bDescriptorType = usb::UsbDescriptorType_Endpoint, + .bEndpointAddress = 0x81, + .bmAttributes = usb::UsbEndpointAttributeMask_XferTypeBulk, + .wMaxPacketSize = 0x200, + .bInterval = 0x00, + }, + { + .bLength = usb::UsbDescriptorSize_Endpoint, + .bDescriptorType = usb::UsbDescriptorType_Endpoint, + .bEndpointAddress = 0x01, + .bmAttributes = usb::UsbEndpointAttributeMask_XferTypeBulk, + .wMaxPacketSize = 0x200, + .bInterval = 0x00, + } + }; + + constinit usb::UsbEndpointDescriptor UsbEndpointDescriptorsSuperSpeed[2] = { + { + .bLength = usb::UsbDescriptorSize_Endpoint, + .bDescriptorType = usb::UsbDescriptorType_Endpoint, + .bEndpointAddress = 0x81, + .bmAttributes = usb::UsbEndpointAttributeMask_XferTypeBulk, + .wMaxPacketSize = 0x400, + .bInterval = 0x00, + }, + { + .bLength = usb::UsbDescriptorSize_Endpoint, + .bDescriptorType = usb::UsbDescriptorType_Endpoint, + .bEndpointAddress = 0x01, + .bmAttributes = usb::UsbEndpointAttributeMask_XferTypeBulk, + .wMaxPacketSize = 0x400, + .bInterval = 0x00, + } + }; + + constinit usb::UsbEndpointCompanionDescriptor UsbEndpointCompanionDescriptor = { + .bLength = usb::UsbDescriptorSize_EndpointCompanion, + .bDescriptorType = usb::UsbDescriptorType_EndpointCompanion, + .bMaxBurst = 0x0F, + .bmAttributes = 0x00, + .wBytesPerInterval = 0x0000, + }; + + constexpr size_t UsbDmaBufferSize = 0x60000; + + alignas(os::MemoryPageSize) constinit u8 g_usb_receive_buffer[UsbDmaBufferSize]; + alignas(os::MemoryPageSize) constinit u8 g_usb_send_buffer[UsbDmaBufferSize]; + alignas(os::ThreadStackAlignment) constinit u8 g_usb_indication_thread_stack[16_KB]; + + constinit UsbAvailabilityChangeCallback g_availability_change_callback = nullptr; + constinit void *g_availability_change_param = nullptr; + + constinit bool g_usb_interface_initialized = false; + + os::Event g_usb_break_event(os::EventClearMode_ManualClear); + + constinit os::ThreadType g_usb_indication_thread = {}; + + constinit os::SdkMutex g_usb_driver_mutex; + + usb::DsClient g_ds_client; + usb::DsInterface g_ds_interface; + usb::DsEndpoint g_ds_endpoints[2]; + + void InvokeAvailabilityChangeCallback(UsbAvailability availability) { + if (g_availability_change_callback) { + g_availability_change_callback(availability, g_availability_change_param); + } + } + + Result ConvertUsbDriverResult(Result result) { + if (result.GetModule() == R_NAMESPACE_MODULE_ID(usb)) { + if (usb::ResultResourceBusy::Includes(result)) { + return htclow::ResultUsbDriverBusyError(); + } else if (usb::ResultMemAllocFailure::Includes(result)) { + return htclow::ResultOutOfMemory(); + } else { + return htclow::ResultUsbDriverUnknownError(); + } + } else { + return result; + } + } + + Result InitializeDsClient() { + /* Initialize the client. */ + R_TRY(ConvertUsbDriverResult(g_ds_client.Initialize(usb::ComplexId_Tegra21x))); + + /* Clear device data. */ + R_ABORT_UNLESS(g_ds_client.ClearDeviceData()); + + /* Add string descriptors. */ + u8 index; + R_TRY(g_ds_client.AddUsbStringDescriptor(std::addressof(index), std::addressof(LanguageStringDescriptor))); + R_TRY(g_ds_client.AddUsbStringDescriptor(std::addressof(index), std::addressof(ManufacturerStringDescriptor))); + R_TRY(g_ds_client.AddUsbStringDescriptor(std::addressof(index), std::addressof(ProductStringFullSpeedDescriptor))); + R_TRY(g_ds_client.AddUsbStringDescriptor(std::addressof(index), std::addressof(SerialNumberStringDescriptor))); + R_TRY(g_ds_client.AddUsbStringDescriptor(std::addressof(index), std::addressof(InterfaceStringDescriptor))); + + /* Add device descriptors. */ + R_TRY(g_ds_client.SetUsbDeviceDescriptor(std::addressof(UsbDeviceDescriptorFullSpeed), usb::UsbDeviceSpeed_Full)); + R_TRY(g_ds_client.SetUsbDeviceDescriptor(std::addressof(UsbDeviceDescriptorHighSpeed), usb::UsbDeviceSpeed_High)); + R_TRY(g_ds_client.SetUsbDeviceDescriptor(std::addressof(UsbDeviceDescriptorSuperSpeed), usb::UsbDeviceSpeed_Super)); + + /* Set binary object store. */ + R_TRY(g_ds_client.SetBinaryObjectStore(BinaryObjectStore, sizeof(BinaryObjectStore))); + + return ResultSuccess(); + } + + Result InitializeDsInterface() { + /* Initialize the interface. */ + R_TRY(ConvertUsbDriverResult(g_ds_interface.Initialize(std::addressof(g_ds_client), 0))); + + /* Append the interface descriptors for all speeds. */ + R_TRY(g_ds_interface.AppendConfigurationData(usb::UsbDeviceSpeed_Full, std::addressof(UsbInterfaceDescriptor), sizeof(usb::UsbInterfaceDescriptor))); + R_TRY(g_ds_interface.AppendConfigurationData(usb::UsbDeviceSpeed_Full, std::addressof(UsbEndpointDescriptorsFullSpeed[0]), sizeof(usb::UsbEndpointDescriptor))); + R_TRY(g_ds_interface.AppendConfigurationData(usb::UsbDeviceSpeed_Full, std::addressof(UsbEndpointDescriptorsFullSpeed[1]), sizeof(usb::UsbEndpointDescriptor))); + + R_TRY(g_ds_interface.AppendConfigurationData(usb::UsbDeviceSpeed_High, std::addressof(UsbInterfaceDescriptor), sizeof(usb::UsbInterfaceDescriptor))); + R_TRY(g_ds_interface.AppendConfigurationData(usb::UsbDeviceSpeed_High, std::addressof(UsbEndpointDescriptorsHighSpeed[0]), sizeof(usb::UsbEndpointDescriptor))); + R_TRY(g_ds_interface.AppendConfigurationData(usb::UsbDeviceSpeed_High, std::addressof(UsbEndpointDescriptorsHighSpeed[1]), sizeof(usb::UsbEndpointDescriptor))); + + R_TRY(g_ds_interface.AppendConfigurationData(usb::UsbDeviceSpeed_Super, std::addressof(UsbInterfaceDescriptor), sizeof(usb::UsbInterfaceDescriptor))); + R_TRY(g_ds_interface.AppendConfigurationData(usb::UsbDeviceSpeed_Super, std::addressof(UsbEndpointDescriptorsSuperSpeed[0]), sizeof(usb::UsbEndpointDescriptor))); + R_TRY(g_ds_interface.AppendConfigurationData(usb::UsbDeviceSpeed_Super, std::addressof(UsbEndpointCompanionDescriptor), sizeof(usb::UsbEndpointCompanionDescriptor))); + R_TRY(g_ds_interface.AppendConfigurationData(usb::UsbDeviceSpeed_Super, std::addressof(UsbEndpointDescriptorsSuperSpeed[1]), sizeof(usb::UsbEndpointDescriptor))); + R_TRY(g_ds_interface.AppendConfigurationData(usb::UsbDeviceSpeed_Super, std::addressof(UsbEndpointCompanionDescriptor), sizeof(usb::UsbEndpointCompanionDescriptor))); + + return ResultSuccess(); + } + + Result InitializeDsEndpoints() { + R_TRY(g_ds_endpoints[0].Initialize(std::addressof(g_ds_interface), 0x81)); + R_TRY(g_ds_endpoints[1].Initialize(std::addressof(g_ds_interface), 0x01)); + return ResultSuccess(); + } + + void UsbIndicationThreadFunction(void *arg) { + /* Get the state change event. */ + os::SystemEventType *state_change_event = g_ds_client.GetStateChangeEvent(); + + /* Setup waitable manager. */ + os::WaitableManagerType manager; + os::InitializeWaitableManager(std::addressof(manager)); + + /* Link waitable holders. */ + os::WaitableHolderType state_change_holder; + os::WaitableHolderType break_holder; + os::InitializeWaitableHolder(std::addressof(state_change_holder), state_change_event); + os::LinkWaitableHolder(std::addressof(manager), std::addressof(state_change_holder)); + os::InitializeWaitableHolder(std::addressof(break_holder), g_usb_break_event.GetBase()); + os::LinkWaitableHolder(std::addressof(manager), std::addressof(break_holder)); + + /* Loop forever. */ + while (true) { + /* If we should break, do so. */ + if (os::WaitAny(std::addressof(manager)) == std::addressof(break_holder)) { + break; + } + + /* Clear the state change event. */ + os::ClearSystemEvent(state_change_event); + + /* Get the new state. */ + usb::UsbState usb_state; + R_ABORT_UNLESS(g_ds_client.GetState(std::addressof(usb_state))); + + switch (usb_state) { + case UsbState_Detached: + case UsbState_Suspended: + InvokeAvailabilityChangeCallback(UsbAvailability_Unavailable); + break; + case UsbState_Configured: + InvokeAvailabilityChangeCallback(UsbAvailability_Available); + break; + default: + /* Nothing to do. */ + break; + } + } + + /* Clear the break event. */ + g_usb_break_event.Clear(); + + /* Unlink all holders. */ + os::UnlinkAllWaitableHolder(std::addressof(manager)); + + /* Finalize the waitable holders and manager. */ + os::FinalizeWaitableHolder(std::addressof(break_holder)); + os::FinalizeWaitableHolder(std::addressof(state_change_holder)); + os::FinalizeWaitableManager(std::addressof(manager)); + } + + } + + void SetUsbAvailabilityChangeCallback(UsbAvailabilityChangeCallback callback, void *param) { + g_availability_change_callback = callback; + g_availability_change_param = param; + } + + void ClearUsbAvailabilityChangeCallback() { + g_availability_change_callback = nullptr; + g_availability_change_param = nullptr; + } + + Result InitializeUsbInterface() { + /* Set the interface as initialized. */ + g_usb_interface_initialized = true; + + /* If we fail somewhere, finalize. */ + auto init_guard = SCOPE_GUARD { FinalizeUsbInterface(); }; + + /* Get the serial number. */ + { + settings::factory::SerialNumber serial_number; + serial_number.str[0] = '\x00'; + + if (R_FAILED(settings::factory::GetSerialNumber(std::addressof(serial_number))) || serial_number.str[0] == '\x00') { + std::strcpy(serial_number.str, "Corrupted S/N"); + } + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Waddress-of-packed-member" + + u16 *dst = SerialNumberStringDescriptor.wData; + u8 *src = reinterpret_cast(serial_number.str); + u8 count = 0; + +#pragma GCC diagnostic pop + + while (*src) { + *dst++ = static_cast(*src++); + ++count; + } + + SerialNumberStringDescriptor.bLength = 2 + (2 * count); + } + + /* Initialize the client. */ + R_TRY(InitializeDsClient()); + + /* Initialize the interface. */ + R_TRY(InitializeDsInterface()); + + /* Initialize the endpoints. */ + R_TRY(ConvertUsbDriverResult(InitializeDsEndpoints())); + + /* Create the indication thread. */ + R_ABORT_UNLESS(os::CreateThread(std::addressof(g_usb_indication_thread), &UsbIndicationThreadFunction, nullptr, g_usb_indication_thread_stack, sizeof(g_usb_indication_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(htc, HtclowUsbIndication))); + + /* Set the thread name. */ + os::SetThreadNamePointer(std::addressof(g_usb_indication_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, HtclowUsbIndication)); + + /* Start the indication thread. */ + os::StartThread(std::addressof(g_usb_indication_thread)); + + /* Enable the usb device. */ + R_TRY(g_ds_client.EnableDevice()); + + /* We succeeded! */ + init_guard.Cancel(); + return ResultSuccess(); + } + + void FinalizeUsbInterface() { + g_usb_break_event.Signal(); + os::WaitThread(std::addressof(g_usb_indication_thread)); + os::DestroyThread(std::addressof(g_usb_indication_thread)); + g_ds_client.DisableDevice(); + g_ds_endpoints[1].Finalize(); + g_ds_endpoints[0].Finalize(); + g_ds_interface.Finalize(); + g_ds_client.Finalize(); + g_usb_interface_initialized = false; + } + + Result SendUsb(int *out_transferred, const void *src, int src_size) { + /* Acquire exclusive access to the driver. */ + std::scoped_lock lk(g_usb_driver_mutex); + + /* Check that we can send the data. */ + R_UNLESS(src_size <= static_cast(UsbDmaBufferSize), htclow::ResultInvalidArgument()); + + /* Copy the data to the dma buffer. */ + std::memcpy(g_usb_send_buffer, src, src_size); + + /* Transfer data. */ + u32 transferred; + R_UNLESS(R_SUCCEEDED(g_ds_endpoints[0].PostBuffer(std::addressof(transferred), g_usb_send_buffer, src_size)), htclow::ResultUsbDriverSendError()); + R_UNLESS(transferred == static_cast(src_size), htclow::ResultUsbDriverSendError()); + + /* Set output transferred size. */ + *out_transferred = src_size; + return ResultSuccess(); + } + + Result ReceiveUsb(int *out_transferred, void *dst, int dst_size) { + /* Check that we can send the data. */ + R_UNLESS(dst_size <= static_cast(UsbDmaBufferSize), htclow::ResultInvalidArgument()); + + /* Transfer data. */ + u32 transferred; + R_UNLESS(R_SUCCEEDED(g_ds_endpoints[1].PostBuffer(std::addressof(transferred), g_usb_receive_buffer, dst_size)), htclow::ResultUsbDriverReceiveError()); + R_UNLESS(transferred == static_cast(dst_size), htclow::ResultUsbDriverReceiveError()); + + /* Copy the data. */ + std::memcpy(dst, g_usb_receive_buffer, dst_size); + + /* Set output transferred size. */ + *out_transferred = dst_size; + return ResultSuccess(); + } + + void CancelUsbSendReceive() { + if (g_usb_interface_initialized) { + g_ds_endpoints[0].Cancel(); + g_ds_endpoints[1].Cancel(); + } + } + +} diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_usb_impl.hpp b/libraries/libstratosphere/source/htclow/driver/htclow_usb_impl.hpp new file mode 100644 index 000000000..507dffeff --- /dev/null +++ b/libraries/libstratosphere/source/htclow/driver/htclow_usb_impl.hpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htclow::driver { + + enum UsbAvailability { + UsbAvailability_Unavailable = 1, + UsbAvailability_Available = 2, + UsbAvailability_Unknown = 3, + }; + + using UsbAvailabilityChangeCallback = void (*)(UsbAvailability availability, void *param); + + void SetUsbAvailabilityChangeCallback(UsbAvailabilityChangeCallback callback, void *param); + void ClearUsbAvailabilityChangeCallback(); + + Result InitializeUsbInterface(); + void FinalizeUsbInterface(); + + Result SendUsb(int *out_transferred, const void *src, int src_size); + Result ReceiveUsb(int *out_transferred, void *dst, int dst_size); + + void CancelUsbSendReceive(); + + +} diff --git a/libraries/libstratosphere/source/htclow/htclow_listener.cpp b/libraries/libstratosphere/source/htclow/htclow_listener.cpp index cb2a2d7bc..ac2d0a9b4 100644 --- a/libraries/libstratosphere/source/htclow/htclow_listener.cpp +++ b/libraries/libstratosphere/source/htclow/htclow_listener.cpp @@ -18,11 +18,46 @@ namespace ams::htclow { + namespace { + + constexpr inline size_t ThreadStackSize = 4_KB; + + } + Listener::Listener(mem::StandardAllocator *allocator, mux::Mux *mux, ctrl::HtcctrlService *ctrl_srv, Worker *worker) - : m_thread_stack_size(4_KB), m_allocator(allocator), m_mux(mux), m_service(ctrl_srv), m_worker(worker), m_event(os::EventClearMode_ManualClear), m_driver(nullptr), m_thread_running(false), m_cancelled(false) + : m_thread_stack_size(ThreadStackSize), m_allocator(allocator), m_mux(mux), m_service(ctrl_srv), m_worker(worker), m_event(os::EventClearMode_ManualClear), m_driver(nullptr), m_thread_running(false), m_cancelled(false) { /* Allocate stack. */ m_listen_thread_stack = m_allocator->Allocate(m_thread_stack_size, os::ThreadStackAlignment); } + void Listener::Start(driver::IDriver *driver) { + /* Check pre-conditions. */ + AMS_ASSERT(!m_thread_running); + + /* Create the thread. */ + R_ABORT_UNLESS(os::CreateThread(std::addressof(m_listen_thread), ListenThreadEntry, this, m_listen_thread_stack, ThreadStackSize, AMS_GET_SYSTEM_THREAD_PRIORITY(htc, HtclowListen))); + + /* Set the thread name. */ + os::SetThreadNamePointer(std::addressof(m_listen_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, HtclowListen)); + + /* Set our driver. */ + m_driver = driver; + + /* Set state. */ + m_thread_running = true; + m_cancelled = false; + + /* Clear our event. */ + m_event.Clear(); + + /* Start the thread. */ + os::StartThread(std::addressof(m_listen_thread)); + } + + void Listener::ListenThread() { + /* TODO */ + AMS_ABORT("Listener::ListenThread"); + } + } diff --git a/libraries/libstratosphere/source/htclow/htclow_listener.hpp b/libraries/libstratosphere/source/htclow/htclow_listener.hpp index 0567a176f..17e4aa698 100644 --- a/libraries/libstratosphere/source/htclow/htclow_listener.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_listener.hpp @@ -32,8 +32,16 @@ namespace ams::htclow { driver::IDriver *m_driver; bool m_thread_running; bool m_cancelled; + private: + static void ListenThreadEntry(void *arg) { + static_cast(arg)->ListenThread(); + } + + void ListenThread(); public: Listener(mem::StandardAllocator *allocator, mux::Mux *mux, ctrl::HtcctrlService *ctrl_srv, Worker *worker); + + void Start(driver::IDriver *driver); }; } diff --git a/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp b/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp index e7154f25c..865c82dcc 100644 --- a/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp +++ b/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp @@ -22,7 +22,8 @@ namespace ams::htclow { : m_packet_factory(allocator), m_driver_manager(), m_mux(std::addressof(m_packet_factory), std::addressof(m_ctrl_state_machine)), m_ctrl_packet_factory(allocator), m_ctrl_state_machine(), m_ctrl_service(std::addressof(m_ctrl_packet_factory), std::addressof(m_ctrl_state_machine), std::addressof(m_mux)), m_worker(allocator, std::addressof(m_mux), std::addressof(m_ctrl_service)), - m_listener(allocator, std::addressof(m_mux), std::addressof(m_ctrl_service), std::addressof(m_worker)) + m_listener(allocator, std::addressof(m_mux), std::addressof(m_ctrl_service), std::addressof(m_worker)), + m_is_driver_open(false) { /* ... */ } @@ -32,7 +33,23 @@ namespace ams::htclow { } Result HtclowManagerImpl::OpenDriver(impl::DriverType driver_type) { - AMS_ABORT("TODO"); + /* Set the driver type. */ + m_ctrl_service.SetDriverType(driver_type); + + /* Ensure that we don't end up in an invalid state. */ + auto drv_guard = SCOPE_GUARD { m_ctrl_service.SetDriverType(impl::DriverType::Unknown); }; + + /* Try to open the driver. */ + R_TRY(m_driver_manager.OpenDriver(driver_type)); + + /* Start the listener. */ + m_listener.Start(m_driver_manager.GetCurrentDriver()); + + /* Note the driver as open. */ + m_is_driver_open = true; + + drv_guard.Cancel(); + return ResultSuccess(); } //void HtclowManagerImpl::CloseDriver(); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp index af8c27ae5..1afb8c2c9 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp @@ -31,7 +31,7 @@ namespace ams::htclow::mux { GlobalSendBuffer m_global_send_buffer; os::SdkMutex m_mutex; bool m_is_sleeping; - u16 m_version; + s16 m_version; public: Mux(PacketFactory *pf, ctrl::HtcctrlStateMachine *sm); diff --git a/libraries/libvapours/include/vapours/results.hpp b/libraries/libvapours/include/vapours/results.hpp index 0781b5d9e..95c7f83e4 100644 --- a/libraries/libvapours/include/vapours/results.hpp +++ b/libraries/libvapours/include/vapours/results.hpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libvapours/include/vapours/results/htclow_results.hpp b/libraries/libvapours/include/vapours/results/htclow_results.hpp new file mode 100644 index 000000000..e78bf0b2b --- /dev/null +++ b/libraries/libvapours/include/vapours/results/htclow_results.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htclow { + + R_DEFINE_NAMESPACE_RESULT_MODULE(29); + + R_DEFINE_ERROR_RESULT(UnknownDriverType, 3); + + R_DEFINE_ERROR_RANGE(InternalError, 1000, 2999); + R_DEFINE_ERROR_RESULT(OutOfMemory, 1002); + R_DEFINE_ERROR_RESULT(InvalidArgument, 1003); + R_DEFINE_ERROR_RESULT(Cancelled, 1005); + + R_DEFINE_ERROR_RANGE(DriverError, 1200, 1999); + R_DEFINE_ERROR_RESULT(DriverOpened, 1201); + + R_DEFINE_ERROR_RANGE(UsbDriverError, 1400, 1499); + R_DEFINE_ERROR_RESULT(UsbDriverUnknownError, 1401); + R_DEFINE_ERROR_RESULT(UsbDriverBusyError, 1402); + R_DEFINE_ERROR_RESULT(UsbDriverReceiveError, 1403); + R_DEFINE_ERROR_RESULT(UsbDriverSendError, 1404); + +} From c9c41e0e8de12be4ef7ecc5842a0b64a5953f299 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 8 Feb 2021 03:38:20 -0800 Subject: [PATCH 019/280] htc: actually use the usb driver --- .../source/htclow/driver/htclow_driver_manager.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp index ea16c765d..40268bf2c 100644 --- a/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp +++ b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp @@ -37,10 +37,9 @@ namespace ams::htclow::driver { //break; return htclow::ResultUnknownDriverType(); case impl::DriverType::Usb: - //m_usb_driver.Open(); - //m_open_driver = std::addressof(m_usb_driver); - //break; - return htclow::ResultUnknownDriverType(); + m_usb_driver.Open(); + m_open_driver = std::addressof(m_usb_driver); + break; case impl::DriverType::PlainChannel: //m_plain_channel_driver.Open(); //m_open_driver = std::addressof(m_plain_channel_driver); From 2341f18edda75ef5de7df0a1b97e5ae3d5a78985 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 8 Feb 2021 05:45:23 -0800 Subject: [PATCH 020/280] htc: implement htclow listener thread --- .../htclow/htclow_channel_types.hpp | 21 +++ .../htclow/ctrl/htclow_ctrl_service.cpp | 56 ++++++++ .../htclow/ctrl/htclow_ctrl_service.hpp | 7 + .../source/htclow/ctrl/htclow_ctrl_state.hpp | 110 ++++++++++++++- .../htclow/ctrl/htclow_ctrl_state_machine.cpp | 125 +++++++++++++++++- .../htclow/ctrl/htclow_ctrl_state_machine.hpp | 21 +++ .../source/htclow/htclow_listener.cpp | 31 ++++- .../source/htclow/htclow_packet.hpp | 11 ++ .../source/htclow/htclow_packet_factory.cpp | 25 ++++ .../source/htclow/htclow_packet_factory.hpp | 3 + .../source/htclow/htclow_worker.cpp | 67 +++++++++- .../source/htclow/htclow_worker.hpp | 23 +++- .../source/htclow/mux/htclow_mux.cpp | 25 ++++ .../source/htclow/mux/htclow_mux.hpp | 3 + .../htclow/mux/htclow_mux_channel_impl.cpp | 56 ++++++++ .../htclow/mux/htclow_mux_channel_impl.hpp | 10 +- .../htclow/mux/htclow_mux_send_buffer.cpp | 9 ++ .../htclow/mux/htclow_mux_send_buffer.hpp | 2 + .../htclow/mux/htclow_mux_task_manager.cpp | 50 +++++++ .../htclow/mux/htclow_mux_task_manager.hpp | 23 +++- .../vapours/results/htclow_results.hpp | 2 + 21 files changed, 669 insertions(+), 11 deletions(-) create mode 100644 libraries/libstratosphere/source/htclow/htclow_packet_factory.cpp create mode 100644 libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp diff --git a/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp b/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp index c72487fdb..bf60fd44b 100644 --- a/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp @@ -27,4 +27,25 @@ namespace ams::htclow { ChannelId _channel_id; }; + enum ChannelState { + ChannelState_Connectable = 0, + ChannelState_Unconnectable = 1, + ChannelState_Connected = 2, + ChannelState_Shutdown = 3, + }; + + constexpr bool IsStateTransitionAllowed(ChannelState from, ChannelState to) { + switch (from) { + case ChannelState_Connectable: + return to == ChannelState_Unconnectable || to == ChannelState_Connected || to == ChannelState_Shutdown; + case ChannelState_Unconnectable: + return to == ChannelState_Connectable || to == ChannelState_Shutdown; + case ChannelState_Connected: + return to == ChannelState_Shutdown; + case ChannelState_Shutdown: + return to == ChannelState_Shutdown; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + } diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp index 546f9fb1b..756ba4ba6 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp @@ -15,6 +15,8 @@ */ #include #include "htclow_ctrl_service.hpp" +#include "htclow_ctrl_state.hpp" +#include "htclow_ctrl_state_machine.hpp" #include "../mux/htclow_mux.hpp" namespace ams::htclow::ctrl { @@ -81,4 +83,58 @@ namespace ams::htclow::ctrl { this->UpdateBeaconResponse(this->GetConnectionType(driver_type)); } + Result HtcctrlService::NotifyDriverConnected() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + if (m_state_machine->GetHtcctrlState() == HtcctrlState_7) { + R_TRY(this->SetState(HtcctrlState_8)); + } else { + R_TRY(this->SetState(HtcctrlState_0)); + } + + return ResultSuccess(); + } + + Result HtcctrlService::NotifyDriverDisconnected() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + if (m_state_machine->GetHtcctrlState() == HtcctrlState_6) { + R_TRY(this->SetState(HtcctrlState_7)); + } else { + R_TRY(this->SetState(HtcctrlState_11)); + } + + return ResultSuccess(); + } + + Result HtcctrlService::SetState(HtcctrlState state) { + /* Set the state. */ + bool did_transition; + R_TRY(m_state_machine->SetHtcctrlState(std::addressof(did_transition), state)); + + /* Reflect the state transition, if one occurred. */ + if (did_transition) { + this->ReflectState(); + } + + return ResultSuccess(); + } + + void HtcctrlService::ReflectState() { + /* If our connected status changed, update. */ + if (m_state_machine->IsConnectedStatusChanged()) { + m_mux->UpdateChannelState(); + } + + /* If our sleeping status changed, update. */ + if (m_state_machine->IsSleepingStatusChanged()) { + m_mux->UpdateMuxState(); + } + + /* Broadcast our state transition. */ + m_condvar.Broadcast(); + } + } diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp index c6d208325..5d83d5698 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp @@ -17,6 +17,7 @@ #include #include "htclow_ctrl_settings_holder.hpp" #include "htclow_ctrl_send_buffer.hpp" +#include "htclow_ctrl_state.hpp" namespace ams::htclow { @@ -55,10 +56,16 @@ namespace ams::htclow::ctrl { const char *GetConnectionType(impl::DriverType driver_type) const; void UpdateBeaconResponse(const char *connection); + + Result SetState(HtcctrlState state); + void ReflectState(); public: HtcctrlService(HtcctrlPacketFactory *pf, HtcctrlStateMachine *sm, mux::Mux *mux); void SetDriverType(impl::DriverType driver_type); + + Result NotifyDriverConnected(); + Result NotifyDriverDisconnected(); }; } diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state.hpp index 9d739758a..790363f47 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state.hpp @@ -19,7 +19,115 @@ namespace ams::htclow::ctrl { enum HtcctrlState : u32 { - HtcctrlState_Eleven = 11, + HtcctrlState_0 = 0, + HtcctrlState_1 = 1, + HtcctrlState_2 = 2, + HtcctrlState_3 = 3, + HtcctrlState_4 = 4, + HtcctrlState_5 = 5, + HtcctrlState_6 = 6, + HtcctrlState_7 = 7, + HtcctrlState_8 = 8, + HtcctrlState_9 = 9, + HtcctrlState_10 = 10, + HtcctrlState_11 = 11, + HtcctrlState_12 = 12, }; + constexpr bool IsStateTransitionAllowed(HtcctrlState from, HtcctrlState to) { + switch (from) { + case HtcctrlState_0: + return to == HtcctrlState_1 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12; + case HtcctrlState_1: + return to == HtcctrlState_2 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12; + case HtcctrlState_2: + return to == HtcctrlState_3 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12; + case HtcctrlState_3: + return to == HtcctrlState_4 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12; + case HtcctrlState_4: + return to == HtcctrlState_5 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12; + case HtcctrlState_5: + return to == HtcctrlState_6 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12; + case HtcctrlState_6: + return to == HtcctrlState_7 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12; + case HtcctrlState_7: + return to == HtcctrlState_8; + case HtcctrlState_8: + return to == HtcctrlState_9 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12; + case HtcctrlState_9: + return to == HtcctrlState_4 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12; + case HtcctrlState_10: + return to == HtcctrlState_1 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12; + case HtcctrlState_11: + return to == HtcctrlState_0; + case HtcctrlState_12: + return to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + constexpr bool IsDisconnected(HtcctrlState state) { + switch (state) { + case HtcctrlState_10: + case HtcctrlState_11: + return true; + default: + return false; + } + } + + constexpr bool IsConnecting(HtcctrlState state) { + switch (state) { + case HtcctrlState_0: + case HtcctrlState_1: + case HtcctrlState_10: + return true; + default: + return false; + } + } + + constexpr bool IsConnected(HtcctrlState state) { + switch (state) { + case HtcctrlState_2: + case HtcctrlState_3: + case HtcctrlState_4: + case HtcctrlState_5: + case HtcctrlState_6: + case HtcctrlState_7: + case HtcctrlState_8: + case HtcctrlState_9: + return true; + default: + return false; + } + } + + constexpr bool IsReadied(HtcctrlState state) { + switch (state) { + case HtcctrlState_4: + case HtcctrlState_5: + case HtcctrlState_6: + case HtcctrlState_7: + case HtcctrlState_8: + case HtcctrlState_9: + return true; + default: + return false; + } + } + + constexpr bool IsSleeping(HtcctrlState state) { + switch (state) { + case HtcctrlState_5: + case HtcctrlState_6: + case HtcctrlState_7: + case HtcctrlState_8: + case HtcctrlState_9: + return true; + default: + return false; + } + } + } diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp index b740a4bad..62a8297ea 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp @@ -42,7 +42,7 @@ namespace ams::htclow::ctrl { } - HtcctrlStateMachine::HtcctrlStateMachine() : m_map(), m_state(HtcctrlState_Eleven), m_prev_state(HtcctrlState_Eleven), m_mutex() { + HtcctrlStateMachine::HtcctrlStateMachine() : m_map(), m_state(HtcctrlState_11), m_prev_state(HtcctrlState_11), m_mutex() { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); @@ -55,4 +55,127 @@ namespace ams::htclow::ctrl { } } + HtcctrlState HtcctrlStateMachine::GetHtcctrlState() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + return m_state; + } + + Result HtcctrlStateMachine::SetHtcctrlState(bool *out_transitioned, HtcctrlState state) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Check that the transition is allowed. */ + R_UNLESS(ctrl::IsStateTransitionAllowed(m_state, state), htclow::ResultStateTransitionNotAllowed()); + + /* Get the state pre-transition. */ + const auto old_state = m_state; + + /* Set the state. */ + this->SetStateWithoutCheckInternal(state); + + /* Note whether we transitioned. */ + *out_transitioned = state != old_state; + return ResultSuccess(); + } + + bool HtcctrlStateMachine::IsConnected() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + return ctrl::IsConnected(m_state); + } + + bool HtcctrlStateMachine::IsReadied() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + return ctrl::IsReadied(m_state); + } + + bool HtcctrlStateMachine::IsUnconnectable() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + return !ctrl::IsConnected(m_state); + } + + bool HtcctrlStateMachine::IsDisconnected() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + return ctrl::IsDisconnected(m_state); + } + + bool HtcctrlStateMachine::IsSleeping() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + return ctrl::IsSleeping(m_state); + } + + bool HtcctrlStateMachine::IsConnectedStatusChanged() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + return ctrl::IsConnected(m_prev_state) ^ ctrl::IsConnected(m_state); + } + + bool HtcctrlStateMachine::IsSleepingStatusChanged() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + return ctrl::IsSleeping(m_prev_state) ^ ctrl::IsSleeping(m_state); + } + + void HtcctrlStateMachine::SetStateWithoutCheckInternal(HtcctrlState state) { + if (m_state != state) { + /* Clear service channel states, if we should. */ + if (ctrl::IsDisconnected(state)) { + this->ClearServiceChannelStates(); + } + + /* Transition our state. */ + m_prev_state = m_state; + m_state = state; + } + } + + void HtcctrlStateMachine::ClearServiceChannelStates() { + /* Clear all values in our map. */ + for (auto &pair : m_map) { + pair.second = {}; + } + } + + bool HtcctrlStateMachine::IsUnsupportedServiceChannelToShutdown(const impl::ChannelInternalType &channel) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* TODO: What do these values mean? */ + auto it = m_map.find(channel); + return it != m_map.end() && it->second._04 == 2 && it->second._00 == 2; + } + + bool HtcctrlStateMachine::IsConnectable(const impl::ChannelInternalType &channel) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* TODO: What do these values mean? */ + auto it = m_map.find(channel); + return ctrl::IsConnected(m_state) && (!(it != m_map.end()) || it->second._04 != 2); + } + + void HtcctrlStateMachine::SetNotConnecting(const impl::ChannelInternalType &channel) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* TODO: What do these values mean? */ + auto it = m_map.find(channel); + if (it != m_map.end() && it->second._04 != 2) { + it->second._04 = 0; + } + } + } diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp index 10053e793..ad53a2d56 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp @@ -39,6 +39,27 @@ namespace ams::htclow::ctrl { os::SdkMutex m_mutex; public: HtcctrlStateMachine(); + + HtcctrlState GetHtcctrlState(); + Result SetHtcctrlState(bool *out_transitioned, HtcctrlState state); + + bool IsConnectedStatusChanged(); + bool IsSleepingStatusChanged(); + + bool IsConnected(); + bool IsReadied(); + bool IsUnconnectable(); + bool IsDisconnected(); + bool IsSleeping(); + + bool IsUnsupportedServiceChannelToShutdown(const impl::ChannelInternalType &channel); + bool IsConnectable(const impl::ChannelInternalType &channel); + + void SetNotConnecting(const impl::ChannelInternalType &channel); + private: + void SetStateWithoutCheckInternal(HtcctrlState state); + + void ClearServiceChannelStates(); }; } diff --git a/libraries/libstratosphere/source/htclow/htclow_listener.cpp b/libraries/libstratosphere/source/htclow/htclow_listener.cpp index ac2d0a9b4..278cfc7cb 100644 --- a/libraries/libstratosphere/source/htclow/htclow_listener.cpp +++ b/libraries/libstratosphere/source/htclow/htclow_listener.cpp @@ -56,8 +56,35 @@ namespace ams::htclow { } void Listener::ListenThread() { - /* TODO */ - AMS_ABORT("Listener::ListenThread"); + /* Check pre-conditions. */ + AMS_ASSERT(m_driver != nullptr); + AMS_ASSERT(m_worker != nullptr); + + /* Set the worker's driver. */ + m_worker->SetDriver(m_driver); + + /* Loop forever, while we're not cancelled. */ + while (!m_cancelled) { + /* Connect. */ + if (R_FAILED(m_driver->Connect(m_event.GetBase()))) { + return; + } + + /* Notify that we're connected. */ + R_ABORT_UNLESS(m_service->NotifyDriverConnected()); + + /* Start the worker. */ + m_worker->Start(); + + /* Wait for the worker to end. */ + m_worker->Wait(); + + /* Shutdown the driver. */ + m_driver->Shutdown(); + + /* Notify that we're disconnected. */ + R_ABORT_UNLESS(m_service->NotifyDriverDisconnected()); + } } } diff --git a/libraries/libstratosphere/source/htclow/htclow_packet.hpp b/libraries/libstratosphere/source/htclow/htclow_packet.hpp index 11a53b4bd..681d47321 100644 --- a/libraries/libstratosphere/source/htclow/htclow_packet.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_packet.hpp @@ -20,6 +20,17 @@ namespace ams::htclow { class Packet : public util::IntrusiveListBaseNode { /* TODO */ + public: + virtual ~Packet(); + }; + + struct PacketDeleter { + mem::StandardAllocator *m_allocator; + + void operator()(Packet *packet) { + std::destroy_at(packet); + m_allocator->Free(packet); + } }; } diff --git a/libraries/libstratosphere/source/htclow/htclow_packet_factory.cpp b/libraries/libstratosphere/source/htclow/htclow_packet_factory.cpp new file mode 100644 index 000000000..469ab3334 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/htclow_packet_factory.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htclow_packet_factory.hpp" + +namespace ams::htclow { + + void PacketFactory::Delete(Packet *packet) { + PacketDeleter{m_allocator}(packet); + } + +} diff --git a/libraries/libstratosphere/source/htclow/htclow_packet_factory.hpp b/libraries/libstratosphere/source/htclow/htclow_packet_factory.hpp index 5c0f85dd3..8def47b9f 100644 --- a/libraries/libstratosphere/source/htclow/htclow_packet_factory.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_packet_factory.hpp @@ -15,6 +15,7 @@ */ #pragma once #include +#include "htclow_packet.hpp" namespace ams::htclow { @@ -23,6 +24,8 @@ namespace ams::htclow { mem::StandardAllocator *m_allocator; public: PacketFactory(mem::StandardAllocator *allocator) : m_allocator(allocator) { /* ... */ } + + void Delete(Packet *packet); }; } diff --git a/libraries/libstratosphere/source/htclow/htclow_worker.cpp b/libraries/libstratosphere/source/htclow/htclow_worker.cpp index bbe6ab99b..714ebee64 100644 --- a/libraries/libstratosphere/source/htclow/htclow_worker.cpp +++ b/libraries/libstratosphere/source/htclow/htclow_worker.cpp @@ -18,12 +18,77 @@ namespace ams::htclow { + namespace { + + constexpr inline size_t ThreadStackSize = 4_KB; + + } + Worker::Worker(mem::StandardAllocator *allocator, mux::Mux *mux, ctrl::HtcctrlService *ctrl_srv) - : m_thread_stack_size(4_KB), m_allocator(allocator), m_mux(mux), m_service(ctrl_srv), m_driver(nullptr), m_event(os::EventClearMode_ManualClear), m_7C400() + : m_thread_stack_size(ThreadStackSize), m_allocator(allocator), m_mux(mux), m_service(ctrl_srv), m_driver(nullptr), m_event(os::EventClearMode_ManualClear), m_cancelled(false) { /* Allocate stacks. */ m_receive_thread_stack = m_allocator->Allocate(m_thread_stack_size, os::ThreadStackAlignment); m_send_thread_stack = m_allocator->Allocate(m_thread_stack_size, os::ThreadStackAlignment); } + void Worker::SetDriver(driver::IDriver *driver) { + m_driver = driver; + } + + void Worker::Start() { + /* Clear our cancelled state. */ + m_cancelled = false; + + /* Clear our event. */ + m_event.Clear(); + + /* Create our threads. */ + R_ABORT_UNLESS(os::CreateThread(std::addressof(m_receive_thread), ReceiveThreadEntry, this, m_receive_thread_stack, ThreadStackSize, AMS_GET_SYSTEM_THREAD_PRIORITY(htc, HtclowReceive))); + R_ABORT_UNLESS(os::CreateThread(std::addressof(m_send_thread), SendThreadEntry, this, m_send_thread_stack, ThreadStackSize, AMS_GET_SYSTEM_THREAD_PRIORITY(htc, HtclowSend))); + + /* Set thread names. */ + os::SetThreadNamePointer(std::addressof(m_receive_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, HtclowReceive)); + os::SetThreadNamePointer(std::addressof(m_send_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, HtclowSend)); + + /* Start our threads. */ + os::StartThread(std::addressof(m_receive_thread)); + os::StartThread(std::addressof(m_send_thread)); + } + + void Worker::Wait() { + os::WaitThread(std::addressof(m_receive_thread)); + os::WaitThread(std::addressof(m_send_thread)); + os::DestroyThread(std::addressof(m_receive_thread)); + os::DestroyThread(std::addressof(m_send_thread)); + } + + void Worker::ReceiveThread() { + this->ProcessReceive(); + m_driver->CancelSendReceive(); + this->Cancel(); + } + + void Worker::SendThread() { + this->ProcessSend(); + m_driver->CancelSendReceive(); + this->Cancel(); + } + + void Worker::Cancel() { + /* Set ourselves as cancelled, and signal. */ + m_cancelled = true; + m_event.Signal(); + } + + Result Worker::ProcessReceive() { + /* TODO */ + AMS_ABORT("Worker::ProcessReceive"); + } + + Result Worker::ProcessSend() { + /* TODO */ + AMS_ABORT("Worker::ProcessSend"); + } + } diff --git a/libraries/libstratosphere/source/htclow/htclow_worker.hpp b/libraries/libstratosphere/source/htclow/htclow_worker.hpp index 7c3c9f34f..a30214117 100644 --- a/libraries/libstratosphere/source/htclow/htclow_worker.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_worker.hpp @@ -34,9 +34,30 @@ namespace ams::htclow { os::Event m_event; void *m_receive_thread_stack; void *m_send_thread_stack; - u8 m_7C400; + bool m_cancelled; + private: + static void ReceiveThreadEntry(void *arg) { + static_cast(arg)->ReceiveThread(); + } + + static void SendThreadEntry(void *arg) { + static_cast(arg)->SendThread(); + } + + void ReceiveThread(); + void SendThread(); public: Worker(mem::StandardAllocator *allocator, mux::Mux *mux, ctrl::HtcctrlService *ctrl_srv); + + void SetDriver(driver::IDriver *driver); + + void Start(); + void Wait(); + private: + void Cancel(); + + Result ProcessReceive(); + Result ProcessSend(); }; } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp index 474470dbf..1b6d5a43d 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp @@ -15,6 +15,7 @@ */ #include #include "htclow_mux.hpp" +#include "../ctrl/htclow_ctrl_state_machine.hpp" namespace ams::htclow::mux { @@ -37,4 +38,28 @@ namespace ams::htclow::mux { } } + void Mux::UpdateChannelState() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Update the state of all channels in our map. */ + /* NOTE: Nintendo does this highly inefficiently... */ + for (auto pair : m_channel_impl_map.GetMap()) { + m_channel_impl_map[pair.first].UpdateState(); + } + } + + void Mux::UpdateMuxState() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Update whether we're sleeping. */ + if (m_state_machine->IsSleeping()) { + m_is_sleeping = true; + } else { + m_is_sleeping = false; + m_wake_event.Signal(); + } + } + } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp index 1afb8c2c9..f9e6180a9 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp @@ -36,6 +36,9 @@ namespace ams::htclow::mux { Mux(PacketFactory *pf, ctrl::HtcctrlStateMachine *sm); void SetVersion(u16 version); + + void UpdateChannelState(); + void UpdateMuxState(); }; } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp index f10d81a2f..1b8df5361 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp @@ -15,6 +15,7 @@ */ #include #include "htclow_mux_channel_impl.hpp" +#include "../ctrl/htclow_ctrl_state_machine.hpp" namespace ams::htclow::mux { @@ -27,4 +28,59 @@ namespace ams::htclow::mux { m_send_buffer.SetVersion(version); } + void ChannelImpl::UpdateState() { + /* Check if shutdown must be forced. */ + if (m_state_machine->IsUnsupportedServiceChannelToShutdown(m_channel)) { + this->ShutdownForce(); + } + + /* Check if we're readied. */ + if (m_state_machine->IsReadied()) { + m_task_manager->NotifyConnectReady(); + } + + /* Update our state transition. */ + if (m_state_machine->IsConnectable(m_channel)) { + if (m_state == ChannelState_Unconnectable) { + this->SetState(ChannelState_Connectable); + } + } else if (m_state_machine->IsUnconnectable()) { + if (m_state == ChannelState_Connectable) { + this->SetState(ChannelState_Unconnectable); + m_state_machine->SetNotConnecting(m_channel); + } else if (m_state == ChannelState_Connected) { + this->ShutdownForce(); + } + } + } + + void ChannelImpl::ShutdownForce() { + /* Clear our send buffer. */ + m_send_buffer.Clear(); + + /* Set our state to shutdown. */ + this->SetState(ChannelState_Shutdown); + } + + void ChannelImpl::SetState(ChannelState state) { + /* Check that we can perform the transition. */ + AMS_ABORT_UNLESS(IsStateTransitionAllowed(m_state, state)); + + /* Perform the transition. */ + this->SetStateWithoutCheck(state); + } + + void ChannelImpl::SetStateWithoutCheck(ChannelState state) { + /* Change our state. */ + if (m_state != state) { + m_state = state; + m_state_change_event.Signal(); + } + + /* If relevant, notify disconnect. */ + if (m_state == ChannelState_Shutdown) { + m_task_manager->NotifyDisconnect(m_channel); + } + } + } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp index e014cd05a..b24a115a8 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp @@ -45,12 +45,18 @@ namespace ams::htclow::mux { /* TODO: Channel config */ /* TODO: tracking variables. */ std::optional m_108; - os::Event m_send_packet_event; - /* TODO: Channel state. */ + os::Event m_state_change_event; + ChannelState m_state; public: ChannelImpl(impl::ChannelInternalType channel, PacketFactory *pf, ctrl::HtcctrlStateMachine *sm, TaskManager *tm, os::Event *ev); void SetVersion(s16 version); + + void UpdateState(); + private: + void ShutdownForce(); + void SetState(ChannelState state); + void SetStateWithoutCheck(ChannelState state); }; } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp index 2d3693428..0927eba75 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp @@ -15,6 +15,7 @@ */ #include #include "htclow_mux_channel_impl.hpp" +#include "../htclow_packet_factory.hpp" namespace ams::htclow::mux { @@ -23,4 +24,12 @@ namespace ams::htclow::mux { m_version = version; } + void SendBuffer::Clear() { + while (!m_packet_list.empty()) { + auto *packet = std::addressof(m_packet_list.front()); + m_packet_list.pop_front(); + m_packet_factory->Delete(packet); + } + } + } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp index cfc5695be..5d7af800f 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp @@ -41,6 +41,8 @@ namespace ams::htclow::mux { SendBuffer(impl::ChannelInternalType channel, PacketFactory *pf); void SetVersion(s16 version); + + void Clear(); }; } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp new file mode 100644 index 000000000..f396a9cd4 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htclow_mux_task_manager.hpp" + +namespace ams::htclow::mux { + + void TaskManager::NotifyDisconnect(impl::ChannelInternalType channel) { + for (auto i = 0; i < MaxTaskCount; ++i) { + if (m_valid[i] && m_tasks[i].channel == channel) { + this->CompleteTask(i, EventTrigger_Disconnect); + } + } + } + + void TaskManager::NotifyConnectReady() { + for (auto i = 0; i < MaxTaskCount; ++i) { + if (m_valid[i] && m_tasks[i].type == TaskType_Connect) { + this->CompleteTask(i, EventTrigger_ConnectReady); + } + } + } + + void TaskManager::CompleteTask(int index, EventTrigger trigger) { + /* Check pre-conditions. */ + AMS_ASSERT(0 <= index && index < MaxTaskCount); + AMS_ASSERT(m_valid[index]); + + /* Complete the task. */ + if (!m_tasks[index].has_event_trigger) { + m_tasks[index].has_event_trigger = true; + m_tasks[index].event_trigger = trigger; + os::SignalEvent(std::addressof(m_tasks[index].event)); + } + } + +} diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp index b0f7d30a8..d82a97dae 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp @@ -20,14 +20,26 @@ namespace ams::htclow::mux { constexpr inline int MaxTaskCount = 0x80; + enum EventTrigger : u8 { + EventTrigger_Disconnect = 1, + EventTrigger_ConnectReady = 11, + }; + class TaskManager { private: + enum TaskType : u8 { + TaskType_Receive = 0, + TaskType_Send = 1, + TaskType_Flush = 6, + TaskType_Connect = 7, + }; + struct Task { impl::ChannelInternalType channel; os::EventType event; - u8 _30; - u8 _31; - u8 _32; + bool has_event_trigger; + EventTrigger event_trigger; + TaskType type; u64 _38; }; private: @@ -35,6 +47,11 @@ namespace ams::htclow::mux { Task m_tasks[MaxTaskCount]; public: TaskManager() : m_valid() { /* ... */ } + + void NotifyDisconnect(impl::ChannelInternalType channel); + void NotifyConnectReady(); + private: + void CompleteTask(int index, EventTrigger trigger); }; } diff --git a/libraries/libvapours/include/vapours/results/htclow_results.hpp b/libraries/libvapours/include/vapours/results/htclow_results.hpp index e78bf0b2b..4e47508c8 100644 --- a/libraries/libvapours/include/vapours/results/htclow_results.hpp +++ b/libraries/libvapours/include/vapours/results/htclow_results.hpp @@ -36,4 +36,6 @@ namespace ams::htclow { R_DEFINE_ERROR_RESULT(UsbDriverReceiveError, 1403); R_DEFINE_ERROR_RESULT(UsbDriverSendError, 1404); + R_DEFINE_ERROR_RESULT(StateTransitionNotAllowed, 2001); + } From e40eece74ed929e1516d5930b9f9118dda25e0b6 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 8 Feb 2021 06:48:30 -0800 Subject: [PATCH 021/280] htc: free ourselves from the tyranny of numerical enums --- .../htclow/htclow_channel_types.hpp | 15 +- .../htclow/ctrl/htclow_ctrl_service.cpp | 12 +- .../source/htclow/ctrl/htclow_ctrl_state.hpp | 158 +++++++++++------- .../htclow/ctrl/htclow_ctrl_state_machine.cpp | 13 +- .../htclow/ctrl/htclow_ctrl_state_machine.hpp | 16 +- .../htclow/mux/htclow_mux_channel_impl.cpp | 4 +- 6 files changed, 131 insertions(+), 87 deletions(-) diff --git a/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp b/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp index bf60fd44b..b1658127c 100644 --- a/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp @@ -31,19 +31,22 @@ namespace ams::htclow { ChannelState_Connectable = 0, ChannelState_Unconnectable = 1, ChannelState_Connected = 2, - ChannelState_Shutdown = 3, + ChannelState_Disconnected = 3, }; constexpr bool IsStateTransitionAllowed(ChannelState from, ChannelState to) { switch (from) { case ChannelState_Connectable: - return to == ChannelState_Unconnectable || to == ChannelState_Connected || to == ChannelState_Shutdown; + return to == ChannelState_Unconnectable || + to == ChannelState_Connected || + to == ChannelState_Disconnected; case ChannelState_Unconnectable: - return to == ChannelState_Connectable || to == ChannelState_Shutdown; + return to == ChannelState_Connectable || + to == ChannelState_Disconnected; case ChannelState_Connected: - return to == ChannelState_Shutdown; - case ChannelState_Shutdown: - return to == ChannelState_Shutdown; + return to == ChannelState_Disconnected; + case ChannelState_Disconnected: + return to == ChannelState_Disconnected; AMS_UNREACHABLE_DEFAULT_CASE(); } } diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp index 756ba4ba6..9c5e43323 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp @@ -87,10 +87,10 @@ namespace ams::htclow::ctrl { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); - if (m_state_machine->GetHtcctrlState() == HtcctrlState_7) { - R_TRY(this->SetState(HtcctrlState_8)); + if (m_state_machine->GetHtcctrlState() == HtcctrlState_Sleep) { + R_TRY(this->SetState(HtcctrlState_ExitSleep)); } else { - R_TRY(this->SetState(HtcctrlState_0)); + R_TRY(this->SetState(HtcctrlState_DriverConnected)); } return ResultSuccess(); @@ -100,10 +100,10 @@ namespace ams::htclow::ctrl { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); - if (m_state_machine->GetHtcctrlState() == HtcctrlState_6) { - R_TRY(this->SetState(HtcctrlState_7)); + if (m_state_machine->GetHtcctrlState() == HtcctrlState_EnterSleep) { + R_TRY(this->SetState(HtcctrlState_Sleep)); } else { - R_TRY(this->SetState(HtcctrlState_11)); + R_TRY(this->SetState(HtcctrlState_DriverDisconnected)); } return ResultSuccess(); diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state.hpp index 790363f47..189d7ea5b 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state.hpp @@ -19,57 +19,89 @@ namespace ams::htclow::ctrl { enum HtcctrlState : u32 { - HtcctrlState_0 = 0, - HtcctrlState_1 = 1, - HtcctrlState_2 = 2, - HtcctrlState_3 = 3, - HtcctrlState_4 = 4, - HtcctrlState_5 = 5, - HtcctrlState_6 = 6, - HtcctrlState_7 = 7, - HtcctrlState_8 = 8, - HtcctrlState_9 = 9, - HtcctrlState_10 = 10, - HtcctrlState_11 = 11, - HtcctrlState_12 = 12, + HtcctrlState_DriverConnected = 0, + HtcctrlState_SentConnectFromHost = 1, + HtcctrlState_Connected = 2, + HtcctrlState_SentReadyFromHost = 3, + HtcctrlState_Ready = 4, + HtcctrlState_SentSuspendFromTarget = 5, + HtcctrlState_EnterSleep = 6, + HtcctrlState_Sleep = 7, + HtcctrlState_ExitSleep = 8, + HtcctrlState_SentResumeFromTarget = 9, + HtcctrlState_Disconnected = 10, + HtcctrlState_DriverDisconnected = 11, + HtcctrlState_Error = 12, }; constexpr bool IsStateTransitionAllowed(HtcctrlState from, HtcctrlState to) { switch (from) { - case HtcctrlState_0: - return to == HtcctrlState_1 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12; - case HtcctrlState_1: - return to == HtcctrlState_2 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12; - case HtcctrlState_2: - return to == HtcctrlState_3 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12; - case HtcctrlState_3: - return to == HtcctrlState_4 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12; - case HtcctrlState_4: - return to == HtcctrlState_5 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12; - case HtcctrlState_5: - return to == HtcctrlState_6 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12; - case HtcctrlState_6: - return to == HtcctrlState_7 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12; - case HtcctrlState_7: - return to == HtcctrlState_8; - case HtcctrlState_8: - return to == HtcctrlState_9 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12; - case HtcctrlState_9: - return to == HtcctrlState_4 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12; - case HtcctrlState_10: - return to == HtcctrlState_1 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12; - case HtcctrlState_11: - return to == HtcctrlState_0; - case HtcctrlState_12: - return to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12; + case HtcctrlState_DriverConnected: + return to == HtcctrlState_SentConnectFromHost || + to == HtcctrlState_Disconnected || + to == HtcctrlState_DriverDisconnected || + to == HtcctrlState_Error; + case HtcctrlState_SentConnectFromHost: + return to == HtcctrlState_Connected || + to == HtcctrlState_Disconnected || + to == HtcctrlState_DriverDisconnected || + to == HtcctrlState_Error; + case HtcctrlState_Connected: + return to == HtcctrlState_SentReadyFromHost || + to == HtcctrlState_Disconnected || + to == HtcctrlState_DriverDisconnected || + to == HtcctrlState_Error; + case HtcctrlState_SentReadyFromHost: + return to == HtcctrlState_Ready || + to == HtcctrlState_Disconnected || + to == HtcctrlState_DriverDisconnected || + to == HtcctrlState_Error; + case HtcctrlState_Ready: + return to == HtcctrlState_SentSuspendFromTarget || + to == HtcctrlState_Disconnected || + to == HtcctrlState_DriverDisconnected || + to == HtcctrlState_Error; + case HtcctrlState_SentSuspendFromTarget: + return to == HtcctrlState_EnterSleep || + to == HtcctrlState_Disconnected || + to == HtcctrlState_DriverDisconnected || + to == HtcctrlState_Error; + case HtcctrlState_EnterSleep: + return to == HtcctrlState_Sleep || + to == HtcctrlState_Disconnected || + to == HtcctrlState_DriverDisconnected || + to == HtcctrlState_Error; + case HtcctrlState_Sleep: + return to == HtcctrlState_ExitSleep; + case HtcctrlState_ExitSleep: + return to == HtcctrlState_SentResumeFromTarget || + to == HtcctrlState_Disconnected || + to == HtcctrlState_DriverDisconnected || + to == HtcctrlState_Error; + case HtcctrlState_SentResumeFromTarget: + return to == HtcctrlState_Ready || + to == HtcctrlState_Disconnected || + to == HtcctrlState_DriverDisconnected || + to == HtcctrlState_Error; + case HtcctrlState_Disconnected: + return to == HtcctrlState_SentConnectFromHost || + to == HtcctrlState_Disconnected || + to == HtcctrlState_DriverDisconnected || + to == HtcctrlState_Error; + case HtcctrlState_DriverDisconnected: + return to == HtcctrlState_DriverConnected; + case HtcctrlState_Error: + return to == HtcctrlState_Disconnected || + to == HtcctrlState_DriverDisconnected || + to == HtcctrlState_Error; AMS_UNREACHABLE_DEFAULT_CASE(); } } constexpr bool IsDisconnected(HtcctrlState state) { switch (state) { - case HtcctrlState_10: - case HtcctrlState_11: + case HtcctrlState_Disconnected: + case HtcctrlState_DriverDisconnected: return true; default: return false; @@ -78,9 +110,9 @@ namespace ams::htclow::ctrl { constexpr bool IsConnecting(HtcctrlState state) { switch (state) { - case HtcctrlState_0: - case HtcctrlState_1: - case HtcctrlState_10: + case HtcctrlState_DriverConnected: + case HtcctrlState_SentConnectFromHost: + case HtcctrlState_Disconnected: return true; default: return false; @@ -89,14 +121,14 @@ namespace ams::htclow::ctrl { constexpr bool IsConnected(HtcctrlState state) { switch (state) { - case HtcctrlState_2: - case HtcctrlState_3: - case HtcctrlState_4: - case HtcctrlState_5: - case HtcctrlState_6: - case HtcctrlState_7: - case HtcctrlState_8: - case HtcctrlState_9: + case HtcctrlState_Connected: + case HtcctrlState_SentReadyFromHost: + case HtcctrlState_Ready: + case HtcctrlState_SentSuspendFromTarget: + case HtcctrlState_EnterSleep: + case HtcctrlState_Sleep: + case HtcctrlState_ExitSleep: + case HtcctrlState_SentResumeFromTarget: return true; default: return false; @@ -105,12 +137,12 @@ namespace ams::htclow::ctrl { constexpr bool IsReadied(HtcctrlState state) { switch (state) { - case HtcctrlState_4: - case HtcctrlState_5: - case HtcctrlState_6: - case HtcctrlState_7: - case HtcctrlState_8: - case HtcctrlState_9: + case HtcctrlState_Ready: + case HtcctrlState_SentSuspendFromTarget: + case HtcctrlState_EnterSleep: + case HtcctrlState_Sleep: + case HtcctrlState_ExitSleep: + case HtcctrlState_SentResumeFromTarget: return true; default: return false; @@ -119,11 +151,11 @@ namespace ams::htclow::ctrl { constexpr bool IsSleeping(HtcctrlState state) { switch (state) { - case HtcctrlState_5: - case HtcctrlState_6: - case HtcctrlState_7: - case HtcctrlState_8: - case HtcctrlState_9: + case HtcctrlState_SentSuspendFromTarget: + case HtcctrlState_EnterSleep: + case HtcctrlState_Sleep: + case HtcctrlState_ExitSleep: + case HtcctrlState_SentResumeFromTarget: return true; default: return false; diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp index 62a8297ea..c80bd784b 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp @@ -42,7 +42,7 @@ namespace ams::htclow::ctrl { } - HtcctrlStateMachine::HtcctrlStateMachine() : m_map(), m_state(HtcctrlState_11), m_prev_state(HtcctrlState_11), m_mutex() { + HtcctrlStateMachine::HtcctrlStateMachine() : m_map(), m_state(HtcctrlState_DriverDisconnected), m_prev_state(HtcctrlState_DriverDisconnected), m_mutex() { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); @@ -153,28 +153,25 @@ namespace ams::htclow::ctrl { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); - /* TODO: What do these values mean? */ auto it = m_map.find(channel); - return it != m_map.end() && it->second._04 == 2 && it->second._00 == 2; + return it != m_map.end() && it->second.connect == ServiceChannelConnect_ConnectingChecked && it->second.support == ServiceChannelSupport_Unsupported; } bool HtcctrlStateMachine::IsConnectable(const impl::ChannelInternalType &channel) { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); - /* TODO: What do these values mean? */ auto it = m_map.find(channel); - return ctrl::IsConnected(m_state) && (!(it != m_map.end()) || it->second._04 != 2); + return ctrl::IsConnected(m_state) && (it == m_map.end() || it->second.connect != ServiceChannelConnect_ConnectingChecked); } void HtcctrlStateMachine::SetNotConnecting(const impl::ChannelInternalType &channel) { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); - /* TODO: What do these values mean? */ auto it = m_map.find(channel); - if (it != m_map.end() && it->second._04 != 2) { - it->second._04 = 0; + if (it != m_map.end() && it->second.connect != ServiceChannelConnect_ConnectingChecked) { + it->second.connect = ServiceChannelConnect_NotConnecting; } } diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp index ad53a2d56..6651eff6e 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp @@ -21,9 +21,21 @@ namespace ams::htclow::ctrl { class HtcctrlStateMachine { private: + enum ServiceChannelSupport { + ServiceChannelSupport_Unknown = 0, + ServiceChannelSupport_Suppported = 1, + ServiceChannelSupport_Unsupported = 2, + }; + + enum ServiceChannelConnect { + ServiceChannelConnect_NotConnecting = 0, + ServiceChannelConnect_Connecting = 1, + ServiceChannelConnect_ConnectingChecked = 2, + }; + struct ServiceChannelState { - u32 _00; - u32 _04; + ServiceChannelSupport support; + ServiceChannelConnect connect; }; static constexpr int MaxChannelCount = 10; diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp index 1b8df5361..fe54c8ada 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp @@ -59,7 +59,7 @@ namespace ams::htclow::mux { m_send_buffer.Clear(); /* Set our state to shutdown. */ - this->SetState(ChannelState_Shutdown); + this->SetState(ChannelState_Disconnected); } void ChannelImpl::SetState(ChannelState state) { @@ -78,7 +78,7 @@ namespace ams::htclow::mux { } /* If relevant, notify disconnect. */ - if (m_state == ChannelState_Shutdown) { + if (m_state == ChannelState_Disconnected) { m_task_manager->NotifyDisconnect(m_channel); } } From 8f85cc17dc01ada1bdcb3135a74bc4f4ee6f0c7d Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 8 Feb 2021 12:26:00 -0800 Subject: [PATCH 022/280] htc: fix copy/paste error in usb driver --- .../libstratosphere/source/htclow/driver/htclow_usb_driver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_usb_driver.cpp b/libraries/libstratosphere/source/htclow/driver/htclow_usb_driver.cpp index d52c07ea0..1e0072ed6 100644 --- a/libraries/libstratosphere/source/htclow/driver/htclow_usb_driver.cpp +++ b/libraries/libstratosphere/source/htclow/driver/htclow_usb_driver.cpp @@ -102,7 +102,7 @@ namespace ams::htclow::driver { /* Send until we've sent everything. */ for (auto transferred = 0; transferred < dst_size; /* ... */) { int cur; - R_TRY(SendUsb(std::addressof(cur), reinterpret_cast(reinterpret_cast(dst) + transferred), dst_size - transferred)); + R_TRY(ReceiveUsb(std::addressof(cur), reinterpret_cast(reinterpret_cast(dst) + transferred), dst_size - transferred)); transferred += cur; } From 679fec2ddc2019d8bf8410bcfeb6cf8768f685ce Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 8 Feb 2021 14:11:01 -0800 Subject: [PATCH 023/280] htc: implement much of worker receive logic --- .../htclow/htclow_channel_types.hpp | 4 + .../source/htclow/ctrl/htclow_ctrl_packet.hpp | 52 ++++++++- .../htclow/ctrl/htclow_ctrl_service.cpp | 31 +++++ .../htclow/ctrl/htclow_ctrl_service.hpp | 3 + .../source/htclow/htclow_packet.hpp | 82 ++++++++++++- .../source/htclow/htclow_packet_factory.cpp | 72 ++++++++++++ .../source/htclow/htclow_packet_factory.hpp | 6 + .../source/htclow/htclow_worker.cpp | 51 +++++++- .../source/htclow/htclow_worker.hpp | 10 +- .../source/htclow/mux/htclow_mux.cpp | 50 ++++++++ .../source/htclow/mux/htclow_mux.hpp | 7 ++ .../htclow/mux/htclow_mux_channel_impl.cpp | 110 ++++++++++++++++++ .../htclow/mux/htclow_mux_channel_impl.hpp | 16 ++- .../mux/htclow_mux_channel_impl_map.hpp | 4 + .../htclow/mux/htclow_mux_ring_buffer.cpp | 48 ++++++++ .../htclow/mux/htclow_mux_ring_buffer.hpp | 4 + .../htclow/mux/htclow_mux_task_manager.cpp | 8 ++ .../htclow/mux/htclow_mux_task_manager.hpp | 4 +- .../vapours/results/htclow_results.hpp | 15 ++- 19 files changed, 565 insertions(+), 12 deletions(-) create mode 100644 libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.cpp diff --git a/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp b/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp index b1658127c..685f78372 100644 --- a/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp @@ -34,6 +34,10 @@ namespace ams::htclow { ChannelState_Disconnected = 3, }; + struct ChannelConfig { + bool flow_control_enabled; + }; + constexpr bool IsStateTransitionAllowed(ChannelState from, ChannelState to) { switch (from) { case ChannelState_Connectable: diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet.hpp index 574dcf845..bb12eb3dd 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet.hpp @@ -15,11 +15,59 @@ */ #pragma once #include +#include "../htclow_packet.hpp" namespace ams::htclow::ctrl { - class HtcctrlPacket : public util::IntrusiveListBaseNode { - /* TODO */ + enum HtcctrlPacketType : u16 { + HtcctrlPacketType_ConnectFromHost = 16, + HtcctrlPacketType_ConnectFromTarget = 17, + HtcctrlPacketType_ReadyFromHost = 18, + HtcctrlPacketType_ReadyFromTarget = 19, + HtcctrlPacketType_SuspendFromHost = 20, + HtcctrlPacketType_SuspendFromTarget = 21, + HtcctrlPacketType_ResumeFromHost = 22, + HtcctrlPacketType_ResumeFromTarget = 23, + HtcctrlPacketType_DisconnectFromHost = 24, + HtcctrlPacketType_DisconnectFromTarget = 25, + HtcctrlPacketType_BeaconQuery = 28, + HtcctrlPacketType_BeaconResponse = 29, + HtcctrlPacketType_InformationFromTarget = 33, + }; + + static constexpr inline u32 HtcctrlSignature = 0x78825637; + + struct HtcctrlPacketHeader { + u32 signature; + u32 offset; + u32 reserved; + u32 body_size; + s16 version; + HtcctrlPacketType packet_type; + impl::ChannelInternalType channel; + u64 share; + }; + static_assert(util::is_pod::value); + static_assert(sizeof(HtcctrlPacketHeader) == 0x20); + + static constexpr inline size_t HtcctrlPacketBodySizeMax = 0x1000; + + struct HtcctrlPacketBody { + u8 data[HtcctrlPacketBodySizeMax]; + }; + + class HtcctrlPacket : public BasePacket, public util::IntrusiveListBaseNode { + public: + using BasePacket::BasePacket; + }; + + struct HtcctrlPacketDeleter { + mem::StandardAllocator *m_allocator; + + void operator()(HtcctrlPacket *packet) { + std::destroy_at(packet); + m_allocator->Free(packet); + } }; } diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp index 9c5e43323..55bb00d02 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp @@ -83,6 +83,37 @@ namespace ams::htclow::ctrl { this->UpdateBeaconResponse(this->GetConnectionType(driver_type)); } + Result HtcctrlService::CheckReceivedHeader(const HtcctrlPacketHeader &header) const { + /* Check the packet signature. */ + AMS_ASSERT(header.signature == HtcctrlSignature); + + /* Validate version. */ + R_UNLESS(header.version == 1, htclow::ResultProtocolError()); + + /* Switch on the packet type. */ + switch (header.packet_type) { + case HtcctrlPacketType_ConnectFromHost: + case HtcctrlPacketType_SuspendFromHost: + case HtcctrlPacketType_ResumeFromHost: + case HtcctrlPacketType_DisconnectFromHost: + case HtcctrlPacketType_BeaconQuery: + R_UNLESS(header.body_size == 0, htclow::ResultProtocolError()); + break; + case HtcctrlPacketType_ReadyFromHost: + R_UNLESS(0 <= header.body_size && header.body_size <= sizeof(HtcctrlPacketBody), htclow::ResultProtocolError()); + break; + default: + return htclow::ResultProtocolError(); + } + + return ResultSuccess(); + } + + Result HtcctrlService::ProcessReceivePacket(const HtcctrlPacketHeader &header, const void *body, size_t body_size) { + /* TODO */ + AMS_ABORT("HtcctrlService::ProcessReceivePacket"); + } + Result HtcctrlService::NotifyDriverConnected() { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp index 5d83d5698..b39fb6f41 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp @@ -64,6 +64,9 @@ namespace ams::htclow::ctrl { void SetDriverType(impl::DriverType driver_type); + Result CheckReceivedHeader(const HtcctrlPacketHeader &header) const; + Result ProcessReceivePacket(const HtcctrlPacketHeader &header, const void *body, size_t body_size); + Result NotifyDriverConnected(); Result NotifyDriverDisconnected(); }; diff --git a/libraries/libstratosphere/source/htclow/htclow_packet.hpp b/libraries/libstratosphere/source/htclow/htclow_packet.hpp index 681d47321..369753d1d 100644 --- a/libraries/libstratosphere/source/htclow/htclow_packet.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_packet.hpp @@ -18,10 +18,86 @@ namespace ams::htclow { - class Packet : public util::IntrusiveListBaseNode { - /* TODO */ + enum PacketType : u16 { + PacketType_Data = 24, + PacketType_MaxData = 25, + PacketType_Error = 26, + }; + + static constexpr inline u32 HtcGen2Signature = 0xA79F3540; + + struct PacketHeader { + u32 signature; + u32 offset; + u32 reserved; + u32 body_size; + s16 version; + PacketType packet_type; + impl::ChannelInternalType channel; + u64 share; + }; + static_assert(util::is_pod::value); + static_assert(sizeof(PacketHeader) == 0x20); + + static constexpr inline size_t PacketBodySizeMax = 0x3E000; + + struct PacketBody { + u8 data[PacketBodySizeMax]; + }; + + template + class BasePacket { + private: + mem::StandardAllocator *m_allocator; + u8 *m_header; + int m_packet_size; public: - virtual ~Packet(); + BasePacket(mem::StandardAllocator *allocator, int packet_size) : m_allocator(allocator), m_header(nullptr), m_packet_size(packet_size) { + AMS_ASSERT(packet_size >= static_cast(sizeof(HeaderType))); + + m_header = static_cast(m_allocator->Allocate(m_packet_size)); + } + + virtual ~BasePacket() { + if (m_header != nullptr) { + m_allocator->Free(m_header); + } + } + + bool IsAllocationSucceeded() const { + return m_header != nullptr; + } + + HeaderType *GetHeader() { + return reinterpret_cast(m_header); + } + + const HeaderType *GetHeader() const { + return reinterpret_cast(m_header); + } + + int GetBodySize() const { + return m_packet_size - sizeof(HeaderType); + } + + u8 *GetBody() { + if (this->GetBodySize() > 0) { + return m_header + sizeof(HeaderType); + } else { + return nullptr; + } + } + + void CopyBody(const void *src, int src_size) { + AMS_ASSERT(this->GetBodySize() >= 0); + + std::memcpy(this->GetBody(), src, src_size); + } + }; + + class Packet : public BasePacket, public util::IntrusiveListBaseNode { + public: + using BasePacket::BasePacket; }; struct PacketDeleter { diff --git a/libraries/libstratosphere/source/htclow/htclow_packet_factory.cpp b/libraries/libstratosphere/source/htclow/htclow_packet_factory.cpp index 469ab3334..e21d3ab36 100644 --- a/libraries/libstratosphere/source/htclow/htclow_packet_factory.cpp +++ b/libraries/libstratosphere/source/htclow/htclow_packet_factory.cpp @@ -22,4 +22,76 @@ namespace ams::htclow { PacketDeleter{m_allocator}(packet); } + std::unique_ptr PacketFactory::MakeSendPacketCommon(impl::ChannelInternalType channel, s16 version, int body_size) { + /* Allocate memory for the packet. */ + if (void *buffer = m_allocator->Allocate(sizeof(Packet), alignof(Packet)); buffer != nullptr) { + /* Convert the buffer to a packet. */ + Packet *packet = static_cast(buffer); + + /* Construct the packet. */ + std::construct_at(packet, m_allocator, body_size + sizeof(PacketHeader)); + + /* Create the unique pointer. */ + std::unique_ptr ptr(packet, PacketDeleter{m_allocator}); + + /* Set packet header fields. */ + if (ptr && ptr->IsAllocationSucceeded()) { + PacketHeader *header = ptr->GetHeader(); + + header->signature = HtcGen2Signature; + header->offset = 0; + header->reserved = 0; + header->body_size = body_size; + header->version = version; + header->channel = channel; + header->share = 0; + } + + return ptr; + } else { + return std::unique_ptr(nullptr, PacketDeleter{m_allocator}); + } + } + + std::unique_ptr PacketFactory::MakeDataPacket(impl::ChannelInternalType channel, s16 version, const void *body, int body_size, u64 share, u32 offset) { + auto packet = this->MakeSendPacketCommon(channel, version, body_size); + if (packet) { + PacketHeader *header = packet->GetHeader(); + + header->packet_type = PacketType_Data; + header->offset = offset; + header->share = share; + + packet->CopyBody(body, body_size); + + AMS_ASSERT(packet->GetBodySize() == body_size); + } + + return packet; + } + + + std::unique_ptr PacketFactory::MakeMaxDataPacket(impl::ChannelInternalType channel, s16 version, u64 share) { + auto packet = this->MakeSendPacketCommon(channel, version, 0); + if (packet) { + PacketHeader *header = packet->GetHeader(); + + header->packet_type = PacketType_MaxData; + header->share = share; + } + + return packet; + } + + std::unique_ptr PacketFactory::MakeErrorPacket(impl::ChannelInternalType channel) { + auto packet = this->MakeSendPacketCommon(channel, 0, 0); + if (packet) { + PacketHeader *header = packet->GetHeader(); + + header->packet_type = PacketType_Error; + } + + return packet; + } + } diff --git a/libraries/libstratosphere/source/htclow/htclow_packet_factory.hpp b/libraries/libstratosphere/source/htclow/htclow_packet_factory.hpp index 8def47b9f..b14945dd2 100644 --- a/libraries/libstratosphere/source/htclow/htclow_packet_factory.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_packet_factory.hpp @@ -25,7 +25,13 @@ namespace ams::htclow { public: PacketFactory(mem::StandardAllocator *allocator) : m_allocator(allocator) { /* ... */ } + std::unique_ptr MakeDataPacket(impl::ChannelInternalType channel, s16 version, const void *body, int body_size, u64 share, u32 offset); + std::unique_ptr MakeMaxDataPacket(impl::ChannelInternalType channel, s16 version, u64 share); + std::unique_ptr MakeErrorPacket(impl::ChannelInternalType channel); + void Delete(Packet *packet); + private: + std::unique_ptr MakeSendPacketCommon(impl::ChannelInternalType channel, s16 version, int body_size); }; } diff --git a/libraries/libstratosphere/source/htclow/htclow_worker.cpp b/libraries/libstratosphere/source/htclow/htclow_worker.cpp index 714ebee64..d5bc43592 100644 --- a/libraries/libstratosphere/source/htclow/htclow_worker.cpp +++ b/libraries/libstratosphere/source/htclow/htclow_worker.cpp @@ -82,8 +82,55 @@ namespace ams::htclow { } Result Worker::ProcessReceive() { - /* TODO */ - AMS_ABORT("Worker::ProcessReceive"); + /* Forever receive packets. */ + u8 packet_header_storage[sizeof(m_packet_header)]; + while (true) { + /* Receive the packet header. */ + R_TRY(m_driver->Receive(packet_header_storage, sizeof(packet_header_storage))); + + /* Check if the packet is a control packet. */ + if (ctrl::HtcctrlPacketHeader *ctrl_header = reinterpret_cast(packet_header_storage); ctrl_header->signature == ctrl::HtcctrlSignature) { + /* Process the packet. */ + R_TRY(this->ProcessReceive(*ctrl_header)); + } else { + /* Otherwise, we must have a normal packet. */ + PacketHeader *header = reinterpret_cast(packet_header_storage); + R_UNLESS(header->signature == HtcGen2Signature, htclow::ResultProtocolError()); + + /* Process the packet. */ + R_TRY(this->ProcessReceive(*header)); + } + } + } + + Result Worker::ProcessReceive(const ctrl::HtcctrlPacketHeader &header) { + /* Check the header. */ + R_TRY(m_service->CheckReceivedHeader(header)); + + /* Receive the body, if we have one. */ + if (header.body_size > 0) { + R_TRY(m_driver->Receive(m_receive_packet_body, header.body_size)); + } + + /* Process the received packet. */ + m_service->ProcessReceivePacket(header, m_receive_packet_body, header.body_size); + + return ResultSuccess(); + } + + Result Worker::ProcessReceive(const PacketHeader &header) { + /* Check the header. */ + R_TRY(m_mux->CheckReceivedHeader(header)); + + /* Receive the body, if we have one. */ + if (header.body_size > 0) { + R_TRY(m_driver->Receive(m_receive_packet_body, header.body_size)); + } + + /* Process the received packet. */ + m_mux->ProcessReceivePacket(header, m_receive_packet_body, header.body_size); + + return ResultSuccess(); } Result Worker::ProcessSend() { diff --git a/libraries/libstratosphere/source/htclow/htclow_worker.hpp b/libraries/libstratosphere/source/htclow/htclow_worker.hpp index a30214117..1e9529f1f 100644 --- a/libraries/libstratosphere/source/htclow/htclow_worker.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_worker.hpp @@ -22,9 +22,14 @@ namespace ams::htclow { class Worker { + private: + static_assert(sizeof(ctrl::HtcctrlPacketHeader) <= sizeof(PacketHeader)); + static_assert(sizeof(ctrl::HtcctrlPacketBody) <= sizeof(PacketBody)); private: u32 m_thread_stack_size; - u8 m_04[0x7C024]; /* TODO... not knowing what an almost 128 KB field is is embarassing. */ + u8 m_packet_header[sizeof(PacketHeader)]; + u8 m_send_packet_body[sizeof(PacketBody)]; + u8 m_receive_packet_body[sizeof(PacketBody)]; mem::StandardAllocator *m_allocator; mux::Mux *m_mux; ctrl::HtcctrlService *m_service; @@ -58,6 +63,9 @@ namespace ams::htclow { Result ProcessReceive(); Result ProcessSend(); + + Result ProcessReceive(const ctrl::HtcctrlPacketHeader &header); + Result ProcessReceive(const PacketHeader &header); }; } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp index 1b6d5a43d..9db51a24f 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp @@ -38,6 +38,46 @@ namespace ams::htclow::mux { } } + Result Mux::CheckReceivedHeader(const PacketHeader &header) const { + /* Check the packet signature. */ + AMS_ASSERT(header.signature == HtcGen2Signature); + + /* Switch on the packet type. */ + switch (header.packet_type) { + case PacketType_Data: + R_UNLESS(header.version == m_version, htclow::ResultProtocolError()); + R_UNLESS(header.body_size <= sizeof(PacketBody), htclow::ResultProtocolError()); + break; + case PacketType_MaxData: + R_UNLESS(header.version == m_version, htclow::ResultProtocolError()); + R_UNLESS(header.body_size == 0, htclow::ResultProtocolError()); + break; + case PacketType_Error: + R_UNLESS(header.body_size == 0, htclow::ResultProtocolError()); + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + return ResultSuccess(); + } + + Result Mux::ProcessReceivePacket(const PacketHeader &header, const void *body, size_t body_size) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Process for the channel. */ + if (m_channel_impl_map.Exists(header.channel)) { + R_TRY(this->CheckChannelExist(header.channel)); + + return m_channel_impl_map[header.channel].ProcessReceivePacket(header, body, body_size); + } else { + if (header.packet_type == PacketType_Data || header.packet_type == PacketType_MaxData) { + this->SendErrorPacket(header.channel); + } + return htclow::ResultChannelNotExist(); + } + } + void Mux::UpdateChannelState() { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); @@ -62,4 +102,14 @@ namespace ams::htclow::mux { } } + Result Mux::CheckChannelExist(impl::ChannelInternalType channel) { + R_UNLESS(m_channel_impl_map.Exists(channel), htclow::ResultChannelNotExist()); + return ResultSuccess(); + } + + Result Mux::SendErrorPacket(impl::ChannelInternalType channel) { + /* TODO */ + AMS_ABORT("Mux::SendErrorPacket"); + } + } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp index f9e6180a9..8a41df89e 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp @@ -37,8 +37,15 @@ namespace ams::htclow::mux { void SetVersion(u16 version); + Result CheckReceivedHeader(const PacketHeader &header) const; + Result ProcessReceivePacket(const PacketHeader &header, const void *body, size_t body_size); + void UpdateChannelState(); void UpdateMuxState(); + private: + Result CheckChannelExist(impl::ChannelInternalType channel); + + Result SendErrorPacket(impl::ChannelInternalType channel); }; } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp index fe54c8ada..9e523c1ac 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp @@ -28,6 +28,110 @@ namespace ams::htclow::mux { m_send_buffer.SetVersion(version); } + Result ChannelImpl::CheckState(std::initializer_list states) const { + /* Determine if we have a matching state. */ + bool match = false; + for (const auto &state : states) { + match |= m_state == state; + } + + /* If we do, we're good. */ + R_SUCCEED_IF(match); + + /* Otherwise, return appropriate failure error. */ + if (m_state == ChannelState_Disconnected) { + return htclow::ResultInvalidChannelStateDisconnected(); + } else { + return htclow::ResultInvalidChannelState(); + } + } + + Result ChannelImpl::CheckPacketVersion(s16 version) const { + R_UNLESS(version == m_version, htclow::ResultChannelVersionNotMatched()); + return ResultSuccess(); + } + + + Result ChannelImpl::ProcessReceivePacket(const PacketHeader &header, const void *body, size_t body_size) { + switch (header.packet_type) { + case PacketType_Data: + return this->ProcessReceiveDataPacket(header.version, header.share, header.offset, body, body_size); + case PacketType_MaxData: + return this->ProcessReceiveMaxDataPacket(header.version, header.share); + case PacketType_Error: + return this->ProcessReceiveErrorPacket(); + default: + return htclow::ResultProtocolError(); + } + } + + Result ChannelImpl::ProcessReceiveDataPacket(s16 version, u64 share, u32 offset, const void *body, size_t body_size) { + /* Check our state. */ + R_TRY(this->CheckState({ChannelState_Connectable, ChannelState_Connected})); + + /* Check the packet version. */ + R_TRY(this->CheckPacketVersion(version)); + + /* Check that offset matches. */ + R_UNLESS(offset == static_cast(m_offset), htclow::ResultProtocolError()); + + /* Check for flow control, if we should. */ + if (m_config.flow_control_enabled) { + /* Check that the share increases monotonically. */ + if (m_share.has_value()) { + R_UNLESS(m_share.value() <= share, htclow::ResultProtocolError()); + } + + /* Update our share. */ + m_share = share; + + /* Signal our event. */ + this->SignalSendPacketEvent(); + } + + /* Update our offset. */ + m_offset += body_size; + + /* Write the packet body. */ + R_ABORT_UNLESS(m_receive_buffer.Write(body, body_size)); + + /* Notify the data was received. */ + m_task_manager->NotifyReceiveData(m_channel, m_receive_buffer.GetDataSize()); + + return ResultSuccess(); + } + + Result ChannelImpl::ProcessReceiveMaxDataPacket(s16 version, u64 share) { + /* Check our state. */ + R_TRY(this->CheckState({ChannelState_Connectable, ChannelState_Connected})); + + /* Check the packet version. */ + R_TRY(this->CheckPacketVersion(version)); + + /* Check for flow control, if we should. */ + if (m_config.flow_control_enabled) { + /* Check that the share increases monotonically. */ + if (m_share.has_value()) { + R_UNLESS(m_share.value() <= share, htclow::ResultProtocolError()); + } + + /* Update our share. */ + m_share = share; + + /* Signal our event. */ + this->SignalSendPacketEvent(); + } + + return ResultSuccess(); + } + + Result ChannelImpl::ProcessReceiveErrorPacket() { + if (m_state == ChannelState_Connected || m_state == ChannelState_Disconnected) { + this->ShutdownForce(); + } + return ResultSuccess(); + } + void ChannelImpl::UpdateState() { /* Check if shutdown must be forced. */ if (m_state_machine->IsUnsupportedServiceChannelToShutdown(m_channel)) { @@ -83,4 +187,10 @@ namespace ams::htclow::mux { } } + void ChannelImpl::SignalSendPacketEvent() { + if (m_event != nullptr) { + m_event->Signal(); + } + } + } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp index b24a115a8..bc78d4a6f 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp @@ -42,9 +42,10 @@ namespace ams::htclow::mux { SendBuffer m_send_buffer; RingBuffer m_receive_buffer; s16 m_version; - /* TODO: Channel config */ + ChannelConfig m_config; /* TODO: tracking variables. */ - std::optional m_108; + u64 m_offset; + std::optional m_share; os::Event m_state_change_event; ChannelState m_state; public: @@ -52,11 +53,22 @@ namespace ams::htclow::mux { void SetVersion(s16 version); + Result ProcessReceivePacket(const PacketHeader &header, const void *body, size_t body_size); + void UpdateState(); private: void ShutdownForce(); void SetState(ChannelState state); void SetStateWithoutCheck(ChannelState state); + + void SignalSendPacketEvent(); + + Result CheckState(std::initializer_list states) const; + Result CheckPacketVersion(s16 version) const; + + Result ProcessReceiveDataPacket(s16 version, u64 share, u32 offset, const void *body, size_t body_size); + Result ProcessReceiveMaxDataPacket(s16 version, u64 share); + Result ProcessReceiveErrorPacket(); }; } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp index 14886b152..3b193975e 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp @@ -41,6 +41,10 @@ namespace ams::htclow::mux { ChannelImplMap(PacketFactory *pf, ctrl::HtcctrlStateMachine *sm, TaskManager *tm, os::Event *ev); ChannelImpl &GetChannelImpl(impl::ChannelInternalType channel); + + bool Exists(impl::ChannelInternalType channel) const { + return m_map.find(channel) != m_map.end(); + } private: ChannelImpl &GetChannelImpl(int index); public: diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.cpp new file mode 100644 index 000000000..bf76300cb --- /dev/null +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htclow_mux_ring_buffer.hpp" + +namespace ams::htclow::mux { + + Result RingBuffer::Write(const void *data, size_t size) { + /* Validate pre-conditions. */ + AMS_ASSERT(!m_is_read_only); + + /* Check that our buffer can hold the data. */ + R_UNLESS(m_buffer != nullptr, htclow::ResultChannelBufferOverflow()); + R_UNLESS(m_data_size + size <= m_buffer_size, htclow::ResultChannelBufferOverflow()); + + /* Determine position and copy sizes. */ + const size_t pos = (m_data_size + m_offset) % m_buffer_size; + const size_t left = m_buffer_size - pos; + const size_t over = size - left; + + /* Copy. */ + if (left != 0) { + std::memcpy(static_cast(m_buffer) + pos, data, left); + } + if (over != 0) { + std::memcpy(m_buffer, static_cast(data) + left, over); + } + + /* Update our data size. */ + m_data_size += size; + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.hpp index 02557ea68..7cd451f89 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.hpp @@ -29,6 +29,10 @@ namespace ams::htclow::mux { bool m_has_copied; public: RingBuffer() : m_buffer(), m_read_only_buffer(), m_is_read_only(true), m_buffer_size(), m_data_size(), m_offset(), m_has_copied(false) { /* ... */ } + + size_t GetDataSize() { return m_data_size; } + + Result Write(const void *data, size_t size); }; } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp index f396a9cd4..3e2046acf 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp @@ -26,6 +26,14 @@ namespace ams::htclow::mux { } } + void TaskManager::NotifyReceiveData(impl::ChannelInternalType channel, size_t size) { + for (auto i = 0; i < MaxTaskCount; ++i) { + if (m_valid[i] && m_tasks[i].channel == channel && m_tasks[i].size <= size) { + this->CompleteTask(i, EventTrigger_ReceiveData); + } + } + } + void TaskManager::NotifyConnectReady() { for (auto i = 0; i < MaxTaskCount; ++i) { if (m_valid[i] && m_tasks[i].type == TaskType_Connect) { diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp index d82a97dae..9c5c03423 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp @@ -22,6 +22,7 @@ namespace ams::htclow::mux { enum EventTrigger : u8 { EventTrigger_Disconnect = 1, + EventTrigger_ReceiveData = 2, EventTrigger_ConnectReady = 11, }; @@ -40,7 +41,7 @@ namespace ams::htclow::mux { bool has_event_trigger; EventTrigger event_trigger; TaskType type; - u64 _38; + size_t size; }; private: bool m_valid[MaxTaskCount]; @@ -49,6 +50,7 @@ namespace ams::htclow::mux { TaskManager() : m_valid() { /* ... */ } void NotifyDisconnect(impl::ChannelInternalType channel); + void NotifyReceiveData(impl::ChannelInternalType channel, size_t size); void NotifyConnectReady(); private: void CompleteTask(int index, EventTrigger trigger); diff --git a/libraries/libvapours/include/vapours/results/htclow_results.hpp b/libraries/libvapours/include/vapours/results/htclow_results.hpp index 4e47508c8..383d343c4 100644 --- a/libraries/libvapours/include/vapours/results/htclow_results.hpp +++ b/libraries/libvapours/include/vapours/results/htclow_results.hpp @@ -20,13 +20,26 @@ namespace ams::htclow { R_DEFINE_NAMESPACE_RESULT_MODULE(29); - R_DEFINE_ERROR_RESULT(UnknownDriverType, 3); + R_DEFINE_ERROR_RESULT(UnknownDriverType, 3); + R_DEFINE_ERROR_RESULT(ChannelNotExist, 10); + + R_DEFINE_ERROR_RESULT(InvalidChannelState, 200); + R_DEFINE_ERROR_RESULT(InvalidChannelStateDisconnected, 201); R_DEFINE_ERROR_RANGE(InternalError, 1000, 2999); R_DEFINE_ERROR_RESULT(OutOfMemory, 1002); R_DEFINE_ERROR_RESULT(InvalidArgument, 1003); + R_DEFINE_ERROR_RESULT(ProtocolError, 1004); R_DEFINE_ERROR_RESULT(Cancelled, 1005); + R_DEFINE_ERROR_RANGE(MuxError, 1100, 1199); + R_DEFINE_ERROR_RESULT(ChannelBufferOverflow, 1101); + R_DEFINE_ERROR_RESULT(ChannelBufferHasNotEnoughData, 1102); + R_DEFINE_ERROR_RESULT(ChannelVersionNotMatched, 1103); + R_DEFINE_ERROR_RESULT(ChannelStateTransitionError, 1104); + R_DEFINE_ERROR_RESULT(ChannelReceiveBufferEmpty, 1106); + R_DEFINE_ERROR_RESULT(ChannelSequenceIdNotMatched, 1107); + R_DEFINE_ERROR_RANGE(DriverError, 1200, 1999); R_DEFINE_ERROR_RESULT(DriverOpened, 1201); From 4e9bc617bb5175713b56bb2b0cbbe2abb4d911ee Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 8 Feb 2021 14:42:03 -0800 Subject: [PATCH 024/280] rapidjson: import -master as ams::rapidjson --- .../libstratosphere/include/stratosphere.hpp | 5 +- .../include/stratosphere/rapidjson.hpp | 22 + .../stratosphere/rapidjson/allocators.h | 284 ++ .../rapidjson/cursorstreamwrapper.h | 78 + .../include/stratosphere/rapidjson/document.h | 2737 +++++++++++++++++ .../stratosphere/rapidjson/encodedstream.h | 299 ++ .../stratosphere/rapidjson/encodings.h | 716 +++++ .../include/stratosphere/rapidjson/error/en.h | 122 + .../stratosphere/rapidjson/error/error.h | 216 ++ .../stratosphere/rapidjson/filereadstream.h | 99 + .../stratosphere/rapidjson/filewritestream.h | 104 + .../include/stratosphere/rapidjson/fwd.h | 151 + .../rapidjson/internal/biginteger.h | 290 ++ .../stratosphere/rapidjson/internal/clzll.h | 71 + .../stratosphere/rapidjson/internal/diyfp.h | 257 ++ .../stratosphere/rapidjson/internal/dtoa.h | 245 ++ .../stratosphere/rapidjson/internal/ieee754.h | 78 + .../stratosphere/rapidjson/internal/itoa.h | 308 ++ .../stratosphere/rapidjson/internal/meta.h | 186 ++ .../stratosphere/rapidjson/internal/pow10.h | 55 + .../stratosphere/rapidjson/internal/regex.h | 739 +++++ .../stratosphere/rapidjson/internal/stack.h | 232 ++ .../stratosphere/rapidjson/internal/strfunc.h | 69 + .../stratosphere/rapidjson/internal/strtod.h | 290 ++ .../stratosphere/rapidjson/internal/swap.h | 46 + .../stratosphere/rapidjson/istreamwrapper.h | 128 + .../stratosphere/rapidjson/memorybuffer.h | 70 + .../stratosphere/rapidjson/memorystream.h | 71 + .../rapidjson/msinttypes/inttypes.h | 316 ++ .../rapidjson/msinttypes/stdint.h | 300 ++ .../stratosphere/rapidjson/ostreamwrapper.h | 81 + .../include/stratosphere/rapidjson/pointer.h | 1415 +++++++++ .../stratosphere/rapidjson/prettywriter.h | 277 ++ .../stratosphere/rapidjson/rapidjson.h | 692 +++++ .../include/stratosphere/rapidjson/reader.h | 2244 ++++++++++++++ .../include/stratosphere/rapidjson/schema.h | 2644 ++++++++++++++++ .../include/stratosphere/rapidjson/stream.h | 223 ++ .../stratosphere/rapidjson/stringbuffer.h | 121 + .../include/stratosphere/rapidjson/writer.h | 710 +++++ 39 files changed, 16990 insertions(+), 1 deletion(-) create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/allocators.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/cursorstreamwrapper.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/document.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/encodedstream.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/encodings.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/error/en.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/error/error.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/filereadstream.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/filewritestream.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/fwd.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/internal/biginteger.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/internal/clzll.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/internal/diyfp.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/internal/dtoa.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/internal/ieee754.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/internal/itoa.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/internal/meta.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/internal/pow10.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/internal/regex.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/internal/stack.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/internal/strfunc.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/internal/strtod.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/internal/swap.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/istreamwrapper.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/memorybuffer.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/memorystream.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/msinttypes/inttypes.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/msinttypes/stdint.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/ostreamwrapper.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/pointer.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/prettywriter.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/rapidjson.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/reader.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/schema.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/stream.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/stringbuffer.h create mode 100644 libraries/libstratosphere/include/stratosphere/rapidjson/writer.h diff --git a/libraries/libstratosphere/include/stratosphere.hpp b/libraries/libstratosphere/include/stratosphere.hpp index 2b083f991..a05c6edad 100644 --- a/libraries/libstratosphere/include/stratosphere.hpp +++ b/libraries/libstratosphere/include/stratosphere.hpp @@ -89,4 +89,7 @@ /* Include FS last. */ #include #include -#include \ No newline at end of file +#include + +/* External modules that we're including. */ +#include \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson.hpp b/libraries/libstratosphere/include/stratosphere/rapidjson.hpp new file mode 100644 index 000000000..9a68b1674 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson.hpp @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#define RAPIDJSON_NAMESPACE ams::rapidjson + +#include +#include +#include \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/allocators.h b/libraries/libstratosphere/include/stratosphere/rapidjson/allocators.h new file mode 100644 index 000000000..44ec5295c --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/allocators.h @@ -0,0 +1,284 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ALLOCATORS_H_ +#define RAPIDJSON_ALLOCATORS_H_ + +#include "rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Allocator + +/*! \class rapidjson::Allocator + \brief Concept for allocating, resizing and freeing memory block. + + Note that Malloc() and Realloc() are non-static but Free() is static. + + So if an allocator need to support Free(), it needs to put its pointer in + the header of memory block. + +\code +concept Allocator { + static const bool kNeedFree; //!< Whether this allocator needs to call Free(). + + // Allocate a memory block. + // \param size of the memory block in bytes. + // \returns pointer to the memory block. + void* Malloc(size_t size); + + // Resize a memory block. + // \param originalPtr The pointer to current memory block. Null pointer is permitted. + // \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.) + // \param newSize the new size in bytes. + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize); + + // Free a memory block. + // \param pointer to the memory block. Null pointer is permitted. + static void Free(void *ptr); +}; +\endcode +*/ + + +/*! \def RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY + \ingroup RAPIDJSON_CONFIG + \brief User-defined kDefaultChunkCapacity definition. + + User can define this as any \c size that is a power of 2. +*/ + +#ifndef RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY +#define RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY (64 * 1024) +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// CrtAllocator + +//! C-runtime library allocator. +/*! This class is just wrapper for standard C library memory routines. + \note implements Allocator concept +*/ +class CrtAllocator { +public: + static const bool kNeedFree = true; + void* Malloc(size_t size) { + if (size) // behavior of malloc(0) is implementation defined. + return RAPIDJSON_MALLOC(size); + else + return NULL; // standardize to returning NULL. + } + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { + (void)originalSize; + if (newSize == 0) { + RAPIDJSON_FREE(originalPtr); + return NULL; + } + return RAPIDJSON_REALLOC(originalPtr, newSize); + } + static void Free(void *ptr) { RAPIDJSON_FREE(ptr); } +}; + +/////////////////////////////////////////////////////////////////////////////// +// MemoryPoolAllocator + +//! Default memory allocator used by the parser and DOM. +/*! This allocator allocate memory blocks from pre-allocated memory chunks. + + It does not free memory blocks. And Realloc() only allocate new memory. + + The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default. + + User may also supply a buffer as the first chunk. + + If the user-buffer is full then additional chunks are allocated by BaseAllocator. + + The user-buffer is not deallocated by this allocator. + + \tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator. + \note implements Allocator concept +*/ +template +class MemoryPoolAllocator { +public: + static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator) + + //! Constructor with chunkSize. + /*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. + \param baseAllocator The allocator for allocating memory chunks. + */ + MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : + chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0) + { + } + + //! Constructor with user-supplied buffer. + /*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size. + + The user buffer will not be deallocated when this allocator is destructed. + + \param buffer User supplied buffer. + \param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader). + \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. + \param baseAllocator The allocator for allocating memory chunks. + */ + MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : + chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(buffer), baseAllocator_(baseAllocator), ownBaseAllocator_(0) + { + RAPIDJSON_ASSERT(buffer != 0); + RAPIDJSON_ASSERT(size > sizeof(ChunkHeader)); + chunkHead_ = reinterpret_cast(buffer); + chunkHead_->capacity = size - sizeof(ChunkHeader); + chunkHead_->size = 0; + chunkHead_->next = 0; + } + + //! Destructor. + /*! This deallocates all memory chunks, excluding the user-supplied buffer. + */ + ~MemoryPoolAllocator() { + Clear(); + RAPIDJSON_DELETE(ownBaseAllocator_); + } + + //! Deallocates all memory chunks, excluding the user-supplied buffer. + void Clear() { + while (chunkHead_ && chunkHead_ != userBuffer_) { + ChunkHeader* next = chunkHead_->next; + baseAllocator_->Free(chunkHead_); + chunkHead_ = next; + } + if (chunkHead_ && chunkHead_ == userBuffer_) + chunkHead_->size = 0; // Clear user buffer + } + + //! Computes the total capacity of allocated memory chunks. + /*! \return total capacity in bytes. + */ + size_t Capacity() const { + size_t capacity = 0; + for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) + capacity += c->capacity; + return capacity; + } + + //! Computes the memory blocks allocated. + /*! \return total used bytes. + */ + size_t Size() const { + size_t size = 0; + for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) + size += c->size; + return size; + } + + //! Allocates a memory block. (concept Allocator) + void* Malloc(size_t size) { + if (!size) + return NULL; + + size = RAPIDJSON_ALIGN(size); + if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity) + if (!AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size)) + return NULL; + + void *buffer = reinterpret_cast(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size; + chunkHead_->size += size; + return buffer; + } + + //! Resizes a memory block (concept Allocator) + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { + if (originalPtr == 0) + return Malloc(newSize); + + if (newSize == 0) + return NULL; + + originalSize = RAPIDJSON_ALIGN(originalSize); + newSize = RAPIDJSON_ALIGN(newSize); + + // Do not shrink if new size is smaller than original + if (originalSize >= newSize) + return originalPtr; + + // Simply expand it if it is the last allocation and there is sufficient space + if (originalPtr == reinterpret_cast(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size - originalSize) { + size_t increment = static_cast(newSize - originalSize); + if (chunkHead_->size + increment <= chunkHead_->capacity) { + chunkHead_->size += increment; + return originalPtr; + } + } + + // Realloc process: allocate and copy memory, do not free original buffer. + if (void* newBuffer = Malloc(newSize)) { + if (originalSize) + std::memcpy(newBuffer, originalPtr, originalSize); + return newBuffer; + } + else + return NULL; + } + + //! Frees a memory block (concept Allocator) + static void Free(void *ptr) { (void)ptr; } // Do nothing + +private: + //! Copy constructor is not permitted. + MemoryPoolAllocator(const MemoryPoolAllocator& rhs) /* = delete */; + //! Copy assignment operator is not permitted. + MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) /* = delete */; + + //! Creates a new chunk. + /*! \param capacity Capacity of the chunk in bytes. + \return true if success. + */ + bool AddChunk(size_t capacity) { + if (!baseAllocator_) + ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator)(); + if (ChunkHeader* chunk = reinterpret_cast(baseAllocator_->Malloc(RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + capacity))) { + chunk->capacity = capacity; + chunk->size = 0; + chunk->next = chunkHead_; + chunkHead_ = chunk; + return true; + } + else + return false; + } + + static const int kDefaultChunkCapacity = RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY; //!< Default chunk capacity. + + //! Chunk header for perpending to each chunk. + /*! Chunks are stored as a singly linked list. + */ + struct ChunkHeader { + size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself). + size_t size; //!< Current size of allocated memory in bytes. + ChunkHeader *next; //!< Next chunk in the linked list. + }; + + ChunkHeader *chunkHead_; //!< Head of the chunk linked-list. Only the head chunk serves allocation. + size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated. + void *userBuffer_; //!< User supplied buffer. + BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks. + BaseAllocator* ownBaseAllocator_; //!< base allocator created by this object. +}; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ENCODINGS_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/cursorstreamwrapper.h b/libraries/libstratosphere/include/stratosphere/rapidjson/cursorstreamwrapper.h new file mode 100644 index 000000000..fd6513db1 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/cursorstreamwrapper.h @@ -0,0 +1,78 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_CURSORSTREAMWRAPPER_H_ +#define RAPIDJSON_CURSORSTREAMWRAPPER_H_ + +#include "stream.h" + +#if defined(__GNUC__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4702) // unreachable code +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +RAPIDJSON_NAMESPACE_BEGIN + + +//! Cursor stream wrapper for counting line and column number if error exists. +/*! + \tparam InputStream Any stream that implements Stream Concept +*/ +template > +class CursorStreamWrapper : public GenericStreamWrapper { +public: + typedef typename Encoding::Ch Ch; + + CursorStreamWrapper(InputStream& is): + GenericStreamWrapper(is), line_(1), col_(0) {} + + // counting line and column number + Ch Take() { + Ch ch = this->is_.Take(); + if(ch == '\n') { + line_ ++; + col_ = 0; + } else { + col_ ++; + } + return ch; + } + + //! Get the error line number, if error exists. + size_t GetLine() const { return line_; } + //! Get the error column number, if error exists. + size_t GetColumn() const { return col_; } + +private: + size_t line_; //!< Current Line + size_t col_; //!< Current Column +}; + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_POP +#endif + +#if defined(__GNUC__) +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_CURSORSTREAMWRAPPER_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/document.h b/libraries/libstratosphere/include/stratosphere/rapidjson/document.h new file mode 100644 index 000000000..028235ec6 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/document.h @@ -0,0 +1,2737 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_DOCUMENT_H_ +#define RAPIDJSON_DOCUMENT_H_ + +/*! \file document.h */ + +#include "reader.h" +#include "internal/meta.h" +#include "internal/strfunc.h" +#include "memorystream.h" +#include "encodedstream.h" +#include // placement new +#include +#ifdef __cpp_lib_three_way_comparison +#include +#endif + +RAPIDJSON_DIAG_PUSH +#ifdef __clang__ +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +RAPIDJSON_DIAG_OFF(c++98-compat) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +RAPIDJSON_DIAG_OFF(4244) // conversion from kXxxFlags to 'uint16_t', possible loss of data +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_OFF(effc++) +#endif // __GNUC__ + +#ifndef RAPIDJSON_NOMEMBERITERATORCLASS +#include // std::random_access_iterator_tag +#endif + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS +#include // std::move +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +// Forward declaration. +template +class GenericValue; + +template +class GenericDocument; + +/*! \def RAPIDJSON_DEFAULT_ALLOCATOR + \ingroup RAPIDJSON_CONFIG + \brief Allows to choose default allocator. + + User can define this to use CrtAllocator or MemoryPoolAllocator. +*/ +#ifndef RAPIDJSON_DEFAULT_ALLOCATOR +#define RAPIDJSON_DEFAULT_ALLOCATOR MemoryPoolAllocator +#endif + +/*! \def RAPIDJSON_DEFAULT_STACK_ALLOCATOR + \ingroup RAPIDJSON_CONFIG + \brief Allows to choose default stack allocator for Document. + + User can define this to use CrtAllocator or MemoryPoolAllocator. +*/ +#ifndef RAPIDJSON_DEFAULT_STACK_ALLOCATOR +#define RAPIDJSON_DEFAULT_STACK_ALLOCATOR CrtAllocator +#endif + +/*! \def RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY + \ingroup RAPIDJSON_CONFIG + \brief User defined kDefaultObjectCapacity value. + + User can define this as any natural number. +*/ +#ifndef RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY +// number of objects that rapidjson::Value allocates memory for by default +#define RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY 16 +#endif + +/*! \def RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY + \ingroup RAPIDJSON_CONFIG + \brief User defined kDefaultArrayCapacity value. + + User can define this as any natural number. +*/ +#ifndef RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY +// number of array elements that rapidjson::Value allocates memory for by default +#define RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY 16 +#endif + +//! Name-value pair in a JSON object value. +/*! + This class was internal to GenericValue. It used to be a inner struct. + But a compiler (IBM XL C/C++ for AIX) have reported to have problem with that so it moved as a namespace scope struct. + https://code.google.com/p/rapidjson/issues/detail?id=64 +*/ +template +class GenericMember { +public: + GenericValue name; //!< name of member (must be a string) + GenericValue value; //!< value of member. + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericMember(GenericMember&& rhs) RAPIDJSON_NOEXCEPT + : name(std::move(rhs.name)), + value(std::move(rhs.value)) + { + } + + //! Move assignment in C++11 + GenericMember& operator=(GenericMember&& rhs) RAPIDJSON_NOEXCEPT { + return *this = static_cast(rhs); + } +#endif + + //! Assignment with move semantics. + /*! \param rhs Source of the assignment. Its name and value will become a null value after assignment. + */ + GenericMember& operator=(GenericMember& rhs) RAPIDJSON_NOEXCEPT { + if (RAPIDJSON_LIKELY(this != &rhs)) { + name = rhs.name; + value = rhs.value; + } + return *this; + } + + // swap() for std::sort() and other potential use in STL. + friend inline void swap(GenericMember& a, GenericMember& b) RAPIDJSON_NOEXCEPT { + a.name.Swap(b.name); + a.value.Swap(b.value); + } + +private: + //! Copy constructor is not permitted. + GenericMember(const GenericMember& rhs); +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericMemberIterator + +#ifndef RAPIDJSON_NOMEMBERITERATORCLASS + +//! (Constant) member iterator for a JSON object value +/*! + \tparam Const Is this a constant iterator? + \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document) + \tparam Allocator Allocator type for allocating memory of object, array and string. + + This class implements a Random Access Iterator for GenericMember elements + of a GenericValue, see ISO/IEC 14882:2003(E) C++ standard, 24.1 [lib.iterator.requirements]. + + \note This iterator implementation is mainly intended to avoid implicit + conversions from iterator values to \c NULL, + e.g. from GenericValue::FindMember. + + \note Define \c RAPIDJSON_NOMEMBERITERATORCLASS to fall back to a + pointer-based implementation, if your platform doesn't provide + the C++ header. + + \see GenericMember, GenericValue::MemberIterator, GenericValue::ConstMemberIterator + */ +template +class GenericMemberIterator { + + friend class GenericValue; + template friend class GenericMemberIterator; + + typedef GenericMember PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + +public: + //! Iterator type itself + typedef GenericMemberIterator Iterator; + //! Constant iterator type + typedef GenericMemberIterator ConstIterator; + //! Non-constant iterator type + typedef GenericMemberIterator NonConstIterator; + + /** \name std::iterator_traits support */ + //@{ + typedef ValueType value_type; + typedef ValueType * pointer; + typedef ValueType & reference; + typedef std::ptrdiff_t difference_type; + typedef std::random_access_iterator_tag iterator_category; + //@} + + //! Pointer to (const) GenericMember + typedef pointer Pointer; + //! Reference to (const) GenericMember + typedef reference Reference; + //! Signed integer type (e.g. \c ptrdiff_t) + typedef difference_type DifferenceType; + + //! Default constructor (singular value) + /*! Creates an iterator pointing to no element. + \note All operations, except for comparisons, are undefined on such values. + */ + GenericMemberIterator() : ptr_() {} + + //! Iterator conversions to more const + /*! + \param it (Non-const) iterator to copy from + + Allows the creation of an iterator from another GenericMemberIterator + that is "less const". Especially, creating a non-constant iterator + from a constant iterator are disabled: + \li const -> non-const (not ok) + \li const -> const (ok) + \li non-const -> const (ok) + \li non-const -> non-const (ok) + + \note If the \c Const template parameter is already \c false, this + constructor effectively defines a regular copy-constructor. + Otherwise, the copy constructor is implicitly defined. + */ + GenericMemberIterator(const NonConstIterator & it) : ptr_(it.ptr_) {} + Iterator& operator=(const NonConstIterator & it) { ptr_ = it.ptr_; return *this; } + + //! @name stepping + //@{ + Iterator& operator++(){ ++ptr_; return *this; } + Iterator& operator--(){ --ptr_; return *this; } + Iterator operator++(int){ Iterator old(*this); ++ptr_; return old; } + Iterator operator--(int){ Iterator old(*this); --ptr_; return old; } + //@} + + //! @name increment/decrement + //@{ + Iterator operator+(DifferenceType n) const { return Iterator(ptr_+n); } + Iterator operator-(DifferenceType n) const { return Iterator(ptr_-n); } + + Iterator& operator+=(DifferenceType n) { ptr_+=n; return *this; } + Iterator& operator-=(DifferenceType n) { ptr_-=n; return *this; } + //@} + + //! @name relations + //@{ + template bool operator==(const GenericMemberIterator& that) const { return ptr_ == that.ptr_; } + template bool operator!=(const GenericMemberIterator& that) const { return ptr_ != that.ptr_; } + template bool operator<=(const GenericMemberIterator& that) const { return ptr_ <= that.ptr_; } + template bool operator>=(const GenericMemberIterator& that) const { return ptr_ >= that.ptr_; } + template bool operator< (const GenericMemberIterator& that) const { return ptr_ < that.ptr_; } + template bool operator> (const GenericMemberIterator& that) const { return ptr_ > that.ptr_; } + +#ifdef __cpp_lib_three_way_comparison + template std::strong_ordering operator<=>(const GenericMemberIterator& that) const { return ptr_ <=> that.ptr_; } +#endif + //@} + + //! @name dereference + //@{ + Reference operator*() const { return *ptr_; } + Pointer operator->() const { return ptr_; } + Reference operator[](DifferenceType n) const { return ptr_[n]; } + //@} + + //! Distance + DifferenceType operator-(ConstIterator that) const { return ptr_-that.ptr_; } + +private: + //! Internal constructor from plain pointer + explicit GenericMemberIterator(Pointer p) : ptr_(p) {} + + Pointer ptr_; //!< raw pointer +}; + +#else // RAPIDJSON_NOMEMBERITERATORCLASS + +// class-based member iterator implementation disabled, use plain pointers + +template +class GenericMemberIterator; + +//! non-const GenericMemberIterator +template +class GenericMemberIterator { +public: + //! use plain pointer as iterator type + typedef GenericMember* Iterator; +}; +//! const GenericMemberIterator +template +class GenericMemberIterator { +public: + //! use plain const pointer as iterator type + typedef const GenericMember* Iterator; +}; + +#endif // RAPIDJSON_NOMEMBERITERATORCLASS + +/////////////////////////////////////////////////////////////////////////////// +// GenericStringRef + +//! Reference to a constant string (not taking a copy) +/*! + \tparam CharType character type of the string + + This helper class is used to automatically infer constant string + references for string literals, especially from \c const \b (!) + character arrays. + + The main use is for creating JSON string values without copying the + source string via an \ref Allocator. This requires that the referenced + string pointers have a sufficient lifetime, which exceeds the lifetime + of the associated GenericValue. + + \b Example + \code + Value v("foo"); // ok, no need to copy & calculate length + const char foo[] = "foo"; + v.SetString(foo); // ok + + const char* bar = foo; + // Value x(bar); // not ok, can't rely on bar's lifetime + Value x(StringRef(bar)); // lifetime explicitly guaranteed by user + Value y(StringRef(bar, 3)); // ok, explicitly pass length + \endcode + + \see StringRef, GenericValue::SetString +*/ +template +struct GenericStringRef { + typedef CharType Ch; //!< character type of the string + + //! Create string reference from \c const character array +#ifndef __clang__ // -Wdocumentation + /*! + This constructor implicitly creates a constant string reference from + a \c const character array. It has better performance than + \ref StringRef(const CharType*) by inferring the string \ref length + from the array length, and also supports strings containing null + characters. + + \tparam N length of the string, automatically inferred + + \param str Constant character array, lifetime assumed to be longer + than the use of the string in e.g. a GenericValue + + \post \ref s == str + + \note Constant complexity. + \note There is a hidden, private overload to disallow references to + non-const character arrays to be created via this constructor. + By this, e.g. function-scope arrays used to be filled via + \c snprintf are excluded from consideration. + In such cases, the referenced string should be \b copied to the + GenericValue instead. + */ +#endif + template + GenericStringRef(const CharType (&str)[N]) RAPIDJSON_NOEXCEPT + : s(str), length(N-1) {} + + //! Explicitly create string reference from \c const character pointer +#ifndef __clang__ // -Wdocumentation + /*! + This constructor can be used to \b explicitly create a reference to + a constant string pointer. + + \see StringRef(const CharType*) + + \param str Constant character pointer, lifetime assumed to be longer + than the use of the string in e.g. a GenericValue + + \post \ref s == str + + \note There is a hidden, private overload to disallow references to + non-const character arrays to be created via this constructor. + By this, e.g. function-scope arrays used to be filled via + \c snprintf are excluded from consideration. + In such cases, the referenced string should be \b copied to the + GenericValue instead. + */ +#endif + explicit GenericStringRef(const CharType* str) + : s(str), length(NotNullStrLen(str)) {} + + //! Create constant string reference from pointer and length +#ifndef __clang__ // -Wdocumentation + /*! \param str constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \param len length of the string, excluding the trailing NULL terminator + + \post \ref s == str && \ref length == len + \note Constant complexity. + */ +#endif + GenericStringRef(const CharType* str, SizeType len) + : s(RAPIDJSON_LIKELY(str) ? str : emptyString), length(len) { RAPIDJSON_ASSERT(str != 0 || len == 0u); } + + GenericStringRef(const GenericStringRef& rhs) : s(rhs.s), length(rhs.length) {} + + //! implicit conversion to plain CharType pointer + operator const Ch *() const { return s; } + + const Ch* const s; //!< plain CharType pointer + const SizeType length; //!< length of the string (excluding the trailing NULL terminator) + +private: + SizeType NotNullStrLen(const CharType* str) { + RAPIDJSON_ASSERT(str != 0); + return internal::StrLen(str); + } + + /// Empty string - used when passing in a NULL pointer + static const Ch emptyString[]; + + //! Disallow construction from non-const array + template + GenericStringRef(CharType (&str)[N]) /* = delete */; + //! Copy assignment operator not permitted - immutable type + GenericStringRef& operator=(const GenericStringRef& rhs) /* = delete */; +}; + +template +const CharType GenericStringRef::emptyString[] = { CharType() }; + +//! Mark a character pointer as constant string +/*! Mark a plain character pointer as a "string literal". This function + can be used to avoid copying a character string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + \tparam CharType Character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \return GenericStringRef string reference object + \relatesalso GenericStringRef + + \see GenericValue::GenericValue(StringRefType), GenericValue::operator=(StringRefType), GenericValue::SetString(StringRefType), GenericValue::PushBack(StringRefType, Allocator&), GenericValue::AddMember +*/ +template +inline GenericStringRef StringRef(const CharType* str) { + return GenericStringRef(str); +} + +//! Mark a character pointer as constant string +/*! Mark a plain character pointer as a "string literal". This function + can be used to avoid copying a character string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + + This version has better performance with supplied length, and also + supports string containing null characters. + + \tparam CharType character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \param length The length of source string. + \return GenericStringRef string reference object + \relatesalso GenericStringRef +*/ +template +inline GenericStringRef StringRef(const CharType* str, size_t length) { + return GenericStringRef(str, SizeType(length)); +} + +#if RAPIDJSON_HAS_STDSTRING +//! Mark a string object as constant string +/*! Mark a string object (e.g. \c std::string) as a "string literal". + This function can be used to avoid copying a string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + + \tparam CharType character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \return GenericStringRef string reference object + \relatesalso GenericStringRef + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. +*/ +template +inline GenericStringRef StringRef(const std::basic_string& str) { + return GenericStringRef(str.data(), SizeType(str.size())); +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +// GenericValue type traits +namespace internal { + +template +struct IsGenericValueImpl : FalseType {}; + +// select candidates according to nested encoding and allocator types +template struct IsGenericValueImpl::Type, typename Void::Type> + : IsBaseOf, T>::Type {}; + +// helper to match arbitrary GenericValue instantiations, including derived classes +template struct IsGenericValue : IsGenericValueImpl::Type {}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// TypeHelper + +namespace internal { + +template +struct TypeHelper {}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsBool(); } + static bool Get(const ValueType& v) { return v.GetBool(); } + static ValueType& Set(ValueType& v, bool data) { return v.SetBool(data); } + static ValueType& Set(ValueType& v, bool data, typename ValueType::AllocatorType&) { return v.SetBool(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt(); } + static int Get(const ValueType& v) { return v.GetInt(); } + static ValueType& Set(ValueType& v, int data) { return v.SetInt(data); } + static ValueType& Set(ValueType& v, int data, typename ValueType::AllocatorType&) { return v.SetInt(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint(); } + static unsigned Get(const ValueType& v) { return v.GetUint(); } + static ValueType& Set(ValueType& v, unsigned data) { return v.SetUint(data); } + static ValueType& Set(ValueType& v, unsigned data, typename ValueType::AllocatorType&) { return v.SetUint(data); } +}; + +#ifdef _MSC_VER +RAPIDJSON_STATIC_ASSERT(sizeof(long) == sizeof(int)); +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt(); } + static long Get(const ValueType& v) { return v.GetInt(); } + static ValueType& Set(ValueType& v, long data) { return v.SetInt(data); } + static ValueType& Set(ValueType& v, long data, typename ValueType::AllocatorType&) { return v.SetInt(data); } +}; + +RAPIDJSON_STATIC_ASSERT(sizeof(unsigned long) == sizeof(unsigned)); +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint(); } + static unsigned long Get(const ValueType& v) { return v.GetUint(); } + static ValueType& Set(ValueType& v, unsigned long data) { return v.SetUint(data); } + static ValueType& Set(ValueType& v, unsigned long data, typename ValueType::AllocatorType&) { return v.SetUint(data); } +}; +#endif + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt64(); } + static int64_t Get(const ValueType& v) { return v.GetInt64(); } + static ValueType& Set(ValueType& v, int64_t data) { return v.SetInt64(data); } + static ValueType& Set(ValueType& v, int64_t data, typename ValueType::AllocatorType&) { return v.SetInt64(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint64(); } + static uint64_t Get(const ValueType& v) { return v.GetUint64(); } + static ValueType& Set(ValueType& v, uint64_t data) { return v.SetUint64(data); } + static ValueType& Set(ValueType& v, uint64_t data, typename ValueType::AllocatorType&) { return v.SetUint64(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsDouble(); } + static double Get(const ValueType& v) { return v.GetDouble(); } + static ValueType& Set(ValueType& v, double data) { return v.SetDouble(data); } + static ValueType& Set(ValueType& v, double data, typename ValueType::AllocatorType&) { return v.SetDouble(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsFloat(); } + static float Get(const ValueType& v) { return v.GetFloat(); } + static ValueType& Set(ValueType& v, float data) { return v.SetFloat(data); } + static ValueType& Set(ValueType& v, float data, typename ValueType::AllocatorType&) { return v.SetFloat(data); } +}; + +template +struct TypeHelper { + typedef const typename ValueType::Ch* StringType; + static bool Is(const ValueType& v) { return v.IsString(); } + static StringType Get(const ValueType& v) { return v.GetString(); } + static ValueType& Set(ValueType& v, const StringType data) { return v.SetString(typename ValueType::StringRefType(data)); } + static ValueType& Set(ValueType& v, const StringType data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); } +}; + +#if RAPIDJSON_HAS_STDSTRING +template +struct TypeHelper > { + typedef std::basic_string StringType; + static bool Is(const ValueType& v) { return v.IsString(); } + static StringType Get(const ValueType& v) { return StringType(v.GetString(), v.GetStringLength()); } + static ValueType& Set(ValueType& v, const StringType& data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); } +}; +#endif + +template +struct TypeHelper { + typedef typename ValueType::Array ArrayType; + static bool Is(const ValueType& v) { return v.IsArray(); } + static ArrayType Get(ValueType& v) { return v.GetArray(); } + static ValueType& Set(ValueType& v, ArrayType data) { return v = data; } + static ValueType& Set(ValueType& v, ArrayType data, typename ValueType::AllocatorType&) { return v = data; } +}; + +template +struct TypeHelper { + typedef typename ValueType::ConstArray ArrayType; + static bool Is(const ValueType& v) { return v.IsArray(); } + static ArrayType Get(const ValueType& v) { return v.GetArray(); } +}; + +template +struct TypeHelper { + typedef typename ValueType::Object ObjectType; + static bool Is(const ValueType& v) { return v.IsObject(); } + static ObjectType Get(ValueType& v) { return v.GetObject(); } + static ValueType& Set(ValueType& v, ObjectType data) { return v = data; } + static ValueType& Set(ValueType& v, ObjectType data, typename ValueType::AllocatorType&) { return v = data; } +}; + +template +struct TypeHelper { + typedef typename ValueType::ConstObject ObjectType; + static bool Is(const ValueType& v) { return v.IsObject(); } + static ObjectType Get(const ValueType& v) { return v.GetObject(); } +}; + +} // namespace internal + +// Forward declarations +template class GenericArray; +template class GenericObject; + +/////////////////////////////////////////////////////////////////////////////// +// GenericValue + +//! Represents a JSON value. Use Value for UTF8 encoding and default allocator. +/*! + A JSON value can be one of 7 types. This class is a variant type supporting + these types. + + Use the Value if UTF8 and default allocator + + \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document) + \tparam Allocator Allocator type for allocating memory of object, array and string. +*/ +template +class GenericValue { +public: + //! Name-value pair in an object. + typedef GenericMember Member; + typedef Encoding EncodingType; //!< Encoding type from template parameter. + typedef Allocator AllocatorType; //!< Allocator type from template parameter. + typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. + typedef GenericStringRef StringRefType; //!< Reference to a constant string + typedef typename GenericMemberIterator::Iterator MemberIterator; //!< Member iterator for iterating in object. + typedef typename GenericMemberIterator::Iterator ConstMemberIterator; //!< Constant member iterator for iterating in object. + typedef GenericValue* ValueIterator; //!< Value iterator for iterating in array. + typedef const GenericValue* ConstValueIterator; //!< Constant value iterator for iterating in array. + typedef GenericValue ValueType; //!< Value type of itself. + typedef GenericArray Array; + typedef GenericArray ConstArray; + typedef GenericObject Object; + typedef GenericObject ConstObject; + + //!@name Constructors and destructor. + //@{ + + //! Default constructor creates a null value. + GenericValue() RAPIDJSON_NOEXCEPT : data_() { data_.f.flags = kNullFlag; } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_) { + rhs.data_.f.flags = kNullFlag; // give up contents + } +#endif + +private: + //! Copy constructor is not permitted. + GenericValue(const GenericValue& rhs); + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Moving from a GenericDocument is not permitted. + template + GenericValue(GenericDocument&& rhs); + + //! Move assignment from a GenericDocument is not permitted. + template + GenericValue& operator=(GenericDocument&& rhs); +#endif + +public: + + //! Constructor with JSON value type. + /*! This creates a Value of specified type with default content. + \param type Type of the value. + \note Default content for number is zero. + */ + explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_() { + static const uint16_t defaultFlags[] = { + kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kShortStringFlag, + kNumberAnyFlag + }; + RAPIDJSON_NOEXCEPT_ASSERT(type >= kNullType && type <= kNumberType); + data_.f.flags = defaultFlags[type]; + + // Use ShortString to store empty string. + if (type == kStringType) + data_.ss.SetLength(0); + } + + //! Explicit copy constructor (with allocator) + /*! Creates a copy of a Value by using the given Allocator + \tparam SourceAllocator allocator of \c rhs + \param rhs Value to copy from (read-only) + \param allocator Allocator for allocating copied elements and buffers. Commonly use GenericDocument::GetAllocator(). + \param copyConstStrings Force copying of constant strings (e.g. referencing an in-situ buffer) + \see CopyFrom() + */ + template + GenericValue(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings = false) { + switch (rhs.GetType()) { + case kObjectType: { + SizeType count = rhs.data_.o.size; + Member* lm = reinterpret_cast(allocator.Malloc(count * sizeof(Member))); + const typename GenericValue::Member* rm = rhs.GetMembersPointer(); + for (SizeType i = 0; i < count; i++) { + new (&lm[i].name) GenericValue(rm[i].name, allocator, copyConstStrings); + new (&lm[i].value) GenericValue(rm[i].value, allocator, copyConstStrings); + } + data_.f.flags = kObjectFlag; + data_.o.size = data_.o.capacity = count; + SetMembersPointer(lm); + } + break; + case kArrayType: { + SizeType count = rhs.data_.a.size; + GenericValue* le = reinterpret_cast(allocator.Malloc(count * sizeof(GenericValue))); + const GenericValue* re = rhs.GetElementsPointer(); + for (SizeType i = 0; i < count; i++) + new (&le[i]) GenericValue(re[i], allocator, copyConstStrings); + data_.f.flags = kArrayFlag; + data_.a.size = data_.a.capacity = count; + SetElementsPointer(le); + } + break; + case kStringType: + if (rhs.data_.f.flags == kConstStringFlag && !copyConstStrings) { + data_.f.flags = rhs.data_.f.flags; + data_ = *reinterpret_cast(&rhs.data_); + } + else + SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator); + break; + default: + data_.f.flags = rhs.data_.f.flags; + data_ = *reinterpret_cast(&rhs.data_); + break; + } + } + + //! Constructor for boolean value. + /*! \param b Boolean value + \note This constructor is limited to \em real boolean values and rejects + implicitly converted types like arbitrary pointers. Use an explicit cast + to \c bool, if you want to construct a boolean JSON value in such cases. + */ +#ifndef RAPIDJSON_DOXYGEN_RUNNING // hide SFINAE from Doxygen + template + explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame))) RAPIDJSON_NOEXCEPT // See #472 +#else + explicit GenericValue(bool b) RAPIDJSON_NOEXCEPT +#endif + : data_() { + // safe-guard against failing SFINAE + RAPIDJSON_STATIC_ASSERT((internal::IsSame::Value)); + data_.f.flags = b ? kTrueFlag : kFalseFlag; + } + + //! Constructor for int value. + explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_() { + data_.n.i64 = i; + data_.f.flags = (i >= 0) ? (kNumberIntFlag | kUintFlag | kUint64Flag) : kNumberIntFlag; + } + + //! Constructor for unsigned value. + explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_() { + data_.n.u64 = u; + data_.f.flags = (u & 0x80000000) ? kNumberUintFlag : (kNumberUintFlag | kIntFlag | kInt64Flag); + } + + //! Constructor for int64_t value. + explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_() { + data_.n.i64 = i64; + data_.f.flags = kNumberInt64Flag; + if (i64 >= 0) { + data_.f.flags |= kNumberUint64Flag; + if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) + data_.f.flags |= kUintFlag; + if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + data_.f.flags |= kIntFlag; + } + else if (i64 >= static_cast(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + data_.f.flags |= kIntFlag; + } + + //! Constructor for uint64_t value. + explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_() { + data_.n.u64 = u64; + data_.f.flags = kNumberUint64Flag; + if (!(u64 & RAPIDJSON_UINT64_C2(0x80000000, 0x00000000))) + data_.f.flags |= kInt64Flag; + if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) + data_.f.flags |= kUintFlag; + if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + data_.f.flags |= kIntFlag; + } + + //! Constructor for double value. + explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = d; data_.f.flags = kNumberDoubleFlag; } + + //! Constructor for float value. + explicit GenericValue(float f) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = static_cast(f); data_.f.flags = kNumberDoubleFlag; } + + //! Constructor for constant string (i.e. do not make a copy of string) + GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(StringRef(s, length)); } + + //! Constructor for constant string (i.e. do not make a copy of string) + explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(s); } + + //! Constructor for copy-string (i.e. do make a copy of string) + GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_() { SetStringRaw(StringRef(s, length), allocator); } + + //! Constructor for copy-string (i.e. do make a copy of string) + GenericValue(const Ch*s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); } + +#if RAPIDJSON_HAS_STDSTRING + //! Constructor for copy-string from a string object (i.e. do make a copy of string) + /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + GenericValue(const std::basic_string& s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); } +#endif + + //! Constructor for Array. + /*! + \param a An array obtained by \c GetArray(). + \note \c Array is always pass-by-value. + \note the source array is moved into this value and the sourec array becomes empty. + */ + GenericValue(Array a) RAPIDJSON_NOEXCEPT : data_(a.value_.data_) { + a.value_.data_ = Data(); + a.value_.data_.f.flags = kArrayFlag; + } + + //! Constructor for Object. + /*! + \param o An object obtained by \c GetObject(). + \note \c Object is always pass-by-value. + \note the source object is moved into this value and the sourec object becomes empty. + */ + GenericValue(Object o) RAPIDJSON_NOEXCEPT : data_(o.value_.data_) { + o.value_.data_ = Data(); + o.value_.data_.f.flags = kObjectFlag; + } + + //! Destructor. + /*! Need to destruct elements of array, members of object, or copy-string. + */ + ~GenericValue() { + if (Allocator::kNeedFree) { // Shortcut by Allocator's trait + switch(data_.f.flags) { + case kArrayFlag: + { + GenericValue* e = GetElementsPointer(); + for (GenericValue* v = e; v != e + data_.a.size; ++v) + v->~GenericValue(); + Allocator::Free(e); + } + break; + + case kObjectFlag: + for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) + m->~Member(); + Allocator::Free(GetMembersPointer()); + break; + + case kCopyStringFlag: + Allocator::Free(const_cast(GetStringPointer())); + break; + + default: + break; // Do nothing for other types. + } + } + } + + //@} + + //!@name Assignment operators + //@{ + + //! Assignment with move semantics. + /*! \param rhs Source of the assignment. It will become a null value after assignment. + */ + GenericValue& operator=(GenericValue& rhs) RAPIDJSON_NOEXCEPT { + if (RAPIDJSON_LIKELY(this != &rhs)) { + this->~GenericValue(); + RawAssign(rhs); + } + return *this; + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move assignment in C++11 + GenericValue& operator=(GenericValue&& rhs) RAPIDJSON_NOEXCEPT { + return *this = rhs.Move(); + } +#endif + + //! Assignment of constant string reference (no copy) + /*! \param str Constant string reference to be assigned + \note This overload is needed to avoid clashes with the generic primitive type assignment overload below. + \see GenericStringRef, operator=(T) + */ + GenericValue& operator=(StringRefType str) RAPIDJSON_NOEXCEPT { + GenericValue s(str); + return *this = s; + } + + //! Assignment with primitive types. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param value The value to be assigned. + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref SetString(const Ch*, Allocator&) (for copying) or + \ref StringRef() (to explicitly mark the pointer as constant) instead. + All other pointer types would implicitly convert to \c bool, + use \ref SetBool() instead. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::IsPointer), (GenericValue&)) + operator=(T value) { + GenericValue v(value); + return *this = v; + } + + //! Deep-copy assignment from Value + /*! Assigns a \b copy of the Value to the current Value object + \tparam SourceAllocator Allocator type of \c rhs + \param rhs Value to copy from (read-only) + \param allocator Allocator to use for copying + \param copyConstStrings Force copying of constant strings (e.g. referencing an in-situ buffer) + */ + template + GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings = false) { + RAPIDJSON_ASSERT(static_cast(this) != static_cast(&rhs)); + this->~GenericValue(); + new (this) GenericValue(rhs, allocator, copyConstStrings); + return *this; + } + + //! Exchange the contents of this value with those of other. + /*! + \param other Another value. + \note Constant complexity. + */ + GenericValue& Swap(GenericValue& other) RAPIDJSON_NOEXCEPT { + GenericValue temp; + temp.RawAssign(*this); + RawAssign(other); + other.RawAssign(temp); + return *this; + } + + //! free-standing swap function helper + /*! + Helper function to enable support for common swap implementation pattern based on \c std::swap: + \code + void swap(MyClass& a, MyClass& b) { + using std::swap; + swap(a.value, b.value); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericValue& a, GenericValue& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } + + //! Prepare Value for move semantics + /*! \return *this */ + GenericValue& Move() RAPIDJSON_NOEXCEPT { return *this; } + //@} + + //!@name Equal-to and not-equal-to operators + //@{ + //! Equal-to operator + /*! + \note If an object contains duplicated named member, comparing equality with any object is always \c false. + \note Complexity is quadratic in Object's member number and linear for the rest (number of all values in the subtree and total lengths of all strings). + */ + template + bool operator==(const GenericValue& rhs) const { + typedef GenericValue RhsType; + if (GetType() != rhs.GetType()) + return false; + + switch (GetType()) { + case kObjectType: // Warning: O(n^2) inner-loop + if (data_.o.size != rhs.data_.o.size) + return false; + for (ConstMemberIterator lhsMemberItr = MemberBegin(); lhsMemberItr != MemberEnd(); ++lhsMemberItr) { + typename RhsType::ConstMemberIterator rhsMemberItr = rhs.FindMember(lhsMemberItr->name); + if (rhsMemberItr == rhs.MemberEnd() || lhsMemberItr->value != rhsMemberItr->value) + return false; + } + return true; + + case kArrayType: + if (data_.a.size != rhs.data_.a.size) + return false; + for (SizeType i = 0; i < data_.a.size; i++) + if ((*this)[i] != rhs[i]) + return false; + return true; + + case kStringType: + return StringEqual(rhs); + + case kNumberType: + if (IsDouble() || rhs.IsDouble()) { + double a = GetDouble(); // May convert from integer to double. + double b = rhs.GetDouble(); // Ditto + return a >= b && a <= b; // Prevent -Wfloat-equal + } + else + return data_.n.u64 == rhs.data_.n.u64; + + default: + return true; + } + } + + //! Equal-to operator with const C-string pointer + bool operator==(const Ch* rhs) const { return *this == GenericValue(StringRef(rhs)); } + +#if RAPIDJSON_HAS_STDSTRING + //! Equal-to operator with string object + /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + bool operator==(const std::basic_string& rhs) const { return *this == GenericValue(StringRef(rhs)); } +#endif + + //! Equal-to operator with primitive types + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c true, \c false + */ + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr,internal::IsGenericValue >), (bool)) operator==(const T& rhs) const { return *this == GenericValue(rhs); } + + //! Not-equal-to operator + /*! \return !(*this == rhs) + */ + template + bool operator!=(const GenericValue& rhs) const { return !(*this == rhs); } + + //! Not-equal-to operator with const C-string pointer + bool operator!=(const Ch* rhs) const { return !(*this == rhs); } + + //! Not-equal-to operator with arbitrary types + /*! \return !(*this == rhs) + */ + template RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& rhs) const { return !(*this == rhs); } + + //! Equal-to operator with arbitrary types (symmetric version) + /*! \return (rhs == lhs) + */ + template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator==(const T& lhs, const GenericValue& rhs) { return rhs == lhs; } + + //! Not-Equal-to operator with arbitrary types (symmetric version) + /*! \return !(rhs == lhs) + */ + template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& lhs, const GenericValue& rhs) { return !(rhs == lhs); } + //@} + + //!@name Type + //@{ + + Type GetType() const { return static_cast(data_.f.flags & kTypeMask); } + bool IsNull() const { return data_.f.flags == kNullFlag; } + bool IsFalse() const { return data_.f.flags == kFalseFlag; } + bool IsTrue() const { return data_.f.flags == kTrueFlag; } + bool IsBool() const { return (data_.f.flags & kBoolFlag) != 0; } + bool IsObject() const { return data_.f.flags == kObjectFlag; } + bool IsArray() const { return data_.f.flags == kArrayFlag; } + bool IsNumber() const { return (data_.f.flags & kNumberFlag) != 0; } + bool IsInt() const { return (data_.f.flags & kIntFlag) != 0; } + bool IsUint() const { return (data_.f.flags & kUintFlag) != 0; } + bool IsInt64() const { return (data_.f.flags & kInt64Flag) != 0; } + bool IsUint64() const { return (data_.f.flags & kUint64Flag) != 0; } + bool IsDouble() const { return (data_.f.flags & kDoubleFlag) != 0; } + bool IsString() const { return (data_.f.flags & kStringFlag) != 0; } + + // Checks whether a number can be losslessly converted to a double. + bool IsLosslessDouble() const { + if (!IsNumber()) return false; + if (IsUint64()) { + uint64_t u = GetUint64(); + volatile double d = static_cast(u); + return (d >= 0.0) + && (d < static_cast((std::numeric_limits::max)())) + && (u == static_cast(d)); + } + if (IsInt64()) { + int64_t i = GetInt64(); + volatile double d = static_cast(i); + return (d >= static_cast((std::numeric_limits::min)())) + && (d < static_cast((std::numeric_limits::max)())) + && (i == static_cast(d)); + } + return true; // double, int, uint are always lossless + } + + // Checks whether a number is a float (possible lossy). + bool IsFloat() const { + if ((data_.f.flags & kDoubleFlag) == 0) + return false; + double d = GetDouble(); + return d >= -3.4028234e38 && d <= 3.4028234e38; + } + // Checks whether a number can be losslessly converted to a float. + bool IsLosslessFloat() const { + if (!IsNumber()) return false; + double a = GetDouble(); + if (a < static_cast(-(std::numeric_limits::max)()) + || a > static_cast((std::numeric_limits::max)())) + return false; + double b = static_cast(static_cast(a)); + return a >= b && a <= b; // Prevent -Wfloat-equal + } + + //@} + + //!@name Null + //@{ + + GenericValue& SetNull() { this->~GenericValue(); new (this) GenericValue(); return *this; } + + //@} + + //!@name Bool + //@{ + + bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return data_.f.flags == kTrueFlag; } + //!< Set boolean value + /*! \post IsBool() == true */ + GenericValue& SetBool(bool b) { this->~GenericValue(); new (this) GenericValue(b); return *this; } + + //@} + + //!@name Object + //@{ + + //! Set this value as an empty object. + /*! \post IsObject() == true */ + GenericValue& SetObject() { this->~GenericValue(); new (this) GenericValue(kObjectType); return *this; } + + //! Get the number of members in the object. + SizeType MemberCount() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size; } + + //! Get the capacity of object. + SizeType MemberCapacity() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.capacity; } + + //! Check whether the object is empty. + bool ObjectEmpty() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size == 0; } + + //! Get a value from an object associated with the name. + /*! \pre IsObject() == true + \tparam T Either \c Ch or \c const \c Ch (template used for disambiguation with \ref operator[](SizeType)) + \note In version 0.1x, if the member is not found, this function returns a null value. This makes issue 7. + Since 0.2, if the name is not correct, it will assert. + If user is unsure whether a member exists, user should use HasMember() first. + A better approach is to use FindMember(). + \note Linear time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >),(GenericValue&)) operator[](T* name) { + GenericValue n(StringRef(name)); + return (*this)[n]; + } + template + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >),(const GenericValue&)) operator[](T* name) const { return const_cast(*this)[name]; } + + //! Get a value from an object associated with the name. + /*! \pre IsObject() == true + \tparam SourceAllocator Allocator of the \c name value + + \note Compared to \ref operator[](T*), this version is faster because it does not need a StrLen(). + And it can also handle strings with embedded null characters. + + \note Linear time complexity. + */ + template + GenericValue& operator[](const GenericValue& name) { + MemberIterator member = FindMember(name); + if (member != MemberEnd()) + return member->value; + else { + RAPIDJSON_ASSERT(false); // see above note + + // This will generate -Wexit-time-destructors in clang + // static GenericValue NullValue; + // return NullValue; + + // Use static buffer and placement-new to prevent destruction + static char buffer[sizeof(GenericValue)]; + return *new (buffer) GenericValue(); + } + } + template + const GenericValue& operator[](const GenericValue& name) const { return const_cast(*this)[name]; } + +#if RAPIDJSON_HAS_STDSTRING + //! Get a value from an object associated with name (string object). + GenericValue& operator[](const std::basic_string& name) { return (*this)[GenericValue(StringRef(name))]; } + const GenericValue& operator[](const std::basic_string& name) const { return (*this)[GenericValue(StringRef(name))]; } +#endif + + //! Const member iterator + /*! \pre IsObject() == true */ + ConstMemberIterator MemberBegin() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer()); } + //! Const \em past-the-end member iterator + /*! \pre IsObject() == true */ + ConstMemberIterator MemberEnd() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer() + data_.o.size); } + //! Member iterator + /*! \pre IsObject() == true */ + MemberIterator MemberBegin() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer()); } + //! \em Past-the-end member iterator + /*! \pre IsObject() == true */ + MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer() + data_.o.size); } + + //! Request the object to have enough capacity to store members. + /*! \param newCapacity The capacity that the object at least need to have. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note Linear time complexity. + */ + GenericValue& MemberReserve(SizeType newCapacity, Allocator &allocator) { + RAPIDJSON_ASSERT(IsObject()); + if (newCapacity > data_.o.capacity) { + SetMembersPointer(reinterpret_cast(allocator.Realloc(GetMembersPointer(), data_.o.capacity * sizeof(Member), newCapacity * sizeof(Member)))); + data_.o.capacity = newCapacity; + } + return *this; + } + + //! Check whether a member exists in the object. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + bool HasMember(const Ch* name) const { return FindMember(name) != MemberEnd(); } + +#if RAPIDJSON_HAS_STDSTRING + //! Check whether a member exists in the object with string object. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + bool HasMember(const std::basic_string& name) const { return FindMember(name) != MemberEnd(); } +#endif + + //! Check whether a member exists in the object with GenericValue name. + /*! + This version is faster because it does not need a StrLen(). It can also handle string with null character. + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + template + bool HasMember(const GenericValue& name) const { return FindMember(name) != MemberEnd(); } + + //! Find member by name. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + + \note Earlier versions of Rapidjson returned a \c NULL pointer, in case + the requested member doesn't exist. For consistency with e.g. + \c std::map, this has been changed to MemberEnd() now. + \note Linear time complexity. + */ + MemberIterator FindMember(const Ch* name) { + GenericValue n(StringRef(name)); + return FindMember(n); + } + + ConstMemberIterator FindMember(const Ch* name) const { return const_cast(*this).FindMember(name); } + + //! Find member by name. + /*! + This version is faster because it does not need a StrLen(). It can also handle string with null character. + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + + \note Earlier versions of Rapidjson returned a \c NULL pointer, in case + the requested member doesn't exist. For consistency with e.g. + \c std::map, this has been changed to MemberEnd() now. + \note Linear time complexity. + */ + template + MemberIterator FindMember(const GenericValue& name) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(name.IsString()); + MemberIterator member = MemberBegin(); + for ( ; member != MemberEnd(); ++member) + if (name.StringEqual(member->name)) + break; + return member; + } + template ConstMemberIterator FindMember(const GenericValue& name) const { return const_cast(*this).FindMember(name); } + +#if RAPIDJSON_HAS_STDSTRING + //! Find member by string object name. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + */ + MemberIterator FindMember(const std::basic_string& name) { return FindMember(GenericValue(StringRef(name))); } + ConstMemberIterator FindMember(const std::basic_string& name) const { return FindMember(GenericValue(StringRef(name))); } +#endif + + //! Add a member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value Value of any type. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note The ownership of \c name and \c value will be transferred to this object on success. + \pre IsObject() && name.IsString() + \post name.IsNull() && value.IsNull() + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, GenericValue& value, Allocator& allocator) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(name.IsString()); + + ObjectData& o = data_.o; + if (o.size >= o.capacity) + MemberReserve(o.capacity == 0 ? kDefaultObjectCapacity : (o.capacity + (o.capacity + 1) / 2), allocator); + Member* members = GetMembersPointer(); + members[o.size].name.RawAssign(name); + members[o.size].value.RawAssign(value); + o.size++; + return *this; + } + + //! Add a constant string value as member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(GenericValue&,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, StringRefType value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Add a string object as member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(GenericValue&,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, std::basic_string& value, Allocator& allocator) { + GenericValue v(value, allocator); + return AddMember(name, v, allocator); + } +#endif + + //! Add any primitive value as member (name-value pair) to the object. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param name A string value as name of member. + \param value Value of primitive type \c T as value of member + \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref + AddMember(StringRefType, StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized Constant time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + AddMember(GenericValue& name, T value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericValue& AddMember(GenericValue&& name, GenericValue&& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(GenericValue&& name, GenericValue& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(GenericValue& name, GenericValue&& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(StringRefType name, GenericValue&& value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + + + //! Add a member (name-value pair) to the object. + /*! \param name A constant string reference as name of member. + \param value Value of any type. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note The ownership of \c value will be transferred to this object on success. + \pre IsObject() + \post value.IsNull() + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(StringRefType name, GenericValue& value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } + + //! Add a constant string value as member (name-value pair) to the object. + /*! \param name A constant string reference as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(StringRefType,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(StringRefType name, StringRefType value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + + //! Add any primitive value as member (name-value pair) to the object. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param name A constant string reference as name of member. + \param value Value of primitive type \c T as value of member + \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref + AddMember(StringRefType, StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized Constant time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + AddMember(StringRefType name, T value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } + + //! Remove all members in the object. + /*! This function do not deallocate memory in the object, i.e. the capacity is unchanged. + \note Linear time complexity. + */ + void RemoveAllMembers() { + RAPIDJSON_ASSERT(IsObject()); + for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) + m->~Member(); + data_.o.size = 0; + } + + //! Remove a member in object by its name. + /*! \param name Name of member to be removed. + \return Whether the member existed. + \note This function may reorder the object members. Use \ref + EraseMember(ConstMemberIterator) if you need to preserve the + relative order of the remaining members. + \note Linear time complexity. + */ + bool RemoveMember(const Ch* name) { + GenericValue n(StringRef(name)); + return RemoveMember(n); + } + +#if RAPIDJSON_HAS_STDSTRING + bool RemoveMember(const std::basic_string& name) { return RemoveMember(GenericValue(StringRef(name))); } +#endif + + template + bool RemoveMember(const GenericValue& name) { + MemberIterator m = FindMember(name); + if (m != MemberEnd()) { + RemoveMember(m); + return true; + } + else + return false; + } + + //! Remove a member in object by iterator. + /*! \param m member iterator (obtained by FindMember() or MemberBegin()). + \return the new iterator after removal. + \note This function may reorder the object members. Use \ref + EraseMember(ConstMemberIterator) if you need to preserve the + relative order of the remaining members. + \note Constant time complexity. + */ + MemberIterator RemoveMember(MemberIterator m) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(data_.o.size > 0); + RAPIDJSON_ASSERT(GetMembersPointer() != 0); + RAPIDJSON_ASSERT(m >= MemberBegin() && m < MemberEnd()); + + MemberIterator last(GetMembersPointer() + (data_.o.size - 1)); + if (data_.o.size > 1 && m != last) + *m = *last; // Move the last one to this place + else + m->~Member(); // Only one left, just destroy + --data_.o.size; + return m; + } + + //! Remove a member from an object by iterator. + /*! \param pos iterator to the member to remove + \pre IsObject() == true && \ref MemberBegin() <= \c pos < \ref MemberEnd() + \return Iterator following the removed element. + If the iterator \c pos refers to the last element, the \ref MemberEnd() iterator is returned. + \note This function preserves the relative order of the remaining object + members. If you do not need this, use the more efficient \ref RemoveMember(MemberIterator). + \note Linear time complexity. + */ + MemberIterator EraseMember(ConstMemberIterator pos) { + return EraseMember(pos, pos +1); + } + + //! Remove members in the range [first, last) from an object. + /*! \param first iterator to the first member to remove + \param last iterator following the last member to remove + \pre IsObject() == true && \ref MemberBegin() <= \c first <= \c last <= \ref MemberEnd() + \return Iterator following the last removed element. + \note This function preserves the relative order of the remaining object + members. + \note Linear time complexity. + */ + MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(data_.o.size > 0); + RAPIDJSON_ASSERT(GetMembersPointer() != 0); + RAPIDJSON_ASSERT(first >= MemberBegin()); + RAPIDJSON_ASSERT(first <= last); + RAPIDJSON_ASSERT(last <= MemberEnd()); + + MemberIterator pos = MemberBegin() + (first - MemberBegin()); + for (MemberIterator itr = pos; itr != last; ++itr) + itr->~Member(); + std::memmove(static_cast(&*pos), &*last, static_cast(MemberEnd() - last) * sizeof(Member)); + data_.o.size -= static_cast(last - first); + return pos; + } + + //! Erase a member in object by its name. + /*! \param name Name of member to be removed. + \return Whether the member existed. + \note Linear time complexity. + */ + bool EraseMember(const Ch* name) { + GenericValue n(StringRef(name)); + return EraseMember(n); + } + +#if RAPIDJSON_HAS_STDSTRING + bool EraseMember(const std::basic_string& name) { return EraseMember(GenericValue(StringRef(name))); } +#endif + + template + bool EraseMember(const GenericValue& name) { + MemberIterator m = FindMember(name); + if (m != MemberEnd()) { + EraseMember(m); + return true; + } + else + return false; + } + + Object GetObject() { RAPIDJSON_ASSERT(IsObject()); return Object(*this); } + ConstObject GetObject() const { RAPIDJSON_ASSERT(IsObject()); return ConstObject(*this); } + + //@} + + //!@name Array + //@{ + + //! Set this value as an empty array. + /*! \post IsArray == true */ + GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; } + + //! Get the number of elements in array. + SizeType Size() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size; } + + //! Get the capacity of array. + SizeType Capacity() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.capacity; } + + //! Check whether the array is empty. + bool Empty() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size == 0; } + + //! Remove all elements in the array. + /*! This function do not deallocate memory in the array, i.e. the capacity is unchanged. + \note Linear time complexity. + */ + void Clear() { + RAPIDJSON_ASSERT(IsArray()); + GenericValue* e = GetElementsPointer(); + for (GenericValue* v = e; v != e + data_.a.size; ++v) + v->~GenericValue(); + data_.a.size = 0; + } + + //! Get an element from array by index. + /*! \pre IsArray() == true + \param index Zero-based index of element. + \see operator[](T*) + */ + GenericValue& operator[](SizeType index) { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(index < data_.a.size); + return GetElementsPointer()[index]; + } + const GenericValue& operator[](SizeType index) const { return const_cast(*this)[index]; } + + //! Element iterator + /*! \pre IsArray() == true */ + ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer(); } + //! \em Past-the-end element iterator + /*! \pre IsArray() == true */ + ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer() + data_.a.size; } + //! Constant element iterator + /*! \pre IsArray() == true */ + ConstValueIterator Begin() const { return const_cast(*this).Begin(); } + //! Constant \em past-the-end element iterator + /*! \pre IsArray() == true */ + ConstValueIterator End() const { return const_cast(*this).End(); } + + //! Request the array to have enough capacity to store elements. + /*! \param newCapacity The capacity that the array at least need to have. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note Linear time complexity. + */ + GenericValue& Reserve(SizeType newCapacity, Allocator &allocator) { + RAPIDJSON_ASSERT(IsArray()); + if (newCapacity > data_.a.capacity) { + SetElementsPointer(reinterpret_cast(allocator.Realloc(GetElementsPointer(), data_.a.capacity * sizeof(GenericValue), newCapacity * sizeof(GenericValue)))); + data_.a.capacity = newCapacity; + } + return *this; + } + + //! Append a GenericValue at the end of the array. + /*! \param value Value to be appended. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \post value.IsNull() == true + \return The value itself for fluent API. + \note The ownership of \c value will be transferred to this array on success. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + \note Amortized constant time complexity. + */ + GenericValue& PushBack(GenericValue& value, Allocator& allocator) { + RAPIDJSON_ASSERT(IsArray()); + if (data_.a.size >= data_.a.capacity) + Reserve(data_.a.capacity == 0 ? kDefaultArrayCapacity : (data_.a.capacity + (data_.a.capacity + 1) / 2), allocator); + GetElementsPointer()[data_.a.size++].RawAssign(value); + return *this; + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericValue& PushBack(GenericValue&& value, Allocator& allocator) { + return PushBack(value, allocator); + } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + + //! Append a constant string reference at the end of the array. + /*! \param value Constant string reference to be appended. + \param allocator Allocator for reallocating memory. It must be the same one used previously. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \return The value itself for fluent API. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + \note Amortized constant time complexity. + \see GenericStringRef + */ + GenericValue& PushBack(StringRefType value, Allocator& allocator) { + return (*this).template PushBack(value, allocator); + } + + //! Append a primitive value at the end of the array. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param value Value of primitive type T to be appended. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \return The value itself for fluent API. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref PushBack(GenericValue&, Allocator&) or \ref + PushBack(StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized constant time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + PushBack(T value, Allocator& allocator) { + GenericValue v(value); + return PushBack(v, allocator); + } + + //! Remove the last element in the array. + /*! + \note Constant time complexity. + */ + GenericValue& PopBack() { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(!Empty()); + GetElementsPointer()[--data_.a.size].~GenericValue(); + return *this; + } + + //! Remove an element of array by iterator. + /*! + \param pos iterator to the element to remove + \pre IsArray() == true && \ref Begin() <= \c pos < \ref End() + \return Iterator following the removed element. If the iterator pos refers to the last element, the End() iterator is returned. + \note Linear time complexity. + */ + ValueIterator Erase(ConstValueIterator pos) { + return Erase(pos, pos + 1); + } + + //! Remove elements in the range [first, last) of the array. + /*! + \param first iterator to the first element to remove + \param last iterator following the last element to remove + \pre IsArray() == true && \ref Begin() <= \c first <= \c last <= \ref End() + \return Iterator following the last removed element. + \note Linear time complexity. + */ + ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(data_.a.size > 0); + RAPIDJSON_ASSERT(GetElementsPointer() != 0); + RAPIDJSON_ASSERT(first >= Begin()); + RAPIDJSON_ASSERT(first <= last); + RAPIDJSON_ASSERT(last <= End()); + ValueIterator pos = Begin() + (first - Begin()); + for (ValueIterator itr = pos; itr != last; ++itr) + itr->~GenericValue(); + std::memmove(static_cast(pos), last, static_cast(End() - last) * sizeof(GenericValue)); + data_.a.size -= static_cast(last - first); + return pos; + } + + Array GetArray() { RAPIDJSON_ASSERT(IsArray()); return Array(*this); } + ConstArray GetArray() const { RAPIDJSON_ASSERT(IsArray()); return ConstArray(*this); } + + //@} + + //!@name Number + //@{ + + int GetInt() const { RAPIDJSON_ASSERT(data_.f.flags & kIntFlag); return data_.n.i.i; } + unsigned GetUint() const { RAPIDJSON_ASSERT(data_.f.flags & kUintFlag); return data_.n.u.u; } + int64_t GetInt64() const { RAPIDJSON_ASSERT(data_.f.flags & kInt64Flag); return data_.n.i64; } + uint64_t GetUint64() const { RAPIDJSON_ASSERT(data_.f.flags & kUint64Flag); return data_.n.u64; } + + //! Get the value as double type. + /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessDouble() to check whether the converison is lossless. + */ + double GetDouble() const { + RAPIDJSON_ASSERT(IsNumber()); + if ((data_.f.flags & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion. + if ((data_.f.flags & kIntFlag) != 0) return data_.n.i.i; // int -> double + if ((data_.f.flags & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double + if ((data_.f.flags & kInt64Flag) != 0) return static_cast(data_.n.i64); // int64_t -> double (may lose precision) + RAPIDJSON_ASSERT((data_.f.flags & kUint64Flag) != 0); return static_cast(data_.n.u64); // uint64_t -> double (may lose precision) + } + + //! Get the value as float type. + /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessFloat() to check whether the converison is lossless. + */ + float GetFloat() const { + return static_cast(GetDouble()); + } + + GenericValue& SetInt(int i) { this->~GenericValue(); new (this) GenericValue(i); return *this; } + GenericValue& SetUint(unsigned u) { this->~GenericValue(); new (this) GenericValue(u); return *this; } + GenericValue& SetInt64(int64_t i64) { this->~GenericValue(); new (this) GenericValue(i64); return *this; } + GenericValue& SetUint64(uint64_t u64) { this->~GenericValue(); new (this) GenericValue(u64); return *this; } + GenericValue& SetDouble(double d) { this->~GenericValue(); new (this) GenericValue(d); return *this; } + GenericValue& SetFloat(float f) { this->~GenericValue(); new (this) GenericValue(static_cast(f)); return *this; } + + //@} + + //!@name String + //@{ + + const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return (data_.f.flags & kInlineStrFlag) ? data_.ss.str : GetStringPointer(); } + + //! Get the length of string. + /*! Since rapidjson permits "\\u0000" in the json string, strlen(v.GetString()) may not equal to v.GetStringLength(). + */ + SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return ((data_.f.flags & kInlineStrFlag) ? (data_.ss.GetLength()) : data_.s.length); } + + //! Set this value as a string without copying source string. + /*! This version has better performance with supplied length, and also support string containing null character. + \param s source string pointer. + \param length The length of source string, excluding the trailing null terminator. + \return The value itself for fluent API. + \post IsString() == true && GetString() == s && GetStringLength() == length + \see SetString(StringRefType) + */ + GenericValue& SetString(const Ch* s, SizeType length) { return SetString(StringRef(s, length)); } + + //! Set this value as a string without copying source string. + /*! \param s source string reference + \return The value itself for fluent API. + \post IsString() == true && GetString() == s && GetStringLength() == s.length + */ + GenericValue& SetString(StringRefType s) { this->~GenericValue(); SetStringRaw(s); return *this; } + + //! Set this value as a string by copying from source string. + /*! This version has better performance with supplied length, and also support string containing null character. + \param s source string. + \param length The length of source string, excluding the trailing null terminator. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { return SetString(StringRef(s, length), allocator); } + + //! Set this value as a string by copying from source string. + /*! \param s source string. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(StringRef(s), allocator); } + + //! Set this value as a string by copying from source string. + /*! \param s source string reference + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s.s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(StringRefType s, Allocator& allocator) { this->~GenericValue(); SetStringRaw(s, allocator); return *this; } + +#if RAPIDJSON_HAS_STDSTRING + //! Set this value as a string by copying from source string. + /*! \param s source string. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s.data() && strcmp(GetString(),s.data() == 0 && GetStringLength() == s.size() + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + GenericValue& SetString(const std::basic_string& s, Allocator& allocator) { return SetString(StringRef(s), allocator); } +#endif + + //@} + + //!@name Array + //@{ + + //! Templated version for checking whether this value is type T. + /*! + \tparam T Either \c bool, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c float, \c const \c char*, \c std::basic_string + */ + template + bool Is() const { return internal::TypeHelper::Is(*this); } + + template + T Get() const { return internal::TypeHelper::Get(*this); } + + template + T Get() { return internal::TypeHelper::Get(*this); } + + template + ValueType& Set(const T& data) { return internal::TypeHelper::Set(*this, data); } + + template + ValueType& Set(const T& data, AllocatorType& allocator) { return internal::TypeHelper::Set(*this, data, allocator); } + + //@} + + //! Generate events of this value to a Handler. + /*! This function adopts the GoF visitor pattern. + Typical usage is to output this JSON value as JSON text via Writer, which is a Handler. + It can also be used to deep clone this value via GenericDocument, which is also a Handler. + \tparam Handler type of handler. + \param handler An object implementing concept Handler. + */ + template + bool Accept(Handler& handler) const { + switch(GetType()) { + case kNullType: return handler.Null(); + case kFalseType: return handler.Bool(false); + case kTrueType: return handler.Bool(true); + + case kObjectType: + if (RAPIDJSON_UNLIKELY(!handler.StartObject())) + return false; + for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) { + RAPIDJSON_ASSERT(m->name.IsString()); // User may change the type of name by MemberIterator. + if (RAPIDJSON_UNLIKELY(!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.data_.f.flags & kCopyFlag) != 0))) + return false; + if (RAPIDJSON_UNLIKELY(!m->value.Accept(handler))) + return false; + } + return handler.EndObject(data_.o.size); + + case kArrayType: + if (RAPIDJSON_UNLIKELY(!handler.StartArray())) + return false; + for (const GenericValue* v = Begin(); v != End(); ++v) + if (RAPIDJSON_UNLIKELY(!v->Accept(handler))) + return false; + return handler.EndArray(data_.a.size); + + case kStringType: + return handler.String(GetString(), GetStringLength(), (data_.f.flags & kCopyFlag) != 0); + + default: + RAPIDJSON_ASSERT(GetType() == kNumberType); + if (IsDouble()) return handler.Double(data_.n.d); + else if (IsInt()) return handler.Int(data_.n.i.i); + else if (IsUint()) return handler.Uint(data_.n.u.u); + else if (IsInt64()) return handler.Int64(data_.n.i64); + else return handler.Uint64(data_.n.u64); + } + } + +private: + template friend class GenericValue; + template friend class GenericDocument; + + enum { + kBoolFlag = 0x0008, + kNumberFlag = 0x0010, + kIntFlag = 0x0020, + kUintFlag = 0x0040, + kInt64Flag = 0x0080, + kUint64Flag = 0x0100, + kDoubleFlag = 0x0200, + kStringFlag = 0x0400, + kCopyFlag = 0x0800, + kInlineStrFlag = 0x1000, + + // Initial flags of different types. + kNullFlag = kNullType, + // These casts are added to suppress the warning on MSVC about bitwise operations between enums of different types. + kTrueFlag = static_cast(kTrueType) | static_cast(kBoolFlag), + kFalseFlag = static_cast(kFalseType) | static_cast(kBoolFlag), + kNumberIntFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kIntFlag | kInt64Flag), + kNumberUintFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kUintFlag | kUint64Flag | kInt64Flag), + kNumberInt64Flag = static_cast(kNumberType) | static_cast(kNumberFlag | kInt64Flag), + kNumberUint64Flag = static_cast(kNumberType) | static_cast(kNumberFlag | kUint64Flag), + kNumberDoubleFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kDoubleFlag), + kNumberAnyFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kIntFlag | kInt64Flag | kUintFlag | kUint64Flag | kDoubleFlag), + kConstStringFlag = static_cast(kStringType) | static_cast(kStringFlag), + kCopyStringFlag = static_cast(kStringType) | static_cast(kStringFlag | kCopyFlag), + kShortStringFlag = static_cast(kStringType) | static_cast(kStringFlag | kCopyFlag | kInlineStrFlag), + kObjectFlag = kObjectType, + kArrayFlag = kArrayType, + + kTypeMask = 0x07 + }; + + static const SizeType kDefaultArrayCapacity = RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY; + static const SizeType kDefaultObjectCapacity = RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY; + + struct Flag { +#if RAPIDJSON_48BITPOINTER_OPTIMIZATION + char payload[sizeof(SizeType) * 2 + 6]; // 2 x SizeType + lower 48-bit pointer +#elif RAPIDJSON_64BIT + char payload[sizeof(SizeType) * 2 + sizeof(void*) + 6]; // 6 padding bytes +#else + char payload[sizeof(SizeType) * 2 + sizeof(void*) + 2]; // 2 padding bytes +#endif + uint16_t flags; + }; + + struct String { + SizeType length; + SizeType hashcode; //!< reserved + const Ch* str; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + // implementation detail: ShortString can represent zero-terminated strings up to MaxSize chars + // (excluding the terminating zero) and store a value to determine the length of the contained + // string in the last character str[LenPos] by storing "MaxSize - length" there. If the string + // to store has the maximal length of MaxSize then str[LenPos] will be 0 and therefore act as + // the string terminator as well. For getting the string length back from that value just use + // "MaxSize - str[LenPos]". + // This allows to store 13-chars strings in 32-bit mode, 21-chars strings in 64-bit mode, + // 13-chars strings for RAPIDJSON_48BITPOINTER_OPTIMIZATION=1 inline (for `UTF8`-encoded strings). + struct ShortString { + enum { MaxChars = sizeof(static_cast(0)->payload) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize }; + Ch str[MaxChars]; + + inline static bool Usable(SizeType len) { return (MaxSize >= len); } + inline void SetLength(SizeType len) { str[LenPos] = static_cast(MaxSize - len); } + inline SizeType GetLength() const { return static_cast(MaxSize - str[LenPos]); } + }; // at most as many bytes as "String" above => 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + // By using proper binary layout, retrieval of different integer types do not need conversions. + union Number { +#if RAPIDJSON_ENDIAN == RAPIDJSON_LITTLEENDIAN + struct I { + int i; + char padding[4]; + }i; + struct U { + unsigned u; + char padding2[4]; + }u; +#else + struct I { + char padding[4]; + int i; + }i; + struct U { + char padding2[4]; + unsigned u; + }u; +#endif + int64_t i64; + uint64_t u64; + double d; + }; // 8 bytes + + struct ObjectData { + SizeType size; + SizeType capacity; + Member* members; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + struct ArrayData { + SizeType size; + SizeType capacity; + GenericValue* elements; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + union Data { + String s; + ShortString ss; + Number n; + ObjectData o; + ArrayData a; + Flag f; + }; // 16 bytes in 32-bit mode, 24 bytes in 64-bit mode, 16 bytes in 64-bit with RAPIDJSON_48BITPOINTER_OPTIMIZATION + + RAPIDJSON_FORCEINLINE const Ch* GetStringPointer() const { return RAPIDJSON_GETPOINTER(Ch, data_.s.str); } + RAPIDJSON_FORCEINLINE const Ch* SetStringPointer(const Ch* str) { return RAPIDJSON_SETPOINTER(Ch, data_.s.str, str); } + RAPIDJSON_FORCEINLINE GenericValue* GetElementsPointer() const { return RAPIDJSON_GETPOINTER(GenericValue, data_.a.elements); } + RAPIDJSON_FORCEINLINE GenericValue* SetElementsPointer(GenericValue* elements) { return RAPIDJSON_SETPOINTER(GenericValue, data_.a.elements, elements); } + RAPIDJSON_FORCEINLINE Member* GetMembersPointer() const { return RAPIDJSON_GETPOINTER(Member, data_.o.members); } + RAPIDJSON_FORCEINLINE Member* SetMembersPointer(Member* members) { return RAPIDJSON_SETPOINTER(Member, data_.o.members, members); } + + // Initialize this value as array with initial data, without calling destructor. + void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) { + data_.f.flags = kArrayFlag; + if (count) { + GenericValue* e = static_cast(allocator.Malloc(count * sizeof(GenericValue))); + SetElementsPointer(e); + std::memcpy(static_cast(e), values, count * sizeof(GenericValue)); + } + else + SetElementsPointer(0); + data_.a.size = data_.a.capacity = count; + } + + //! Initialize this value as object with initial data, without calling destructor. + void SetObjectRaw(Member* members, SizeType count, Allocator& allocator) { + data_.f.flags = kObjectFlag; + if (count) { + Member* m = static_cast(allocator.Malloc(count * sizeof(Member))); + SetMembersPointer(m); + std::memcpy(static_cast(m), members, count * sizeof(Member)); + } + else + SetMembersPointer(0); + data_.o.size = data_.o.capacity = count; + } + + //! Initialize this value as constant string, without calling destructor. + void SetStringRaw(StringRefType s) RAPIDJSON_NOEXCEPT { + data_.f.flags = kConstStringFlag; + SetStringPointer(s); + data_.s.length = s.length; + } + + //! Initialize this value as copy string with initial data, without calling destructor. + void SetStringRaw(StringRefType s, Allocator& allocator) { + Ch* str = 0; + if (ShortString::Usable(s.length)) { + data_.f.flags = kShortStringFlag; + data_.ss.SetLength(s.length); + str = data_.ss.str; + } else { + data_.f.flags = kCopyStringFlag; + data_.s.length = s.length; + str = static_cast(allocator.Malloc((s.length + 1) * sizeof(Ch))); + SetStringPointer(str); + } + std::memcpy(str, s, s.length * sizeof(Ch)); + str[s.length] = '\0'; + } + + //! Assignment without calling destructor + void RawAssign(GenericValue& rhs) RAPIDJSON_NOEXCEPT { + data_ = rhs.data_; + // data_.f.flags = rhs.data_.f.flags; + rhs.data_.f.flags = kNullFlag; + } + + template + bool StringEqual(const GenericValue& rhs) const { + RAPIDJSON_ASSERT(IsString()); + RAPIDJSON_ASSERT(rhs.IsString()); + + const SizeType len1 = GetStringLength(); + const SizeType len2 = rhs.GetStringLength(); + if(len1 != len2) { return false; } + + const Ch* const str1 = GetString(); + const Ch* const str2 = rhs.GetString(); + if(str1 == str2) { return true; } // fast path for constant string + + return (std::memcmp(str1, str2, sizeof(Ch) * len1) == 0); + } + + Data data_; +}; + +//! GenericValue with UTF8 encoding +typedef GenericValue > Value; + +/////////////////////////////////////////////////////////////////////////////// +// GenericDocument + +//! A document for parsing JSON text as DOM. +/*! + \note implements Handler concept + \tparam Encoding Encoding for both parsing and string storage. + \tparam Allocator Allocator for allocating memory for the DOM + \tparam StackAllocator Allocator for allocating memory for stack during parsing. + \warning Although GenericDocument inherits from GenericValue, the API does \b not provide any virtual functions, especially no virtual destructor. To avoid memory leaks, do not \c delete a GenericDocument object via a pointer to a GenericValue. +*/ +template +class GenericDocument : public GenericValue { +public: + typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. + typedef GenericValue ValueType; //!< Value type of the document. + typedef Allocator AllocatorType; //!< Allocator type from template parameter. + + //! Constructor + /*! Creates an empty document of specified type. + \param type Mandatory type of object to create. + \param allocator Optional allocator for allocating memory. + \param stackCapacity Optional initial capacity of stack in bytes. + \param stackAllocator Optional allocator for allocating memory for stack. + */ + explicit GenericDocument(Type type, Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : + GenericValue(type), allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() + { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + } + + //! Constructor + /*! Creates an empty document which type is Null. + \param allocator Optional allocator for allocating memory. + \param stackCapacity Optional initial capacity of stack in bytes. + \param stackAllocator Optional allocator for allocating memory for stack. + */ + GenericDocument(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : + allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() + { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericDocument(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT + : ValueType(std::forward(rhs)), // explicit cast to avoid prohibited move from Document + allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + stack_(std::move(rhs.stack_)), + parseResult_(rhs.parseResult_) + { + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.parseResult_ = ParseResult(); + } +#endif + + ~GenericDocument() { + Destroy(); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move assignment in C++11 + GenericDocument& operator=(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT + { + // The cast to ValueType is necessary here, because otherwise it would + // attempt to call GenericValue's templated assignment operator. + ValueType::operator=(std::forward(rhs)); + + // Calling the destructor here would prematurely call stack_'s destructor + Destroy(); + + allocator_ = rhs.allocator_; + ownAllocator_ = rhs.ownAllocator_; + stack_ = std::move(rhs.stack_); + parseResult_ = rhs.parseResult_; + + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.parseResult_ = ParseResult(); + + return *this; + } +#endif + + //! Exchange the contents of this document with those of another. + /*! + \param rhs Another document. + \note Constant complexity. + \see GenericValue::Swap + */ + GenericDocument& Swap(GenericDocument& rhs) RAPIDJSON_NOEXCEPT { + ValueType::Swap(rhs); + stack_.Swap(rhs.stack_); + internal::Swap(allocator_, rhs.allocator_); + internal::Swap(ownAllocator_, rhs.ownAllocator_); + internal::Swap(parseResult_, rhs.parseResult_); + return *this; + } + + // Allow Swap with ValueType. + // Refer to Effective C++ 3rd Edition/Item 33: Avoid hiding inherited names. + using ValueType::Swap; + + //! free-standing swap function helper + /*! + Helper function to enable support for common swap implementation pattern based on \c std::swap: + \code + void swap(MyClass& a, MyClass& b) { + using std::swap; + swap(a.doc, b.doc); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericDocument& a, GenericDocument& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } + + //! Populate this document by a generator which produces SAX events. + /*! \tparam Generator A functor with bool f(Handler) prototype. + \param g Generator functor which sends SAX events to the parameter. + \return The document itself for fluent API. + */ + template + GenericDocument& Populate(Generator& g) { + ClearStackOnExit scope(*this); + if (g(*this)) { + RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object + ValueType::operator=(*stack_.template Pop(1));// Move value from stack to document + } + return *this; + } + + //!@name Parse from stream + //!@{ + + //! Parse JSON text from an input stream (with Encoding conversion) + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam SourceEncoding Encoding of input stream + \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + GenericReader reader( + stack_.HasAllocator() ? &stack_.GetAllocator() : 0); + ClearStackOnExit scope(*this); + parseResult_ = reader.template Parse(is, *this); + if (parseResult_) { + RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object + ValueType::operator=(*stack_.template Pop(1));// Move value from stack to document + } + return *this; + } + + //! Parse JSON text from an input stream + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + return ParseStream(is); + } + + //! Parse JSON text from an input stream (with \ref kParseDefaultFlags) + /*! \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + return ParseStream(is); + } + //!@} + + //!@name Parse in-place from mutable string + //!@{ + + //! Parse JSON text from a mutable string + /*! \tparam parseFlags Combination of \ref ParseFlag. + \param str Mutable zero-terminated string to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseInsitu(Ch* str) { + GenericInsituStringStream s(str); + return ParseStream(s); + } + + //! Parse JSON text from a mutable string (with \ref kParseDefaultFlags) + /*! \param str Mutable zero-terminated string to be parsed. + \return The document itself for fluent API. + */ + GenericDocument& ParseInsitu(Ch* str) { + return ParseInsitu(str); + } + //!@} + + //!@name Parse from read-only string + //!@{ + + //! Parse JSON text from a read-only string (with Encoding conversion) + /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). + \tparam SourceEncoding Transcoding from input Encoding + \param str Read-only zero-terminated string to be parsed. + */ + template + GenericDocument& Parse(const typename SourceEncoding::Ch* str) { + RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); + GenericStringStream s(str); + return ParseStream(s); + } + + //! Parse JSON text from a read-only string + /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). + \param str Read-only zero-terminated string to be parsed. + */ + template + GenericDocument& Parse(const Ch* str) { + return Parse(str); + } + + //! Parse JSON text from a read-only string (with \ref kParseDefaultFlags) + /*! \param str Read-only zero-terminated string to be parsed. + */ + GenericDocument& Parse(const Ch* str) { + return Parse(str); + } + + template + GenericDocument& Parse(const typename SourceEncoding::Ch* str, size_t length) { + RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); + MemoryStream ms(reinterpret_cast(str), length * sizeof(typename SourceEncoding::Ch)); + EncodedInputStream is(ms); + ParseStream(is); + return *this; + } + + template + GenericDocument& Parse(const Ch* str, size_t length) { + return Parse(str, length); + } + + GenericDocument& Parse(const Ch* str, size_t length) { + return Parse(str, length); + } + +#if RAPIDJSON_HAS_STDSTRING + template + GenericDocument& Parse(const std::basic_string& str) { + // c_str() is constant complexity according to standard. Should be faster than Parse(const char*, size_t) + return Parse(str.c_str()); + } + + template + GenericDocument& Parse(const std::basic_string& str) { + return Parse(str.c_str()); + } + + GenericDocument& Parse(const std::basic_string& str) { + return Parse(str); + } +#endif // RAPIDJSON_HAS_STDSTRING + + //!@} + + //!@name Handling parse errors + //!@{ + + //! Whether a parse error has occurred in the last parsing. + bool HasParseError() const { return parseResult_.IsError(); } + + //! Get the \ref ParseErrorCode of last parsing. + ParseErrorCode GetParseError() const { return parseResult_.Code(); } + + //! Get the position of last parsing error in input, 0 otherwise. + size_t GetErrorOffset() const { return parseResult_.Offset(); } + + //! Implicit conversion to get the last parse result +#ifndef __clang // -Wdocumentation + /*! \return \ref ParseResult of the last parse operation + + \code + Document doc; + ParseResult ok = doc.Parse(json); + if (!ok) + printf( "JSON parse error: %s (%u)\n", GetParseError_En(ok.Code()), ok.Offset()); + \endcode + */ +#endif + operator ParseResult() const { return parseResult_; } + //!@} + + //! Get the allocator of this document. + Allocator& GetAllocator() { + RAPIDJSON_ASSERT(allocator_); + return *allocator_; + } + + //! Get the capacity of stack in bytes. + size_t GetStackCapacity() const { return stack_.GetCapacity(); } + +private: + // clear stack on any exit from ParseStream, e.g. due to exception + struct ClearStackOnExit { + explicit ClearStackOnExit(GenericDocument& d) : d_(d) {} + ~ClearStackOnExit() { d_.ClearStack(); } + private: + ClearStackOnExit(const ClearStackOnExit&); + ClearStackOnExit& operator=(const ClearStackOnExit&); + GenericDocument& d_; + }; + + // callers of the following private Handler functions + // template friend class GenericReader; // for parsing + template friend class GenericValue; // for deep copying + +public: + // Implementation of Handler + bool Null() { new (stack_.template Push()) ValueType(); return true; } + bool Bool(bool b) { new (stack_.template Push()) ValueType(b); return true; } + bool Int(int i) { new (stack_.template Push()) ValueType(i); return true; } + bool Uint(unsigned i) { new (stack_.template Push()) ValueType(i); return true; } + bool Int64(int64_t i) { new (stack_.template Push()) ValueType(i); return true; } + bool Uint64(uint64_t i) { new (stack_.template Push()) ValueType(i); return true; } + bool Double(double d) { new (stack_.template Push()) ValueType(d); return true; } + + bool RawNumber(const Ch* str, SizeType length, bool copy) { + if (copy) + new (stack_.template Push()) ValueType(str, length, GetAllocator()); + else + new (stack_.template Push()) ValueType(str, length); + return true; + } + + bool String(const Ch* str, SizeType length, bool copy) { + if (copy) + new (stack_.template Push()) ValueType(str, length, GetAllocator()); + else + new (stack_.template Push()) ValueType(str, length); + return true; + } + + bool StartObject() { new (stack_.template Push()) ValueType(kObjectType); return true; } + + bool Key(const Ch* str, SizeType length, bool copy) { return String(str, length, copy); } + + bool EndObject(SizeType memberCount) { + typename ValueType::Member* members = stack_.template Pop(memberCount); + stack_.template Top()->SetObjectRaw(members, memberCount, GetAllocator()); + return true; + } + + bool StartArray() { new (stack_.template Push()) ValueType(kArrayType); return true; } + + bool EndArray(SizeType elementCount) { + ValueType* elements = stack_.template Pop(elementCount); + stack_.template Top()->SetArrayRaw(elements, elementCount, GetAllocator()); + return true; + } + +private: + //! Prohibit copying + GenericDocument(const GenericDocument&); + //! Prohibit assignment + GenericDocument& operator=(const GenericDocument&); + + void ClearStack() { + if (Allocator::kNeedFree) + while (stack_.GetSize() > 0) // Here assumes all elements in stack array are GenericValue (Member is actually 2 GenericValue objects) + (stack_.template Pop(1))->~ValueType(); + else + stack_.Clear(); + stack_.ShrinkToFit(); + } + + void Destroy() { + RAPIDJSON_DELETE(ownAllocator_); + } + + static const size_t kDefaultStackCapacity = 1024; + Allocator* allocator_; + Allocator* ownAllocator_; + internal::Stack stack_; + ParseResult parseResult_; +}; + +//! GenericDocument with UTF8 encoding +typedef GenericDocument > Document; + + +//! Helper class for accessing Value of array type. +/*! + Instance of this helper class is obtained by \c GenericValue::GetArray(). + In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1. +*/ +template +class GenericArray { +public: + typedef GenericArray ConstArray; + typedef GenericArray Array; + typedef ValueT PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + typedef ValueType* ValueIterator; // This may be const or non-const iterator + typedef const ValueT* ConstValueIterator; + typedef typename ValueType::AllocatorType AllocatorType; + typedef typename ValueType::StringRefType StringRefType; + + template + friend class GenericValue; + + GenericArray(const GenericArray& rhs) : value_(rhs.value_) {} + GenericArray& operator=(const GenericArray& rhs) { value_ = rhs.value_; return *this; } + ~GenericArray() {} + + operator ValueType&() const { return value_; } + SizeType Size() const { return value_.Size(); } + SizeType Capacity() const { return value_.Capacity(); } + bool Empty() const { return value_.Empty(); } + void Clear() const { value_.Clear(); } + ValueType& operator[](SizeType index) const { return value_[index]; } + ValueIterator Begin() const { return value_.Begin(); } + ValueIterator End() const { return value_.End(); } + GenericArray Reserve(SizeType newCapacity, AllocatorType &allocator) const { value_.Reserve(newCapacity, allocator); return *this; } + GenericArray PushBack(ValueType& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericArray PushBack(ValueType&& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericArray PushBack(StringRefType value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (const GenericArray&)) PushBack(T value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } + GenericArray PopBack() const { value_.PopBack(); return *this; } + ValueIterator Erase(ConstValueIterator pos) const { return value_.Erase(pos); } + ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) const { return value_.Erase(first, last); } + +#if RAPIDJSON_HAS_CXX11_RANGE_FOR + ValueIterator begin() const { return value_.Begin(); } + ValueIterator end() const { return value_.End(); } +#endif + +private: + GenericArray(); + GenericArray(ValueType& value) : value_(value) {} + ValueType& value_; +}; + +//! Helper class for accessing Value of object type. +/*! + Instance of this helper class is obtained by \c GenericValue::GetObject(). + In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1. +*/ +template +class GenericObject { +public: + typedef GenericObject ConstObject; + typedef GenericObject Object; + typedef ValueT PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + typedef GenericMemberIterator MemberIterator; // This may be const or non-const iterator + typedef GenericMemberIterator ConstMemberIterator; + typedef typename ValueType::AllocatorType AllocatorType; + typedef typename ValueType::StringRefType StringRefType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename ValueType::Ch Ch; + + template + friend class GenericValue; + + GenericObject(const GenericObject& rhs) : value_(rhs.value_) {} + GenericObject& operator=(const GenericObject& rhs) { value_ = rhs.value_; return *this; } + ~GenericObject() {} + + operator ValueType&() const { return value_; } + SizeType MemberCount() const { return value_.MemberCount(); } + SizeType MemberCapacity() const { return value_.MemberCapacity(); } + bool ObjectEmpty() const { return value_.ObjectEmpty(); } + template ValueType& operator[](T* name) const { return value_[name]; } + template ValueType& operator[](const GenericValue& name) const { return value_[name]; } +#if RAPIDJSON_HAS_STDSTRING + ValueType& operator[](const std::basic_string& name) const { return value_[name]; } +#endif + MemberIterator MemberBegin() const { return value_.MemberBegin(); } + MemberIterator MemberEnd() const { return value_.MemberEnd(); } + GenericObject MemberReserve(SizeType newCapacity, AllocatorType &allocator) const { value_.MemberReserve(newCapacity, allocator); return *this; } + bool HasMember(const Ch* name) const { return value_.HasMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool HasMember(const std::basic_string& name) const { return value_.HasMember(name); } +#endif + template bool HasMember(const GenericValue& name) const { return value_.HasMember(name); } + MemberIterator FindMember(const Ch* name) const { return value_.FindMember(name); } + template MemberIterator FindMember(const GenericValue& name) const { return value_.FindMember(name); } +#if RAPIDJSON_HAS_STDSTRING + MemberIterator FindMember(const std::basic_string& name) const { return value_.FindMember(name); } +#endif + GenericObject AddMember(ValueType& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType& name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#if RAPIDJSON_HAS_STDSTRING + GenericObject AddMember(ValueType& name, std::basic_string& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#endif + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) AddMember(ValueType& name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericObject AddMember(ValueType&& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType&& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(StringRefType name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericObject AddMember(StringRefType name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(StringRefType name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericObject)) AddMember(StringRefType name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + void RemoveAllMembers() { value_.RemoveAllMembers(); } + bool RemoveMember(const Ch* name) const { return value_.RemoveMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool RemoveMember(const std::basic_string& name) const { return value_.RemoveMember(name); } +#endif + template bool RemoveMember(const GenericValue& name) const { return value_.RemoveMember(name); } + MemberIterator RemoveMember(MemberIterator m) const { return value_.RemoveMember(m); } + MemberIterator EraseMember(ConstMemberIterator pos) const { return value_.EraseMember(pos); } + MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) const { return value_.EraseMember(first, last); } + bool EraseMember(const Ch* name) const { return value_.EraseMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool EraseMember(const std::basic_string& name) const { return EraseMember(ValueType(StringRef(name))); } +#endif + template bool EraseMember(const GenericValue& name) const { return value_.EraseMember(name); } + +#if RAPIDJSON_HAS_CXX11_RANGE_FOR + MemberIterator begin() const { return value_.MemberBegin(); } + MemberIterator end() const { return value_.MemberEnd(); } +#endif + +private: + GenericObject(); + GenericObject(ValueType& value) : value_(value) {} + ValueType& value_; +}; + +RAPIDJSON_NAMESPACE_END +RAPIDJSON_DIAG_POP + +#endif // RAPIDJSON_DOCUMENT_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/encodedstream.h b/libraries/libstratosphere/include/stratosphere/rapidjson/encodedstream.h new file mode 100644 index 000000000..cf046b892 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/encodedstream.h @@ -0,0 +1,299 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ENCODEDSTREAM_H_ +#define RAPIDJSON_ENCODEDSTREAM_H_ + +#include "stream.h" +#include "memorystream.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Input byte stream wrapper with a statically bound encoding. +/*! + \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. + \tparam InputByteStream Type of input byte stream. For example, FileReadStream. +*/ +template +class EncodedInputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); +public: + typedef typename Encoding::Ch Ch; + + EncodedInputStream(InputByteStream& is) : is_(is) { + current_ = Encoding::TakeBOM(is_); + } + + Ch Peek() const { return current_; } + Ch Take() { Ch c = current_; current_ = Encoding::Take(is_); return c; } + size_t Tell() const { return is_.Tell(); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + EncodedInputStream(const EncodedInputStream&); + EncodedInputStream& operator=(const EncodedInputStream&); + + InputByteStream& is_; + Ch current_; +}; + +//! Specialized for UTF8 MemoryStream. +template <> +class EncodedInputStream, MemoryStream> { +public: + typedef UTF8<>::Ch Ch; + + EncodedInputStream(MemoryStream& is) : is_(is) { + if (static_cast(is_.Peek()) == 0xEFu) is_.Take(); + if (static_cast(is_.Peek()) == 0xBBu) is_.Take(); + if (static_cast(is_.Peek()) == 0xBFu) is_.Take(); + } + Ch Peek() const { return is_.Peek(); } + Ch Take() { return is_.Take(); } + size_t Tell() const { return is_.Tell(); } + + // Not implemented + void Put(Ch) {} + void Flush() {} + Ch* PutBegin() { return 0; } + size_t PutEnd(Ch*) { return 0; } + + MemoryStream& is_; + +private: + EncodedInputStream(const EncodedInputStream&); + EncodedInputStream& operator=(const EncodedInputStream&); +}; + +//! Output byte stream wrapper with statically bound encoding. +/*! + \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. + \tparam OutputByteStream Type of input byte stream. For example, FileWriteStream. +*/ +template +class EncodedOutputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); +public: + typedef typename Encoding::Ch Ch; + + EncodedOutputStream(OutputByteStream& os, bool putBOM = true) : os_(os) { + if (putBOM) + Encoding::PutBOM(os_); + } + + void Put(Ch c) { Encoding::Put(os_, c); } + void Flush() { os_.Flush(); } + + // Not implemented + Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;} + Ch Take() { RAPIDJSON_ASSERT(false); return 0;} + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + EncodedOutputStream(const EncodedOutputStream&); + EncodedOutputStream& operator=(const EncodedOutputStream&); + + OutputByteStream& os_; +}; + +#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x + +//! Input stream wrapper with dynamically bound encoding and automatic encoding detection. +/*! + \tparam CharType Type of character for reading. + \tparam InputByteStream type of input byte stream to be wrapped. +*/ +template +class AutoUTFInputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); +public: + typedef CharType Ch; + + //! Constructor. + /*! + \param is input stream to be wrapped. + \param type UTF encoding type if it is not detected from the stream. + */ + AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) { + RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); + DetectType(); + static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) }; + takeFunc_ = f[type_]; + current_ = takeFunc_(*is_); + } + + UTFType GetType() const { return type_; } + bool HasBOM() const { return hasBOM_; } + + Ch Peek() const { return current_; } + Ch Take() { Ch c = current_; current_ = takeFunc_(*is_); return c; } + size_t Tell() const { return is_->Tell(); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + AutoUTFInputStream(const AutoUTFInputStream&); + AutoUTFInputStream& operator=(const AutoUTFInputStream&); + + // Detect encoding type with BOM or RFC 4627 + void DetectType() { + // BOM (Byte Order Mark): + // 00 00 FE FF UTF-32BE + // FF FE 00 00 UTF-32LE + // FE FF UTF-16BE + // FF FE UTF-16LE + // EF BB BF UTF-8 + + const unsigned char* c = reinterpret_cast(is_->Peek4()); + if (!c) + return; + + unsigned bom = static_cast(c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24)); + hasBOM_ = false; + if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } + else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } + else if ((bom & 0xFFFF) == 0xFFFE) { type_ = kUTF16BE; hasBOM_ = true; is_->Take(); is_->Take(); } + else if ((bom & 0xFFFF) == 0xFEFF) { type_ = kUTF16LE; hasBOM_ = true; is_->Take(); is_->Take(); } + else if ((bom & 0xFFFFFF) == 0xBFBBEF) { type_ = kUTF8; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); } + + // RFC 4627: Section 3 + // "Since the first two characters of a JSON text will always be ASCII + // characters [RFC0020], it is possible to determine whether an octet + // stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking + // at the pattern of nulls in the first four octets." + // 00 00 00 xx UTF-32BE + // 00 xx 00 xx UTF-16BE + // xx 00 00 00 UTF-32LE + // xx 00 xx 00 UTF-16LE + // xx xx xx xx UTF-8 + + if (!hasBOM_) { + int pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0); + switch (pattern) { + case 0x08: type_ = kUTF32BE; break; + case 0x0A: type_ = kUTF16BE; break; + case 0x01: type_ = kUTF32LE; break; + case 0x05: type_ = kUTF16LE; break; + case 0x0F: type_ = kUTF8; break; + default: break; // Use type defined by user. + } + } + + // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. + if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); + if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4); + } + + typedef Ch (*TakeFunc)(InputByteStream& is); + InputByteStream* is_; + UTFType type_; + Ch current_; + TakeFunc takeFunc_; + bool hasBOM_; +}; + +//! Output stream wrapper with dynamically bound encoding and automatic encoding detection. +/*! + \tparam CharType Type of character for writing. + \tparam OutputByteStream type of output byte stream to be wrapped. +*/ +template +class AutoUTFOutputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); +public: + typedef CharType Ch; + + //! Constructor. + /*! + \param os output stream to be wrapped. + \param type UTF encoding type. + \param putBOM Whether to write BOM at the beginning of the stream. + */ + AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) { + RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); + + // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. + if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); + if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4); + + static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) }; + putFunc_ = f[type_]; + + if (putBOM) + PutBOM(); + } + + UTFType GetType() const { return type_; } + + void Put(Ch c) { putFunc_(*os_, c); } + void Flush() { os_->Flush(); } + + // Not implemented + Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;} + Ch Take() { RAPIDJSON_ASSERT(false); return 0;} + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + AutoUTFOutputStream(const AutoUTFOutputStream&); + AutoUTFOutputStream& operator=(const AutoUTFOutputStream&); + + void PutBOM() { + typedef void (*PutBOMFunc)(OutputByteStream&); + static const PutBOMFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(PutBOM) }; + f[type_](*os_); + } + + typedef void (*PutFunc)(OutputByteStream&, Ch); + + OutputByteStream* os_; + UTFType type_; + PutFunc putFunc_; +}; + +#undef RAPIDJSON_ENCODINGS_FUNC + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/encodings.h b/libraries/libstratosphere/include/stratosphere/rapidjson/encodings.h new file mode 100644 index 000000000..50ad18bdc --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/encodings.h @@ -0,0 +1,716 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ENCODINGS_H_ +#define RAPIDJSON_ENCODINGS_H_ + +#include "rapidjson.h" + +#if defined(_MSC_VER) && !defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data +RAPIDJSON_DIAG_OFF(4702) // unreachable code +#elif defined(__GNUC__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +RAPIDJSON_DIAG_OFF(overflow) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Encoding + +/*! \class rapidjson::Encoding + \brief Concept for encoding of Unicode characters. + +\code +concept Encoding { + typename Ch; //! Type of character. A "character" is actually a code unit in unicode's definition. + + enum { supportUnicode = 1 }; // or 0 if not supporting unicode + + //! \brief Encode a Unicode codepoint to an output stream. + //! \param os Output stream. + //! \param codepoint An unicode codepoint, ranging from 0x0 to 0x10FFFF inclusively. + template + static void Encode(OutputStream& os, unsigned codepoint); + + //! \brief Decode a Unicode codepoint from an input stream. + //! \param is Input stream. + //! \param codepoint Output of the unicode codepoint. + //! \return true if a valid codepoint can be decoded from the stream. + template + static bool Decode(InputStream& is, unsigned* codepoint); + + //! \brief Validate one Unicode codepoint from an encoded stream. + //! \param is Input stream to obtain codepoint. + //! \param os Output for copying one codepoint. + //! \return true if it is valid. + //! \note This function just validating and copying the codepoint without actually decode it. + template + static bool Validate(InputStream& is, OutputStream& os); + + // The following functions are deal with byte streams. + + //! Take a character from input byte stream, skip BOM if exist. + template + static CharType TakeBOM(InputByteStream& is); + + //! Take a character from input byte stream. + template + static Ch Take(InputByteStream& is); + + //! Put BOM to output byte stream. + template + static void PutBOM(OutputByteStream& os); + + //! Put a character to output byte stream. + template + static void Put(OutputByteStream& os, Ch c); +}; +\endcode +*/ + +/////////////////////////////////////////////////////////////////////////////// +// UTF8 + +//! UTF-8 encoding. +/*! http://en.wikipedia.org/wiki/UTF-8 + http://tools.ietf.org/html/rfc3629 + \tparam CharType Code unit for storing 8-bit UTF-8 data. Default is char. + \note implements Encoding concept +*/ +template +struct UTF8 { + typedef CharType Ch; + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + if (codepoint <= 0x7F) + os.Put(static_cast(codepoint & 0xFF)); + else if (codepoint <= 0x7FF) { + os.Put(static_cast(0xC0 | ((codepoint >> 6) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint & 0x3F)))); + } + else if (codepoint <= 0xFFFF) { + os.Put(static_cast(0xE0 | ((codepoint >> 12) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + os.Put(static_cast(0x80 | (codepoint & 0x3F))); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + os.Put(static_cast(0xF0 | ((codepoint >> 18) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint >> 12) & 0x3F))); + os.Put(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + os.Put(static_cast(0x80 | (codepoint & 0x3F))); + } + } + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + if (codepoint <= 0x7F) + PutUnsafe(os, static_cast(codepoint & 0xFF)); + else if (codepoint <= 0x7FF) { + PutUnsafe(os, static_cast(0xC0 | ((codepoint >> 6) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint & 0x3F)))); + } + else if (codepoint <= 0xFFFF) { + PutUnsafe(os, static_cast(0xE0 | ((codepoint >> 12) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | (codepoint & 0x3F))); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + PutUnsafe(os, static_cast(0xF0 | ((codepoint >> 18) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 12) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | (codepoint & 0x3F))); + } + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { +#define RAPIDJSON_COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast(c) & 0x3Fu) +#define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) +#define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70) + typename InputStream::Ch c = is.Take(); + if (!(c & 0x80)) { + *codepoint = static_cast(c); + return true; + } + + unsigned char type = GetRange(static_cast(c)); + if (type >= 32) { + *codepoint = 0; + } else { + *codepoint = (0xFFu >> type) & static_cast(c); + } + bool result = true; + switch (type) { + case 2: RAPIDJSON_TAIL(); return result; + case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result; + case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result; + case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + default: return false; + } +#undef RAPIDJSON_COPY +#undef RAPIDJSON_TRANS +#undef RAPIDJSON_TAIL + } + + template + static bool Validate(InputStream& is, OutputStream& os) { +#define RAPIDJSON_COPY() os.Put(c = is.Take()) +#define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) +#define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70) + Ch c; + RAPIDJSON_COPY(); + if (!(c & 0x80)) + return true; + + bool result = true; + switch (GetRange(static_cast(c))) { + case 2: RAPIDJSON_TAIL(); return result; + case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result; + case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result; + case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + default: return false; + } +#undef RAPIDJSON_COPY +#undef RAPIDJSON_TRANS +#undef RAPIDJSON_TAIL + } + + static unsigned char GetRange(unsigned char c) { + // Referring to DFA of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ + // With new mapping 1 -> 0x10, 7 -> 0x20, 9 -> 0x40, such that AND operation can test multiple types. + static const unsigned char type[] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, + }; + return type[c]; + } + + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + typename InputByteStream::Ch c = Take(is); + if (static_cast(c) != 0xEFu) return c; + c = is.Take(); + if (static_cast(c) != 0xBBu) return c; + c = is.Take(); + if (static_cast(c) != 0xBFu) return c; + c = is.Take(); + return c; + } + + template + static Ch Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + return static_cast(is.Take()); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xEFu)); + os.Put(static_cast(0xBBu)); + os.Put(static_cast(0xBFu)); + } + + template + static void Put(OutputByteStream& os, Ch c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(c)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// UTF16 + +//! UTF-16 encoding. +/*! http://en.wikipedia.org/wiki/UTF-16 + http://tools.ietf.org/html/rfc2781 + \tparam CharType Type for storing 16-bit UTF-16 data. Default is wchar_t. C++11 may use char16_t instead. + \note implements Encoding concept + + \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. + For streaming, use UTF16LE and UTF16BE, which handle endianness. +*/ +template +struct UTF16 { + typedef CharType Ch; + RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2); + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + if (codepoint <= 0xFFFF) { + RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair + os.Put(static_cast(codepoint)); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + unsigned v = codepoint - 0x10000; + os.Put(static_cast((v >> 10) | 0xD800)); + os.Put(static_cast((v & 0x3FF) | 0xDC00)); + } + } + + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + if (codepoint <= 0xFFFF) { + RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair + PutUnsafe(os, static_cast(codepoint)); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + unsigned v = codepoint - 0x10000; + PutUnsafe(os, static_cast((v >> 10) | 0xD800)); + PutUnsafe(os, static_cast((v & 0x3FF) | 0xDC00)); + } + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); + typename InputStream::Ch c = is.Take(); + if (c < 0xD800 || c > 0xDFFF) { + *codepoint = static_cast(c); + return true; + } + else if (c <= 0xDBFF) { + *codepoint = (static_cast(c) & 0x3FF) << 10; + c = is.Take(); + *codepoint |= (static_cast(c) & 0x3FF); + *codepoint += 0x10000; + return c >= 0xDC00 && c <= 0xDFFF; + } + return false; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + typename InputStream::Ch c; + os.Put(static_cast(c = is.Take())); + if (c < 0xD800 || c > 0xDFFF) + return true; + else if (c <= 0xDBFF) { + os.Put(c = is.Take()); + return c >= 0xDC00 && c <= 0xDFFF; + } + return false; + } +}; + +//! UTF-16 little endian encoding. +template +struct UTF16LE : UTF16 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0xFEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(is.Take()); + c |= static_cast(static_cast(is.Take())) << 8; + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xFFu)); + os.Put(static_cast(0xFEu)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(static_cast(c) & 0xFFu)); + os.Put(static_cast((static_cast(c) >> 8) & 0xFFu)); + } +}; + +//! UTF-16 big endian encoding. +template +struct UTF16BE : UTF16 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0xFEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(static_cast(is.Take())) << 8; + c |= static_cast(static_cast(is.Take())); + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0xFFu)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast((static_cast(c) >> 8) & 0xFFu)); + os.Put(static_cast(static_cast(c) & 0xFFu)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// UTF32 + +//! UTF-32 encoding. +/*! http://en.wikipedia.org/wiki/UTF-32 + \tparam CharType Type for storing 32-bit UTF-32 data. Default is unsigned. C++11 may use char32_t instead. + \note implements Encoding concept + + \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. + For streaming, use UTF32LE and UTF32BE, which handle endianness. +*/ +template +struct UTF32 { + typedef CharType Ch; + RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4); + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + os.Put(codepoint); + } + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + PutUnsafe(os, codepoint); + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); + Ch c = is.Take(); + *codepoint = c; + return c <= 0x10FFFF; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); + Ch c; + os.Put(c = is.Take()); + return c <= 0x10FFFF; + } +}; + +//! UTF-32 little endian enocoding. +template +struct UTF32LE : UTF32 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0x0000FEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(is.Take()); + c |= static_cast(static_cast(is.Take())) << 8; + c |= static_cast(static_cast(is.Take())) << 16; + c |= static_cast(static_cast(is.Take())) << 24; + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xFFu)); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0x00u)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(c & 0xFFu)); + os.Put(static_cast((c >> 8) & 0xFFu)); + os.Put(static_cast((c >> 16) & 0xFFu)); + os.Put(static_cast((c >> 24) & 0xFFu)); + } +}; + +//! UTF-32 big endian encoding. +template +struct UTF32BE : UTF32 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0x0000FEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(static_cast(is.Take())) << 24; + c |= static_cast(static_cast(is.Take())) << 16; + c |= static_cast(static_cast(is.Take())) << 8; + c |= static_cast(static_cast(is.Take())); + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0xFFu)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast((c >> 24) & 0xFFu)); + os.Put(static_cast((c >> 16) & 0xFFu)); + os.Put(static_cast((c >> 8) & 0xFFu)); + os.Put(static_cast(c & 0xFFu)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// ASCII + +//! ASCII encoding. +/*! http://en.wikipedia.org/wiki/ASCII + \tparam CharType Code unit for storing 7-bit ASCII data. Default is char. + \note implements Encoding concept +*/ +template +struct ASCII { + typedef CharType Ch; + + enum { supportUnicode = 0 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_ASSERT(codepoint <= 0x7F); + os.Put(static_cast(codepoint & 0xFF)); + } + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_ASSERT(codepoint <= 0x7F); + PutUnsafe(os, static_cast(codepoint & 0xFF)); + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + uint8_t c = static_cast(is.Take()); + *codepoint = c; + return c <= 0X7F; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + uint8_t c = static_cast(is.Take()); + os.Put(static_cast(c)); + return c <= 0x7F; + } + + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + uint8_t c = static_cast(Take(is)); + return static_cast(c); + } + + template + static Ch Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + return static_cast(is.Take()); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + (void)os; + } + + template + static void Put(OutputByteStream& os, Ch c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(c)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// AutoUTF + +//! Runtime-specified UTF encoding type of a stream. +enum UTFType { + kUTF8 = 0, //!< UTF-8. + kUTF16LE = 1, //!< UTF-16 little endian. + kUTF16BE = 2, //!< UTF-16 big endian. + kUTF32LE = 3, //!< UTF-32 little endian. + kUTF32BE = 4 //!< UTF-32 big endian. +}; + +//! Dynamically select encoding according to stream's runtime-specified UTF encoding type. +/*! \note This class can be used with AutoUTFInputtStream and AutoUTFOutputStream, which provides GetType(). +*/ +template +struct AutoUTF { + typedef CharType Ch; + + enum { supportUnicode = 1 }; + +#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x + + template + static RAPIDJSON_FORCEINLINE void Encode(OutputStream& os, unsigned codepoint) { + typedef void (*EncodeFunc)(OutputStream&, unsigned); + static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) }; + (*f[os.GetType()])(os, codepoint); + } + + template + static RAPIDJSON_FORCEINLINE void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + typedef void (*EncodeFunc)(OutputStream&, unsigned); + static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(EncodeUnsafe) }; + (*f[os.GetType()])(os, codepoint); + } + + template + static RAPIDJSON_FORCEINLINE bool Decode(InputStream& is, unsigned* codepoint) { + typedef bool (*DecodeFunc)(InputStream&, unsigned*); + static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) }; + return (*f[is.GetType()])(is, codepoint); + } + + template + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { + typedef bool (*ValidateFunc)(InputStream&, OutputStream&); + static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) }; + return (*f[is.GetType()])(is, os); + } + +#undef RAPIDJSON_ENCODINGS_FUNC +}; + +/////////////////////////////////////////////////////////////////////////////// +// Transcoder + +//! Encoding conversion. +template +struct Transcoder { + //! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream. + template + static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) { + unsigned codepoint; + if (!SourceEncoding::Decode(is, &codepoint)) + return false; + TargetEncoding::Encode(os, codepoint); + return true; + } + + template + static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) { + unsigned codepoint; + if (!SourceEncoding::Decode(is, &codepoint)) + return false; + TargetEncoding::EncodeUnsafe(os, codepoint); + return true; + } + + //! Validate one Unicode codepoint from an encoded stream. + template + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { + return Transcode(is, os); // Since source/target encoding is different, must transcode. + } +}; + +// Forward declaration. +template +inline void PutUnsafe(Stream& stream, typename Stream::Ch c); + +//! Specialization of Transcoder with same source and target encoding. +template +struct Transcoder { + template + static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) { + os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class. + return true; + } + + template + static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) { + PutUnsafe(os, is.Take()); // Just copy one code unit. This semantic is different from primary template class. + return true; + } + + template + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { + return Encoding::Validate(is, os); // source/target encoding are the same + } +}; + +RAPIDJSON_NAMESPACE_END + +#if defined(__GNUC__) || (defined(_MSC_VER) && !defined(__clang__)) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ENCODINGS_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/error/en.h b/libraries/libstratosphere/include/stratosphere/rapidjson/error/en.h new file mode 100644 index 000000000..5d2e57b7f --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/error/en.h @@ -0,0 +1,122 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ERROR_EN_H_ +#define RAPIDJSON_ERROR_EN_H_ + +#include "error.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(switch-enum) +RAPIDJSON_DIAG_OFF(covered-switch-default) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Maps error code of parsing into error message. +/*! + \ingroup RAPIDJSON_ERRORS + \param parseErrorCode Error code obtained in parsing. + \return the error message. + \note User can make a copy of this function for localization. + Using switch-case is safer for future modification of error codes. +*/ +inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErrorCode) { + switch (parseErrorCode) { + case kParseErrorNone: return RAPIDJSON_ERROR_STRING("No error."); + + case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty."); + case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not be followed by other values."); + + case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value."); + + case kParseErrorObjectMissName: return RAPIDJSON_ERROR_STRING("Missing a name for object member."); + case kParseErrorObjectMissColon: return RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member."); + case kParseErrorObjectMissCommaOrCurlyBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member."); + + case kParseErrorArrayMissCommaOrSquareBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or ']' after an array element."); + + case kParseErrorStringUnicodeEscapeInvalidHex: return RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string."); + case kParseErrorStringUnicodeSurrogateInvalid: return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid."); + case kParseErrorStringEscapeInvalid: return RAPIDJSON_ERROR_STRING("Invalid escape character in string."); + case kParseErrorStringMissQuotationMark: return RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string."); + case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoding in string."); + + case kParseErrorNumberTooBig: return RAPIDJSON_ERROR_STRING("Number too big to be stored in double."); + case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number."); + case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number."); + + case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error."); + case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error."); + + default: return RAPIDJSON_ERROR_STRING("Unknown error."); + } +} + +//! Maps error code of validation into error message. +/*! + \ingroup RAPIDJSON_ERRORS + \param validateErrorCode Error code obtained from validator. + \return the error message. + \note User can make a copy of this function for localization. + Using switch-case is safer for future modification of error codes. +*/ +inline const RAPIDJSON_ERROR_CHARTYPE* GetValidateError_En(ValidateErrorCode validateErrorCode) { + switch (validateErrorCode) { + case kValidateErrors: return RAPIDJSON_ERROR_STRING("One or more validation errors have occurred"); + case kValidateErrorNone: return RAPIDJSON_ERROR_STRING("No error."); + + case kValidateErrorMultipleOf: return RAPIDJSON_ERROR_STRING("Number '%actual' is not a multiple of the 'multipleOf' value '%expected'."); + case kValidateErrorMaximum: return RAPIDJSON_ERROR_STRING("Number '%actual' is greater than the 'maximum' value '%expected'."); + case kValidateErrorExclusiveMaximum: return RAPIDJSON_ERROR_STRING("Number '%actual' is greater than or equal to the 'exclusiveMaximum' value '%expected'."); + case kValidateErrorMinimum: return RAPIDJSON_ERROR_STRING("Number '%actual' is less than the 'minimum' value '%expected'."); + case kValidateErrorExclusiveMinimum: return RAPIDJSON_ERROR_STRING("Number '%actual' is less than or equal to the 'exclusiveMinimum' value '%expected'."); + + case kValidateErrorMaxLength: return RAPIDJSON_ERROR_STRING("String '%actual' is longer than the 'maxLength' value '%expected'."); + case kValidateErrorMinLength: return RAPIDJSON_ERROR_STRING("String '%actual' is shorter than the 'minLength' value '%expected'."); + case kValidateErrorPattern: return RAPIDJSON_ERROR_STRING("String '%actual' does not match the 'pattern' regular expression."); + + case kValidateErrorMaxItems: return RAPIDJSON_ERROR_STRING("Array of length '%actual' is longer than the 'maxItems' value '%expected'."); + case kValidateErrorMinItems: return RAPIDJSON_ERROR_STRING("Array of length '%actual' is shorter than the 'minItems' value '%expected'."); + case kValidateErrorUniqueItems: return RAPIDJSON_ERROR_STRING("Array has duplicate items at indices '%duplicates' but 'uniqueItems' is true."); + case kValidateErrorAdditionalItems: return RAPIDJSON_ERROR_STRING("Array has an additional item at index '%disallowed' that is not allowed by the schema."); + + case kValidateErrorMaxProperties: return RAPIDJSON_ERROR_STRING("Object has '%actual' members which is more than 'maxProperties' value '%expected'."); + case kValidateErrorMinProperties: return RAPIDJSON_ERROR_STRING("Object has '%actual' members which is less than 'minProperties' value '%expected'."); + case kValidateErrorRequired: return RAPIDJSON_ERROR_STRING("Object is missing the following members required by the schema: '%missing'."); + case kValidateErrorAdditionalProperties: return RAPIDJSON_ERROR_STRING("Object has an additional member '%disallowed' that is not allowed by the schema."); + case kValidateErrorPatternProperties: return RAPIDJSON_ERROR_STRING("Object has 'patternProperties' that are not allowed by the schema."); + case kValidateErrorDependencies: return RAPIDJSON_ERROR_STRING("Object has missing property or schema dependencies, refer to following errors."); + + case kValidateErrorEnum: return RAPIDJSON_ERROR_STRING("Property has a value that is not one of its allowed enumerated values."); + case kValidateErrorType: return RAPIDJSON_ERROR_STRING("Property has a type '%actual' that is not in the following list: '%expected'."); + + case kValidateErrorOneOf: return RAPIDJSON_ERROR_STRING("Property did not match any of the sub-schemas specified by 'oneOf', refer to following errors."); + case kValidateErrorOneOfMatch: return RAPIDJSON_ERROR_STRING("Property matched more than one of the sub-schemas specified by 'oneOf'."); + case kValidateErrorAllOf: return RAPIDJSON_ERROR_STRING("Property did not match all of the sub-schemas specified by 'allOf', refer to following errors."); + case kValidateErrorAnyOf: return RAPIDJSON_ERROR_STRING("Property did not match any of the sub-schemas specified by 'anyOf', refer to following errors."); + case kValidateErrorNot: return RAPIDJSON_ERROR_STRING("Property matched the sub-schema specified by 'not'."); + + default: return RAPIDJSON_ERROR_STRING("Unknown error."); + } +} + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ERROR_EN_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/error/error.h b/libraries/libstratosphere/include/stratosphere/rapidjson/error/error.h new file mode 100644 index 000000000..6270da11a --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/error/error.h @@ -0,0 +1,216 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ERROR_ERROR_H_ +#define RAPIDJSON_ERROR_ERROR_H_ + +#include "../rapidjson.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +/*! \file error.h */ + +/*! \defgroup RAPIDJSON_ERRORS RapidJSON error handling */ + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ERROR_CHARTYPE + +//! Character type of error messages. +/*! \ingroup RAPIDJSON_ERRORS + The default character type is \c char. + On Windows, user can define this macro as \c TCHAR for supporting both + unicode/non-unicode settings. +*/ +#ifndef RAPIDJSON_ERROR_CHARTYPE +#define RAPIDJSON_ERROR_CHARTYPE char +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ERROR_STRING + +//! Macro for converting string literial to \ref RAPIDJSON_ERROR_CHARTYPE[]. +/*! \ingroup RAPIDJSON_ERRORS + By default this conversion macro does nothing. + On Windows, user can define this macro as \c _T(x) for supporting both + unicode/non-unicode settings. +*/ +#ifndef RAPIDJSON_ERROR_STRING +#define RAPIDJSON_ERROR_STRING(x) x +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// ParseErrorCode + +//! Error code of parsing. +/*! \ingroup RAPIDJSON_ERRORS + \see GenericReader::Parse, GenericReader::GetParseErrorCode +*/ +enum ParseErrorCode { + kParseErrorNone = 0, //!< No error. + + kParseErrorDocumentEmpty, //!< The document is empty. + kParseErrorDocumentRootNotSingular, //!< The document root must not follow by other values. + + kParseErrorValueInvalid, //!< Invalid value. + + kParseErrorObjectMissName, //!< Missing a name for object member. + kParseErrorObjectMissColon, //!< Missing a colon after a name of object member. + kParseErrorObjectMissCommaOrCurlyBracket, //!< Missing a comma or '}' after an object member. + + kParseErrorArrayMissCommaOrSquareBracket, //!< Missing a comma or ']' after an array element. + + kParseErrorStringUnicodeEscapeInvalidHex, //!< Incorrect hex digit after \\u escape in string. + kParseErrorStringUnicodeSurrogateInvalid, //!< The surrogate pair in string is invalid. + kParseErrorStringEscapeInvalid, //!< Invalid escape character in string. + kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string. + kParseErrorStringInvalidEncoding, //!< Invalid encoding in string. + + kParseErrorNumberTooBig, //!< Number too big to be stored in double. + kParseErrorNumberMissFraction, //!< Miss fraction part in number. + kParseErrorNumberMissExponent, //!< Miss exponent in number. + + kParseErrorTermination, //!< Parsing was terminated. + kParseErrorUnspecificSyntaxError //!< Unspecific syntax error. +}; + +//! Result of parsing (wraps ParseErrorCode) +/*! + \ingroup RAPIDJSON_ERRORS + \code + Document doc; + ParseResult ok = doc.Parse("[42]"); + if (!ok) { + fprintf(stderr, "JSON parse error: %s (%u)", + GetParseError_En(ok.Code()), ok.Offset()); + exit(EXIT_FAILURE); + } + \endcode + \see GenericReader::Parse, GenericDocument::Parse +*/ +struct ParseResult { + //!! Unspecified boolean type + typedef bool (ParseResult::*BooleanType)() const; +public: + //! Default constructor, no error. + ParseResult() : code_(kParseErrorNone), offset_(0) {} + //! Constructor to set an error. + ParseResult(ParseErrorCode code, size_t offset) : code_(code), offset_(offset) {} + + //! Get the error code. + ParseErrorCode Code() const { return code_; } + //! Get the error offset, if \ref IsError(), 0 otherwise. + size_t Offset() const { return offset_; } + + //! Explicit conversion to \c bool, returns \c true, iff !\ref IsError(). + operator BooleanType() const { return !IsError() ? &ParseResult::IsError : NULL; } + //! Whether the result is an error. + bool IsError() const { return code_ != kParseErrorNone; } + + bool operator==(const ParseResult& that) const { return code_ == that.code_; } + bool operator==(ParseErrorCode code) const { return code_ == code; } + friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; } + + bool operator!=(const ParseResult& that) const { return !(*this == that); } + bool operator!=(ParseErrorCode code) const { return !(*this == code); } + friend bool operator!=(ParseErrorCode code, const ParseResult & err) { return err != code; } + + //! Reset error code. + void Clear() { Set(kParseErrorNone); } + //! Update error code and offset. + void Set(ParseErrorCode code, size_t offset = 0) { code_ = code; offset_ = offset; } + +private: + ParseErrorCode code_; + size_t offset_; +}; + +//! Function pointer type of GetParseError(). +/*! \ingroup RAPIDJSON_ERRORS + + This is the prototype for \c GetParseError_X(), where \c X is a locale. + User can dynamically change locale in runtime, e.g.: +\code + GetParseErrorFunc GetParseError = GetParseError_En; // or whatever + const RAPIDJSON_ERROR_CHARTYPE* s = GetParseError(document.GetParseErrorCode()); +\endcode +*/ +typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode); + +/////////////////////////////////////////////////////////////////////////////// +// ValidateErrorCode + +//! Error codes when validating. +/*! \ingroup RAPIDJSON_ERRORS + \see GenericSchemaValidator +*/ +enum ValidateErrorCode { + kValidateErrors = -1, //!< Top level error code when kValidateContinueOnErrorsFlag set. + kValidateErrorNone = 0, //!< No error. + + kValidateErrorMultipleOf, //!< Number is not a multiple of the 'multipleOf' value. + kValidateErrorMaximum, //!< Number is greater than the 'maximum' value. + kValidateErrorExclusiveMaximum, //!< Number is greater than or equal to the 'maximum' value. + kValidateErrorMinimum, //!< Number is less than the 'minimum' value. + kValidateErrorExclusiveMinimum, //!< Number is less than or equal to the 'minimum' value. + + kValidateErrorMaxLength, //!< String is longer than the 'maxLength' value. + kValidateErrorMinLength, //!< String is longer than the 'maxLength' value. + kValidateErrorPattern, //!< String does not match the 'pattern' regular expression. + + kValidateErrorMaxItems, //!< Array is longer than the 'maxItems' value. + kValidateErrorMinItems, //!< Array is shorter than the 'minItems' value. + kValidateErrorUniqueItems, //!< Array has duplicate items but 'uniqueItems' is true. + kValidateErrorAdditionalItems, //!< Array has additional items that are not allowed by the schema. + + kValidateErrorMaxProperties, //!< Object has more members than 'maxProperties' value. + kValidateErrorMinProperties, //!< Object has less members than 'minProperties' value. + kValidateErrorRequired, //!< Object is missing one or more members required by the schema. + kValidateErrorAdditionalProperties, //!< Object has additional members that are not allowed by the schema. + kValidateErrorPatternProperties, //!< See other errors. + kValidateErrorDependencies, //!< Object has missing property or schema dependencies. + + kValidateErrorEnum, //!< Property has a value that is not one of its allowed enumerated values + kValidateErrorType, //!< Property has a type that is not allowed by the schema.. + + kValidateErrorOneOf, //!< Property did not match any of the sub-schemas specified by 'oneOf'. + kValidateErrorOneOfMatch, //!< Property matched more than one of the sub-schemas specified by 'oneOf'. + kValidateErrorAllOf, //!< Property did not match all of the sub-schemas specified by 'allOf'. + kValidateErrorAnyOf, //!< Property did not match any of the sub-schemas specified by 'anyOf'. + kValidateErrorNot //!< Property matched the sub-schema specified by 'not'. +}; + +//! Function pointer type of GetValidateError(). +/*! \ingroup RAPIDJSON_ERRORS + + This is the prototype for \c GetValidateError_X(), where \c X is a locale. + User can dynamically change locale in runtime, e.g.: +\code + GetValidateErrorFunc GetValidateError = GetValidateError_En; // or whatever + const RAPIDJSON_ERROR_CHARTYPE* s = GetValidateError(validator.GetInvalidSchemaCode()); +\endcode +*/ +typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetValidateErrorFunc)(ValidateErrorCode); + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ERROR_ERROR_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/filereadstream.h b/libraries/libstratosphere/include/stratosphere/rapidjson/filereadstream.h new file mode 100644 index 000000000..f8bb43cb0 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/filereadstream.h @@ -0,0 +1,99 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_FILEREADSTREAM_H_ +#define RAPIDJSON_FILEREADSTREAM_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(unreachable-code) +RAPIDJSON_DIAG_OFF(missing-noreturn) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! File byte stream for input using fread(). +/*! + \note implements Stream concept +*/ +class FileReadStream { +public: + typedef char Ch; //!< Character type (byte). + + //! Constructor. + /*! + \param fp File pointer opened for read. + \param buffer user-supplied buffer. + \param bufferSize size of buffer in bytes. Must >=4 bytes. + */ + FileReadStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { + RAPIDJSON_ASSERT(fp_ != 0); + RAPIDJSON_ASSERT(bufferSize >= 4); + Read(); + } + + Ch Peek() const { return *current_; } + Ch Take() { Ch c = *current_; Read(); return c; } + size_t Tell() const { return count_ + static_cast(current_ - buffer_); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + return (current_ + 4 - !eof_ <= bufferLast_) ? current_ : 0; + } + +private: + void Read() { + if (current_ < bufferLast_) + ++current_; + else if (!eof_) { + count_ += readCount_; + readCount_ = std::fread(buffer_, 1, bufferSize_, fp_); + bufferLast_ = buffer_ + readCount_ - 1; + current_ = buffer_; + + if (readCount_ < bufferSize_) { + buffer_[readCount_] = '\0'; + ++bufferLast_; + eof_ = true; + } + } + } + + std::FILE* fp_; + Ch *buffer_; + size_t bufferSize_; + Ch *bufferLast_; + Ch *current_; + size_t readCount_; + size_t count_; //!< Number of characters read + bool eof_; +}; + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/filewritestream.h b/libraries/libstratosphere/include/stratosphere/rapidjson/filewritestream.h new file mode 100644 index 000000000..5d89588c2 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/filewritestream.h @@ -0,0 +1,104 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_FILEWRITESTREAM_H_ +#define RAPIDJSON_FILEWRITESTREAM_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(unreachable-code) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of C file stream for output using fwrite(). +/*! + \note implements Stream concept +*/ +class FileWriteStream { +public: + typedef char Ch; //!< Character type. Only support char. + + FileWriteStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferEnd_(buffer + bufferSize), current_(buffer_) { + RAPIDJSON_ASSERT(fp_ != 0); + } + + void Put(char c) { + if (current_ >= bufferEnd_) + Flush(); + + *current_++ = c; + } + + void PutN(char c, size_t n) { + size_t avail = static_cast(bufferEnd_ - current_); + while (n > avail) { + std::memset(current_, c, avail); + current_ += avail; + Flush(); + n -= avail; + avail = static_cast(bufferEnd_ - current_); + } + + if (n > 0) { + std::memset(current_, c, n); + current_ += n; + } + } + + void Flush() { + if (current_ != buffer_) { + size_t result = std::fwrite(buffer_, 1, static_cast(current_ - buffer_), fp_); + if (result < static_cast(current_ - buffer_)) { + // failure deliberately ignored at this time + // added to avoid warn_unused_result build errors + } + current_ = buffer_; + } + } + + // Not implemented + char Peek() const { RAPIDJSON_ASSERT(false); return 0; } + char Take() { RAPIDJSON_ASSERT(false); return 0; } + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + // Prohibit copy constructor & assignment operator. + FileWriteStream(const FileWriteStream&); + FileWriteStream& operator=(const FileWriteStream&); + + std::FILE* fp_; + char *buffer_; + char *bufferEnd_; + char *current_; +}; + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(FileWriteStream& stream, char c, size_t n) { + stream.PutN(c, n); +} + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/fwd.h b/libraries/libstratosphere/include/stratosphere/rapidjson/fwd.h new file mode 100644 index 000000000..d62f77f0e --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/fwd.h @@ -0,0 +1,151 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_FWD_H_ +#define RAPIDJSON_FWD_H_ + +#include "rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN + +// encodings.h + +template struct UTF8; +template struct UTF16; +template struct UTF16BE; +template struct UTF16LE; +template struct UTF32; +template struct UTF32BE; +template struct UTF32LE; +template struct ASCII; +template struct AutoUTF; + +template +struct Transcoder; + +// allocators.h + +class CrtAllocator; + +template +class MemoryPoolAllocator; + +// stream.h + +template +struct GenericStringStream; + +typedef GenericStringStream > StringStream; + +template +struct GenericInsituStringStream; + +typedef GenericInsituStringStream > InsituStringStream; + +// stringbuffer.h + +template +class GenericStringBuffer; + +typedef GenericStringBuffer, CrtAllocator> StringBuffer; + +// filereadstream.h + +class FileReadStream; + +// filewritestream.h + +class FileWriteStream; + +// memorybuffer.h + +template +struct GenericMemoryBuffer; + +typedef GenericMemoryBuffer MemoryBuffer; + +// memorystream.h + +struct MemoryStream; + +// reader.h + +template +struct BaseReaderHandler; + +template +class GenericReader; + +typedef GenericReader, UTF8, CrtAllocator> Reader; + +// writer.h + +template +class Writer; + +// prettywriter.h + +template +class PrettyWriter; + +// document.h + +template +class GenericMember; + +template +class GenericMemberIterator; + +template +struct GenericStringRef; + +template +class GenericValue; + +typedef GenericValue, MemoryPoolAllocator > Value; + +template +class GenericDocument; + +typedef GenericDocument, MemoryPoolAllocator, CrtAllocator> Document; + +// pointer.h + +template +class GenericPointer; + +typedef GenericPointer Pointer; + +// schema.h + +template +class IGenericRemoteSchemaDocumentProvider; + +template +class GenericSchemaDocument; + +typedef GenericSchemaDocument SchemaDocument; +typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProvider; + +template < + typename SchemaDocumentType, + typename OutputHandler, + typename StateAllocator> +class GenericSchemaValidator; + +typedef GenericSchemaValidator, void>, CrtAllocator> SchemaValidator; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_RAPIDJSONFWD_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/internal/biginteger.h b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/biginteger.h new file mode 100644 index 000000000..12455788f --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/biginteger.h @@ -0,0 +1,290 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_BIGINTEGER_H_ +#define RAPIDJSON_BIGINTEGER_H_ + +#include "../rapidjson.h" + +#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) && defined(_M_AMD64) +#include // for _umul128 +#pragma intrinsic(_umul128) +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +class BigInteger { +public: + typedef uint64_t Type; + + BigInteger(const BigInteger& rhs) : count_(rhs.count_) { + std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type)); + } + + explicit BigInteger(uint64_t u) : count_(1) { + digits_[0] = u; + } + + BigInteger(const char* decimals, size_t length) : count_(1) { + RAPIDJSON_ASSERT(length > 0); + digits_[0] = 0; + size_t i = 0; + const size_t kMaxDigitPerIteration = 19; // 2^64 = 18446744073709551616 > 10^19 + while (length >= kMaxDigitPerIteration) { + AppendDecimal64(decimals + i, decimals + i + kMaxDigitPerIteration); + length -= kMaxDigitPerIteration; + i += kMaxDigitPerIteration; + } + + if (length > 0) + AppendDecimal64(decimals + i, decimals + i + length); + } + + BigInteger& operator=(const BigInteger &rhs) + { + if (this != &rhs) { + count_ = rhs.count_; + std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type)); + } + return *this; + } + + BigInteger& operator=(uint64_t u) { + digits_[0] = u; + count_ = 1; + return *this; + } + + BigInteger& operator+=(uint64_t u) { + Type backup = digits_[0]; + digits_[0] += u; + for (size_t i = 0; i < count_ - 1; i++) { + if (digits_[i] >= backup) + return *this; // no carry + backup = digits_[i + 1]; + digits_[i + 1] += 1; + } + + // Last carry + if (digits_[count_ - 1] < backup) + PushBack(1); + + return *this; + } + + BigInteger& operator*=(uint64_t u) { + if (u == 0) return *this = 0; + if (u == 1) return *this; + if (*this == 1) return *this = u; + + uint64_t k = 0; + for (size_t i = 0; i < count_; i++) { + uint64_t hi; + digits_[i] = MulAdd64(digits_[i], u, k, &hi); + k = hi; + } + + if (k > 0) + PushBack(k); + + return *this; + } + + BigInteger& operator*=(uint32_t u) { + if (u == 0) return *this = 0; + if (u == 1) return *this; + if (*this == 1) return *this = u; + + uint64_t k = 0; + for (size_t i = 0; i < count_; i++) { + const uint64_t c = digits_[i] >> 32; + const uint64_t d = digits_[i] & 0xFFFFFFFF; + const uint64_t uc = u * c; + const uint64_t ud = u * d; + const uint64_t p0 = ud + k; + const uint64_t p1 = uc + (p0 >> 32); + digits_[i] = (p0 & 0xFFFFFFFF) | (p1 << 32); + k = p1 >> 32; + } + + if (k > 0) + PushBack(k); + + return *this; + } + + BigInteger& operator<<=(size_t shift) { + if (IsZero() || shift == 0) return *this; + + size_t offset = shift / kTypeBit; + size_t interShift = shift % kTypeBit; + RAPIDJSON_ASSERT(count_ + offset <= kCapacity); + + if (interShift == 0) { + std::memmove(digits_ + offset, digits_, count_ * sizeof(Type)); + count_ += offset; + } + else { + digits_[count_] = 0; + for (size_t i = count_; i > 0; i--) + digits_[i + offset] = (digits_[i] << interShift) | (digits_[i - 1] >> (kTypeBit - interShift)); + digits_[offset] = digits_[0] << interShift; + count_ += offset; + if (digits_[count_]) + count_++; + } + + std::memset(digits_, 0, offset * sizeof(Type)); + + return *this; + } + + bool operator==(const BigInteger& rhs) const { + return count_ == rhs.count_ && std::memcmp(digits_, rhs.digits_, count_ * sizeof(Type)) == 0; + } + + bool operator==(const Type rhs) const { + return count_ == 1 && digits_[0] == rhs; + } + + BigInteger& MultiplyPow5(unsigned exp) { + static const uint32_t kPow5[12] = { + 5, + 5 * 5, + 5 * 5 * 5, + 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 + }; + if (exp == 0) return *this; + for (; exp >= 27; exp -= 27) *this *= RAPIDJSON_UINT64_C2(0X6765C793, 0XFA10079D); // 5^27 + for (; exp >= 13; exp -= 13) *this *= static_cast(1220703125u); // 5^13 + if (exp > 0) *this *= kPow5[exp - 1]; + return *this; + } + + // Compute absolute difference of this and rhs. + // Assume this != rhs + bool Difference(const BigInteger& rhs, BigInteger* out) const { + int cmp = Compare(rhs); + RAPIDJSON_ASSERT(cmp != 0); + const BigInteger *a, *b; // Makes a > b + bool ret; + if (cmp < 0) { a = &rhs; b = this; ret = true; } + else { a = this; b = &rhs; ret = false; } + + Type borrow = 0; + for (size_t i = 0; i < a->count_; i++) { + Type d = a->digits_[i] - borrow; + if (i < b->count_) + d -= b->digits_[i]; + borrow = (d > a->digits_[i]) ? 1 : 0; + out->digits_[i] = d; + if (d != 0) + out->count_ = i + 1; + } + + return ret; + } + + int Compare(const BigInteger& rhs) const { + if (count_ != rhs.count_) + return count_ < rhs.count_ ? -1 : 1; + + for (size_t i = count_; i-- > 0;) + if (digits_[i] != rhs.digits_[i]) + return digits_[i] < rhs.digits_[i] ? -1 : 1; + + return 0; + } + + size_t GetCount() const { return count_; } + Type GetDigit(size_t index) const { RAPIDJSON_ASSERT(index < count_); return digits_[index]; } + bool IsZero() const { return count_ == 1 && digits_[0] == 0; } + +private: + void AppendDecimal64(const char* begin, const char* end) { + uint64_t u = ParseUint64(begin, end); + if (IsZero()) + *this = u; + else { + unsigned exp = static_cast(end - begin); + (MultiplyPow5(exp) <<= exp) += u; // *this = *this * 10^exp + u + } + } + + void PushBack(Type digit) { + RAPIDJSON_ASSERT(count_ < kCapacity); + digits_[count_++] = digit; + } + + static uint64_t ParseUint64(const char* begin, const char* end) { + uint64_t r = 0; + for (const char* p = begin; p != end; ++p) { + RAPIDJSON_ASSERT(*p >= '0' && *p <= '9'); + r = r * 10u + static_cast(*p - '0'); + } + return r; + } + + // Assume a * b + k < 2^128 + static uint64_t MulAdd64(uint64_t a, uint64_t b, uint64_t k, uint64_t* outHigh) { +#if defined(_MSC_VER) && defined(_M_AMD64) + uint64_t low = _umul128(a, b, outHigh) + k; + if (low < k) + (*outHigh)++; + return low; +#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) + __extension__ typedef unsigned __int128 uint128; + uint128 p = static_cast(a) * static_cast(b); + p += k; + *outHigh = static_cast(p >> 64); + return static_cast(p); +#else + const uint64_t a0 = a & 0xFFFFFFFF, a1 = a >> 32, b0 = b & 0xFFFFFFFF, b1 = b >> 32; + uint64_t x0 = a0 * b0, x1 = a0 * b1, x2 = a1 * b0, x3 = a1 * b1; + x1 += (x0 >> 32); // can't give carry + x1 += x2; + if (x1 < x2) + x3 += (static_cast(1) << 32); + uint64_t lo = (x1 << 32) + (x0 & 0xFFFFFFFF); + uint64_t hi = x3 + (x1 >> 32); + + lo += k; + if (lo < k) + hi++; + *outHigh = hi; + return lo; +#endif + } + + static const size_t kBitCount = 3328; // 64bit * 54 > 10^1000 + static const size_t kCapacity = kBitCount / sizeof(Type); + static const size_t kTypeBit = sizeof(Type) * 8; + + Type digits_[kCapacity]; + size_t count_; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_BIGINTEGER_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/internal/clzll.h b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/clzll.h new file mode 100644 index 000000000..8fc5118aa --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/clzll.h @@ -0,0 +1,71 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_CLZLL_H_ +#define RAPIDJSON_CLZLL_H_ + +#include "../rapidjson.h" + +#if defined(_MSC_VER) && !defined(UNDER_CE) +#include +#if defined(_WIN64) +#pragma intrinsic(_BitScanReverse64) +#else +#pragma intrinsic(_BitScanReverse) +#endif +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +inline uint32_t clzll(uint64_t x) { + // Passing 0 to __builtin_clzll is UB in GCC and results in an + // infinite loop in the software implementation. + RAPIDJSON_ASSERT(x != 0); + +#if defined(_MSC_VER) && !defined(UNDER_CE) + unsigned long r = 0; +#if defined(_WIN64) + _BitScanReverse64(&r, x); +#else + // Scan the high 32 bits. + if (_BitScanReverse(&r, static_cast(x >> 32))) + return 63 - (r + 32); + + // Scan the low 32 bits. + _BitScanReverse(&r, static_cast(x & 0xFFFFFFFF)); +#endif // _WIN64 + + return 63 - r; +#elif (defined(__GNUC__) && __GNUC__ >= 4) || RAPIDJSON_HAS_BUILTIN(__builtin_clzll) + // __builtin_clzll wrapper + return static_cast(__builtin_clzll(x)); +#else + // naive version + uint32_t r = 0; + while (!(x & (static_cast(1) << 63))) { + x <<= 1; + ++r; + } + + return r; +#endif // _MSC_VER +} + +#define RAPIDJSON_CLZLL RAPIDJSON_NAMESPACE::internal::clzll + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_CLZLL_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/internal/diyfp.h b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/diyfp.h new file mode 100644 index 000000000..a40797ec2 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/diyfp.h @@ -0,0 +1,257 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// This is a C++ header-only implementation of Grisu2 algorithm from the publication: +// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with +// integers." ACM Sigplan Notices 45.6 (2010): 233-243. + +#ifndef RAPIDJSON_DIYFP_H_ +#define RAPIDJSON_DIYFP_H_ + +#include "../rapidjson.h" +#include "clzll.h" +#include + +#if defined(_MSC_VER) && defined(_M_AMD64) && !defined(__INTEL_COMPILER) +#include +#pragma intrinsic(_umul128) +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +struct DiyFp { + DiyFp() : f(), e() {} + + DiyFp(uint64_t fp, int exp) : f(fp), e(exp) {} + + explicit DiyFp(double d) { + union { + double d; + uint64_t u64; + } u = { d }; + + int biased_e = static_cast((u.u64 & kDpExponentMask) >> kDpSignificandSize); + uint64_t significand = (u.u64 & kDpSignificandMask); + if (biased_e != 0) { + f = significand + kDpHiddenBit; + e = biased_e - kDpExponentBias; + } + else { + f = significand; + e = kDpMinExponent + 1; + } + } + + DiyFp operator-(const DiyFp& rhs) const { + return DiyFp(f - rhs.f, e); + } + + DiyFp operator*(const DiyFp& rhs) const { +#if defined(_MSC_VER) && defined(_M_AMD64) + uint64_t h; + uint64_t l = _umul128(f, rhs.f, &h); + if (l & (uint64_t(1) << 63)) // rounding + h++; + return DiyFp(h, e + rhs.e + 64); +#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) + __extension__ typedef unsigned __int128 uint128; + uint128 p = static_cast(f) * static_cast(rhs.f); + uint64_t h = static_cast(p >> 64); + uint64_t l = static_cast(p); + if (l & (uint64_t(1) << 63)) // rounding + h++; + return DiyFp(h, e + rhs.e + 64); +#else + const uint64_t M32 = 0xFFFFFFFF; + const uint64_t a = f >> 32; + const uint64_t b = f & M32; + const uint64_t c = rhs.f >> 32; + const uint64_t d = rhs.f & M32; + const uint64_t ac = a * c; + const uint64_t bc = b * c; + const uint64_t ad = a * d; + const uint64_t bd = b * d; + uint64_t tmp = (bd >> 32) + (ad & M32) + (bc & M32); + tmp += 1U << 31; /// mult_round + return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64); +#endif + } + + DiyFp Normalize() const { + int s = static_cast(clzll(f)); + return DiyFp(f << s, e - s); + } + + DiyFp NormalizeBoundary() const { + DiyFp res = *this; + while (!(res.f & (kDpHiddenBit << 1))) { + res.f <<= 1; + res.e--; + } + res.f <<= (kDiySignificandSize - kDpSignificandSize - 2); + res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2); + return res; + } + + void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const { + DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary(); + DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1); + mi.f <<= mi.e - pl.e; + mi.e = pl.e; + *plus = pl; + *minus = mi; + } + + double ToDouble() const { + union { + double d; + uint64_t u64; + }u; + RAPIDJSON_ASSERT(f <= kDpHiddenBit + kDpSignificandMask); + if (e < kDpDenormalExponent) { + // Underflow. + return 0.0; + } + if (e >= kDpMaxExponent) { + // Overflow. + return std::numeric_limits::infinity(); + } + const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 : + static_cast(e + kDpExponentBias); + u.u64 = (f & kDpSignificandMask) | (be << kDpSignificandSize); + return u.d; + } + + static const int kDiySignificandSize = 64; + static const int kDpSignificandSize = 52; + static const int kDpExponentBias = 0x3FF + kDpSignificandSize; + static const int kDpMaxExponent = 0x7FF - kDpExponentBias; + static const int kDpMinExponent = -kDpExponentBias; + static const int kDpDenormalExponent = -kDpExponentBias + 1; + static const uint64_t kDpExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); + static const uint64_t kDpSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); + static const uint64_t kDpHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); + + uint64_t f; + int e; +}; + +inline DiyFp GetCachedPowerByIndex(size_t index) { + // 10^-348, 10^-340, ..., 10^340 + static const uint64_t kCachedPowers_F[] = { + RAPIDJSON_UINT64_C2(0xfa8fd5a0, 0x081c0288), RAPIDJSON_UINT64_C2(0xbaaee17f, 0xa23ebf76), + RAPIDJSON_UINT64_C2(0x8b16fb20, 0x3055ac76), RAPIDJSON_UINT64_C2(0xcf42894a, 0x5dce35ea), + RAPIDJSON_UINT64_C2(0x9a6bb0aa, 0x55653b2d), RAPIDJSON_UINT64_C2(0xe61acf03, 0x3d1a45df), + RAPIDJSON_UINT64_C2(0xab70fe17, 0xc79ac6ca), RAPIDJSON_UINT64_C2(0xff77b1fc, 0xbebcdc4f), + RAPIDJSON_UINT64_C2(0xbe5691ef, 0x416bd60c), RAPIDJSON_UINT64_C2(0x8dd01fad, 0x907ffc3c), + RAPIDJSON_UINT64_C2(0xd3515c28, 0x31559a83), RAPIDJSON_UINT64_C2(0x9d71ac8f, 0xada6c9b5), + RAPIDJSON_UINT64_C2(0xea9c2277, 0x23ee8bcb), RAPIDJSON_UINT64_C2(0xaecc4991, 0x4078536d), + RAPIDJSON_UINT64_C2(0x823c1279, 0x5db6ce57), RAPIDJSON_UINT64_C2(0xc2109436, 0x4dfb5637), + RAPIDJSON_UINT64_C2(0x9096ea6f, 0x3848984f), RAPIDJSON_UINT64_C2(0xd77485cb, 0x25823ac7), + RAPIDJSON_UINT64_C2(0xa086cfcd, 0x97bf97f4), RAPIDJSON_UINT64_C2(0xef340a98, 0x172aace5), + RAPIDJSON_UINT64_C2(0xb23867fb, 0x2a35b28e), RAPIDJSON_UINT64_C2(0x84c8d4df, 0xd2c63f3b), + RAPIDJSON_UINT64_C2(0xc5dd4427, 0x1ad3cdba), RAPIDJSON_UINT64_C2(0x936b9fce, 0xbb25c996), + RAPIDJSON_UINT64_C2(0xdbac6c24, 0x7d62a584), RAPIDJSON_UINT64_C2(0xa3ab6658, 0x0d5fdaf6), + RAPIDJSON_UINT64_C2(0xf3e2f893, 0xdec3f126), RAPIDJSON_UINT64_C2(0xb5b5ada8, 0xaaff80b8), + RAPIDJSON_UINT64_C2(0x87625f05, 0x6c7c4a8b), RAPIDJSON_UINT64_C2(0xc9bcff60, 0x34c13053), + RAPIDJSON_UINT64_C2(0x964e858c, 0x91ba2655), RAPIDJSON_UINT64_C2(0xdff97724, 0x70297ebd), + RAPIDJSON_UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), RAPIDJSON_UINT64_C2(0xf8a95fcf, 0x88747d94), + RAPIDJSON_UINT64_C2(0xb9447093, 0x8fa89bcf), RAPIDJSON_UINT64_C2(0x8a08f0f8, 0xbf0f156b), + RAPIDJSON_UINT64_C2(0xcdb02555, 0x653131b6), RAPIDJSON_UINT64_C2(0x993fe2c6, 0xd07b7fac), + RAPIDJSON_UINT64_C2(0xe45c10c4, 0x2a2b3b06), RAPIDJSON_UINT64_C2(0xaa242499, 0x697392d3), + RAPIDJSON_UINT64_C2(0xfd87b5f2, 0x8300ca0e), RAPIDJSON_UINT64_C2(0xbce50864, 0x92111aeb), + RAPIDJSON_UINT64_C2(0x8cbccc09, 0x6f5088cc), RAPIDJSON_UINT64_C2(0xd1b71758, 0xe219652c), + RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), RAPIDJSON_UINT64_C2(0xe8d4a510, 0x00000000), + RAPIDJSON_UINT64_C2(0xad78ebc5, 0xac620000), RAPIDJSON_UINT64_C2(0x813f3978, 0xf8940984), + RAPIDJSON_UINT64_C2(0xc097ce7b, 0xc90715b3), RAPIDJSON_UINT64_C2(0x8f7e32ce, 0x7bea5c70), + RAPIDJSON_UINT64_C2(0xd5d238a4, 0xabe98068), RAPIDJSON_UINT64_C2(0x9f4f2726, 0x179a2245), + RAPIDJSON_UINT64_C2(0xed63a231, 0xd4c4fb27), RAPIDJSON_UINT64_C2(0xb0de6538, 0x8cc8ada8), + RAPIDJSON_UINT64_C2(0x83c7088e, 0x1aab65db), RAPIDJSON_UINT64_C2(0xc45d1df9, 0x42711d9a), + RAPIDJSON_UINT64_C2(0x924d692c, 0xa61be758), RAPIDJSON_UINT64_C2(0xda01ee64, 0x1a708dea), + RAPIDJSON_UINT64_C2(0xa26da399, 0x9aef774a), RAPIDJSON_UINT64_C2(0xf209787b, 0xb47d6b85), + RAPIDJSON_UINT64_C2(0xb454e4a1, 0x79dd1877), RAPIDJSON_UINT64_C2(0x865b8692, 0x5b9bc5c2), + RAPIDJSON_UINT64_C2(0xc83553c5, 0xc8965d3d), RAPIDJSON_UINT64_C2(0x952ab45c, 0xfa97a0b3), + RAPIDJSON_UINT64_C2(0xde469fbd, 0x99a05fe3), RAPIDJSON_UINT64_C2(0xa59bc234, 0xdb398c25), + RAPIDJSON_UINT64_C2(0xf6c69a72, 0xa3989f5c), RAPIDJSON_UINT64_C2(0xb7dcbf53, 0x54e9bece), + RAPIDJSON_UINT64_C2(0x88fcf317, 0xf22241e2), RAPIDJSON_UINT64_C2(0xcc20ce9b, 0xd35c78a5), + RAPIDJSON_UINT64_C2(0x98165af3, 0x7b2153df), RAPIDJSON_UINT64_C2(0xe2a0b5dc, 0x971f303a), + RAPIDJSON_UINT64_C2(0xa8d9d153, 0x5ce3b396), RAPIDJSON_UINT64_C2(0xfb9b7cd9, 0xa4a7443c), + RAPIDJSON_UINT64_C2(0xbb764c4c, 0xa7a44410), RAPIDJSON_UINT64_C2(0x8bab8eef, 0xb6409c1a), + RAPIDJSON_UINT64_C2(0xd01fef10, 0xa657842c), RAPIDJSON_UINT64_C2(0x9b10a4e5, 0xe9913129), + RAPIDJSON_UINT64_C2(0xe7109bfb, 0xa19c0c9d), RAPIDJSON_UINT64_C2(0xac2820d9, 0x623bf429), + RAPIDJSON_UINT64_C2(0x80444b5e, 0x7aa7cf85), RAPIDJSON_UINT64_C2(0xbf21e440, 0x03acdd2d), + RAPIDJSON_UINT64_C2(0x8e679c2f, 0x5e44ff8f), RAPIDJSON_UINT64_C2(0xd433179d, 0x9c8cb841), + RAPIDJSON_UINT64_C2(0x9e19db92, 0xb4e31ba9), RAPIDJSON_UINT64_C2(0xeb96bf6e, 0xbadf77d9), + RAPIDJSON_UINT64_C2(0xaf87023b, 0x9bf0ee6b) + }; + static const int16_t kCachedPowers_E[] = { + -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, + -954, -927, -901, -874, -847, -821, -794, -768, -741, -715, + -688, -661, -635, -608, -582, -555, -529, -502, -475, -449, + -422, -396, -369, -343, -316, -289, -263, -236, -210, -183, + -157, -130, -103, -77, -50, -24, 3, 30, 56, 83, + 109, 136, 162, 189, 216, 242, 269, 295, 322, 348, + 375, 402, 428, 455, 481, 508, 534, 561, 588, 614, + 641, 667, 694, 720, 747, 774, 800, 827, 853, 880, + 907, 933, 960, 986, 1013, 1039, 1066 + }; + RAPIDJSON_ASSERT(index < 87); + return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]); +} + +inline DiyFp GetCachedPower(int e, int* K) { + + //int k = static_cast(ceil((-61 - e) * 0.30102999566398114)) + 374; + double dk = (-61 - e) * 0.30102999566398114 + 347; // dk must be positive, so can do ceiling in positive + int k = static_cast(dk); + if (dk - k > 0.0) + k++; + + unsigned index = static_cast((k >> 3) + 1); + *K = -(-348 + static_cast(index << 3)); // decimal exponent no need lookup table + + return GetCachedPowerByIndex(index); +} + +inline DiyFp GetCachedPower10(int exp, int *outExp) { + RAPIDJSON_ASSERT(exp >= -348); + unsigned index = static_cast(exp + 348) / 8u; + *outExp = -348 + static_cast(index) * 8; + return GetCachedPowerByIndex(index); +} + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +RAPIDJSON_DIAG_OFF(padded) +#endif + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_DIYFP_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/internal/dtoa.h b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/dtoa.h new file mode 100644 index 000000000..621402fd3 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/dtoa.h @@ -0,0 +1,245 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// This is a C++ header-only implementation of Grisu2 algorithm from the publication: +// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with +// integers." ACM Sigplan Notices 45.6 (2010): 233-243. + +#ifndef RAPIDJSON_DTOA_ +#define RAPIDJSON_DTOA_ + +#include "itoa.h" // GetDigitsLut() +#include "diyfp.h" +#include "ieee754.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +RAPIDJSON_DIAG_OFF(array-bounds) // some gcc versions generate wrong warnings https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59124 +#endif + +inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) { + while (rest < wp_w && delta - rest >= ten_kappa && + (rest + ten_kappa < wp_w || /// closer + wp_w - rest > rest + ten_kappa - wp_w)) { + buffer[len - 1]--; + rest += ten_kappa; + } +} + +inline int CountDecimalDigit32(uint32_t n) { + // Simple pure C++ implementation was faster than __builtin_clz version in this situation. + if (n < 10) return 1; + if (n < 100) return 2; + if (n < 1000) return 3; + if (n < 10000) return 4; + if (n < 100000) return 5; + if (n < 1000000) return 6; + if (n < 10000000) return 7; + if (n < 100000000) return 8; + // Will not reach 10 digits in DigitGen() + //if (n < 1000000000) return 9; + //return 10; + return 9; +} + +inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) { + static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; + const DiyFp one(uint64_t(1) << -Mp.e, Mp.e); + const DiyFp wp_w = Mp - W; + uint32_t p1 = static_cast(Mp.f >> -one.e); + uint64_t p2 = Mp.f & (one.f - 1); + int kappa = CountDecimalDigit32(p1); // kappa in [0, 9] + *len = 0; + + while (kappa > 0) { + uint32_t d = 0; + switch (kappa) { + case 9: d = p1 / 100000000; p1 %= 100000000; break; + case 8: d = p1 / 10000000; p1 %= 10000000; break; + case 7: d = p1 / 1000000; p1 %= 1000000; break; + case 6: d = p1 / 100000; p1 %= 100000; break; + case 5: d = p1 / 10000; p1 %= 10000; break; + case 4: d = p1 / 1000; p1 %= 1000; break; + case 3: d = p1 / 100; p1 %= 100; break; + case 2: d = p1 / 10; p1 %= 10; break; + case 1: d = p1; p1 = 0; break; + default:; + } + if (d || *len) + buffer[(*len)++] = static_cast('0' + static_cast(d)); + kappa--; + uint64_t tmp = (static_cast(p1) << -one.e) + p2; + if (tmp <= delta) { + *K += kappa; + GrisuRound(buffer, *len, delta, tmp, static_cast(kPow10[kappa]) << -one.e, wp_w.f); + return; + } + } + + // kappa = 0 + for (;;) { + p2 *= 10; + delta *= 10; + char d = static_cast(p2 >> -one.e); + if (d || *len) + buffer[(*len)++] = static_cast('0' + d); + p2 &= one.f - 1; + kappa--; + if (p2 < delta) { + *K += kappa; + int index = -kappa; + GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 9 ? kPow10[index] : 0)); + return; + } + } +} + +inline void Grisu2(double value, char* buffer, int* length, int* K) { + const DiyFp v(value); + DiyFp w_m, w_p; + v.NormalizedBoundaries(&w_m, &w_p); + + const DiyFp c_mk = GetCachedPower(w_p.e, K); + const DiyFp W = v.Normalize() * c_mk; + DiyFp Wp = w_p * c_mk; + DiyFp Wm = w_m * c_mk; + Wm.f++; + Wp.f--; + DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K); +} + +inline char* WriteExponent(int K, char* buffer) { + if (K < 0) { + *buffer++ = '-'; + K = -K; + } + + if (K >= 100) { + *buffer++ = static_cast('0' + static_cast(K / 100)); + K %= 100; + const char* d = GetDigitsLut() + K * 2; + *buffer++ = d[0]; + *buffer++ = d[1]; + } + else if (K >= 10) { + const char* d = GetDigitsLut() + K * 2; + *buffer++ = d[0]; + *buffer++ = d[1]; + } + else + *buffer++ = static_cast('0' + static_cast(K)); + + return buffer; +} + +inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces) { + const int kk = length + k; // 10^(kk-1) <= v < 10^kk + + if (0 <= k && kk <= 21) { + // 1234e7 -> 12340000000 + for (int i = length; i < kk; i++) + buffer[i] = '0'; + buffer[kk] = '.'; + buffer[kk + 1] = '0'; + return &buffer[kk + 2]; + } + else if (0 < kk && kk <= 21) { + // 1234e-2 -> 12.34 + std::memmove(&buffer[kk + 1], &buffer[kk], static_cast(length - kk)); + buffer[kk] = '.'; + if (0 > k + maxDecimalPlaces) { + // When maxDecimalPlaces = 2, 1.2345 -> 1.23, 1.102 -> 1.1 + // Remove extra trailing zeros (at least one) after truncation. + for (int i = kk + maxDecimalPlaces; i > kk + 1; i--) + if (buffer[i] != '0') + return &buffer[i + 1]; + return &buffer[kk + 2]; // Reserve one zero + } + else + return &buffer[length + 1]; + } + else if (-6 < kk && kk <= 0) { + // 1234e-6 -> 0.001234 + const int offset = 2 - kk; + std::memmove(&buffer[offset], &buffer[0], static_cast(length)); + buffer[0] = '0'; + buffer[1] = '.'; + for (int i = 2; i < offset; i++) + buffer[i] = '0'; + if (length - kk > maxDecimalPlaces) { + // When maxDecimalPlaces = 2, 0.123 -> 0.12, 0.102 -> 0.1 + // Remove extra trailing zeros (at least one) after truncation. + for (int i = maxDecimalPlaces + 1; i > 2; i--) + if (buffer[i] != '0') + return &buffer[i + 1]; + return &buffer[3]; // Reserve one zero + } + else + return &buffer[length + offset]; + } + else if (kk < -maxDecimalPlaces) { + // Truncate to zero + buffer[0] = '0'; + buffer[1] = '.'; + buffer[2] = '0'; + return &buffer[3]; + } + else if (length == 1) { + // 1e30 + buffer[1] = 'e'; + return WriteExponent(kk - 1, &buffer[2]); + } + else { + // 1234e30 -> 1.234e33 + std::memmove(&buffer[2], &buffer[1], static_cast(length - 1)); + buffer[1] = '.'; + buffer[length + 1] = 'e'; + return WriteExponent(kk - 1, &buffer[0 + length + 2]); + } +} + +inline char* dtoa(double value, char* buffer, int maxDecimalPlaces = 324) { + RAPIDJSON_ASSERT(maxDecimalPlaces >= 1); + Double d(value); + if (d.IsZero()) { + if (d.Sign()) + *buffer++ = '-'; // -0.0, Issue #289 + buffer[0] = '0'; + buffer[1] = '.'; + buffer[2] = '0'; + return &buffer[3]; + } + else { + if (value < 0) { + *buffer++ = '-'; + value = -value; + } + int length, K; + Grisu2(value, buffer, &length, &K); + return Prettify(buffer, length, K, maxDecimalPlaces); + } +} + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_DTOA_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/internal/ieee754.h b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/ieee754.h new file mode 100644 index 000000000..68c9e9664 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/ieee754.h @@ -0,0 +1,78 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_IEEE754_ +#define RAPIDJSON_IEEE754_ + +#include "../rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +class Double { +public: + Double() {} + Double(double d) : d_(d) {} + Double(uint64_t u) : u_(u) {} + + double Value() const { return d_; } + uint64_t Uint64Value() const { return u_; } + + double NextPositiveDouble() const { + RAPIDJSON_ASSERT(!Sign()); + return Double(u_ + 1).Value(); + } + + bool Sign() const { return (u_ & kSignMask) != 0; } + uint64_t Significand() const { return u_ & kSignificandMask; } + int Exponent() const { return static_cast(((u_ & kExponentMask) >> kSignificandSize) - kExponentBias); } + + bool IsNan() const { return (u_ & kExponentMask) == kExponentMask && Significand() != 0; } + bool IsInf() const { return (u_ & kExponentMask) == kExponentMask && Significand() == 0; } + bool IsNanOrInf() const { return (u_ & kExponentMask) == kExponentMask; } + bool IsNormal() const { return (u_ & kExponentMask) != 0 || Significand() == 0; } + bool IsZero() const { return (u_ & (kExponentMask | kSignificandMask)) == 0; } + + uint64_t IntegerSignificand() const { return IsNormal() ? Significand() | kHiddenBit : Significand(); } + int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; } + uint64_t ToBias() const { return (u_ & kSignMask) ? ~u_ + 1 : u_ | kSignMask; } + + static int EffectiveSignificandSize(int order) { + if (order >= -1021) + return 53; + else if (order <= -1074) + return 0; + else + return order + 1074; + } + +private: + static const int kSignificandSize = 52; + static const int kExponentBias = 0x3FF; + static const int kDenormalExponent = 1 - kExponentBias; + static const uint64_t kSignMask = RAPIDJSON_UINT64_C2(0x80000000, 0x00000000); + static const uint64_t kExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); + static const uint64_t kSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); + static const uint64_t kHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); + + union { + double d_; + uint64_t u_; + }; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_IEEE754_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/internal/itoa.h b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/itoa.h new file mode 100644 index 000000000..9fe8c932f --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/itoa.h @@ -0,0 +1,308 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ITOA_ +#define RAPIDJSON_ITOA_ + +#include "../rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +inline const char* GetDigitsLut() { + static const char cDigitsLut[200] = { + '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9', + '1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9', + '2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9', + '3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9', + '4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9', + '5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9', + '6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9', + '7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9', + '8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9', + '9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9' + }; + return cDigitsLut; +} + +inline char* u32toa(uint32_t value, char* buffer) { + RAPIDJSON_ASSERT(buffer != 0); + + const char* cDigitsLut = GetDigitsLut(); + + if (value < 10000) { + const uint32_t d1 = (value / 100) << 1; + const uint32_t d2 = (value % 100) << 1; + + if (value >= 1000) + *buffer++ = cDigitsLut[d1]; + if (value >= 100) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 10) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + } + else if (value < 100000000) { + // value = bbbbcccc + const uint32_t b = value / 10000; + const uint32_t c = value % 10000; + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + if (value >= 10000000) + *buffer++ = cDigitsLut[d1]; + if (value >= 1000000) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 100000) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + else { + // value = aabbbbcccc in decimal + + const uint32_t a = value / 100000000; // 1 to 42 + value %= 100000000; + + if (a >= 10) { + const unsigned i = a << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else + *buffer++ = static_cast('0' + static_cast(a)); + + const uint32_t b = value / 10000; // 0 to 9999 + const uint32_t c = value % 10000; // 0 to 9999 + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + *buffer++ = cDigitsLut[d1]; + *buffer++ = cDigitsLut[d1 + 1]; + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + return buffer; +} + +inline char* i32toa(int32_t value, char* buffer) { + RAPIDJSON_ASSERT(buffer != 0); + uint32_t u = static_cast(value); + if (value < 0) { + *buffer++ = '-'; + u = ~u + 1; + } + + return u32toa(u, buffer); +} + +inline char* u64toa(uint64_t value, char* buffer) { + RAPIDJSON_ASSERT(buffer != 0); + const char* cDigitsLut = GetDigitsLut(); + const uint64_t kTen8 = 100000000; + const uint64_t kTen9 = kTen8 * 10; + const uint64_t kTen10 = kTen8 * 100; + const uint64_t kTen11 = kTen8 * 1000; + const uint64_t kTen12 = kTen8 * 10000; + const uint64_t kTen13 = kTen8 * 100000; + const uint64_t kTen14 = kTen8 * 1000000; + const uint64_t kTen15 = kTen8 * 10000000; + const uint64_t kTen16 = kTen8 * kTen8; + + if (value < kTen8) { + uint32_t v = static_cast(value); + if (v < 10000) { + const uint32_t d1 = (v / 100) << 1; + const uint32_t d2 = (v % 100) << 1; + + if (v >= 1000) + *buffer++ = cDigitsLut[d1]; + if (v >= 100) + *buffer++ = cDigitsLut[d1 + 1]; + if (v >= 10) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + } + else { + // value = bbbbcccc + const uint32_t b = v / 10000; + const uint32_t c = v % 10000; + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + if (value >= 10000000) + *buffer++ = cDigitsLut[d1]; + if (value >= 1000000) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 100000) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + } + else if (value < kTen16) { + const uint32_t v0 = static_cast(value / kTen8); + const uint32_t v1 = static_cast(value % kTen8); + + const uint32_t b0 = v0 / 10000; + const uint32_t c0 = v0 % 10000; + + const uint32_t d1 = (b0 / 100) << 1; + const uint32_t d2 = (b0 % 100) << 1; + + const uint32_t d3 = (c0 / 100) << 1; + const uint32_t d4 = (c0 % 100) << 1; + + const uint32_t b1 = v1 / 10000; + const uint32_t c1 = v1 % 10000; + + const uint32_t d5 = (b1 / 100) << 1; + const uint32_t d6 = (b1 % 100) << 1; + + const uint32_t d7 = (c1 / 100) << 1; + const uint32_t d8 = (c1 % 100) << 1; + + if (value >= kTen15) + *buffer++ = cDigitsLut[d1]; + if (value >= kTen14) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= kTen13) + *buffer++ = cDigitsLut[d2]; + if (value >= kTen12) + *buffer++ = cDigitsLut[d2 + 1]; + if (value >= kTen11) + *buffer++ = cDigitsLut[d3]; + if (value >= kTen10) + *buffer++ = cDigitsLut[d3 + 1]; + if (value >= kTen9) + *buffer++ = cDigitsLut[d4]; + + *buffer++ = cDigitsLut[d4 + 1]; + *buffer++ = cDigitsLut[d5]; + *buffer++ = cDigitsLut[d5 + 1]; + *buffer++ = cDigitsLut[d6]; + *buffer++ = cDigitsLut[d6 + 1]; + *buffer++ = cDigitsLut[d7]; + *buffer++ = cDigitsLut[d7 + 1]; + *buffer++ = cDigitsLut[d8]; + *buffer++ = cDigitsLut[d8 + 1]; + } + else { + const uint32_t a = static_cast(value / kTen16); // 1 to 1844 + value %= kTen16; + + if (a < 10) + *buffer++ = static_cast('0' + static_cast(a)); + else if (a < 100) { + const uint32_t i = a << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else if (a < 1000) { + *buffer++ = static_cast('0' + static_cast(a / 100)); + + const uint32_t i = (a % 100) << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else { + const uint32_t i = (a / 100) << 1; + const uint32_t j = (a % 100) << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + *buffer++ = cDigitsLut[j]; + *buffer++ = cDigitsLut[j + 1]; + } + + const uint32_t v0 = static_cast(value / kTen8); + const uint32_t v1 = static_cast(value % kTen8); + + const uint32_t b0 = v0 / 10000; + const uint32_t c0 = v0 % 10000; + + const uint32_t d1 = (b0 / 100) << 1; + const uint32_t d2 = (b0 % 100) << 1; + + const uint32_t d3 = (c0 / 100) << 1; + const uint32_t d4 = (c0 % 100) << 1; + + const uint32_t b1 = v1 / 10000; + const uint32_t c1 = v1 % 10000; + + const uint32_t d5 = (b1 / 100) << 1; + const uint32_t d6 = (b1 % 100) << 1; + + const uint32_t d7 = (c1 / 100) << 1; + const uint32_t d8 = (c1 % 100) << 1; + + *buffer++ = cDigitsLut[d1]; + *buffer++ = cDigitsLut[d1 + 1]; + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + *buffer++ = cDigitsLut[d5]; + *buffer++ = cDigitsLut[d5 + 1]; + *buffer++ = cDigitsLut[d6]; + *buffer++ = cDigitsLut[d6 + 1]; + *buffer++ = cDigitsLut[d7]; + *buffer++ = cDigitsLut[d7 + 1]; + *buffer++ = cDigitsLut[d8]; + *buffer++ = cDigitsLut[d8 + 1]; + } + + return buffer; +} + +inline char* i64toa(int64_t value, char* buffer) { + RAPIDJSON_ASSERT(buffer != 0); + uint64_t u = static_cast(value); + if (value < 0) { + *buffer++ = '-'; + u = ~u + 1; + } + + return u64toa(u, buffer); +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ITOA_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/internal/meta.h b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/meta.h new file mode 100644 index 000000000..27092dc0d --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/meta.h @@ -0,0 +1,186 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_META_H_ +#define RAPIDJSON_INTERNAL_META_H_ + +#include "../rapidjson.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#if defined(_MSC_VER) && !defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(6334) +#endif + +#if RAPIDJSON_HAS_CXX11_TYPETRAITS +#include +#endif + +//@cond RAPIDJSON_INTERNAL +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +// Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching +template struct Void { typedef void Type; }; + +/////////////////////////////////////////////////////////////////////////////// +// BoolType, TrueType, FalseType +// +template struct BoolType { + static const bool Value = Cond; + typedef BoolType Type; +}; +typedef BoolType TrueType; +typedef BoolType FalseType; + + +/////////////////////////////////////////////////////////////////////////////// +// SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr +// + +template struct SelectIfImpl { template struct Apply { typedef T1 Type; }; }; +template <> struct SelectIfImpl { template struct Apply { typedef T2 Type; }; }; +template struct SelectIfCond : SelectIfImpl::template Apply {}; +template struct SelectIf : SelectIfCond {}; + +template struct AndExprCond : FalseType {}; +template <> struct AndExprCond : TrueType {}; +template struct OrExprCond : TrueType {}; +template <> struct OrExprCond : FalseType {}; + +template struct BoolExpr : SelectIf::Type {}; +template struct NotExpr : SelectIf::Type {}; +template struct AndExpr : AndExprCond::Type {}; +template struct OrExpr : OrExprCond::Type {}; + + +/////////////////////////////////////////////////////////////////////////////// +// AddConst, MaybeAddConst, RemoveConst +template struct AddConst { typedef const T Type; }; +template struct MaybeAddConst : SelectIfCond {}; +template struct RemoveConst { typedef T Type; }; +template struct RemoveConst { typedef T Type; }; + + +/////////////////////////////////////////////////////////////////////////////// +// IsSame, IsConst, IsMoreConst, IsPointer +// +template struct IsSame : FalseType {}; +template struct IsSame : TrueType {}; + +template struct IsConst : FalseType {}; +template struct IsConst : TrueType {}; + +template +struct IsMoreConst + : AndExpr::Type, typename RemoveConst::Type>, + BoolType::Value >= IsConst::Value> >::Type {}; + +template struct IsPointer : FalseType {}; +template struct IsPointer : TrueType {}; + +/////////////////////////////////////////////////////////////////////////////// +// IsBaseOf +// +#if RAPIDJSON_HAS_CXX11_TYPETRAITS + +template struct IsBaseOf + : BoolType< ::std::is_base_of::value> {}; + +#else // simplified version adopted from Boost + +template struct IsBaseOfImpl { + RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0); + RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0); + + typedef char (&Yes)[1]; + typedef char (&No) [2]; + + template + static Yes Check(const D*, T); + static No Check(const B*, int); + + struct Host { + operator const B*() const; + operator const D*(); + }; + + enum { Value = (sizeof(Check(Host(), 0)) == sizeof(Yes)) }; +}; + +template struct IsBaseOf + : OrExpr, BoolExpr > >::Type {}; + +#endif // RAPIDJSON_HAS_CXX11_TYPETRAITS + + +////////////////////////////////////////////////////////////////////////// +// EnableIf / DisableIf +// +template struct EnableIfCond { typedef T Type; }; +template struct EnableIfCond { /* empty */ }; + +template struct DisableIfCond { typedef T Type; }; +template struct DisableIfCond { /* empty */ }; + +template +struct EnableIf : EnableIfCond {}; + +template +struct DisableIf : DisableIfCond {}; + +// SFINAE helpers +struct SfinaeTag {}; +template struct RemoveSfinaeTag; +template struct RemoveSfinaeTag { typedef T Type; }; + +#define RAPIDJSON_REMOVEFPTR_(type) \ + typename ::RAPIDJSON_NAMESPACE::internal::RemoveSfinaeTag \ + < ::RAPIDJSON_NAMESPACE::internal::SfinaeTag&(*) type>::Type + +#define RAPIDJSON_ENABLEIF(cond) \ + typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ + ::Type * = NULL + +#define RAPIDJSON_DISABLEIF(cond) \ + typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ + ::Type * = NULL + +#define RAPIDJSON_ENABLEIF_RETURN(cond,returntype) \ + typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ + ::Type + +#define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \ + typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ + ::Type + +} // namespace internal +RAPIDJSON_NAMESPACE_END +//@endcond + +#if defined(_MSC_VER) && !defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_META_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/internal/pow10.h b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/pow10.h new file mode 100644 index 000000000..eae1a43ed --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/pow10.h @@ -0,0 +1,55 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_POW10_ +#define RAPIDJSON_POW10_ + +#include "../rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Computes integer powers of 10 in double (10.0^n). +/*! This function uses lookup table for fast and accurate results. + \param n non-negative exponent. Must <= 308. + \return 10.0^n +*/ +inline double Pow10(int n) { + static const double e[] = { // 1e-0...1e308: 309 * 8 bytes = 2472 bytes + 1e+0, + 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20, + 1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39, 1e+40, + 1e+41, 1e+42, 1e+43, 1e+44, 1e+45, 1e+46, 1e+47, 1e+48, 1e+49, 1e+50, 1e+51, 1e+52, 1e+53, 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59, 1e+60, + 1e+61, 1e+62, 1e+63, 1e+64, 1e+65, 1e+66, 1e+67, 1e+68, 1e+69, 1e+70, 1e+71, 1e+72, 1e+73, 1e+74, 1e+75, 1e+76, 1e+77, 1e+78, 1e+79, 1e+80, + 1e+81, 1e+82, 1e+83, 1e+84, 1e+85, 1e+86, 1e+87, 1e+88, 1e+89, 1e+90, 1e+91, 1e+92, 1e+93, 1e+94, 1e+95, 1e+96, 1e+97, 1e+98, 1e+99, 1e+100, + 1e+101,1e+102,1e+103,1e+104,1e+105,1e+106,1e+107,1e+108,1e+109,1e+110,1e+111,1e+112,1e+113,1e+114,1e+115,1e+116,1e+117,1e+118,1e+119,1e+120, + 1e+121,1e+122,1e+123,1e+124,1e+125,1e+126,1e+127,1e+128,1e+129,1e+130,1e+131,1e+132,1e+133,1e+134,1e+135,1e+136,1e+137,1e+138,1e+139,1e+140, + 1e+141,1e+142,1e+143,1e+144,1e+145,1e+146,1e+147,1e+148,1e+149,1e+150,1e+151,1e+152,1e+153,1e+154,1e+155,1e+156,1e+157,1e+158,1e+159,1e+160, + 1e+161,1e+162,1e+163,1e+164,1e+165,1e+166,1e+167,1e+168,1e+169,1e+170,1e+171,1e+172,1e+173,1e+174,1e+175,1e+176,1e+177,1e+178,1e+179,1e+180, + 1e+181,1e+182,1e+183,1e+184,1e+185,1e+186,1e+187,1e+188,1e+189,1e+190,1e+191,1e+192,1e+193,1e+194,1e+195,1e+196,1e+197,1e+198,1e+199,1e+200, + 1e+201,1e+202,1e+203,1e+204,1e+205,1e+206,1e+207,1e+208,1e+209,1e+210,1e+211,1e+212,1e+213,1e+214,1e+215,1e+216,1e+217,1e+218,1e+219,1e+220, + 1e+221,1e+222,1e+223,1e+224,1e+225,1e+226,1e+227,1e+228,1e+229,1e+230,1e+231,1e+232,1e+233,1e+234,1e+235,1e+236,1e+237,1e+238,1e+239,1e+240, + 1e+241,1e+242,1e+243,1e+244,1e+245,1e+246,1e+247,1e+248,1e+249,1e+250,1e+251,1e+252,1e+253,1e+254,1e+255,1e+256,1e+257,1e+258,1e+259,1e+260, + 1e+261,1e+262,1e+263,1e+264,1e+265,1e+266,1e+267,1e+268,1e+269,1e+270,1e+271,1e+272,1e+273,1e+274,1e+275,1e+276,1e+277,1e+278,1e+279,1e+280, + 1e+281,1e+282,1e+283,1e+284,1e+285,1e+286,1e+287,1e+288,1e+289,1e+290,1e+291,1e+292,1e+293,1e+294,1e+295,1e+296,1e+297,1e+298,1e+299,1e+300, + 1e+301,1e+302,1e+303,1e+304,1e+305,1e+306,1e+307,1e+308 + }; + RAPIDJSON_ASSERT(n >= 0 && n <= 308); + return e[n]; +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_POW10_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/internal/regex.h b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/regex.h new file mode 100644 index 000000000..6446c403a --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/regex.h @@ -0,0 +1,739 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_REGEX_H_ +#define RAPIDJSON_INTERNAL_REGEX_H_ + +#include "../allocators.h" +#include "../stream.h" +#include "stack.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifndef RAPIDJSON_REGEX_VERBOSE +#define RAPIDJSON_REGEX_VERBOSE 0 +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +/////////////////////////////////////////////////////////////////////////////// +// DecodedStream + +template +class DecodedStream { +public: + DecodedStream(SourceStream& ss) : ss_(ss), codepoint_() { Decode(); } + unsigned Peek() { return codepoint_; } + unsigned Take() { + unsigned c = codepoint_; + if (c) // No further decoding when '\0' + Decode(); + return c; + } + +private: + void Decode() { + if (!Encoding::Decode(ss_, &codepoint_)) + codepoint_ = 0; + } + + SourceStream& ss_; + unsigned codepoint_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericRegex + +static const SizeType kRegexInvalidState = ~SizeType(0); //!< Represents an invalid index in GenericRegex::State::out, out1 +static const SizeType kRegexInvalidRange = ~SizeType(0); + +template +class GenericRegexSearch; + +//! Regular expression engine with subset of ECMAscript grammar. +/*! + Supported regular expression syntax: + - \c ab Concatenation + - \c a|b Alternation + - \c a? Zero or one + - \c a* Zero or more + - \c a+ One or more + - \c a{3} Exactly 3 times + - \c a{3,} At least 3 times + - \c a{3,5} 3 to 5 times + - \c (ab) Grouping + - \c ^a At the beginning + - \c a$ At the end + - \c . Any character + - \c [abc] Character classes + - \c [a-c] Character class range + - \c [a-z0-9_] Character class combination + - \c [^abc] Negated character classes + - \c [^a-c] Negated character class range + - \c [\b] Backspace (U+0008) + - \c \\| \\\\ ... Escape characters + - \c \\f Form feed (U+000C) + - \c \\n Line feed (U+000A) + - \c \\r Carriage return (U+000D) + - \c \\t Tab (U+0009) + - \c \\v Vertical tab (U+000B) + + \note This is a Thompson NFA engine, implemented with reference to + Cox, Russ. "Regular Expression Matching Can Be Simple And Fast (but is slow in Java, Perl, PHP, Python, Ruby,...).", + https://swtch.com/~rsc/regexp/regexp1.html +*/ +template +class GenericRegex { +public: + typedef Encoding EncodingType; + typedef typename Encoding::Ch Ch; + template friend class GenericRegexSearch; + + GenericRegex(const Ch* source, Allocator* allocator = 0) : + ownAllocator_(allocator ? 0 : RAPIDJSON_NEW(Allocator)()), allocator_(allocator ? allocator : ownAllocator_), + states_(allocator_, 256), ranges_(allocator_, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(), + anchorBegin_(), anchorEnd_() + { + GenericStringStream ss(source); + DecodedStream, Encoding> ds(ss); + Parse(ds); + } + + ~GenericRegex() + { + RAPIDJSON_DELETE(ownAllocator_); + } + + bool IsValid() const { + return root_ != kRegexInvalidState; + } + +private: + enum Operator { + kZeroOrOne, + kZeroOrMore, + kOneOrMore, + kConcatenation, + kAlternation, + kLeftParenthesis + }; + + static const unsigned kAnyCharacterClass = 0xFFFFFFFF; //!< For '.' + static const unsigned kRangeCharacterClass = 0xFFFFFFFE; + static const unsigned kRangeNegationFlag = 0x80000000; + + struct Range { + unsigned start; // + unsigned end; + SizeType next; + }; + + struct State { + SizeType out; //!< Equals to kInvalid for matching state + SizeType out1; //!< Equals to non-kInvalid for split + SizeType rangeStart; + unsigned codepoint; + }; + + struct Frag { + Frag(SizeType s, SizeType o, SizeType m) : start(s), out(o), minIndex(m) {} + SizeType start; + SizeType out; //!< link-list of all output states + SizeType minIndex; + }; + + State& GetState(SizeType index) { + RAPIDJSON_ASSERT(index < stateCount_); + return states_.template Bottom()[index]; + } + + const State& GetState(SizeType index) const { + RAPIDJSON_ASSERT(index < stateCount_); + return states_.template Bottom()[index]; + } + + Range& GetRange(SizeType index) { + RAPIDJSON_ASSERT(index < rangeCount_); + return ranges_.template Bottom()[index]; + } + + const Range& GetRange(SizeType index) const { + RAPIDJSON_ASSERT(index < rangeCount_); + return ranges_.template Bottom()[index]; + } + + template + void Parse(DecodedStream& ds) { + Stack operandStack(allocator_, 256); // Frag + Stack operatorStack(allocator_, 256); // Operator + Stack atomCountStack(allocator_, 256); // unsigned (Atom per parenthesis) + + *atomCountStack.template Push() = 0; + + unsigned codepoint; + while (ds.Peek() != 0) { + switch (codepoint = ds.Take()) { + case '^': + anchorBegin_ = true; + break; + + case '$': + anchorEnd_ = true; + break; + + case '|': + while (!operatorStack.Empty() && *operatorStack.template Top() < kAlternation) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + *operatorStack.template Push() = kAlternation; + *atomCountStack.template Top() = 0; + break; + + case '(': + *operatorStack.template Push() = kLeftParenthesis; + *atomCountStack.template Push() = 0; + break; + + case ')': + while (!operatorStack.Empty() && *operatorStack.template Top() != kLeftParenthesis) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + if (operatorStack.Empty()) + return; + operatorStack.template Pop(1); + atomCountStack.template Pop(1); + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '?': + if (!Eval(operandStack, kZeroOrOne)) + return; + break; + + case '*': + if (!Eval(operandStack, kZeroOrMore)) + return; + break; + + case '+': + if (!Eval(operandStack, kOneOrMore)) + return; + break; + + case '{': + { + unsigned n, m; + if (!ParseUnsigned(ds, &n)) + return; + + if (ds.Peek() == ',') { + ds.Take(); + if (ds.Peek() == '}') + m = kInfinityQuantifier; + else if (!ParseUnsigned(ds, &m) || m < n) + return; + } + else + m = n; + + if (!EvalQuantifier(operandStack, n, m) || ds.Peek() != '}') + return; + ds.Take(); + } + break; + + case '.': + PushOperand(operandStack, kAnyCharacterClass); + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '[': + { + SizeType range; + if (!ParseRange(ds, &range)) + return; + SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, kRangeCharacterClass); + GetState(s).rangeStart = range; + *operandStack.template Push() = Frag(s, s, s); + } + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '\\': // Escape character + if (!CharacterEscape(ds, &codepoint)) + return; // Unsupported escape character + // fall through to default + RAPIDJSON_DELIBERATE_FALLTHROUGH; + + default: // Pattern character + PushOperand(operandStack, codepoint); + ImplicitConcatenation(atomCountStack, operatorStack); + } + } + + while (!operatorStack.Empty()) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + + // Link the operand to matching state. + if (operandStack.GetSize() == sizeof(Frag)) { + Frag* e = operandStack.template Pop(1); + Patch(e->out, NewState(kRegexInvalidState, kRegexInvalidState, 0)); + root_ = e->start; + +#if RAPIDJSON_REGEX_VERBOSE + printf("root: %d\n", root_); + for (SizeType i = 0; i < stateCount_ ; i++) { + State& s = GetState(i); + printf("[%2d] out: %2d out1: %2d c: '%c'\n", i, s.out, s.out1, (char)s.codepoint); + } + printf("\n"); +#endif + } + } + + SizeType NewState(SizeType out, SizeType out1, unsigned codepoint) { + State* s = states_.template Push(); + s->out = out; + s->out1 = out1; + s->codepoint = codepoint; + s->rangeStart = kRegexInvalidRange; + return stateCount_++; + } + + void PushOperand(Stack& operandStack, unsigned codepoint) { + SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, codepoint); + *operandStack.template Push() = Frag(s, s, s); + } + + void ImplicitConcatenation(Stack& atomCountStack, Stack& operatorStack) { + if (*atomCountStack.template Top()) + *operatorStack.template Push() = kConcatenation; + (*atomCountStack.template Top())++; + } + + SizeType Append(SizeType l1, SizeType l2) { + SizeType old = l1; + while (GetState(l1).out != kRegexInvalidState) + l1 = GetState(l1).out; + GetState(l1).out = l2; + return old; + } + + void Patch(SizeType l, SizeType s) { + for (SizeType next; l != kRegexInvalidState; l = next) { + next = GetState(l).out; + GetState(l).out = s; + } + } + + bool Eval(Stack& operandStack, Operator op) { + switch (op) { + case kConcatenation: + RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag) * 2); + { + Frag e2 = *operandStack.template Pop(1); + Frag e1 = *operandStack.template Pop(1); + Patch(e1.out, e2.start); + *operandStack.template Push() = Frag(e1.start, e2.out, Min(e1.minIndex, e2.minIndex)); + } + return true; + + case kAlternation: + if (operandStack.GetSize() >= sizeof(Frag) * 2) { + Frag e2 = *operandStack.template Pop(1); + Frag e1 = *operandStack.template Pop(1); + SizeType s = NewState(e1.start, e2.start, 0); + *operandStack.template Push() = Frag(s, Append(e1.out, e2.out), Min(e1.minIndex, e2.minIndex)); + return true; + } + return false; + + case kZeroOrOne: + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + *operandStack.template Push() = Frag(s, Append(e.out, s), e.minIndex); + return true; + } + return false; + + case kZeroOrMore: + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + Patch(e.out, s); + *operandStack.template Push() = Frag(s, s, e.minIndex); + return true; + } + return false; + + case kOneOrMore: + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + Patch(e.out, s); + *operandStack.template Push() = Frag(e.start, s, e.minIndex); + return true; + } + return false; + + default: + // syntax error (e.g. unclosed kLeftParenthesis) + return false; + } + } + + bool EvalQuantifier(Stack& operandStack, unsigned n, unsigned m) { + RAPIDJSON_ASSERT(n <= m); + RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag)); + + if (n == 0) { + if (m == 0) // a{0} not support + return false; + else if (m == kInfinityQuantifier) + Eval(operandStack, kZeroOrMore); // a{0,} -> a* + else { + Eval(operandStack, kZeroOrOne); // a{0,5} -> a? + for (unsigned i = 0; i < m - 1; i++) + CloneTopOperand(operandStack); // a{0,5} -> a? a? a? a? a? + for (unsigned i = 0; i < m - 1; i++) + Eval(operandStack, kConcatenation); // a{0,5} -> a?a?a?a?a? + } + return true; + } + + for (unsigned i = 0; i < n - 1; i++) // a{3} -> a a a + CloneTopOperand(operandStack); + + if (m == kInfinityQuantifier) + Eval(operandStack, kOneOrMore); // a{3,} -> a a a+ + else if (m > n) { + CloneTopOperand(operandStack); // a{3,5} -> a a a a + Eval(operandStack, kZeroOrOne); // a{3,5} -> a a a a? + for (unsigned i = n; i < m - 1; i++) + CloneTopOperand(operandStack); // a{3,5} -> a a a a? a? + for (unsigned i = n; i < m; i++) + Eval(operandStack, kConcatenation); // a{3,5} -> a a aa?a? + } + + for (unsigned i = 0; i < n - 1; i++) + Eval(operandStack, kConcatenation); // a{3} -> aaa, a{3,} -> aaa+, a{3.5} -> aaaa?a? + + return true; + } + + static SizeType Min(SizeType a, SizeType b) { return a < b ? a : b; } + + void CloneTopOperand(Stack& operandStack) { + const Frag src = *operandStack.template Top(); // Copy constructor to prevent invalidation + SizeType count = stateCount_ - src.minIndex; // Assumes top operand contains states in [src->minIndex, stateCount_) + State* s = states_.template Push(count); + memcpy(s, &GetState(src.minIndex), count * sizeof(State)); + for (SizeType j = 0; j < count; j++) { + if (s[j].out != kRegexInvalidState) + s[j].out += count; + if (s[j].out1 != kRegexInvalidState) + s[j].out1 += count; + } + *operandStack.template Push() = Frag(src.start + count, src.out + count, src.minIndex + count); + stateCount_ += count; + } + + template + bool ParseUnsigned(DecodedStream& ds, unsigned* u) { + unsigned r = 0; + if (ds.Peek() < '0' || ds.Peek() > '9') + return false; + while (ds.Peek() >= '0' && ds.Peek() <= '9') { + if (r >= 429496729 && ds.Peek() > '5') // 2^32 - 1 = 4294967295 + return false; // overflow + r = r * 10 + (ds.Take() - '0'); + } + *u = r; + return true; + } + + template + bool ParseRange(DecodedStream& ds, SizeType* range) { + bool isBegin = true; + bool negate = false; + int step = 0; + SizeType start = kRegexInvalidRange; + SizeType current = kRegexInvalidRange; + unsigned codepoint; + while ((codepoint = ds.Take()) != 0) { + if (isBegin) { + isBegin = false; + if (codepoint == '^') { + negate = true; + continue; + } + } + + switch (codepoint) { + case ']': + if (start == kRegexInvalidRange) + return false; // Error: nothing inside [] + if (step == 2) { // Add trailing '-' + SizeType r = NewRange('-'); + RAPIDJSON_ASSERT(current != kRegexInvalidRange); + GetRange(current).next = r; + } + if (negate) + GetRange(start).start |= kRangeNegationFlag; + *range = start; + return true; + + case '\\': + if (ds.Peek() == 'b') { + ds.Take(); + codepoint = 0x0008; // Escape backspace character + } + else if (!CharacterEscape(ds, &codepoint)) + return false; + // fall through to default + RAPIDJSON_DELIBERATE_FALLTHROUGH; + + default: + switch (step) { + case 1: + if (codepoint == '-') { + step++; + break; + } + // fall through to step 0 for other characters + RAPIDJSON_DELIBERATE_FALLTHROUGH; + + case 0: + { + SizeType r = NewRange(codepoint); + if (current != kRegexInvalidRange) + GetRange(current).next = r; + if (start == kRegexInvalidRange) + start = r; + current = r; + } + step = 1; + break; + + default: + RAPIDJSON_ASSERT(step == 2); + GetRange(current).end = codepoint; + step = 0; + } + } + } + return false; + } + + SizeType NewRange(unsigned codepoint) { + Range* r = ranges_.template Push(); + r->start = r->end = codepoint; + r->next = kRegexInvalidRange; + return rangeCount_++; + } + + template + bool CharacterEscape(DecodedStream& ds, unsigned* escapedCodepoint) { + unsigned codepoint; + switch (codepoint = ds.Take()) { + case '^': + case '$': + case '|': + case '(': + case ')': + case '?': + case '*': + case '+': + case '.': + case '[': + case ']': + case '{': + case '}': + case '\\': + *escapedCodepoint = codepoint; return true; + case 'f': *escapedCodepoint = 0x000C; return true; + case 'n': *escapedCodepoint = 0x000A; return true; + case 'r': *escapedCodepoint = 0x000D; return true; + case 't': *escapedCodepoint = 0x0009; return true; + case 'v': *escapedCodepoint = 0x000B; return true; + default: + return false; // Unsupported escape character + } + } + + Allocator* ownAllocator_; + Allocator* allocator_; + Stack states_; + Stack ranges_; + SizeType root_; + SizeType stateCount_; + SizeType rangeCount_; + + static const unsigned kInfinityQuantifier = ~0u; + + // For SearchWithAnchoring() + bool anchorBegin_; + bool anchorEnd_; +}; + +template +class GenericRegexSearch { +public: + typedef typename RegexType::EncodingType Encoding; + typedef typename Encoding::Ch Ch; + + GenericRegexSearch(const RegexType& regex, Allocator* allocator = 0) : + regex_(regex), allocator_(allocator), ownAllocator_(0), + state0_(allocator, 0), state1_(allocator, 0), stateSet_() + { + RAPIDJSON_ASSERT(regex_.IsValid()); + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + stateSet_ = static_cast(allocator_->Malloc(GetStateSetSize())); + state0_.template Reserve(regex_.stateCount_); + state1_.template Reserve(regex_.stateCount_); + } + + ~GenericRegexSearch() { + Allocator::Free(stateSet_); + RAPIDJSON_DELETE(ownAllocator_); + } + + template + bool Match(InputStream& is) { + return SearchWithAnchoring(is, true, true); + } + + bool Match(const Ch* s) { + GenericStringStream is(s); + return Match(is); + } + + template + bool Search(InputStream& is) { + return SearchWithAnchoring(is, regex_.anchorBegin_, regex_.anchorEnd_); + } + + bool Search(const Ch* s) { + GenericStringStream is(s); + return Search(is); + } + +private: + typedef typename RegexType::State State; + typedef typename RegexType::Range Range; + + template + bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) { + DecodedStream ds(is); + + state0_.Clear(); + Stack *current = &state0_, *next = &state1_; + const size_t stateSetSize = GetStateSetSize(); + std::memset(stateSet_, 0, stateSetSize); + + bool matched = AddState(*current, regex_.root_); + unsigned codepoint; + while (!current->Empty() && (codepoint = ds.Take()) != 0) { + std::memset(stateSet_, 0, stateSetSize); + next->Clear(); + matched = false; + for (const SizeType* s = current->template Bottom(); s != current->template End(); ++s) { + const State& sr = regex_.GetState(*s); + if (sr.codepoint == codepoint || + sr.codepoint == RegexType::kAnyCharacterClass || + (sr.codepoint == RegexType::kRangeCharacterClass && MatchRange(sr.rangeStart, codepoint))) + { + matched = AddState(*next, sr.out) || matched; + if (!anchorEnd && matched) + return true; + } + if (!anchorBegin) + AddState(*next, regex_.root_); + } + internal::Swap(current, next); + } + + return matched; + } + + size_t GetStateSetSize() const { + return (regex_.stateCount_ + 31) / 32 * 4; + } + + // Return whether the added states is a match state + bool AddState(Stack& l, SizeType index) { + RAPIDJSON_ASSERT(index != kRegexInvalidState); + + const State& s = regex_.GetState(index); + if (s.out1 != kRegexInvalidState) { // Split + bool matched = AddState(l, s.out); + return AddState(l, s.out1) || matched; + } + else if (!(stateSet_[index >> 5] & (1u << (index & 31)))) { + stateSet_[index >> 5] |= (1u << (index & 31)); + *l.template PushUnsafe() = index; + } + return s.out == kRegexInvalidState; // by using PushUnsafe() above, we can ensure s is not validated due to reallocation. + } + + bool MatchRange(SizeType rangeIndex, unsigned codepoint) const { + bool yes = (regex_.GetRange(rangeIndex).start & RegexType::kRangeNegationFlag) == 0; + while (rangeIndex != kRegexInvalidRange) { + const Range& r = regex_.GetRange(rangeIndex); + if (codepoint >= (r.start & ~RegexType::kRangeNegationFlag) && codepoint <= r.end) + return yes; + rangeIndex = r.next; + } + return !yes; + } + + const RegexType& regex_; + Allocator* allocator_; + Allocator* ownAllocator_; + Stack state0_; + Stack state1_; + uint32_t* stateSet_; +}; + +typedef GenericRegex > Regex; +typedef GenericRegexSearch RegexSearch; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#if defined(__clang__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_REGEX_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/internal/stack.h b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/stack.h new file mode 100644 index 000000000..73abd706e --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/stack.h @@ -0,0 +1,232 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_STACK_H_ +#define RAPIDJSON_INTERNAL_STACK_H_ + +#include "../allocators.h" +#include "swap.h" +#include + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +/////////////////////////////////////////////////////////////////////////////// +// Stack + +//! A type-unsafe stack for storing different types of data. +/*! \tparam Allocator Allocator for allocating stack memory. +*/ +template +class Stack { +public: + // Optimization note: Do not allocate memory for stack_ in constructor. + // Do it lazily when first Push() -> Expand() -> Resize(). + Stack(Allocator* allocator, size_t stackCapacity) : allocator_(allocator), ownAllocator_(0), stack_(0), stackTop_(0), stackEnd_(0), initialCapacity_(stackCapacity) { + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + Stack(Stack&& rhs) + : allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + stack_(rhs.stack_), + stackTop_(rhs.stackTop_), + stackEnd_(rhs.stackEnd_), + initialCapacity_(rhs.initialCapacity_) + { + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.stack_ = 0; + rhs.stackTop_ = 0; + rhs.stackEnd_ = 0; + rhs.initialCapacity_ = 0; + } +#endif + + ~Stack() { + Destroy(); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + Stack& operator=(Stack&& rhs) { + if (&rhs != this) + { + Destroy(); + + allocator_ = rhs.allocator_; + ownAllocator_ = rhs.ownAllocator_; + stack_ = rhs.stack_; + stackTop_ = rhs.stackTop_; + stackEnd_ = rhs.stackEnd_; + initialCapacity_ = rhs.initialCapacity_; + + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.stack_ = 0; + rhs.stackTop_ = 0; + rhs.stackEnd_ = 0; + rhs.initialCapacity_ = 0; + } + return *this; + } +#endif + + void Swap(Stack& rhs) RAPIDJSON_NOEXCEPT { + internal::Swap(allocator_, rhs.allocator_); + internal::Swap(ownAllocator_, rhs.ownAllocator_); + internal::Swap(stack_, rhs.stack_); + internal::Swap(stackTop_, rhs.stackTop_); + internal::Swap(stackEnd_, rhs.stackEnd_); + internal::Swap(initialCapacity_, rhs.initialCapacity_); + } + + void Clear() { stackTop_ = stack_; } + + void ShrinkToFit() { + if (Empty()) { + // If the stack is empty, completely deallocate the memory. + Allocator::Free(stack_); // NOLINT (+clang-analyzer-unix.Malloc) + stack_ = 0; + stackTop_ = 0; + stackEnd_ = 0; + } + else + Resize(GetSize()); + } + + // Optimization note: try to minimize the size of this function for force inline. + // Expansion is run very infrequently, so it is moved to another (probably non-inline) function. + template + RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) { + // Expand the stack if needed + if (RAPIDJSON_UNLIKELY(static_cast(sizeof(T) * count) > (stackEnd_ - stackTop_))) + Expand(count); + } + + template + RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) { + Reserve(count); + return PushUnsafe(count); + } + + template + RAPIDJSON_FORCEINLINE T* PushUnsafe(size_t count = 1) { + RAPIDJSON_ASSERT(stackTop_); + RAPIDJSON_ASSERT(static_cast(sizeof(T) * count) <= (stackEnd_ - stackTop_)); + T* ret = reinterpret_cast(stackTop_); + stackTop_ += sizeof(T) * count; + return ret; + } + + template + T* Pop(size_t count) { + RAPIDJSON_ASSERT(GetSize() >= count * sizeof(T)); + stackTop_ -= count * sizeof(T); + return reinterpret_cast(stackTop_); + } + + template + T* Top() { + RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); + return reinterpret_cast(stackTop_ - sizeof(T)); + } + + template + const T* Top() const { + RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); + return reinterpret_cast(stackTop_ - sizeof(T)); + } + + template + T* End() { return reinterpret_cast(stackTop_); } + + template + const T* End() const { return reinterpret_cast(stackTop_); } + + template + T* Bottom() { return reinterpret_cast(stack_); } + + template + const T* Bottom() const { return reinterpret_cast(stack_); } + + bool HasAllocator() const { + return allocator_ != 0; + } + + Allocator& GetAllocator() { + RAPIDJSON_ASSERT(allocator_); + return *allocator_; + } + + bool Empty() const { return stackTop_ == stack_; } + size_t GetSize() const { return static_cast(stackTop_ - stack_); } + size_t GetCapacity() const { return static_cast(stackEnd_ - stack_); } + +private: + template + void Expand(size_t count) { + // Only expand the capacity if the current stack exists. Otherwise just create a stack with initial capacity. + size_t newCapacity; + if (stack_ == 0) { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + newCapacity = initialCapacity_; + } else { + newCapacity = GetCapacity(); + newCapacity += (newCapacity + 1) / 2; + } + size_t newSize = GetSize() + sizeof(T) * count; + if (newCapacity < newSize) + newCapacity = newSize; + + Resize(newCapacity); + } + + void Resize(size_t newCapacity) { + const size_t size = GetSize(); // Backup the current size + stack_ = static_cast(allocator_->Realloc(stack_, GetCapacity(), newCapacity)); + stackTop_ = stack_ + size; + stackEnd_ = stack_ + newCapacity; + } + + void Destroy() { + Allocator::Free(stack_); + RAPIDJSON_DELETE(ownAllocator_); // Only delete if it is owned by the stack + } + + // Prohibit copy constructor & assignment operator. + Stack(const Stack&); + Stack& operator=(const Stack&); + + Allocator* allocator_; + Allocator* ownAllocator_; + char *stack_; + char *stackTop_; + char *stackEnd_; + size_t initialCapacity_; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_STACK_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/internal/strfunc.h b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/strfunc.h new file mode 100644 index 000000000..baecb6cc8 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/strfunc.h @@ -0,0 +1,69 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_STRFUNC_H_ +#define RAPIDJSON_INTERNAL_STRFUNC_H_ + +#include "../stream.h" +#include + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Custom strlen() which works on different character types. +/*! \tparam Ch Character type (e.g. char, wchar_t, short) + \param s Null-terminated input string. + \return Number of characters in the string. + \note This has the same semantics as strlen(), the return value is not number of Unicode codepoints. +*/ +template +inline SizeType StrLen(const Ch* s) { + RAPIDJSON_ASSERT(s != 0); + const Ch* p = s; + while (*p) ++p; + return SizeType(p - s); +} + +template <> +inline SizeType StrLen(const char* s) { + return SizeType(std::strlen(s)); +} + +template <> +inline SizeType StrLen(const wchar_t* s) { + return SizeType(std::wcslen(s)); +} + +//! Returns number of code points in a encoded string. +template +bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) { + RAPIDJSON_ASSERT(s != 0); + RAPIDJSON_ASSERT(outCount != 0); + GenericStringStream is(s); + const typename Encoding::Ch* end = s + length; + SizeType count = 0; + while (is.src_ < end) { + unsigned codepoint; + if (!Encoding::Decode(is, &codepoint)) + return false; + count++; + } + *outCount = count; + return true; +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_INTERNAL_STRFUNC_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/internal/strtod.h b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/strtod.h new file mode 100644 index 000000000..d61a67a49 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/strtod.h @@ -0,0 +1,290 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_STRTOD_ +#define RAPIDJSON_STRTOD_ + +#include "ieee754.h" +#include "biginteger.h" +#include "diyfp.h" +#include "pow10.h" +#include +#include + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +inline double FastPath(double significand, int exp) { + if (exp < -308) + return 0.0; + else if (exp >= 0) + return significand * internal::Pow10(exp); + else + return significand / internal::Pow10(-exp); +} + +inline double StrtodNormalPrecision(double d, int p) { + if (p < -308) { + // Prevent expSum < -308, making Pow10(p) = 0 + d = FastPath(d, -308); + d = FastPath(d, p + 308); + } + else + d = FastPath(d, p); + return d; +} + +template +inline T Min3(T a, T b, T c) { + T m = a; + if (m > b) m = b; + if (m > c) m = c; + return m; +} + +inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp) { + const Double db(b); + const uint64_t bInt = db.IntegerSignificand(); + const int bExp = db.IntegerExponent(); + const int hExp = bExp - 1; + + int dS_Exp2 = 0, dS_Exp5 = 0, bS_Exp2 = 0, bS_Exp5 = 0, hS_Exp2 = 0, hS_Exp5 = 0; + + // Adjust for decimal exponent + if (dExp >= 0) { + dS_Exp2 += dExp; + dS_Exp5 += dExp; + } + else { + bS_Exp2 -= dExp; + bS_Exp5 -= dExp; + hS_Exp2 -= dExp; + hS_Exp5 -= dExp; + } + + // Adjust for binary exponent + if (bExp >= 0) + bS_Exp2 += bExp; + else { + dS_Exp2 -= bExp; + hS_Exp2 -= bExp; + } + + // Adjust for half ulp exponent + if (hExp >= 0) + hS_Exp2 += hExp; + else { + dS_Exp2 -= hExp; + bS_Exp2 -= hExp; + } + + // Remove common power of two factor from all three scaled values + int common_Exp2 = Min3(dS_Exp2, bS_Exp2, hS_Exp2); + dS_Exp2 -= common_Exp2; + bS_Exp2 -= common_Exp2; + hS_Exp2 -= common_Exp2; + + BigInteger dS = d; + dS.MultiplyPow5(static_cast(dS_Exp5)) <<= static_cast(dS_Exp2); + + BigInteger bS(bInt); + bS.MultiplyPow5(static_cast(bS_Exp5)) <<= static_cast(bS_Exp2); + + BigInteger hS(1); + hS.MultiplyPow5(static_cast(hS_Exp5)) <<= static_cast(hS_Exp2); + + BigInteger delta(0); + dS.Difference(bS, &delta); + + return delta.Compare(hS); +} + +inline bool StrtodFast(double d, int p, double* result) { + // Use fast path for string-to-double conversion if possible + // see http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/ + if (p > 22 && p < 22 + 16) { + // Fast Path Cases In Disguise + d *= internal::Pow10(p - 22); + p = 22; + } + + if (p >= -22 && p <= 22 && d <= 9007199254740991.0) { // 2^53 - 1 + *result = FastPath(d, p); + return true; + } + else + return false; +} + +// Compute an approximation and see if it is within 1/2 ULP +inline bool StrtodDiyFp(const char* decimals, int dLen, int dExp, double* result) { + uint64_t significand = 0; + int i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999 + for (; i < dLen; i++) { + if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || + (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > '5')) + break; + significand = significand * 10u + static_cast(decimals[i] - '0'); + } + + if (i < dLen && decimals[i] >= '5') // Rounding + significand++; + + int remaining = dLen - i; + const int kUlpShift = 3; + const int kUlp = 1 << kUlpShift; + int64_t error = (remaining == 0) ? 0 : kUlp / 2; + + DiyFp v(significand, 0); + v = v.Normalize(); + error <<= -v.e; + + dExp += remaining; + + int actualExp; + DiyFp cachedPower = GetCachedPower10(dExp, &actualExp); + if (actualExp != dExp) { + static const DiyFp kPow10[] = { + DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 0x00000000), -60), // 10^1 + DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 0x00000000), -57), // 10^2 + DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 0x00000000), -54), // 10^3 + DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), -50), // 10^4 + DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 0x00000000), -47), // 10^5 + DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 0x00000000), -44), // 10^6 + DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 0x00000000), -40) // 10^7 + }; + int adjustment = dExp - actualExp; + RAPIDJSON_ASSERT(adjustment >= 1 && adjustment < 8); + v = v * kPow10[adjustment - 1]; + if (dLen + adjustment > 19) // has more digits than decimal digits in 64-bit + error += kUlp / 2; + } + + v = v * cachedPower; + + error += kUlp + (error == 0 ? 0 : 1); + + const int oldExp = v.e; + v = v.Normalize(); + error <<= oldExp - v.e; + + const int effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e); + int precisionSize = 64 - effectiveSignificandSize; + if (precisionSize + kUlpShift >= 64) { + int scaleExp = (precisionSize + kUlpShift) - 63; + v.f >>= scaleExp; + v.e += scaleExp; + error = (error >> scaleExp) + 1 + kUlp; + precisionSize -= scaleExp; + } + + DiyFp rounded(v.f >> precisionSize, v.e + precisionSize); + const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp; + const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp; + if (precisionBits >= halfWay + static_cast(error)) { + rounded.f++; + if (rounded.f & (DiyFp::kDpHiddenBit << 1)) { // rounding overflows mantissa (issue #340) + rounded.f >>= 1; + rounded.e++; + } + } + + *result = rounded.ToDouble(); + + return halfWay - static_cast(error) >= precisionBits || precisionBits >= halfWay + static_cast(error); +} + +inline double StrtodBigInteger(double approx, const char* decimals, int dLen, int dExp) { + RAPIDJSON_ASSERT(dLen >= 0); + const BigInteger dInt(decimals, static_cast(dLen)); + Double a(approx); + int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp); + if (cmp < 0) + return a.Value(); // within half ULP + else if (cmp == 0) { + // Round towards even + if (a.Significand() & 1) + return a.NextPositiveDouble(); + else + return a.Value(); + } + else // adjustment + return a.NextPositiveDouble(); +} + +inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t length, size_t decimalPosition, int exp) { + RAPIDJSON_ASSERT(d >= 0.0); + RAPIDJSON_ASSERT(length >= 1); + + double result = 0.0; + if (StrtodFast(d, p, &result)) + return result; + + RAPIDJSON_ASSERT(length <= INT_MAX); + int dLen = static_cast(length); + + RAPIDJSON_ASSERT(length >= decimalPosition); + RAPIDJSON_ASSERT(length - decimalPosition <= INT_MAX); + int dExpAdjust = static_cast(length - decimalPosition); + + RAPIDJSON_ASSERT(exp >= INT_MIN + dExpAdjust); + int dExp = exp - dExpAdjust; + + // Make sure length+dExp does not overflow + RAPIDJSON_ASSERT(dExp <= INT_MAX - dLen); + + // Trim leading zeros + while (dLen > 0 && *decimals == '0') { + dLen--; + decimals++; + } + + // Trim trailing zeros + while (dLen > 0 && decimals[dLen - 1] == '0') { + dLen--; + dExp++; + } + + if (dLen == 0) { // Buffer only contains zeros. + return 0.0; + } + + // Trim right-most digits + const int kMaxDecimalDigit = 767 + 1; + if (dLen > kMaxDecimalDigit) { + dExp += dLen - kMaxDecimalDigit; + dLen = kMaxDecimalDigit; + } + + // If too small, underflow to zero. + // Any x <= 10^-324 is interpreted as zero. + if (dLen + dExp <= -324) + return 0.0; + + // If too large, overflow to infinity. + // Any x >= 10^309 is interpreted as +infinity. + if (dLen + dExp > 309) + return std::numeric_limits::infinity(); + + if (StrtodDiyFp(decimals, dLen, dExp, &result)) + return result; + + // Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison + return StrtodBigInteger(result, decimals, dLen, dExp); +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_STRTOD_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/internal/swap.h b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/swap.h new file mode 100644 index 000000000..2cf92f93a --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/internal/swap.h @@ -0,0 +1,46 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_SWAP_H_ +#define RAPIDJSON_INTERNAL_SWAP_H_ + +#include "../rapidjson.h" + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Custom swap() to avoid dependency on C++ header +/*! \tparam T Type of the arguments to swap, should be instantiated with primitive C++ types only. + \note This has the same semantics as std::swap(). +*/ +template +inline void Swap(T& a, T& b) RAPIDJSON_NOEXCEPT { + T tmp = a; + a = b; + b = tmp; +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_SWAP_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/istreamwrapper.h b/libraries/libstratosphere/include/stratosphere/rapidjson/istreamwrapper.h new file mode 100644 index 000000000..01437ec01 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/istreamwrapper.h @@ -0,0 +1,128 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ISTREAMWRAPPER_H_ +#define RAPIDJSON_ISTREAMWRAPPER_H_ + +#include "stream.h" +#include +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4351) // new behavior: elements of array 'array' will be default initialized +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of \c std::basic_istream into RapidJSON's Stream concept. +/*! + The classes can be wrapped including but not limited to: + + - \c std::istringstream + - \c std::stringstream + - \c std::wistringstream + - \c std::wstringstream + - \c std::ifstream + - \c std::fstream + - \c std::wifstream + - \c std::wfstream + + \tparam StreamType Class derived from \c std::basic_istream. +*/ + +template +class BasicIStreamWrapper { +public: + typedef typename StreamType::char_type Ch; + + //! Constructor. + /*! + \param stream stream opened for read. + */ + BasicIStreamWrapper(StreamType &stream) : stream_(stream), buffer_(peekBuffer_), bufferSize_(4), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { + Read(); + } + + //! Constructor. + /*! + \param stream stream opened for read. + \param buffer user-supplied buffer. + \param bufferSize size of buffer in bytes. Must >=4 bytes. + */ + BasicIStreamWrapper(StreamType &stream, char* buffer, size_t bufferSize) : stream_(stream), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { + RAPIDJSON_ASSERT(bufferSize >= 4); + Read(); + } + + Ch Peek() const { return *current_; } + Ch Take() { Ch c = *current_; Read(); return c; } + size_t Tell() const { return count_ + static_cast(current_ - buffer_); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + return (current_ + 4 - !eof_ <= bufferLast_) ? current_ : 0; + } + +private: + BasicIStreamWrapper(); + BasicIStreamWrapper(const BasicIStreamWrapper&); + BasicIStreamWrapper& operator=(const BasicIStreamWrapper&); + + void Read() { + if (current_ < bufferLast_) + ++current_; + else if (!eof_) { + count_ += readCount_; + readCount_ = bufferSize_; + bufferLast_ = buffer_ + readCount_ - 1; + current_ = buffer_; + + if (!stream_.read(buffer_, static_cast(bufferSize_))) { + readCount_ = static_cast(stream_.gcount()); + *(bufferLast_ = buffer_ + readCount_) = '\0'; + eof_ = true; + } + } + } + + StreamType &stream_; + Ch peekBuffer_[4], *buffer_; + size_t bufferSize_; + Ch *bufferLast_; + Ch *current_; + size_t readCount_; + size_t count_; //!< Number of characters read + bool eof_; +}; + +typedef BasicIStreamWrapper IStreamWrapper; +typedef BasicIStreamWrapper WIStreamWrapper; + +#if defined(__clang__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ISTREAMWRAPPER_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/memorybuffer.h b/libraries/libstratosphere/include/stratosphere/rapidjson/memorybuffer.h new file mode 100644 index 000000000..ffbc41ed1 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/memorybuffer.h @@ -0,0 +1,70 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_MEMORYBUFFER_H_ +#define RAPIDJSON_MEMORYBUFFER_H_ + +#include "stream.h" +#include "internal/stack.h" + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory output byte stream. +/*! + This class is mainly for being wrapped by EncodedOutputStream or AutoUTFOutputStream. + + It is similar to FileWriteBuffer but the destination is an in-memory buffer instead of a file. + + Differences between MemoryBuffer and StringBuffer: + 1. StringBuffer has Encoding but MemoryBuffer is only a byte buffer. + 2. StringBuffer::GetString() returns a null-terminated string. MemoryBuffer::GetBuffer() returns a buffer without terminator. + + \tparam Allocator type for allocating memory buffer. + \note implements Stream concept +*/ +template +struct GenericMemoryBuffer { + typedef char Ch; // byte + + GenericMemoryBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} + + void Put(Ch c) { *stack_.template Push() = c; } + void Flush() {} + + void Clear() { stack_.Clear(); } + void ShrinkToFit() { stack_.ShrinkToFit(); } + Ch* Push(size_t count) { return stack_.template Push(count); } + void Pop(size_t count) { stack_.template Pop(count); } + + const Ch* GetBuffer() const { + return stack_.template Bottom(); + } + + size_t GetSize() const { return stack_.GetSize(); } + + static const size_t kDefaultCapacity = 256; + mutable internal::Stack stack_; +}; + +typedef GenericMemoryBuffer<> MemoryBuffer; + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(MemoryBuffer& memoryBuffer, char c, size_t n) { + std::memset(memoryBuffer.stack_.Push(n), c, n * sizeof(c)); +} + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/memorystream.h b/libraries/libstratosphere/include/stratosphere/rapidjson/memorystream.h new file mode 100644 index 000000000..77af6c999 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/memorystream.h @@ -0,0 +1,71 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_MEMORYSTREAM_H_ +#define RAPIDJSON_MEMORYSTREAM_H_ + +#include "stream.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(unreachable-code) +RAPIDJSON_DIAG_OFF(missing-noreturn) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory input byte stream. +/*! + This class is mainly for being wrapped by EncodedInputStream or AutoUTFInputStream. + + It is similar to FileReadBuffer but the source is an in-memory buffer instead of a file. + + Differences between MemoryStream and StringStream: + 1. StringStream has encoding but MemoryStream is a byte stream. + 2. MemoryStream needs size of the source buffer and the buffer don't need to be null terminated. StringStream assume null-terminated string as source. + 3. MemoryStream supports Peek4() for encoding detection. StringStream is specified with an encoding so it should not have Peek4(). + \note implements Stream concept +*/ +struct MemoryStream { + typedef char Ch; // byte + + MemoryStream(const Ch *src, size_t size) : src_(src), begin_(src), end_(src + size), size_(size) {} + + Ch Peek() const { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_; } + Ch Take() { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_++; } + size_t Tell() const { return static_cast(src_ - begin_); } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + return Tell() + 4 <= size_ ? src_ : 0; + } + + const Ch* src_; //!< Current read position. + const Ch* begin_; //!< Original head of the string. + const Ch* end_; //!< End of stream. + size_t size_; //!< Size of the stream. +}; + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/msinttypes/inttypes.h b/libraries/libstratosphere/include/stratosphere/rapidjson/msinttypes/inttypes.h new file mode 100644 index 000000000..18111286b --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/msinttypes/inttypes.h @@ -0,0 +1,316 @@ +// ISO C9x compliant inttypes.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2013 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the product nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +// The above software in this distribution may have been modified by +// THL A29 Limited ("Tencent Modifications"). +// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited. + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_INTTYPES_H_ // [ +#define _MSC_INTTYPES_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#include "stdint.h" + +// miloyip: VC supports inttypes.h since VC2013 +#if _MSC_VER >= 1800 +#include +#else + +// 7.8 Format conversion of integer types + +typedef struct { + intmax_t quot; + intmax_t rem; +} imaxdiv_t; + +// 7.8.1 Macros for format specifiers + +#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198 + +// The fprintf macros for signed integers are: +#define PRId8 "d" +#define PRIi8 "i" +#define PRIdLEAST8 "d" +#define PRIiLEAST8 "i" +#define PRIdFAST8 "d" +#define PRIiFAST8 "i" + +#define PRId16 "hd" +#define PRIi16 "hi" +#define PRIdLEAST16 "hd" +#define PRIiLEAST16 "hi" +#define PRIdFAST16 "hd" +#define PRIiFAST16 "hi" + +#define PRId32 "I32d" +#define PRIi32 "I32i" +#define PRIdLEAST32 "I32d" +#define PRIiLEAST32 "I32i" +#define PRIdFAST32 "I32d" +#define PRIiFAST32 "I32i" + +#define PRId64 "I64d" +#define PRIi64 "I64i" +#define PRIdLEAST64 "I64d" +#define PRIiLEAST64 "I64i" +#define PRIdFAST64 "I64d" +#define PRIiFAST64 "I64i" + +#define PRIdMAX "I64d" +#define PRIiMAX "I64i" + +#define PRIdPTR "Id" +#define PRIiPTR "Ii" + +// The fprintf macros for unsigned integers are: +#define PRIo8 "o" +#define PRIu8 "u" +#define PRIx8 "x" +#define PRIX8 "X" +#define PRIoLEAST8 "o" +#define PRIuLEAST8 "u" +#define PRIxLEAST8 "x" +#define PRIXLEAST8 "X" +#define PRIoFAST8 "o" +#define PRIuFAST8 "u" +#define PRIxFAST8 "x" +#define PRIXFAST8 "X" + +#define PRIo16 "ho" +#define PRIu16 "hu" +#define PRIx16 "hx" +#define PRIX16 "hX" +#define PRIoLEAST16 "ho" +#define PRIuLEAST16 "hu" +#define PRIxLEAST16 "hx" +#define PRIXLEAST16 "hX" +#define PRIoFAST16 "ho" +#define PRIuFAST16 "hu" +#define PRIxFAST16 "hx" +#define PRIXFAST16 "hX" + +#define PRIo32 "I32o" +#define PRIu32 "I32u" +#define PRIx32 "I32x" +#define PRIX32 "I32X" +#define PRIoLEAST32 "I32o" +#define PRIuLEAST32 "I32u" +#define PRIxLEAST32 "I32x" +#define PRIXLEAST32 "I32X" +#define PRIoFAST32 "I32o" +#define PRIuFAST32 "I32u" +#define PRIxFAST32 "I32x" +#define PRIXFAST32 "I32X" + +#define PRIo64 "I64o" +#define PRIu64 "I64u" +#define PRIx64 "I64x" +#define PRIX64 "I64X" +#define PRIoLEAST64 "I64o" +#define PRIuLEAST64 "I64u" +#define PRIxLEAST64 "I64x" +#define PRIXLEAST64 "I64X" +#define PRIoFAST64 "I64o" +#define PRIuFAST64 "I64u" +#define PRIxFAST64 "I64x" +#define PRIXFAST64 "I64X" + +#define PRIoMAX "I64o" +#define PRIuMAX "I64u" +#define PRIxMAX "I64x" +#define PRIXMAX "I64X" + +#define PRIoPTR "Io" +#define PRIuPTR "Iu" +#define PRIxPTR "Ix" +#define PRIXPTR "IX" + +// The fscanf macros for signed integers are: +#define SCNd8 "d" +#define SCNi8 "i" +#define SCNdLEAST8 "d" +#define SCNiLEAST8 "i" +#define SCNdFAST8 "d" +#define SCNiFAST8 "i" + +#define SCNd16 "hd" +#define SCNi16 "hi" +#define SCNdLEAST16 "hd" +#define SCNiLEAST16 "hi" +#define SCNdFAST16 "hd" +#define SCNiFAST16 "hi" + +#define SCNd32 "ld" +#define SCNi32 "li" +#define SCNdLEAST32 "ld" +#define SCNiLEAST32 "li" +#define SCNdFAST32 "ld" +#define SCNiFAST32 "li" + +#define SCNd64 "I64d" +#define SCNi64 "I64i" +#define SCNdLEAST64 "I64d" +#define SCNiLEAST64 "I64i" +#define SCNdFAST64 "I64d" +#define SCNiFAST64 "I64i" + +#define SCNdMAX "I64d" +#define SCNiMAX "I64i" + +#ifdef _WIN64 // [ +# define SCNdPTR "I64d" +# define SCNiPTR "I64i" +#else // _WIN64 ][ +# define SCNdPTR "ld" +# define SCNiPTR "li" +#endif // _WIN64 ] + +// The fscanf macros for unsigned integers are: +#define SCNo8 "o" +#define SCNu8 "u" +#define SCNx8 "x" +#define SCNX8 "X" +#define SCNoLEAST8 "o" +#define SCNuLEAST8 "u" +#define SCNxLEAST8 "x" +#define SCNXLEAST8 "X" +#define SCNoFAST8 "o" +#define SCNuFAST8 "u" +#define SCNxFAST8 "x" +#define SCNXFAST8 "X" + +#define SCNo16 "ho" +#define SCNu16 "hu" +#define SCNx16 "hx" +#define SCNX16 "hX" +#define SCNoLEAST16 "ho" +#define SCNuLEAST16 "hu" +#define SCNxLEAST16 "hx" +#define SCNXLEAST16 "hX" +#define SCNoFAST16 "ho" +#define SCNuFAST16 "hu" +#define SCNxFAST16 "hx" +#define SCNXFAST16 "hX" + +#define SCNo32 "lo" +#define SCNu32 "lu" +#define SCNx32 "lx" +#define SCNX32 "lX" +#define SCNoLEAST32 "lo" +#define SCNuLEAST32 "lu" +#define SCNxLEAST32 "lx" +#define SCNXLEAST32 "lX" +#define SCNoFAST32 "lo" +#define SCNuFAST32 "lu" +#define SCNxFAST32 "lx" +#define SCNXFAST32 "lX" + +#define SCNo64 "I64o" +#define SCNu64 "I64u" +#define SCNx64 "I64x" +#define SCNX64 "I64X" +#define SCNoLEAST64 "I64o" +#define SCNuLEAST64 "I64u" +#define SCNxLEAST64 "I64x" +#define SCNXLEAST64 "I64X" +#define SCNoFAST64 "I64o" +#define SCNuFAST64 "I64u" +#define SCNxFAST64 "I64x" +#define SCNXFAST64 "I64X" + +#define SCNoMAX "I64o" +#define SCNuMAX "I64u" +#define SCNxMAX "I64x" +#define SCNXMAX "I64X" + +#ifdef _WIN64 // [ +# define SCNoPTR "I64o" +# define SCNuPTR "I64u" +# define SCNxPTR "I64x" +# define SCNXPTR "I64X" +#else // _WIN64 ][ +# define SCNoPTR "lo" +# define SCNuPTR "lu" +# define SCNxPTR "lx" +# define SCNXPTR "lX" +#endif // _WIN64 ] + +#endif // __STDC_FORMAT_MACROS ] + +// 7.8.2 Functions for greatest-width integer types + +// 7.8.2.1 The imaxabs function +#define imaxabs _abs64 + +// 7.8.2.2 The imaxdiv function + +// This is modified version of div() function from Microsoft's div.c found +// in %MSVC.NET%\crt\src\div.c +#ifdef STATIC_IMAXDIV // [ +static +#else // STATIC_IMAXDIV ][ +_inline +#endif // STATIC_IMAXDIV ] +imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom) +{ + imaxdiv_t result; + + result.quot = numer / denom; + result.rem = numer % denom; + + if (numer < 0 && result.rem > 0) { + // did division wrong; must fix up + ++result.quot; + result.rem -= denom; + } + + return result; +} + +// 7.8.2.3 The strtoimax and strtoumax functions +#define strtoimax _strtoi64 +#define strtoumax _strtoui64 + +// 7.8.2.4 The wcstoimax and wcstoumax functions +#define wcstoimax _wcstoi64 +#define wcstoumax _wcstoui64 + +#endif // _MSC_VER >= 1800 + +#endif // _MSC_INTTYPES_H_ ] diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/msinttypes/stdint.h b/libraries/libstratosphere/include/stratosphere/rapidjson/msinttypes/stdint.h new file mode 100644 index 000000000..3d4477b9a --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/msinttypes/stdint.h @@ -0,0 +1,300 @@ +// ISO C9x compliant stdint.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2013 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the product nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +// The above software in this distribution may have been modified by +// THL A29 Limited ("Tencent Modifications"). +// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited. + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_STDINT_H_ // [ +#define _MSC_STDINT_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +// miloyip: Originally Visual Studio 2010 uses its own stdint.h. However it generates warning with INT64_C(), so change to use this file for vs2010. +#if _MSC_VER >= 1600 // [ +#include + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +#undef INT8_C +#undef INT16_C +#undef INT32_C +#undef INT64_C +#undef UINT8_C +#undef UINT16_C +#undef UINT32_C +#undef UINT64_C + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +// These #ifndef's are needed to prevent collisions with . +// Check out Issue 9 for the details. +#ifndef INTMAX_C // [ +# define INTMAX_C INT64_C +#endif // INTMAX_C ] +#ifndef UINTMAX_C // [ +# define UINTMAX_C UINT64_C +#endif // UINTMAX_C ] + +#endif // __STDC_CONSTANT_MACROS ] + +#else // ] _MSC_VER >= 1700 [ + +#include + +// For Visual Studio 6 in C++ mode and for many Visual Studio versions when +// compiling for ARM we have to wrap include with 'extern "C++" {}' +// or compiler would give many errors like this: +// error C2733: second C linkage of overloaded function 'wmemchr' not allowed +#if defined(__cplusplus) && !defined(_M_ARM) +extern "C" { +#endif +# include +#if defined(__cplusplus) && !defined(_M_ARM) +} +#endif + +// Define _W64 macros to mark types changing their size, like intptr_t. +#ifndef _W64 +# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 +# define _W64 __w64 +# else +# define _W64 +# endif +#endif + + +// 7.18.1 Integer types + +// 7.18.1.1 Exact-width integer types + +// Visual Studio 6 and Embedded Visual C++ 4 doesn't +// realize that, e.g. char has the same size as __int8 +// so we give up on __intX for them. +#if (_MSC_VER < 1300) + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; +#else + typedef signed __int8 int8_t; + typedef signed __int16 int16_t; + typedef signed __int32 int32_t; + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; +#endif +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; + + +// 7.18.1.2 Minimum-width integer types +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +typedef int64_t int_least64_t; +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +typedef uint64_t uint_least64_t; + +// 7.18.1.3 Fastest minimum-width integer types +typedef int8_t int_fast8_t; +typedef int16_t int_fast16_t; +typedef int32_t int_fast32_t; +typedef int64_t int_fast64_t; +typedef uint8_t uint_fast8_t; +typedef uint16_t uint_fast16_t; +typedef uint32_t uint_fast32_t; +typedef uint64_t uint_fast64_t; + +// 7.18.1.4 Integer types capable of holding object pointers +#ifdef _WIN64 // [ + typedef signed __int64 intptr_t; + typedef unsigned __int64 uintptr_t; +#else // _WIN64 ][ + typedef _W64 signed int intptr_t; + typedef _W64 unsigned int uintptr_t; +#endif // _WIN64 ] + +// 7.18.1.5 Greatest-width integer types +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + + +// 7.18.2 Limits of specified-width integer types + +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 + +// 7.18.2.1 Limits of exact-width integer types +#define INT8_MIN ((int8_t)_I8_MIN) +#define INT8_MAX _I8_MAX +#define INT16_MIN ((int16_t)_I16_MIN) +#define INT16_MAX _I16_MAX +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX _I32_MAX +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX _I64_MAX +#define UINT8_MAX _UI8_MAX +#define UINT16_MAX _UI16_MAX +#define UINT32_MAX _UI32_MAX +#define UINT64_MAX _UI64_MAX + +// 7.18.2.2 Limits of minimum-width integer types +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MIN INT64_MIN +#define INT_LEAST64_MAX INT64_MAX +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +// 7.18.2.3 Limits of fastest minimum-width integer types +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MIN INT16_MIN +#define INT_FAST16_MAX INT16_MAX +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT16_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +// 7.18.2.4 Limits of integer types capable of holding object pointers +#ifdef _WIN64 // [ +# define INTPTR_MIN INT64_MIN +# define INTPTR_MAX INT64_MAX +# define UINTPTR_MAX UINT64_MAX +#else // _WIN64 ][ +# define INTPTR_MIN INT32_MIN +# define INTPTR_MAX INT32_MAX +# define UINTPTR_MAX UINT32_MAX +#endif // _WIN64 ] + +// 7.18.2.5 Limits of greatest-width integer types +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +// 7.18.3 Limits of other integer types + +#ifdef _WIN64 // [ +# define PTRDIFF_MIN _I64_MIN +# define PTRDIFF_MAX _I64_MAX +#else // _WIN64 ][ +# define PTRDIFF_MIN _I32_MIN +# define PTRDIFF_MAX _I32_MAX +#endif // _WIN64 ] + +#define SIG_ATOMIC_MIN INT_MIN +#define SIG_ATOMIC_MAX INT_MAX + +#ifndef SIZE_MAX // [ +# ifdef _WIN64 // [ +# define SIZE_MAX _UI64_MAX +# else // _WIN64 ][ +# define SIZE_MAX _UI32_MAX +# endif // _WIN64 ] +#endif // SIZE_MAX ] + +// WCHAR_MIN and WCHAR_MAX are also defined in +#ifndef WCHAR_MIN // [ +# define WCHAR_MIN 0 +#endif // WCHAR_MIN ] +#ifndef WCHAR_MAX // [ +# define WCHAR_MAX _UI16_MAX +#endif // WCHAR_MAX ] + +#define WINT_MIN 0 +#define WINT_MAX _UI16_MAX + +#endif // __STDC_LIMIT_MACROS ] + + +// 7.18.4 Limits of other integer types + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +// These #ifndef's are needed to prevent collisions with . +// Check out Issue 9 for the details. +#ifndef INTMAX_C // [ +# define INTMAX_C INT64_C +#endif // INTMAX_C ] +#ifndef UINTMAX_C // [ +# define UINTMAX_C UINT64_C +#endif // UINTMAX_C ] + +#endif // __STDC_CONSTANT_MACROS ] + +#endif // _MSC_VER >= 1600 ] + +#endif // _MSC_STDINT_H_ ] diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/ostreamwrapper.h b/libraries/libstratosphere/include/stratosphere/rapidjson/ostreamwrapper.h new file mode 100644 index 000000000..11ed4d33f --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/ostreamwrapper.h @@ -0,0 +1,81 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_OSTREAMWRAPPER_H_ +#define RAPIDJSON_OSTREAMWRAPPER_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of \c std::basic_ostream into RapidJSON's Stream concept. +/*! + The classes can be wrapped including but not limited to: + + - \c std::ostringstream + - \c std::stringstream + - \c std::wpstringstream + - \c std::wstringstream + - \c std::ifstream + - \c std::fstream + - \c std::wofstream + - \c std::wfstream + + \tparam StreamType Class derived from \c std::basic_ostream. +*/ + +template +class BasicOStreamWrapper { +public: + typedef typename StreamType::char_type Ch; + BasicOStreamWrapper(StreamType& stream) : stream_(stream) {} + + void Put(Ch c) { + stream_.put(c); + } + + void Flush() { + stream_.flush(); + } + + // Not implemented + char Peek() const { RAPIDJSON_ASSERT(false); return 0; } + char Take() { RAPIDJSON_ASSERT(false); return 0; } + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + BasicOStreamWrapper(const BasicOStreamWrapper&); + BasicOStreamWrapper& operator=(const BasicOStreamWrapper&); + + StreamType& stream_; +}; + +typedef BasicOStreamWrapper OStreamWrapper; +typedef BasicOStreamWrapper WOStreamWrapper; + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_OSTREAMWRAPPER_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/pointer.h b/libraries/libstratosphere/include/stratosphere/rapidjson/pointer.h new file mode 100644 index 000000000..90e5903bc --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/pointer.h @@ -0,0 +1,1415 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_POINTER_H_ +#define RAPIDJSON_POINTER_H_ + +#include "document.h" +#include "internal/itoa.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(switch-enum) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +static const SizeType kPointerInvalidIndex = ~SizeType(0); //!< Represents an invalid index in GenericPointer::Token + +//! Error code of parsing. +/*! \ingroup RAPIDJSON_ERRORS + \see GenericPointer::GenericPointer, GenericPointer::GetParseErrorCode +*/ +enum PointerParseErrorCode { + kPointerParseErrorNone = 0, //!< The parse is successful + + kPointerParseErrorTokenMustBeginWithSolidus, //!< A token must begin with a '/' + kPointerParseErrorInvalidEscape, //!< Invalid escape + kPointerParseErrorInvalidPercentEncoding, //!< Invalid percent encoding in URI fragment + kPointerParseErrorCharacterMustPercentEncode //!< A character must percent encoded in URI fragment +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericPointer + +//! Represents a JSON Pointer. Use Pointer for UTF8 encoding and default allocator. +/*! + This class implements RFC 6901 "JavaScript Object Notation (JSON) Pointer" + (https://tools.ietf.org/html/rfc6901). + + A JSON pointer is for identifying a specific value in a JSON document + (GenericDocument). It can simplify coding of DOM tree manipulation, because it + can access multiple-level depth of DOM tree with single API call. + + After it parses a string representation (e.g. "/foo/0" or URI fragment + representation (e.g. "#/foo/0") into its internal representation (tokens), + it can be used to resolve a specific value in multiple documents, or sub-tree + of documents. + + Contrary to GenericValue, Pointer can be copy constructed and copy assigned. + Apart from assignment, a Pointer cannot be modified after construction. + + Although Pointer is very convenient, please aware that constructing Pointer + involves parsing and dynamic memory allocation. A special constructor with user- + supplied tokens eliminates these. + + GenericPointer depends on GenericDocument and GenericValue. + + \tparam ValueType The value type of the DOM tree. E.g. GenericValue > + \tparam Allocator The allocator type for allocating memory for internal representation. + + \note GenericPointer uses same encoding of ValueType. + However, Allocator of GenericPointer is independent of Allocator of Value. +*/ +template +class GenericPointer { +public: + typedef typename ValueType::EncodingType EncodingType; //!< Encoding type from Value + typedef typename ValueType::Ch Ch; //!< Character type from Value + + //! A token is the basic units of internal representation. + /*! + A JSON pointer string representation "/foo/123" is parsed to two tokens: + "foo" and 123. 123 will be represented in both numeric form and string form. + They are resolved according to the actual value type (object or array). + + For token that are not numbers, or the numeric value is out of bound + (greater than limits of SizeType), they are only treated as string form + (i.e. the token's index will be equal to kPointerInvalidIndex). + + This struct is public so that user can create a Pointer without parsing and + allocation, using a special constructor. + */ + struct Token { + const Ch* name; //!< Name of the token. It has null character at the end but it can contain null character. + SizeType length; //!< Length of the name. + SizeType index; //!< A valid array index, if it is not equal to kPointerInvalidIndex. + }; + + //!@name Constructors and destructor. + //@{ + + //! Default constructor. + GenericPointer(Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} + + //! Constructor that parses a string or URI fragment representation. + /*! + \param source A null-terminated, string or URI fragment representation of JSON pointer. + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. + */ + explicit GenericPointer(const Ch* source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + Parse(source, internal::StrLen(source)); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Constructor that parses a string or URI fragment representation. + /*! + \param source A string or URI fragment representation of JSON pointer. + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + explicit GenericPointer(const std::basic_string& source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + Parse(source.c_str(), source.size()); + } +#endif + + //! Constructor that parses a string or URI fragment representation, with length of the source string. + /*! + \param source A string or URI fragment representation of JSON pointer. + \param length Length of source. + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. + \note Slightly faster than the overload without length. + */ + GenericPointer(const Ch* source, size_t length, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + Parse(source, length); + } + + //! Constructor with user-supplied tokens. + /*! + This constructor let user supplies const array of tokens. + This prevents the parsing process and eliminates allocation. + This is preferred for memory constrained environments. + + \param tokens An constant array of tokens representing the JSON pointer. + \param tokenCount Number of tokens. + + \b Example + \code + #define NAME(s) { s, sizeof(s) / sizeof(s[0]) - 1, kPointerInvalidIndex } + #define INDEX(i) { #i, sizeof(#i) - 1, i } + + static const Pointer::Token kTokens[] = { NAME("foo"), INDEX(123) }; + static const Pointer p(kTokens, sizeof(kTokens) / sizeof(kTokens[0])); + // Equivalent to static const Pointer p("/foo/123"); + + #undef NAME + #undef INDEX + \endcode + */ + GenericPointer(const Token* tokens, size_t tokenCount) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(const_cast(tokens)), tokenCount_(tokenCount), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} + + //! Copy constructor. + GenericPointer(const GenericPointer& rhs) : allocator_(rhs.allocator_), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + *this = rhs; + } + + //! Copy constructor. + GenericPointer(const GenericPointer& rhs, Allocator* allocator) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + *this = rhs; + } + + //! Destructor. + ~GenericPointer() { + if (nameBuffer_) // If user-supplied tokens constructor is used, nameBuffer_ is nullptr and tokens_ are not deallocated. + Allocator::Free(tokens_); + RAPIDJSON_DELETE(ownAllocator_); + } + + //! Assignment operator. + GenericPointer& operator=(const GenericPointer& rhs) { + if (this != &rhs) { + // Do not delete ownAllcator + if (nameBuffer_) + Allocator::Free(tokens_); + + tokenCount_ = rhs.tokenCount_; + parseErrorOffset_ = rhs.parseErrorOffset_; + parseErrorCode_ = rhs.parseErrorCode_; + + if (rhs.nameBuffer_) + CopyFromRaw(rhs); // Normally parsed tokens. + else { + tokens_ = rhs.tokens_; // User supplied const tokens. + nameBuffer_ = 0; + } + } + return *this; + } + + //! Swap the content of this pointer with an other. + /*! + \param other The pointer to swap with. + \note Constant complexity. + */ + GenericPointer& Swap(GenericPointer& other) RAPIDJSON_NOEXCEPT { + internal::Swap(allocator_, other.allocator_); + internal::Swap(ownAllocator_, other.ownAllocator_); + internal::Swap(nameBuffer_, other.nameBuffer_); + internal::Swap(tokens_, other.tokens_); + internal::Swap(tokenCount_, other.tokenCount_); + internal::Swap(parseErrorOffset_, other.parseErrorOffset_); + internal::Swap(parseErrorCode_, other.parseErrorCode_); + return *this; + } + + //! free-standing swap function helper + /*! + Helper function to enable support for common swap implementation pattern based on \c std::swap: + \code + void swap(MyClass& a, MyClass& b) { + using std::swap; + swap(a.pointer, b.pointer); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericPointer& a, GenericPointer& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } + + //@} + + //!@name Append token + //@{ + + //! Append a token and return a new Pointer + /*! + \param token Token to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const Token& token, Allocator* allocator = 0) const { + GenericPointer r; + r.allocator_ = allocator; + Ch *p = r.CopyFromRaw(*this, 1, token.length + 1); + std::memcpy(p, token.name, (token.length + 1) * sizeof(Ch)); + r.tokens_[tokenCount_].name = p; + r.tokens_[tokenCount_].length = token.length; + r.tokens_[tokenCount_].index = token.index; + return r; + } + + //! Append a name token with length, and return a new Pointer + /*! + \param name Name to be appended. + \param length Length of name. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const Ch* name, SizeType length, Allocator* allocator = 0) const { + Token token = { name, length, kPointerInvalidIndex }; + return Append(token, allocator); + } + + //! Append a name token without length, and return a new Pointer + /*! + \param name Name (const Ch*) to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >), (GenericPointer)) + Append(T* name, Allocator* allocator = 0) const { + return Append(name, internal::StrLen(name), allocator); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Append a name token, and return a new Pointer + /*! + \param name Name to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const std::basic_string& name, Allocator* allocator = 0) const { + return Append(name.c_str(), static_cast(name.size()), allocator); + } +#endif + + //! Append a index token, and return a new Pointer + /*! + \param index Index to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(SizeType index, Allocator* allocator = 0) const { + char buffer[21]; + char* end = sizeof(SizeType) == 4 ? internal::u32toa(index, buffer) : internal::u64toa(index, buffer); + SizeType length = static_cast(end - buffer); + buffer[length] = '\0'; + + if (sizeof(Ch) == 1) { + Token token = { reinterpret_cast(buffer), length, index }; + return Append(token, allocator); + } + else { + Ch name[21]; + for (size_t i = 0; i <= length; i++) + name[i] = static_cast(buffer[i]); + Token token = { name, length, index }; + return Append(token, allocator); + } + } + + //! Append a token by value, and return a new Pointer + /*! + \param token token to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const ValueType& token, Allocator* allocator = 0) const { + if (token.IsString()) + return Append(token.GetString(), token.GetStringLength(), allocator); + else { + RAPIDJSON_ASSERT(token.IsUint64()); + RAPIDJSON_ASSERT(token.GetUint64() <= SizeType(~0)); + return Append(static_cast(token.GetUint64()), allocator); + } + } + + //!@name Handling Parse Error + //@{ + + //! Check whether this is a valid pointer. + bool IsValid() const { return parseErrorCode_ == kPointerParseErrorNone; } + + //! Get the parsing error offset in code unit. + size_t GetParseErrorOffset() const { return parseErrorOffset_; } + + //! Get the parsing error code. + PointerParseErrorCode GetParseErrorCode() const { return parseErrorCode_; } + + //@} + + //! Get the allocator of this pointer. + Allocator& GetAllocator() { return *allocator_; } + + //!@name Tokens + //@{ + + //! Get the token array (const version only). + const Token* GetTokens() const { return tokens_; } + + //! Get the number of tokens. + size_t GetTokenCount() const { return tokenCount_; } + + //@} + + //!@name Equality/inequality operators + //@{ + + //! Equality operator. + /*! + \note When any pointers are invalid, always returns false. + */ + bool operator==(const GenericPointer& rhs) const { + if (!IsValid() || !rhs.IsValid() || tokenCount_ != rhs.tokenCount_) + return false; + + for (size_t i = 0; i < tokenCount_; i++) { + if (tokens_[i].index != rhs.tokens_[i].index || + tokens_[i].length != rhs.tokens_[i].length || + (tokens_[i].length != 0 && std::memcmp(tokens_[i].name, rhs.tokens_[i].name, sizeof(Ch)* tokens_[i].length) != 0)) + { + return false; + } + } + + return true; + } + + //! Inequality operator. + /*! + \note When any pointers are invalid, always returns true. + */ + bool operator!=(const GenericPointer& rhs) const { return !(*this == rhs); } + + //! Less than operator. + /*! + \note Invalid pointers are always greater than valid ones. + */ + bool operator<(const GenericPointer& rhs) const { + if (!IsValid()) + return false; + if (!rhs.IsValid()) + return true; + + if (tokenCount_ != rhs.tokenCount_) + return tokenCount_ < rhs.tokenCount_; + + for (size_t i = 0; i < tokenCount_; i++) { + if (tokens_[i].index != rhs.tokens_[i].index) + return tokens_[i].index < rhs.tokens_[i].index; + + if (tokens_[i].length != rhs.tokens_[i].length) + return tokens_[i].length < rhs.tokens_[i].length; + + if (int cmp = std::memcmp(tokens_[i].name, rhs.tokens_[i].name, sizeof(Ch) * tokens_[i].length)) + return cmp < 0; + } + + return false; + } + + //@} + + //!@name Stringify + //@{ + + //! Stringify the pointer into string representation. + /*! + \tparam OutputStream Type of output stream. + \param os The output stream. + */ + template + bool Stringify(OutputStream& os) const { + return Stringify(os); + } + + //! Stringify the pointer into URI fragment representation. + /*! + \tparam OutputStream Type of output stream. + \param os The output stream. + */ + template + bool StringifyUriFragment(OutputStream& os) const { + return Stringify(os); + } + + //@} + + //!@name Create value + //@{ + + //! Create a value in a subtree. + /*! + If the value is not exist, it creates all parent values and a JSON Null value. + So it always succeed and return the newly created or existing value. + + Remind that it may change types of parents according to tokens, so it + potentially removes previously stored values. For example, if a document + was an array, and "/foo" is used to create a value, then the document + will be changed to an object, and all existing array elements are lost. + + \param root Root value of a DOM subtree to be resolved. It can be any value other than document root. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \param alreadyExist If non-null, it stores whether the resolved value is already exist. + \return The resolved newly created (a JSON Null value), or already exists value. + */ + ValueType& Create(ValueType& root, typename ValueType::AllocatorType& allocator, bool* alreadyExist = 0) const { + RAPIDJSON_ASSERT(IsValid()); + ValueType* v = &root; + bool exist = true; + for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + if (v->IsArray() && t->name[0] == '-' && t->length == 1) { + v->PushBack(ValueType().Move(), allocator); + v = &((*v)[v->Size() - 1]); + exist = false; + } + else { + if (t->index == kPointerInvalidIndex) { // must be object name + if (!v->IsObject()) + v->SetObject(); // Change to Object + } + else { // object name or array index + if (!v->IsArray() && !v->IsObject()) + v->SetArray(); // Change to Array + } + + if (v->IsArray()) { + if (t->index >= v->Size()) { + v->Reserve(t->index + 1, allocator); + while (t->index >= v->Size()) + v->PushBack(ValueType().Move(), allocator); + exist = false; + } + v = &((*v)[t->index]); + } + else { + typename ValueType::MemberIterator m = v->FindMember(GenericValue(GenericStringRef(t->name, t->length))); + if (m == v->MemberEnd()) { + v->AddMember(ValueType(t->name, t->length, allocator).Move(), ValueType().Move(), allocator); + m = v->MemberEnd(); + v = &(--m)->value; // Assumes AddMember() appends at the end + exist = false; + } + else + v = &m->value; + } + } + } + + if (alreadyExist) + *alreadyExist = exist; + + return *v; + } + + //! Creates a value in a document. + /*! + \param document A document to be resolved. + \param alreadyExist If non-null, it stores whether the resolved value is already exist. + \return The resolved newly created, or already exists value. + */ + template + ValueType& Create(GenericDocument& document, bool* alreadyExist = 0) const { + return Create(document, document.GetAllocator(), alreadyExist); + } + + //@} + + //!@name Query value + //@{ + + //! Query a value in a subtree. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param unresolvedTokenIndex If the pointer cannot resolve a token in the pointer, this parameter can obtain the index of unresolved token. + \return Pointer to the value if it can be resolved. Otherwise null. + + \note + There are only 3 situations when a value cannot be resolved: + 1. A value in the path is not an array nor object. + 2. An object value does not contain the token. + 3. A token is out of range of an array value. + + Use unresolvedTokenIndex to retrieve the token index. + */ + ValueType* Get(ValueType& root, size_t* unresolvedTokenIndex = 0) const { + RAPIDJSON_ASSERT(IsValid()); + ValueType* v = &root; + for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + switch (v->GetType()) { + case kObjectType: + { + typename ValueType::MemberIterator m = v->FindMember(GenericValue(GenericStringRef(t->name, t->length))); + if (m == v->MemberEnd()) + break; + v = &m->value; + } + continue; + case kArrayType: + if (t->index == kPointerInvalidIndex || t->index >= v->Size()) + break; + v = &((*v)[t->index]); + continue; + default: + break; + } + + // Error: unresolved token + if (unresolvedTokenIndex) + *unresolvedTokenIndex = static_cast(t - tokens_); + return 0; + } + return v; + } + + //! Query a const value in a const subtree. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \return Pointer to the value if it can be resolved. Otherwise null. + */ + const ValueType* Get(const ValueType& root, size_t* unresolvedTokenIndex = 0) const { + return Get(const_cast(root), unresolvedTokenIndex); + } + + //@} + + //!@name Query a value with default + //@{ + + //! Query a value in a subtree with default value. + /*! + Similar to Get(), but if the specified value do not exists, it creates all parents and clone the default value. + So that this function always succeed. + + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param defaultValue Default value to be cloned if the value was not exists. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \see Create() + */ + ValueType& GetWithDefault(ValueType& root, const ValueType& defaultValue, typename ValueType::AllocatorType& allocator) const { + bool alreadyExist; + ValueType& v = Create(root, allocator, &alreadyExist); + return alreadyExist ? v : v.CopyFrom(defaultValue, allocator); + } + + //! Query a value in a subtree with default null-terminated string. + ValueType& GetWithDefault(ValueType& root, const Ch* defaultValue, typename ValueType::AllocatorType& allocator) const { + bool alreadyExist; + ValueType& v = Create(root, allocator, &alreadyExist); + return alreadyExist ? v : v.SetString(defaultValue, allocator); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Query a value in a subtree with default std::basic_string. + ValueType& GetWithDefault(ValueType& root, const std::basic_string& defaultValue, typename ValueType::AllocatorType& allocator) const { + bool alreadyExist; + ValueType& v = Create(root, allocator, &alreadyExist); + return alreadyExist ? v : v.SetString(defaultValue, allocator); + } +#endif + + //! Query a value in a subtree with default primitive value. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + GetWithDefault(ValueType& root, T defaultValue, typename ValueType::AllocatorType& allocator) const { + return GetWithDefault(root, ValueType(defaultValue).Move(), allocator); + } + + //! Query a value in a document with default value. + template + ValueType& GetWithDefault(GenericDocument& document, const ValueType& defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } + + //! Query a value in a document with default null-terminated string. + template + ValueType& GetWithDefault(GenericDocument& document, const Ch* defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Query a value in a document with default std::basic_string. + template + ValueType& GetWithDefault(GenericDocument& document, const std::basic_string& defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } +#endif + + //! Query a value in a document with default primitive value. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + GetWithDefault(GenericDocument& document, T defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } + + //@} + + //!@name Set a value + //@{ + + //! Set a value in a subtree, with move semantics. + /*! + It creates all parents if they are not exist or types are different to the tokens. + So this function always succeeds but potentially remove existing values. + + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param value Value to be set. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \see Create() + */ + ValueType& Set(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = value; + } + + //! Set a value in a subtree, with copy semantics. + ValueType& Set(ValueType& root, const ValueType& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator).CopyFrom(value, allocator); + } + + //! Set a null-terminated string in a subtree. + ValueType& Set(ValueType& root, const Ch* value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = ValueType(value, allocator).Move(); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Set a std::basic_string in a subtree. + ValueType& Set(ValueType& root, const std::basic_string& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = ValueType(value, allocator).Move(); + } +#endif + + //! Set a primitive value in a subtree. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + Set(ValueType& root, T value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = ValueType(value).Move(); + } + + //! Set a value in a document, with move semantics. + template + ValueType& Set(GenericDocument& document, ValueType& value) const { + return Create(document) = value; + } + + //! Set a value in a document, with copy semantics. + template + ValueType& Set(GenericDocument& document, const ValueType& value) const { + return Create(document).CopyFrom(value, document.GetAllocator()); + } + + //! Set a null-terminated string in a document. + template + ValueType& Set(GenericDocument& document, const Ch* value) const { + return Create(document) = ValueType(value, document.GetAllocator()).Move(); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Sets a std::basic_string in a document. + template + ValueType& Set(GenericDocument& document, const std::basic_string& value) const { + return Create(document) = ValueType(value, document.GetAllocator()).Move(); + } +#endif + + //! Set a primitive value in a document. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + Set(GenericDocument& document, T value) const { + return Create(document) = value; + } + + //@} + + //!@name Swap a value + //@{ + + //! Swap a value with a value in a subtree. + /*! + It creates all parents if they are not exist or types are different to the tokens. + So this function always succeeds but potentially remove existing values. + + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param value Value to be swapped. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \see Create() + */ + ValueType& Swap(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator).Swap(value); + } + + //! Swap a value with a value in a document. + template + ValueType& Swap(GenericDocument& document, ValueType& value) const { + return Create(document).Swap(value); + } + + //@} + + //! Erase a value in a subtree. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \return Whether the resolved value is found and erased. + + \note Erasing with an empty pointer \c Pointer(""), i.e. the root, always fail and return false. + */ + bool Erase(ValueType& root) const { + RAPIDJSON_ASSERT(IsValid()); + if (tokenCount_ == 0) // Cannot erase the root + return false; + + ValueType* v = &root; + const Token* last = tokens_ + (tokenCount_ - 1); + for (const Token *t = tokens_; t != last; ++t) { + switch (v->GetType()) { + case kObjectType: + { + typename ValueType::MemberIterator m = v->FindMember(GenericValue(GenericStringRef(t->name, t->length))); + if (m == v->MemberEnd()) + return false; + v = &m->value; + } + break; + case kArrayType: + if (t->index == kPointerInvalidIndex || t->index >= v->Size()) + return false; + v = &((*v)[t->index]); + break; + default: + return false; + } + } + + switch (v->GetType()) { + case kObjectType: + return v->EraseMember(GenericStringRef(last->name, last->length)); + case kArrayType: + if (last->index == kPointerInvalidIndex || last->index >= v->Size()) + return false; + v->Erase(v->Begin() + last->index); + return true; + default: + return false; + } + } + +private: + //! Clone the content from rhs to this. + /*! + \param rhs Source pointer. + \param extraToken Extra tokens to be allocated. + \param extraNameBufferSize Extra name buffer size (in number of Ch) to be allocated. + \return Start of non-occupied name buffer, for storing extra names. + */ + Ch* CopyFromRaw(const GenericPointer& rhs, size_t extraToken = 0, size_t extraNameBufferSize = 0) { + if (!allocator_) // allocator is independently owned. + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + + size_t nameBufferSize = rhs.tokenCount_; // null terminators for tokens + for (Token *t = rhs.tokens_; t != rhs.tokens_ + rhs.tokenCount_; ++t) + nameBufferSize += t->length; + + tokenCount_ = rhs.tokenCount_ + extraToken; + tokens_ = static_cast(allocator_->Malloc(tokenCount_ * sizeof(Token) + (nameBufferSize + extraNameBufferSize) * sizeof(Ch))); + nameBuffer_ = reinterpret_cast(tokens_ + tokenCount_); + if (rhs.tokenCount_ > 0) { + std::memcpy(tokens_, rhs.tokens_, rhs.tokenCount_ * sizeof(Token)); + } + if (nameBufferSize > 0) { + std::memcpy(nameBuffer_, rhs.nameBuffer_, nameBufferSize * sizeof(Ch)); + } + + // Adjust pointers to name buffer + std::ptrdiff_t diff = nameBuffer_ - rhs.nameBuffer_; + for (Token *t = tokens_; t != tokens_ + rhs.tokenCount_; ++t) + t->name += diff; + + return nameBuffer_ + nameBufferSize; + } + + //! Check whether a character should be percent-encoded. + /*! + According to RFC 3986 2.3 Unreserved Characters. + \param c The character (code unit) to be tested. + */ + bool NeedPercentEncode(Ch c) const { + return !((c >= '0' && c <= '9') || (c >= 'A' && c <='Z') || (c >= 'a' && c <= 'z') || c == '-' || c == '.' || c == '_' || c =='~'); + } + + //! Parse a JSON String or its URI fragment representation into tokens. +#ifndef __clang__ // -Wdocumentation + /*! + \param source Either a JSON Pointer string, or its URI fragment representation. Not need to be null terminated. + \param length Length of the source string. + \note Source cannot be JSON String Representation of JSON Pointer, e.g. In "/\u0000", \u0000 will not be unescaped. + */ +#endif + void Parse(const Ch* source, size_t length) { + RAPIDJSON_ASSERT(source != NULL); + RAPIDJSON_ASSERT(nameBuffer_ == 0); + RAPIDJSON_ASSERT(tokens_ == 0); + + // Create own allocator if user did not supply. + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + + // Count number of '/' as tokenCount + tokenCount_ = 0; + for (const Ch* s = source; s != source + length; s++) + if (*s == '/') + tokenCount_++; + + Token* token = tokens_ = static_cast(allocator_->Malloc(tokenCount_ * sizeof(Token) + length * sizeof(Ch))); + Ch* name = nameBuffer_ = reinterpret_cast(tokens_ + tokenCount_); + size_t i = 0; + + // Detect if it is a URI fragment + bool uriFragment = false; + if (source[i] == '#') { + uriFragment = true; + i++; + } + + if (i != length && source[i] != '/') { + parseErrorCode_ = kPointerParseErrorTokenMustBeginWithSolidus; + goto error; + } + + while (i < length) { + RAPIDJSON_ASSERT(source[i] == '/'); + i++; // consumes '/' + + token->name = name; + bool isNumber = true; + + while (i < length && source[i] != '/') { + Ch c = source[i]; + if (uriFragment) { + // Decoding percent-encoding for URI fragment + if (c == '%') { + PercentDecodeStream is(&source[i], source + length); + GenericInsituStringStream os(name); + Ch* begin = os.PutBegin(); + if (!Transcoder, EncodingType>().Validate(is, os) || !is.IsValid()) { + parseErrorCode_ = kPointerParseErrorInvalidPercentEncoding; + goto error; + } + size_t len = os.PutEnd(begin); + i += is.Tell() - 1; + if (len == 1) + c = *name; + else { + name += len; + isNumber = false; + i++; + continue; + } + } + else if (NeedPercentEncode(c)) { + parseErrorCode_ = kPointerParseErrorCharacterMustPercentEncode; + goto error; + } + } + + i++; + + // Escaping "~0" -> '~', "~1" -> '/' + if (c == '~') { + if (i < length) { + c = source[i]; + if (c == '0') c = '~'; + else if (c == '1') c = '/'; + else { + parseErrorCode_ = kPointerParseErrorInvalidEscape; + goto error; + } + i++; + } + else { + parseErrorCode_ = kPointerParseErrorInvalidEscape; + goto error; + } + } + + // First check for index: all of characters are digit + if (c < '0' || c > '9') + isNumber = false; + + *name++ = c; + } + token->length = static_cast(name - token->name); + if (token->length == 0) + isNumber = false; + *name++ = '\0'; // Null terminator + + // Second check for index: more than one digit cannot have leading zero + if (isNumber && token->length > 1 && token->name[0] == '0') + isNumber = false; + + // String to SizeType conversion + SizeType n = 0; + if (isNumber) { + for (size_t j = 0; j < token->length; j++) { + SizeType m = n * 10 + static_cast(token->name[j] - '0'); + if (m < n) { // overflow detection + isNumber = false; + break; + } + n = m; + } + } + + token->index = isNumber ? n : kPointerInvalidIndex; + token++; + } + + RAPIDJSON_ASSERT(name <= nameBuffer_ + length); // Should not overflow buffer + parseErrorCode_ = kPointerParseErrorNone; + return; + + error: + Allocator::Free(tokens_); + nameBuffer_ = 0; + tokens_ = 0; + tokenCount_ = 0; + parseErrorOffset_ = i; + return; + } + + //! Stringify to string or URI fragment representation. + /*! + \tparam uriFragment True for stringifying to URI fragment representation. False for string representation. + \tparam OutputStream type of output stream. + \param os The output stream. + */ + template + bool Stringify(OutputStream& os) const { + RAPIDJSON_ASSERT(IsValid()); + + if (uriFragment) + os.Put('#'); + + for (Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + os.Put('/'); + for (size_t j = 0; j < t->length; j++) { + Ch c = t->name[j]; + if (c == '~') { + os.Put('~'); + os.Put('0'); + } + else if (c == '/') { + os.Put('~'); + os.Put('1'); + } + else if (uriFragment && NeedPercentEncode(c)) { + // Transcode to UTF8 sequence + GenericStringStream source(&t->name[j]); + PercentEncodeStream target(os); + if (!Transcoder >().Validate(source, target)) + return false; + j += source.Tell() - 1; + } + else + os.Put(c); + } + } + return true; + } + + //! A helper stream for decoding a percent-encoded sequence into code unit. + /*! + This stream decodes %XY triplet into code unit (0-255). + If it encounters invalid characters, it sets output code unit as 0 and + mark invalid, and to be checked by IsValid(). + */ + class PercentDecodeStream { + public: + typedef typename ValueType::Ch Ch; + + //! Constructor + /*! + \param source Start of the stream + \param end Past-the-end of the stream. + */ + PercentDecodeStream(const Ch* source, const Ch* end) : src_(source), head_(source), end_(end), valid_(true) {} + + Ch Take() { + if (*src_ != '%' || src_ + 3 > end_) { // %XY triplet + valid_ = false; + return 0; + } + src_++; + Ch c = 0; + for (int j = 0; j < 2; j++) { + c = static_cast(c << 4); + Ch h = *src_; + if (h >= '0' && h <= '9') c = static_cast(c + h - '0'); + else if (h >= 'A' && h <= 'F') c = static_cast(c + h - 'A' + 10); + else if (h >= 'a' && h <= 'f') c = static_cast(c + h - 'a' + 10); + else { + valid_ = false; + return 0; + } + src_++; + } + return c; + } + + size_t Tell() const { return static_cast(src_ - head_); } + bool IsValid() const { return valid_; } + + private: + const Ch* src_; //!< Current read position. + const Ch* head_; //!< Original head of the string. + const Ch* end_; //!< Past-the-end position. + bool valid_; //!< Whether the parsing is valid. + }; + + //! A helper stream to encode character (UTF-8 code unit) into percent-encoded sequence. + template + class PercentEncodeStream { + public: + PercentEncodeStream(OutputStream& os) : os_(os) {} + void Put(char c) { // UTF-8 must be byte + unsigned char u = static_cast(c); + static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + os_.Put('%'); + os_.Put(static_cast(hexDigits[u >> 4])); + os_.Put(static_cast(hexDigits[u & 15])); + } + private: + OutputStream& os_; + }; + + Allocator* allocator_; //!< The current allocator. It is either user-supplied or equal to ownAllocator_. + Allocator* ownAllocator_; //!< Allocator owned by this Pointer. + Ch* nameBuffer_; //!< A buffer containing all names in tokens. + Token* tokens_; //!< A list of tokens. + size_t tokenCount_; //!< Number of tokens in tokens_. + size_t parseErrorOffset_; //!< Offset in code unit when parsing fail. + PointerParseErrorCode parseErrorCode_; //!< Parsing error code. +}; + +//! GenericPointer for Value (UTF-8, default allocator). +typedef GenericPointer Pointer; + +//!@name Helper functions for GenericPointer +//@{ + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& CreateValueByPointer(T& root, const GenericPointer& pointer, typename T::AllocatorType& a) { + return pointer.Create(root, a); +} + +template +typename T::ValueType& CreateValueByPointer(T& root, const CharType(&source)[N], typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Create(root, a); +} + +// No allocator parameter + +template +typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const GenericPointer& pointer) { + return pointer.Create(document); +} + +template +typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const CharType(&source)[N]) { + return GenericPointer(source, N - 1).Create(document); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType* GetValueByPointer(T& root, const GenericPointer& pointer, size_t* unresolvedTokenIndex = 0) { + return pointer.Get(root, unresolvedTokenIndex); +} + +template +const typename T::ValueType* GetValueByPointer(const T& root, const GenericPointer& pointer, size_t* unresolvedTokenIndex = 0) { + return pointer.Get(root, unresolvedTokenIndex); +} + +template +typename T::ValueType* GetValueByPointer(T& root, const CharType (&source)[N], size_t* unresolvedTokenIndex = 0) { + return GenericPointer(source, N - 1).Get(root, unresolvedTokenIndex); +} + +template +const typename T::ValueType* GetValueByPointer(const T& root, const CharType(&source)[N], size_t* unresolvedTokenIndex = 0) { + return GenericPointer(source, N - 1).Get(root, unresolvedTokenIndex); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const typename T::ValueType& defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const typename T::Ch* defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const std::basic_string& defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, T2 defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::ValueType& defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::Ch* defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const std::basic_string& defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +GetValueByPointerWithDefault(T& root, const CharType(&source)[N], T2 defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} + +// No allocator parameter + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::ValueType& defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::Ch* defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const std::basic_string& defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, T2 defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const std::basic_string& defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], T2 defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, typename T::ValueType& value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const typename T::ValueType& value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const typename T::Ch* value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const std::basic_string& value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +SetValueByPointer(T& root, const GenericPointer& pointer, T2 value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::ValueType& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::Ch* value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const std::basic_string& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +SetValueByPointer(T& root, const CharType(&source)[N], T2 value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +// No allocator parameter + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, typename DocumentType::ValueType& value) { + return pointer.Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::ValueType& value) { + return pointer.Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::Ch* value) { + return pointer.Set(document, value); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const std::basic_string& value) { + return pointer.Set(document, value); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +SetValueByPointer(DocumentType& document, const GenericPointer& pointer, T2 value) { + return pointer.Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const std::basic_string& value) { + return GenericPointer(source, N - 1).Set(document, value); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +SetValueByPointer(DocumentType& document, const CharType(&source)[N], T2 value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& SwapValueByPointer(T& root, const GenericPointer& pointer, typename T::ValueType& value, typename T::AllocatorType& a) { + return pointer.Swap(root, value, a); +} + +template +typename T::ValueType& SwapValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Swap(root, value, a); +} + +template +typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const GenericPointer& pointer, typename DocumentType::ValueType& value) { + return pointer.Swap(document, value); +} + +template +typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) { + return GenericPointer(source, N - 1).Swap(document, value); +} + +////////////////////////////////////////////////////////////////////////////// + +template +bool EraseValueByPointer(T& root, const GenericPointer& pointer) { + return pointer.Erase(root); +} + +template +bool EraseValueByPointer(T& root, const CharType(&source)[N]) { + return GenericPointer(source, N - 1).Erase(root); +} + +//@} + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_POINTER_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/prettywriter.h b/libraries/libstratosphere/include/stratosphere/rapidjson/prettywriter.h new file mode 100644 index 000000000..fe45df1d1 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/prettywriter.h @@ -0,0 +1,277 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_PRETTYWRITER_H_ +#define RAPIDJSON_PRETTYWRITER_H_ + +#include "writer.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Combination of PrettyWriter format flags. +/*! \see PrettyWriter::SetFormatOptions + */ +enum PrettyFormatOptions { + kFormatDefault = 0, //!< Default pretty formatting. + kFormatSingleLineArray = 1 //!< Format arrays on a single line. +}; + +//! Writer with indentation and spacing. +/*! + \tparam OutputStream Type of output os. + \tparam SourceEncoding Encoding of source string. + \tparam TargetEncoding Encoding of output stream. + \tparam StackAllocator Type of allocator for allocating memory of stack. +*/ +template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> +class PrettyWriter : public Writer { +public: + typedef Writer Base; + typedef typename Base::Ch Ch; + + //! Constructor + /*! \param os Output stream. + \param allocator User supplied allocator. If it is null, it will create a private one. + \param levelDepth Initial capacity of stack. + */ + explicit PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : + Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {} + + + explicit PrettyWriter(StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : + Base(allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + PrettyWriter(PrettyWriter&& rhs) : + Base(std::forward(rhs)), indentChar_(rhs.indentChar_), indentCharCount_(rhs.indentCharCount_), formatOptions_(rhs.formatOptions_) {} +#endif + + //! Set custom indentation. + /*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r'). + \param indentCharCount Number of indent characters for each indentation level. + \note The default indentation is 4 spaces. + */ + PrettyWriter& SetIndent(Ch indentChar, unsigned indentCharCount) { + RAPIDJSON_ASSERT(indentChar == ' ' || indentChar == '\t' || indentChar == '\n' || indentChar == '\r'); + indentChar_ = indentChar; + indentCharCount_ = indentCharCount; + return *this; + } + + //! Set pretty writer formatting options. + /*! \param options Formatting options. + */ + PrettyWriter& SetFormatOptions(PrettyFormatOptions options) { + formatOptions_ = options; + return *this; + } + + /*! @name Implementation of Handler + \see Handler + */ + //@{ + + bool Null() { PrettyPrefix(kNullType); return Base::EndValue(Base::WriteNull()); } + bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::EndValue(Base::WriteBool(b)); } + bool Int(int i) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt(i)); } + bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint(u)); } + bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt64(i64)); } + bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint64(u64)); } + bool Double(double d) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteDouble(d)); } + + bool RawNumber(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + PrettyPrefix(kNumberType); + return Base::EndValue(Base::WriteString(str, length)); + } + + bool String(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + PrettyPrefix(kStringType); + return Base::EndValue(Base::WriteString(str, length)); + } + +#if RAPIDJSON_HAS_STDSTRING + bool String(const std::basic_string& str) { + return String(str.data(), SizeType(str.size())); + } +#endif + + bool StartObject() { + PrettyPrefix(kObjectType); + new (Base::level_stack_.template Push()) typename Base::Level(false); + return Base::WriteStartObject(); + } + + bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + +#if RAPIDJSON_HAS_STDSTRING + bool Key(const std::basic_string& str) { + return Key(str.data(), SizeType(str.size())); + } +#endif + + bool EndObject(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); // not inside an Object + RAPIDJSON_ASSERT(!Base::level_stack_.template Top()->inArray); // currently inside an Array, not Object + RAPIDJSON_ASSERT(0 == Base::level_stack_.template Top()->valueCount % 2); // Object has a Key without a Value + + bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; + + if (!empty) { + Base::os_->Put('\n'); + WriteIndent(); + } + bool ret = Base::EndValue(Base::WriteEndObject()); + (void)ret; + RAPIDJSON_ASSERT(ret == true); + if (Base::level_stack_.Empty()) // end of json text + Base::Flush(); + return true; + } + + bool StartArray() { + PrettyPrefix(kArrayType); + new (Base::level_stack_.template Push()) typename Base::Level(true); + return Base::WriteStartArray(); + } + + bool EndArray(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); + RAPIDJSON_ASSERT(Base::level_stack_.template Top()->inArray); + bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; + + if (!empty && !(formatOptions_ & kFormatSingleLineArray)) { + Base::os_->Put('\n'); + WriteIndent(); + } + bool ret = Base::EndValue(Base::WriteEndArray()); + (void)ret; + RAPIDJSON_ASSERT(ret == true); + if (Base::level_stack_.Empty()) // end of json text + Base::Flush(); + return true; + } + + //@} + + /*! @name Convenience extensions */ + //@{ + + //! Simpler but slower overload. + bool String(const Ch* str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } + + //@} + + //! Write a raw JSON value. + /*! + For user to write a stringified JSON as a value. + + \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range. + \param length Length of the json. + \param type Type of the root of json. + \note When using PrettyWriter::RawValue(), the result json may not be indented correctly. + */ + bool RawValue(const Ch* json, size_t length, Type type) { + RAPIDJSON_ASSERT(json != 0); + PrettyPrefix(type); + return Base::EndValue(Base::WriteRawValue(json, length)); + } + +protected: + void PrettyPrefix(Type type) { + (void)type; + if (Base::level_stack_.GetSize() != 0) { // this value is not at root + typename Base::Level* level = Base::level_stack_.template Top(); + + if (level->inArray) { + if (level->valueCount > 0) { + Base::os_->Put(','); // add comma if it is not the first element in array + if (formatOptions_ & kFormatSingleLineArray) + Base::os_->Put(' '); + } + + if (!(formatOptions_ & kFormatSingleLineArray)) { + Base::os_->Put('\n'); + WriteIndent(); + } + } + else { // in object + if (level->valueCount > 0) { + if (level->valueCount % 2 == 0) { + Base::os_->Put(','); + Base::os_->Put('\n'); + } + else { + Base::os_->Put(':'); + Base::os_->Put(' '); + } + } + else + Base::os_->Put('\n'); + + if (level->valueCount % 2 == 0) + WriteIndent(); + } + if (!level->inArray && level->valueCount % 2 == 0) + RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name + level->valueCount++; + } + else { + RAPIDJSON_ASSERT(!Base::hasRoot_); // Should only has one and only one root. + Base::hasRoot_ = true; + } + } + + void WriteIndent() { + size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_; + PutN(*Base::os_, static_cast(indentChar_), count); + } + + Ch indentChar_; + unsigned indentCharCount_; + PrettyFormatOptions formatOptions_; + +private: + // Prohibit copy constructor & assignment operator. + PrettyWriter(const PrettyWriter&); + PrettyWriter& operator=(const PrettyWriter&); +}; + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/rapidjson.h b/libraries/libstratosphere/include/stratosphere/rapidjson/rapidjson.h new file mode 100644 index 000000000..78aa89a0e --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/rapidjson.h @@ -0,0 +1,692 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_RAPIDJSON_H_ +#define RAPIDJSON_RAPIDJSON_H_ + +/*!\file rapidjson.h + \brief common definitions and configuration + + \see RAPIDJSON_CONFIG + */ + +/*! \defgroup RAPIDJSON_CONFIG RapidJSON configuration + \brief Configuration macros for library features + + Some RapidJSON features are configurable to adapt the library to a wide + variety of platforms, environments and usage scenarios. Most of the + features can be configured in terms of overridden or predefined + preprocessor macros at compile-time. + + Some additional customization is available in the \ref RAPIDJSON_ERRORS APIs. + + \note These macros should be given on the compiler command-line + (where applicable) to avoid inconsistent values when compiling + different translation units of a single application. + */ + +#include // malloc(), realloc(), free(), size_t +#include // memset(), memcpy(), memmove(), memcmp() + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_VERSION_STRING +// +// ALWAYS synchronize the following 3 macros with corresponding variables in /CMakeLists.txt. +// + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +// token stringification +#define RAPIDJSON_STRINGIFY(x) RAPIDJSON_DO_STRINGIFY(x) +#define RAPIDJSON_DO_STRINGIFY(x) #x + +// token concatenation +#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y) +#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y) +#define RAPIDJSON_DO_JOIN2(X, Y) X##Y +//!@endcond + +/*! \def RAPIDJSON_MAJOR_VERSION + \ingroup RAPIDJSON_CONFIG + \brief Major version of RapidJSON in integer. +*/ +/*! \def RAPIDJSON_MINOR_VERSION + \ingroup RAPIDJSON_CONFIG + \brief Minor version of RapidJSON in integer. +*/ +/*! \def RAPIDJSON_PATCH_VERSION + \ingroup RAPIDJSON_CONFIG + \brief Patch version of RapidJSON in integer. +*/ +/*! \def RAPIDJSON_VERSION_STRING + \ingroup RAPIDJSON_CONFIG + \brief Version of RapidJSON in ".." string format. +*/ +#define RAPIDJSON_MAJOR_VERSION 1 +#define RAPIDJSON_MINOR_VERSION 1 +#define RAPIDJSON_PATCH_VERSION 0 +#define RAPIDJSON_VERSION_STRING \ + RAPIDJSON_STRINGIFY(RAPIDJSON_MAJOR_VERSION.RAPIDJSON_MINOR_VERSION.RAPIDJSON_PATCH_VERSION) + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NAMESPACE_(BEGIN|END) +/*! \def RAPIDJSON_NAMESPACE + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace + + In order to avoid symbol clashes and/or "One Definition Rule" errors + between multiple inclusions of (different versions of) RapidJSON in + a single binary, users can customize the name of the main RapidJSON + namespace. + + In case of a single nesting level, defining \c RAPIDJSON_NAMESPACE + to a custom name (e.g. \c MyRapidJSON) is sufficient. If multiple + levels are needed, both \ref RAPIDJSON_NAMESPACE_BEGIN and \ref + RAPIDJSON_NAMESPACE_END need to be defined as well: + + \code + // in some .cpp file + #define RAPIDJSON_NAMESPACE my::rapidjson + #define RAPIDJSON_NAMESPACE_BEGIN namespace my { namespace rapidjson { + #define RAPIDJSON_NAMESPACE_END } } + #include "rapidjson/..." + \endcode + + \see rapidjson + */ +/*! \def RAPIDJSON_NAMESPACE_BEGIN + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace (opening expression) + \see RAPIDJSON_NAMESPACE +*/ +/*! \def RAPIDJSON_NAMESPACE_END + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace (closing expression) + \see RAPIDJSON_NAMESPACE +*/ +#ifndef RAPIDJSON_NAMESPACE +#define RAPIDJSON_NAMESPACE rapidjson +#endif +#ifndef RAPIDJSON_NAMESPACE_BEGIN +#define RAPIDJSON_NAMESPACE_BEGIN namespace RAPIDJSON_NAMESPACE { +#endif +#ifndef RAPIDJSON_NAMESPACE_END +#define RAPIDJSON_NAMESPACE_END } +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_HAS_STDSTRING + +#ifndef RAPIDJSON_HAS_STDSTRING +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_HAS_STDSTRING 1 // force generation of documentation +#else +#define RAPIDJSON_HAS_STDSTRING 0 // no std::string support by default +#endif +/*! \def RAPIDJSON_HAS_STDSTRING + \ingroup RAPIDJSON_CONFIG + \brief Enable RapidJSON support for \c std::string + + By defining this preprocessor symbol to \c 1, several convenience functions for using + \ref rapidjson::GenericValue with \c std::string are enabled, especially + for construction and comparison. + + \hideinitializer +*/ +#endif // !defined(RAPIDJSON_HAS_STDSTRING) + +#if RAPIDJSON_HAS_STDSTRING +#include +#endif // RAPIDJSON_HAS_STDSTRING + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NO_INT64DEFINE + +/*! \def RAPIDJSON_NO_INT64DEFINE + \ingroup RAPIDJSON_CONFIG + \brief Use external 64-bit integer types. + + RapidJSON requires the 64-bit integer types \c int64_t and \c uint64_t types + to be available at global scope. + + If users have their own definition, define RAPIDJSON_NO_INT64DEFINE to + prevent RapidJSON from defining its own types. +*/ +#ifndef RAPIDJSON_NO_INT64DEFINE +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#if defined(_MSC_VER) && (_MSC_VER < 1800) // Visual Studio 2013 +#include "msinttypes/stdint.h" +#include "msinttypes/inttypes.h" +#else +// Other compilers should have this. +#include +#include +#endif +//!@endcond +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_NO_INT64DEFINE +#endif +#endif // RAPIDJSON_NO_INT64TYPEDEF + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_FORCEINLINE + +#ifndef RAPIDJSON_FORCEINLINE +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#if defined(_MSC_VER) && defined(NDEBUG) +#define RAPIDJSON_FORCEINLINE __forceinline +#elif defined(__GNUC__) && __GNUC__ >= 4 && defined(NDEBUG) +#define RAPIDJSON_FORCEINLINE __attribute__((always_inline)) +#else +#define RAPIDJSON_FORCEINLINE +#endif +//!@endcond +#endif // RAPIDJSON_FORCEINLINE + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ENDIAN +#define RAPIDJSON_LITTLEENDIAN 0 //!< Little endian machine +#define RAPIDJSON_BIGENDIAN 1 //!< Big endian machine + +//! Endianness of the machine. +/*! + \def RAPIDJSON_ENDIAN + \ingroup RAPIDJSON_CONFIG + + GCC 4.6 provided macro for detecting endianness of the target machine. But other + compilers may not have this. User can define RAPIDJSON_ENDIAN to either + \ref RAPIDJSON_LITTLEENDIAN or \ref RAPIDJSON_BIGENDIAN. + + Default detection implemented with reference to + \li https://gcc.gnu.org/onlinedocs/gcc-4.6.0/cpp/Common-Predefined-Macros.html + \li http://www.boost.org/doc/libs/1_42_0/boost/detail/endian.hpp +*/ +#ifndef RAPIDJSON_ENDIAN +// Detect with GCC 4.6's macro +# ifdef __BYTE_ORDER__ +# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# else +# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN. +# endif // __BYTE_ORDER__ +// Detect with GLIBC's endian.h +# elif defined(__GLIBC__) +# include +# if (__BYTE_ORDER == __LITTLE_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif (__BYTE_ORDER == __BIG_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# else +# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN. +# endif // __GLIBC__ +// Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro +# elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +// Detect with architecture macros +# elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || defined(__s390__) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64)) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(RAPIDJSON_DOXYGEN_RUNNING) +# define RAPIDJSON_ENDIAN +# else +# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN. +# endif +#endif // RAPIDJSON_ENDIAN + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_64BIT + +//! Whether using 64-bit architecture +#ifndef RAPIDJSON_64BIT +#if defined(__LP64__) || (defined(__x86_64__) && defined(__ILP32__)) || defined(_WIN64) || defined(__EMSCRIPTEN__) +#define RAPIDJSON_64BIT 1 +#else +#define RAPIDJSON_64BIT 0 +#endif +#endif // RAPIDJSON_64BIT + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ALIGN + +//! Data alignment of the machine. +/*! \ingroup RAPIDJSON_CONFIG + \param x pointer to align + + Some machines require strict data alignment. The default is 8 bytes. + User can customize by defining the RAPIDJSON_ALIGN function macro. +*/ +#ifndef RAPIDJSON_ALIGN +#define RAPIDJSON_ALIGN(x) (((x) + static_cast(7u)) & ~static_cast(7u)) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_UINT64_C2 + +//! Construct a 64-bit literal by a pair of 32-bit integer. +/*! + 64-bit literal with or without ULL suffix is prone to compiler warnings. + UINT64_C() is C macro which cause compilation problems. + Use this macro to define 64-bit constants by a pair of 32-bit integer. +*/ +#ifndef RAPIDJSON_UINT64_C2 +#define RAPIDJSON_UINT64_C2(high32, low32) ((static_cast(high32) << 32) | static_cast(low32)) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_48BITPOINTER_OPTIMIZATION + +//! Use only lower 48-bit address for some pointers. +/*! + \ingroup RAPIDJSON_CONFIG + + This optimization uses the fact that current X86-64 architecture only implement lower 48-bit virtual address. + The higher 16-bit can be used for storing other data. + \c GenericValue uses this optimization to reduce its size form 24 bytes to 16 bytes in 64-bit architecture. +*/ +#ifndef RAPIDJSON_48BITPOINTER_OPTIMIZATION +#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) +#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 1 +#else +#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 0 +#endif +#endif // RAPIDJSON_48BITPOINTER_OPTIMIZATION + +#if RAPIDJSON_48BITPOINTER_OPTIMIZATION == 1 +#if RAPIDJSON_64BIT != 1 +#error RAPIDJSON_48BITPOINTER_OPTIMIZATION can only be set to 1 when RAPIDJSON_64BIT=1 +#endif +#define RAPIDJSON_SETPOINTER(type, p, x) (p = reinterpret_cast((reinterpret_cast(p) & static_cast(RAPIDJSON_UINT64_C2(0xFFFF0000, 0x00000000))) | reinterpret_cast(reinterpret_cast(x)))) +#define RAPIDJSON_GETPOINTER(type, p) (reinterpret_cast(reinterpret_cast(p) & static_cast(RAPIDJSON_UINT64_C2(0x0000FFFF, 0xFFFFFFFF)))) +#else +#define RAPIDJSON_SETPOINTER(type, p, x) (p = (x)) +#define RAPIDJSON_GETPOINTER(type, p) (p) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_NEON/RAPIDJSON_SIMD + +/*! \def RAPIDJSON_SIMD + \ingroup RAPIDJSON_CONFIG + \brief Enable SSE2/SSE4.2/Neon optimization. + + RapidJSON supports optimized implementations for some parsing operations + based on the SSE2, SSE4.2 or NEon SIMD extensions on modern Intel + or ARM compatible processors. + + To enable these optimizations, three different symbols can be defined; + \code + // Enable SSE2 optimization. + #define RAPIDJSON_SSE2 + + // Enable SSE4.2 optimization. + #define RAPIDJSON_SSE42 + \endcode + + // Enable ARM Neon optimization. + #define RAPIDJSON_NEON + \endcode + + \c RAPIDJSON_SSE42 takes precedence over SSE2, if both are defined. + + If any of these symbols is defined, RapidJSON defines the macro + \c RAPIDJSON_SIMD to indicate the availability of the optimized code. +*/ +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) \ + || defined(RAPIDJSON_NEON) || defined(RAPIDJSON_DOXYGEN_RUNNING) +#define RAPIDJSON_SIMD +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NO_SIZETYPEDEFINE + +#ifndef RAPIDJSON_NO_SIZETYPEDEFINE +/*! \def RAPIDJSON_NO_SIZETYPEDEFINE + \ingroup RAPIDJSON_CONFIG + \brief User-provided \c SizeType definition. + + In order to avoid using 32-bit size types for indexing strings and arrays, + define this preprocessor symbol and provide the type rapidjson::SizeType + before including RapidJSON: + \code + #define RAPIDJSON_NO_SIZETYPEDEFINE + namespace rapidjson { typedef ::std::size_t SizeType; } + #include "rapidjson/..." + \endcode + + \see rapidjson::SizeType +*/ +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_NO_SIZETYPEDEFINE +#endif +RAPIDJSON_NAMESPACE_BEGIN +//! Size type (for string lengths, array sizes, etc.) +/*! RapidJSON uses 32-bit array/string indices even on 64-bit platforms, + instead of using \c size_t. Users may override the SizeType by defining + \ref RAPIDJSON_NO_SIZETYPEDEFINE. +*/ +typedef unsigned SizeType; +RAPIDJSON_NAMESPACE_END +#endif + +// always import std::size_t to rapidjson namespace +RAPIDJSON_NAMESPACE_BEGIN +using std::size_t; +RAPIDJSON_NAMESPACE_END + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ASSERT + +//! Assertion. +/*! \ingroup RAPIDJSON_CONFIG + By default, rapidjson uses C \c assert() for internal assertions. + User can override it by defining RAPIDJSON_ASSERT(x) macro. + + \note Parsing errors are handled and can be customized by the + \ref RAPIDJSON_ERRORS APIs. +*/ +#ifndef RAPIDJSON_ASSERT +#include +#define RAPIDJSON_ASSERT(x) assert(x) +#endif // RAPIDJSON_ASSERT + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_STATIC_ASSERT + +// Prefer C++11 static_assert, if available +#ifndef RAPIDJSON_STATIC_ASSERT +#if __cplusplus >= 201103L || ( defined(_MSC_VER) && _MSC_VER >= 1800 ) +#define RAPIDJSON_STATIC_ASSERT(x) \ + static_assert(x, RAPIDJSON_STRINGIFY(x)) +#endif // C++11 +#endif // RAPIDJSON_STATIC_ASSERT + +// Adopt C++03 implementation from boost +#ifndef RAPIDJSON_STATIC_ASSERT +#ifndef __clang__ +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#endif +RAPIDJSON_NAMESPACE_BEGIN +template struct STATIC_ASSERTION_FAILURE; +template <> struct STATIC_ASSERTION_FAILURE { enum { value = 1 }; }; +template struct StaticAssertTest {}; +RAPIDJSON_NAMESPACE_END + +#if defined(__GNUC__) || defined(__clang__) +#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused)) +#else +#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE +#endif +#ifndef __clang__ +//!@endcond +#endif + +/*! \def RAPIDJSON_STATIC_ASSERT + \brief (Internal) macro to check for conditions at compile-time + \param x compile-time condition + \hideinitializer + */ +#define RAPIDJSON_STATIC_ASSERT(x) \ + typedef ::RAPIDJSON_NAMESPACE::StaticAssertTest< \ + sizeof(::RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE)> \ + RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE +#endif // RAPIDJSON_STATIC_ASSERT + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_LIKELY, RAPIDJSON_UNLIKELY + +//! Compiler branching hint for expression with high probability to be true. +/*! + \ingroup RAPIDJSON_CONFIG + \param x Boolean expression likely to be true. +*/ +#ifndef RAPIDJSON_LIKELY +#if defined(__GNUC__) || defined(__clang__) +#define RAPIDJSON_LIKELY(x) __builtin_expect(!!(x), 1) +#else +#define RAPIDJSON_LIKELY(x) (x) +#endif +#endif + +//! Compiler branching hint for expression with low probability to be true. +/*! + \ingroup RAPIDJSON_CONFIG + \param x Boolean expression unlikely to be true. +*/ +#ifndef RAPIDJSON_UNLIKELY +#if defined(__GNUC__) || defined(__clang__) +#define RAPIDJSON_UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define RAPIDJSON_UNLIKELY(x) (x) +#endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Helpers + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN + +#define RAPIDJSON_MULTILINEMACRO_BEGIN do { +#define RAPIDJSON_MULTILINEMACRO_END \ +} while((void)0, 0) + +// adopted from Boost +#define RAPIDJSON_VERSION_CODE(x,y,z) \ + (((x)*100000) + ((y)*100) + (z)) + +#if defined(__has_builtin) +#define RAPIDJSON_HAS_BUILTIN(x) __has_builtin(x) +#else +#define RAPIDJSON_HAS_BUILTIN(x) 0 +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_DIAG_PUSH/POP, RAPIDJSON_DIAG_OFF + +#if defined(__GNUC__) +#define RAPIDJSON_GNUC \ + RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) +#endif + +#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,2,0)) + +#define RAPIDJSON_PRAGMA(x) _Pragma(RAPIDJSON_STRINGIFY(x)) +#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(GCC diagnostic x) +#define RAPIDJSON_DIAG_OFF(x) \ + RAPIDJSON_DIAG_PRAGMA(ignored RAPIDJSON_STRINGIFY(RAPIDJSON_JOIN(-W,x))) + +// push/pop support in Clang and GCC>=4.6 +#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) +#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) +#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) +#else // GCC >= 4.2, < 4.6 +#define RAPIDJSON_DIAG_PUSH /* ignored */ +#define RAPIDJSON_DIAG_POP /* ignored */ +#endif + +#elif defined(_MSC_VER) + +// pragma (MSVC specific) +#define RAPIDJSON_PRAGMA(x) __pragma(x) +#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(warning(x)) + +#define RAPIDJSON_DIAG_OFF(x) RAPIDJSON_DIAG_PRAGMA(disable: x) +#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) +#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) + +#else + +#define RAPIDJSON_DIAG_OFF(x) /* ignored */ +#define RAPIDJSON_DIAG_PUSH /* ignored */ +#define RAPIDJSON_DIAG_POP /* ignored */ + +#endif // RAPIDJSON_DIAG_* + +/////////////////////////////////////////////////////////////////////////////// +// C++11 features + +#ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS +#if defined(__clang__) +#if __has_feature(cxx_rvalue_references) && \ + (defined(_MSC_VER) || defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306) +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 +#else +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 +#endif +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1600) || \ + (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) + +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 +#else +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 +#endif +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + +#ifndef RAPIDJSON_HAS_CXX11_NOEXCEPT +#if defined(__clang__) +#define RAPIDJSON_HAS_CXX11_NOEXCEPT __has_feature(cxx_noexcept) +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1900) || \ + (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1 +#else +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 0 +#endif +#endif +#if RAPIDJSON_HAS_CXX11_NOEXCEPT +#define RAPIDJSON_NOEXCEPT noexcept +#else +#define RAPIDJSON_NOEXCEPT /* noexcept */ +#endif // RAPIDJSON_HAS_CXX11_NOEXCEPT + +// no automatic detection, yet +#ifndef RAPIDJSON_HAS_CXX11_TYPETRAITS +#if (defined(_MSC_VER) && _MSC_VER >= 1700) +#define RAPIDJSON_HAS_CXX11_TYPETRAITS 1 +#else +#define RAPIDJSON_HAS_CXX11_TYPETRAITS 0 +#endif +#endif + +#ifndef RAPIDJSON_HAS_CXX11_RANGE_FOR +#if defined(__clang__) +#define RAPIDJSON_HAS_CXX11_RANGE_FOR __has_feature(cxx_range_for) +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1700) || \ + (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) +#define RAPIDJSON_HAS_CXX11_RANGE_FOR 1 +#else +#define RAPIDJSON_HAS_CXX11_RANGE_FOR 0 +#endif +#endif // RAPIDJSON_HAS_CXX11_RANGE_FOR + +/////////////////////////////////////////////////////////////////////////////// +// C++17 features + +#if defined(__has_cpp_attribute) +# if __has_cpp_attribute(fallthrough) +# define RAPIDJSON_DELIBERATE_FALLTHROUGH [[fallthrough]] +# else +# define RAPIDJSON_DELIBERATE_FALLTHROUGH +# endif +#else +# define RAPIDJSON_DELIBERATE_FALLTHROUGH +#endif + +//!@endcond + +//! Assertion (in non-throwing contexts). + /*! \ingroup RAPIDJSON_CONFIG + Some functions provide a \c noexcept guarantee, if the compiler supports it. + In these cases, the \ref RAPIDJSON_ASSERT macro cannot be overridden to + throw an exception. This macro adds a separate customization point for + such cases. + + Defaults to C \c assert() (as \ref RAPIDJSON_ASSERT), if \c noexcept is + supported, and to \ref RAPIDJSON_ASSERT otherwise. + */ + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NOEXCEPT_ASSERT + +#ifndef RAPIDJSON_NOEXCEPT_ASSERT +#ifdef RAPIDJSON_ASSERT_THROWS +#if RAPIDJSON_HAS_CXX11_NOEXCEPT +#define RAPIDJSON_NOEXCEPT_ASSERT(x) +#else +#include +#define RAPIDJSON_NOEXCEPT_ASSERT(x) assert(x) +#endif // RAPIDJSON_HAS_CXX11_NOEXCEPT +#else +#define RAPIDJSON_NOEXCEPT_ASSERT(x) RAPIDJSON_ASSERT(x) +#endif // RAPIDJSON_ASSERT_THROWS +#endif // RAPIDJSON_NOEXCEPT_ASSERT + +/////////////////////////////////////////////////////////////////////////////// +// malloc/realloc/free + +#ifndef RAPIDJSON_MALLOC +///! customization point for global \c malloc +#define RAPIDJSON_MALLOC(size) std::malloc(size) +#endif +#ifndef RAPIDJSON_REALLOC +///! customization point for global \c realloc +#define RAPIDJSON_REALLOC(ptr, new_size) std::realloc(ptr, new_size) +#endif +#ifndef RAPIDJSON_FREE +///! customization point for global \c free +#define RAPIDJSON_FREE(ptr) std::free(ptr) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// new/delete + +#ifndef RAPIDJSON_NEW +///! customization point for global \c new +#define RAPIDJSON_NEW(TypeName) new TypeName +#endif +#ifndef RAPIDJSON_DELETE +///! customization point for global \c delete +#define RAPIDJSON_DELETE(x) delete x +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Type + +/*! \namespace rapidjson + \brief main RapidJSON namespace + \see RAPIDJSON_NAMESPACE +*/ +RAPIDJSON_NAMESPACE_BEGIN + +//! Type of JSON value +enum Type { + kNullType = 0, //!< null + kFalseType = 1, //!< false + kTrueType = 2, //!< true + kObjectType = 3, //!< object + kArrayType = 4, //!< array + kStringType = 5, //!< string + kNumberType = 6 //!< number +}; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/reader.h b/libraries/libstratosphere/include/stratosphere/rapidjson/reader.h new file mode 100644 index 000000000..09ace4eba --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/reader.h @@ -0,0 +1,2244 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_READER_H_ +#define RAPIDJSON_READER_H_ + +/*! \file reader.h */ + +#include "allocators.h" +#include "stream.h" +#include "encodedstream.h" +#include "internal/clzll.h" +#include "internal/meta.h" +#include "internal/stack.h" +#include "internal/strtod.h" +#include + +#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) +#include +#pragma intrinsic(_BitScanForward) +#endif +#ifdef RAPIDJSON_SSE42 +#include +#elif defined(RAPIDJSON_SSE2) +#include +#elif defined(RAPIDJSON_NEON) +#include +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(old-style-cast) +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +RAPIDJSON_DIAG_OFF(4702) // unreachable code +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define RAPIDJSON_NOTHING /* deliberately empty */ +#ifndef RAPIDJSON_PARSE_ERROR_EARLY_RETURN +#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN(value) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + if (RAPIDJSON_UNLIKELY(HasParseError())) { return value; } \ + RAPIDJSON_MULTILINEMACRO_END +#endif +#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID \ + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(RAPIDJSON_NOTHING) +//!@endcond + +/*! \def RAPIDJSON_PARSE_ERROR_NORETURN + \ingroup RAPIDJSON_ERRORS + \brief Macro to indicate a parse error. + \param parseErrorCode \ref rapidjson::ParseErrorCode of the error + \param offset position of the error in JSON input (\c size_t) + + This macros can be used as a customization point for the internal + error handling mechanism of RapidJSON. + + A common usage model is to throw an exception instead of requiring the + caller to explicitly check the \ref rapidjson::GenericReader::Parse's + return value: + + \code + #define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode,offset) \ + throw ParseException(parseErrorCode, #parseErrorCode, offset) + + #include // std::runtime_error + #include "rapidjson/error/error.h" // rapidjson::ParseResult + + struct ParseException : std::runtime_error, rapidjson::ParseResult { + ParseException(rapidjson::ParseErrorCode code, const char* msg, size_t offset) + : std::runtime_error(msg), ParseResult(code, offset) {} + }; + + #include "rapidjson/reader.h" + \endcode + + \see RAPIDJSON_PARSE_ERROR, rapidjson::GenericReader::Parse + */ +#ifndef RAPIDJSON_PARSE_ERROR_NORETURN +#define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + RAPIDJSON_ASSERT(!HasParseError()); /* Error can only be assigned once */ \ + SetParseError(parseErrorCode, offset); \ + RAPIDJSON_MULTILINEMACRO_END +#endif + +/*! \def RAPIDJSON_PARSE_ERROR + \ingroup RAPIDJSON_ERRORS + \brief (Internal) macro to indicate and handle a parse error. + \param parseErrorCode \ref rapidjson::ParseErrorCode of the error + \param offset position of the error in JSON input (\c size_t) + + Invokes RAPIDJSON_PARSE_ERROR_NORETURN and stops the parsing. + + \see RAPIDJSON_PARSE_ERROR_NORETURN + \hideinitializer + */ +#ifndef RAPIDJSON_PARSE_ERROR +#define RAPIDJSON_PARSE_ERROR(parseErrorCode, offset) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset); \ + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; \ + RAPIDJSON_MULTILINEMACRO_END +#endif + +#include "error/error.h" // ParseErrorCode, ParseResult + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// ParseFlag + +/*! \def RAPIDJSON_PARSE_DEFAULT_FLAGS + \ingroup RAPIDJSON_CONFIG + \brief User-defined kParseDefaultFlags definition. + + User can define this as any \c ParseFlag combinations. +*/ +#ifndef RAPIDJSON_PARSE_DEFAULT_FLAGS +#define RAPIDJSON_PARSE_DEFAULT_FLAGS kParseNoFlags +#endif + +//! Combination of parseFlags +/*! \see Reader::Parse, Document::Parse, Document::ParseInsitu, Document::ParseStream + */ +enum ParseFlag { + kParseNoFlags = 0, //!< No flags are set. + kParseInsituFlag = 1, //!< In-situ(destructive) parsing. + kParseValidateEncodingFlag = 2, //!< Validate encoding of JSON strings. + kParseIterativeFlag = 4, //!< Iterative(constant complexity in terms of function call stack size) parsing. + kParseStopWhenDoneFlag = 8, //!< After parsing a complete JSON root from stream, stop further processing the rest of stream. When this flag is used, parser will not generate kParseErrorDocumentRootNotSingular error. + kParseFullPrecisionFlag = 16, //!< Parse number in full precision (but slower). + kParseCommentsFlag = 32, //!< Allow one-line (//) and multi-line (/**/) comments. + kParseNumbersAsStringsFlag = 64, //!< Parse all numbers (ints/doubles) as strings. + kParseTrailingCommasFlag = 128, //!< Allow trailing commas at the end of objects and arrays. + kParseNanAndInfFlag = 256, //!< Allow parsing NaN, Inf, Infinity, -Inf and -Infinity as doubles. + kParseEscapedApostropheFlag = 512, //!< Allow escaped apostrophe in strings. + kParseDefaultFlags = RAPIDJSON_PARSE_DEFAULT_FLAGS //!< Default parse flags. Can be customized by defining RAPIDJSON_PARSE_DEFAULT_FLAGS +}; + +/////////////////////////////////////////////////////////////////////////////// +// Handler + +/*! \class rapidjson::Handler + \brief Concept for receiving events from GenericReader upon parsing. + The functions return true if no error occurs. If they return false, + the event publisher should terminate the process. +\code +concept Handler { + typename Ch; + + bool Null(); + bool Bool(bool b); + bool Int(int i); + bool Uint(unsigned i); + bool Int64(int64_t i); + bool Uint64(uint64_t i); + bool Double(double d); + /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) + bool RawNumber(const Ch* str, SizeType length, bool copy); + bool String(const Ch* str, SizeType length, bool copy); + bool StartObject(); + bool Key(const Ch* str, SizeType length, bool copy); + bool EndObject(SizeType memberCount); + bool StartArray(); + bool EndArray(SizeType elementCount); +}; +\endcode +*/ +/////////////////////////////////////////////////////////////////////////////// +// BaseReaderHandler + +//! Default implementation of Handler. +/*! This can be used as base class of any reader handler. + \note implements Handler concept +*/ +template, typename Derived = void> +struct BaseReaderHandler { + typedef typename Encoding::Ch Ch; + + typedef typename internal::SelectIf, BaseReaderHandler, Derived>::Type Override; + + bool Default() { return true; } + bool Null() { return static_cast(*this).Default(); } + bool Bool(bool) { return static_cast(*this).Default(); } + bool Int(int) { return static_cast(*this).Default(); } + bool Uint(unsigned) { return static_cast(*this).Default(); } + bool Int64(int64_t) { return static_cast(*this).Default(); } + bool Uint64(uint64_t) { return static_cast(*this).Default(); } + bool Double(double) { return static_cast(*this).Default(); } + /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) + bool RawNumber(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } + bool String(const Ch*, SizeType, bool) { return static_cast(*this).Default(); } + bool StartObject() { return static_cast(*this).Default(); } + bool Key(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } + bool EndObject(SizeType) { return static_cast(*this).Default(); } + bool StartArray() { return static_cast(*this).Default(); } + bool EndArray(SizeType) { return static_cast(*this).Default(); } +}; + +/////////////////////////////////////////////////////////////////////////////// +// StreamLocalCopy + +namespace internal { + +template::copyOptimization> +class StreamLocalCopy; + +//! Do copy optimization. +template +class StreamLocalCopy { +public: + StreamLocalCopy(Stream& original) : s(original), original_(original) {} + ~StreamLocalCopy() { original_ = s; } + + Stream s; + +private: + StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; + + Stream& original_; +}; + +//! Keep reference. +template +class StreamLocalCopy { +public: + StreamLocalCopy(Stream& original) : s(original) {} + + Stream& s; + +private: + StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; +}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// SkipWhitespace + +//! Skip the JSON white spaces in a stream. +/*! \param is A input stream for skipping white spaces. + \note This function has SSE2/SSE4.2 specialization. +*/ +template +void SkipWhitespace(InputStream& is) { + internal::StreamLocalCopy copy(is); + InputStream& s(copy.s); + + typename InputStream::Ch c; + while ((c = s.Peek()) == ' ' || c == '\n' || c == '\r' || c == '\t') + s.Take(); +} + +inline const char* SkipWhitespace(const char* p, const char* end) { + while (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + return p; +} + +#ifdef RAPIDJSON_SSE42 +//! Skip whitespace with SSE 4.2 pcmpistrm instruction, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // The rest of string using SIMD + static const char whitespace[16] = " \n\r\t"; + const __m128i w = _mm_loadu_si128(reinterpret_cast(&whitespace[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const int r = _mm_cmpistri(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY); + if (r != 16) // some of characters is non-whitespace + return p + r; + } +} + +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + // The middle of string using SIMD + static const char whitespace[16] = " \n\r\t"; + const __m128i w = _mm_loadu_si128(reinterpret_cast(&whitespace[0])); + + for (; p <= end - 16; p += 16) { + const __m128i s = _mm_loadu_si128(reinterpret_cast(p)); + const int r = _mm_cmpistri(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY); + if (r != 16) // some of characters is non-whitespace + return p + r; + } + + return SkipWhitespace(p, end); +} + +#elif defined(RAPIDJSON_SSE2) + +//! Skip whitespace with SSE2 instructions, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // The rest of string + #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c } + static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') }; + #undef C16 + + const __m128i w0 = _mm_loadu_si128(reinterpret_cast(&whitespaces[0][0])); + const __m128i w1 = _mm_loadu_si128(reinterpret_cast(&whitespaces[1][0])); + const __m128i w2 = _mm_loadu_si128(reinterpret_cast(&whitespaces[2][0])); + const __m128i w3 = _mm_loadu_si128(reinterpret_cast(&whitespaces[3][0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + __m128i x = _mm_cmpeq_epi8(s, w0); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); + unsigned short r = static_cast(~_mm_movemask_epi8(x)); + if (r != 0) { // some of characters may be non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } +} + +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + // The rest of string + #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c } + static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') }; + #undef C16 + + const __m128i w0 = _mm_loadu_si128(reinterpret_cast(&whitespaces[0][0])); + const __m128i w1 = _mm_loadu_si128(reinterpret_cast(&whitespaces[1][0])); + const __m128i w2 = _mm_loadu_si128(reinterpret_cast(&whitespaces[2][0])); + const __m128i w3 = _mm_loadu_si128(reinterpret_cast(&whitespaces[3][0])); + + for (; p <= end - 16; p += 16) { + const __m128i s = _mm_loadu_si128(reinterpret_cast(p)); + __m128i x = _mm_cmpeq_epi8(s, w0); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); + unsigned short r = static_cast(~_mm_movemask_epi8(x)); + if (r != 0) { // some of characters may be non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } + + return SkipWhitespace(p, end); +} + +#elif defined(RAPIDJSON_NEON) + +//! Skip whitespace with ARM Neon instructions, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + const uint8x16_t w0 = vmovq_n_u8(' '); + const uint8x16_t w1 = vmovq_n_u8('\n'); + const uint8x16_t w2 = vmovq_n_u8('\r'); + const uint8x16_t w3 = vmovq_n_u8('\t'); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, w0); + x = vorrq_u8(x, vceqq_u8(s, w1)); + x = vorrq_u8(x, vceqq_u8(s, w2)); + x = vorrq_u8(x, vceqq_u8(s, w3)); + + x = vmvnq_u8(x); // Negate + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + return p + 8 + (lz >> 3); + } + } else { + uint32_t lz = internal::clzll(low); + return p + (lz >> 3); + } + } +} + +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + const uint8x16_t w0 = vmovq_n_u8(' '); + const uint8x16_t w1 = vmovq_n_u8('\n'); + const uint8x16_t w2 = vmovq_n_u8('\r'); + const uint8x16_t w3 = vmovq_n_u8('\t'); + + for (; p <= end - 16; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, w0); + x = vorrq_u8(x, vceqq_u8(s, w1)); + x = vorrq_u8(x, vceqq_u8(s, w2)); + x = vorrq_u8(x, vceqq_u8(s, w3)); + + x = vmvnq_u8(x); // Negate + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + return p + 8 + (lz >> 3); + } + } else { + uint32_t lz = internal::clzll(low); + return p + (lz >> 3); + } + } + + return SkipWhitespace(p, end); +} + +#endif // RAPIDJSON_NEON + +#ifdef RAPIDJSON_SIMD +//! Template function specialization for InsituStringStream +template<> inline void SkipWhitespace(InsituStringStream& is) { + is.src_ = const_cast(SkipWhitespace_SIMD(is.src_)); +} + +//! Template function specialization for StringStream +template<> inline void SkipWhitespace(StringStream& is) { + is.src_ = SkipWhitespace_SIMD(is.src_); +} + +template<> inline void SkipWhitespace(EncodedInputStream, MemoryStream>& is) { + is.is_.src_ = SkipWhitespace_SIMD(is.is_.src_, is.is_.end_); +} +#endif // RAPIDJSON_SIMD + +/////////////////////////////////////////////////////////////////////////////// +// GenericReader + +//! SAX-style JSON parser. Use \ref Reader for UTF8 encoding and default allocator. +/*! GenericReader parses JSON text from a stream, and send events synchronously to an + object implementing Handler concept. + + It needs to allocate a stack for storing a single decoded string during + non-destructive parsing. + + For in-situ parsing, the decoded string is directly written to the source + text string, no temporary buffer is required. + + A GenericReader object can be reused for parsing multiple JSON text. + + \tparam SourceEncoding Encoding of the input stream. + \tparam TargetEncoding Encoding of the parse output. + \tparam StackAllocator Allocator type for stack. +*/ +template +class GenericReader { +public: + typedef typename SourceEncoding::Ch Ch; //!< SourceEncoding character type + + //! Constructor. + /*! \param stackAllocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing) + \param stackCapacity stack capacity in bytes for storing a single decoded string. (Only use for non-destructive parsing) + */ + GenericReader(StackAllocator* stackAllocator = 0, size_t stackCapacity = kDefaultStackCapacity) : + stack_(stackAllocator, stackCapacity), parseResult_(), state_(IterativeParsingStartState) {} + + //! Parse JSON text. + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept. + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + ParseResult Parse(InputStream& is, Handler& handler) { + if (parseFlags & kParseIterativeFlag) + return IterativeParse(is, handler); + + parseResult_.Clear(); + + ClearStackOnExit scope(*this); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell()); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + else { + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (!(parseFlags & kParseStopWhenDoneFlag)) { + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (RAPIDJSON_UNLIKELY(is.Peek() != '\0')) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell()); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + } + } + + return parseResult_; + } + + //! Parse JSON text (with \ref kParseDefaultFlags) + /*! \tparam InputStream Type of input stream, implementing Stream concept + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + ParseResult Parse(InputStream& is, Handler& handler) { + return Parse(is, handler); + } + + //! Initialize JSON text token-by-token parsing + /*! + */ + void IterativeParseInit() { + parseResult_.Clear(); + state_ = IterativeParsingStartState; + } + + //! Parse one token from JSON text + /*! \tparam InputStream Type of input stream, implementing Stream concept + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + bool IterativeParseNext(InputStream& is, Handler& handler) { + while (RAPIDJSON_LIKELY(is.Peek() != '\0')) { + SkipWhitespaceAndComments(is); + + Token t = Tokenize(is.Peek()); + IterativeParsingState n = Predict(state_, t); + IterativeParsingState d = Transit(state_, t, n, is, handler); + + // If we've finished or hit an error... + if (RAPIDJSON_UNLIKELY(IsIterativeParsingCompleteState(d))) { + // Report errors. + if (d == IterativeParsingErrorState) { + HandleError(state_, is); + return false; + } + + // Transition to the finish state. + RAPIDJSON_ASSERT(d == IterativeParsingFinishState); + state_ = d; + + // If StopWhenDone is not set... + if (!(parseFlags & kParseStopWhenDoneFlag)) { + // ... and extra non-whitespace data is found... + SkipWhitespaceAndComments(is); + if (is.Peek() != '\0') { + // ... this is considered an error. + HandleError(state_, is); + return false; + } + } + + // Success! We are done! + return true; + } + + // Transition to the new state. + state_ = d; + + // If we parsed anything other than a delimiter, we invoked the handler, so we can return true now. + if (!IsIterativeParsingDelimiterState(n)) + return true; + } + + // We reached the end of file. + stack_.Clear(); + + if (state_ != IterativeParsingFinishState) { + HandleError(state_, is); + return false; + } + + return true; + } + + //! Check if token-by-token parsing JSON text is complete + /*! \return Whether the JSON has been fully decoded. + */ + RAPIDJSON_FORCEINLINE bool IterativeParseComplete() const { + return IsIterativeParsingCompleteState(state_); + } + + //! Whether a parse error has occurred in the last parsing. + bool HasParseError() const { return parseResult_.IsError(); } + + //! Get the \ref ParseErrorCode of last parsing. + ParseErrorCode GetParseErrorCode() const { return parseResult_.Code(); } + + //! Get the position of last parsing error in input, 0 otherwise. + size_t GetErrorOffset() const { return parseResult_.Offset(); } + +protected: + void SetParseError(ParseErrorCode code, size_t offset) { parseResult_.Set(code, offset); } + +private: + // Prohibit copy constructor & assignment operator. + GenericReader(const GenericReader&); + GenericReader& operator=(const GenericReader&); + + void ClearStack() { stack_.Clear(); } + + // clear stack on any exit from ParseStream, e.g. due to exception + struct ClearStackOnExit { + explicit ClearStackOnExit(GenericReader& r) : r_(r) {} + ~ClearStackOnExit() { r_.ClearStack(); } + private: + GenericReader& r_; + ClearStackOnExit(const ClearStackOnExit&); + ClearStackOnExit& operator=(const ClearStackOnExit&); + }; + + template + void SkipWhitespaceAndComments(InputStream& is) { + SkipWhitespace(is); + + if (parseFlags & kParseCommentsFlag) { + while (RAPIDJSON_UNLIKELY(Consume(is, '/'))) { + if (Consume(is, '*')) { + while (true) { + if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) + RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); + else if (Consume(is, '*')) { + if (Consume(is, '/')) + break; + } + else + is.Take(); + } + } + else if (RAPIDJSON_LIKELY(Consume(is, '/'))) + while (is.Peek() != '\0' && is.Take() != '\n') {} + else + RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); + + SkipWhitespace(is); + } + } + } + + // Parse object: { string : value, ... } + template + void ParseObject(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == '{'); + is.Take(); // Skip '{' + + if (RAPIDJSON_UNLIKELY(!handler.StartObject())) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (Consume(is, '}')) { + if (RAPIDJSON_UNLIKELY(!handler.EndObject(0))) // empty object + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + + for (SizeType memberCount = 0;;) { + if (RAPIDJSON_UNLIKELY(is.Peek() != '"')) + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); + + ParseString(is, handler, true); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (RAPIDJSON_UNLIKELY(!Consume(is, ':'))) + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ++memberCount; + + switch (is.Peek()) { + case ',': + is.Take(); + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + break; + case '}': + is.Take(); + if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + default: + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); break; // This useless break is only for making warning and coverage happy + } + + if (parseFlags & kParseTrailingCommasFlag) { + if (is.Peek() == '}') { + if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + is.Take(); + return; + } + } + } + } + + // Parse array: [ value, ... ] + template + void ParseArray(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == '['); + is.Take(); // Skip '[' + + if (RAPIDJSON_UNLIKELY(!handler.StartArray())) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (Consume(is, ']')) { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(0))) // empty array + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + + for (SizeType elementCount = 0;;) { + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ++elementCount; + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (Consume(is, ',')) { + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + } + else if (Consume(is, ']')) { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); + + if (parseFlags & kParseTrailingCommasFlag) { + if (is.Peek() == ']') { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + is.Take(); + return; + } + } + } + } + + template + void ParseNull(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 'n'); + is.Take(); + + if (RAPIDJSON_LIKELY(Consume(is, 'u') && Consume(is, 'l') && Consume(is, 'l'))) { + if (RAPIDJSON_UNLIKELY(!handler.Null())) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + void ParseTrue(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 't'); + is.Take(); + + if (RAPIDJSON_LIKELY(Consume(is, 'r') && Consume(is, 'u') && Consume(is, 'e'))) { + if (RAPIDJSON_UNLIKELY(!handler.Bool(true))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + void ParseFalse(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 'f'); + is.Take(); + + if (RAPIDJSON_LIKELY(Consume(is, 'a') && Consume(is, 'l') && Consume(is, 's') && Consume(is, 'e'))) { + if (RAPIDJSON_UNLIKELY(!handler.Bool(false))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + RAPIDJSON_FORCEINLINE static bool Consume(InputStream& is, typename InputStream::Ch expect) { + if (RAPIDJSON_LIKELY(is.Peek() == expect)) { + is.Take(); + return true; + } + else + return false; + } + + // Helper function to parse four hexadecimal digits in \uXXXX in ParseString(). + template + unsigned ParseHex4(InputStream& is, size_t escapeOffset) { + unsigned codepoint = 0; + for (int i = 0; i < 4; i++) { + Ch c = is.Peek(); + codepoint <<= 4; + codepoint += static_cast(c); + if (c >= '0' && c <= '9') + codepoint -= '0'; + else if (c >= 'A' && c <= 'F') + codepoint -= 'A' - 10; + else if (c >= 'a' && c <= 'f') + codepoint -= 'a' - 10; + else { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, escapeOffset); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(0); + } + is.Take(); + } + return codepoint; + } + + template + class StackStream { + public: + typedef CharType Ch; + + StackStream(internal::Stack& stack) : stack_(stack), length_(0) {} + RAPIDJSON_FORCEINLINE void Put(Ch c) { + *stack_.template Push() = c; + ++length_; + } + + RAPIDJSON_FORCEINLINE void* Push(SizeType count) { + length_ += count; + return stack_.template Push(count); + } + + size_t Length() const { return length_; } + + Ch* Pop() { + return stack_.template Pop(length_); + } + + private: + StackStream(const StackStream&); + StackStream& operator=(const StackStream&); + + internal::Stack& stack_; + SizeType length_; + }; + + // Parse string and generate String event. Different code paths for kParseInsituFlag. + template + void ParseString(InputStream& is, Handler& handler, bool isKey = false) { + internal::StreamLocalCopy copy(is); + InputStream& s(copy.s); + + RAPIDJSON_ASSERT(s.Peek() == '\"'); + s.Take(); // Skip '\"' + + bool success = false; + if (parseFlags & kParseInsituFlag) { + typename InputStream::Ch *head = s.PutBegin(); + ParseStringToStream(s, s); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + size_t length = s.PutEnd(head) - 1; + RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); + const typename TargetEncoding::Ch* const str = reinterpret_cast(head); + success = (isKey ? handler.Key(str, SizeType(length), false) : handler.String(str, SizeType(length), false)); + } + else { + StackStream stackStream(stack_); + ParseStringToStream(s, stackStream); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + SizeType length = static_cast(stackStream.Length()) - 1; + const typename TargetEncoding::Ch* const str = stackStream.Pop(); + success = (isKey ? handler.Key(str, length, true) : handler.String(str, length, true)); + } + if (RAPIDJSON_UNLIKELY(!success)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); + } + + // Parse string to an output is + // This function handles the prefix/suffix double quotes, escaping, and optional encoding validation. + template + RAPIDJSON_FORCEINLINE void ParseStringToStream(InputStream& is, OutputStream& os) { +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + static const char escape[256] = { + Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '/', + Z16, Z16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, + 0, 0,'\b', 0, 0, 0,'\f', 0, 0, 0, 0, 0, 0, 0,'\n', 0, + 0, 0,'\r', 0,'\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 + }; +#undef Z16 +//!@endcond + + for (;;) { + // Scan and copy string before "\\\"" or < 0x20. This is an optional optimzation. + if (!(parseFlags & kParseValidateEncodingFlag)) + ScanCopyUnescapedString(is, os); + + Ch c = is.Peek(); + if (RAPIDJSON_UNLIKELY(c == '\\')) { // Escape + size_t escapeOffset = is.Tell(); // For invalid escaping, report the initial '\\' as error offset + is.Take(); + Ch e = is.Peek(); + if ((sizeof(Ch) == 1 || unsigned(e) < 256) && RAPIDJSON_LIKELY(escape[static_cast(e)])) { + is.Take(); + os.Put(static_cast(escape[static_cast(e)])); + } + else if ((parseFlags & kParseEscapedApostropheFlag) && RAPIDJSON_LIKELY(e == '\'')) { // Allow escaped apostrophe + is.Take(); + os.Put('\''); + } + else if (RAPIDJSON_LIKELY(e == 'u')) { // Unicode + is.Take(); + unsigned codepoint = ParseHex4(is, escapeOffset); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + if (RAPIDJSON_UNLIKELY(codepoint >= 0xD800 && codepoint <= 0xDFFF)) { + // high surrogate, check if followed by valid low surrogate + if (RAPIDJSON_LIKELY(codepoint <= 0xDBFF)) { + // Handle UTF-16 surrogate pair + if (RAPIDJSON_UNLIKELY(!Consume(is, '\\') || !Consume(is, 'u'))) + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); + unsigned codepoint2 = ParseHex4(is, escapeOffset); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + if (RAPIDJSON_UNLIKELY(codepoint2 < 0xDC00 || codepoint2 > 0xDFFF)) + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); + codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000; + } + // single low surrogate + else + { + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); + } + } + TEncoding::Encode(os, codepoint); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, escapeOffset); + } + else if (RAPIDJSON_UNLIKELY(c == '"')) { // Closing double quote + is.Take(); + os.Put('\0'); // null-terminate the string + return; + } + else if (RAPIDJSON_UNLIKELY(static_cast(c) < 0x20)) { // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + if (c == '\0') + RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell()); + else + RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, is.Tell()); + } + else { + size_t offset = is.Tell(); + if (RAPIDJSON_UNLIKELY((parseFlags & kParseValidateEncodingFlag ? + !Transcoder::Validate(is, os) : + !Transcoder::Transcode(is, os)))) + RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, offset); + } + } + } + + template + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InputStream&, OutputStream&) { + // Do nothing for generic version + } + +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) + // StringStream -> StackStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream& os) { + const char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + return; + } + else + os.Put(*p++); + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + SizeType length; + #ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; + #else + length = static_cast(__builtin_ffs(r) - 1); + #endif + if (length != 0) { + char* q = reinterpret_cast(os.Push(length)); + for (size_t i = 0; i < length; i++) + q[i] = p[i]; + + p += length; + } + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(os.Push(16)), s); + } + + is.src_ = p; + } + + // InsituStringStream -> InsituStringStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) { + RAPIDJSON_ASSERT(&is == &os); + (void)os; + + if (is.src_ == is.dst_) { + SkipUnescapedString(is); + return; + } + + char* p = is.src_; + char *q = is.dst_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + is.dst_ = q; + return; + } + else + *q++ = *p++; + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16, q += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + size_t length; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; +#else + length = static_cast(__builtin_ffs(r) - 1); +#endif + for (const char* pend = p + length; p != pend; ) + *q++ = *p++; + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(q), s); + } + + is.src_ = p; + is.dst_ = q; + } + + // When read/write pointers are the same for insitu stream, just skip unescaped characters + static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) { + RAPIDJSON_ASSERT(is.src_ == is.dst_); + char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + for (; p != nextAligned; p++) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = is.dst_ = p; + return; + } + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + size_t length; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; +#else + length = static_cast(__builtin_ffs(r) - 1); +#endif + p += length; + break; + } + } + + is.src_ = is.dst_ = p; + } +#elif defined(RAPIDJSON_NEON) + // StringStream -> StackStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream& os) { + const char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + return; + } + else + os.Put(*p++); + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + SizeType length = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + length = 8 + (lz >> 3); + escaped = true; + } + } else { + uint32_t lz = internal::clzll(low); + length = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + if (length != 0) { + char* q = reinterpret_cast(os.Push(length)); + for (size_t i = 0; i < length; i++) + q[i] = p[i]; + + p += length; + } + break; + } + vst1q_u8(reinterpret_cast(os.Push(16)), s); + } + + is.src_ = p; + } + + // InsituStringStream -> InsituStringStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) { + RAPIDJSON_ASSERT(&is == &os); + (void)os; + + if (is.src_ == is.dst_) { + SkipUnescapedString(is); + return; + } + + char* p = is.src_; + char *q = is.dst_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + is.dst_ = q; + return; + } + else + *q++ = *p++; + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16, q += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + SizeType length = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + length = 8 + (lz >> 3); + escaped = true; + } + } else { + uint32_t lz = internal::clzll(low); + length = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + for (const char* pend = p + length; p != pend; ) { + *q++ = *p++; + } + break; + } + vst1q_u8(reinterpret_cast(q), s); + } + + is.src_ = p; + is.dst_ = q; + } + + // When read/write pointers are the same for insitu stream, just skip unescaped characters + static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) { + RAPIDJSON_ASSERT(is.src_ == is.dst_); + char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + for (; p != nextAligned; p++) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = is.dst_ = p; + return; + } + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + p += 8 + (lz >> 3); + break; + } + } else { + uint32_t lz = internal::clzll(low); + p += lz >> 3; + break; + } + } + + is.src_ = is.dst_ = p; + } +#endif // RAPIDJSON_NEON + + template + class NumberStream; + + template + class NumberStream { + public: + typedef typename InputStream::Ch Ch; + + NumberStream(GenericReader& reader, InputStream& s) : is(s) { (void)reader; } + + RAPIDJSON_FORCEINLINE Ch Peek() const { return is.Peek(); } + RAPIDJSON_FORCEINLINE Ch TakePush() { return is.Take(); } + RAPIDJSON_FORCEINLINE Ch Take() { return is.Take(); } + RAPIDJSON_FORCEINLINE void Push(char) {} + + size_t Tell() { return is.Tell(); } + size_t Length() { return 0; } + const char* Pop() { return 0; } + + protected: + NumberStream& operator=(const NumberStream&); + + InputStream& is; + }; + + template + class NumberStream : public NumberStream { + typedef NumberStream Base; + public: + NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is), stackStream(reader.stack_) {} + + RAPIDJSON_FORCEINLINE Ch TakePush() { + stackStream.Put(static_cast(Base::is.Peek())); + return Base::is.Take(); + } + + RAPIDJSON_FORCEINLINE void Push(char c) { + stackStream.Put(c); + } + + size_t Length() { return stackStream.Length(); } + + const char* Pop() { + stackStream.Put('\0'); + return stackStream.Pop(); + } + + private: + StackStream stackStream; + }; + + template + class NumberStream : public NumberStream { + typedef NumberStream Base; + public: + NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is) {} + + RAPIDJSON_FORCEINLINE Ch Take() { return Base::TakePush(); } + }; + + template + void ParseNumber(InputStream& is, Handler& handler) { + internal::StreamLocalCopy copy(is); + NumberStream s(*this, copy.s); + + size_t startOffset = s.Tell(); + double d = 0.0; + bool useNanOrInf = false; + + // Parse minus + bool minus = Consume(s, '-'); + + // Parse int: zero / ( digit1-9 *DIGIT ) + unsigned i = 0; + uint64_t i64 = 0; + bool use64bit = false; + int significandDigit = 0; + if (RAPIDJSON_UNLIKELY(s.Peek() == '0')) { + i = 0; + s.TakePush(); + } + else if (RAPIDJSON_LIKELY(s.Peek() >= '1' && s.Peek() <= '9')) { + i = static_cast(s.TakePush() - '0'); + + if (minus) + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i >= 214748364)) { // 2^31 = 2147483648 + if (RAPIDJSON_LIKELY(i != 214748364 || s.Peek() > '8')) { + i64 = i; + use64bit = true; + break; + } + } + i = i * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + else + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i >= 429496729)) { // 2^32 - 1 = 4294967295 + if (RAPIDJSON_LIKELY(i != 429496729 || s.Peek() > '5')) { + i64 = i; + use64bit = true; + break; + } + } + i = i * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + } + // Parse NaN or Infinity here + else if ((parseFlags & kParseNanAndInfFlag) && RAPIDJSON_LIKELY((s.Peek() == 'I' || s.Peek() == 'N'))) { + if (Consume(s, 'N')) { + if (Consume(s, 'a') && Consume(s, 'N')) { + d = std::numeric_limits::quiet_NaN(); + useNanOrInf = true; + } + } + else if (RAPIDJSON_LIKELY(Consume(s, 'I'))) { + if (Consume(s, 'n') && Consume(s, 'f')) { + d = (minus ? -std::numeric_limits::infinity() : std::numeric_limits::infinity()); + useNanOrInf = true; + + if (RAPIDJSON_UNLIKELY(s.Peek() == 'i' && !(Consume(s, 'i') && Consume(s, 'n') + && Consume(s, 'i') && Consume(s, 't') && Consume(s, 'y')))) { + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + } + } + } + + if (RAPIDJSON_UNLIKELY(!useNanOrInf)) { + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + } + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + + // Parse 64bit int + bool useDouble = false; + if (use64bit) { + if (minus) + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC))) // 2^63 = 9223372036854775808 + if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC) || s.Peek() > '8')) { + d = static_cast(i64); + useDouble = true; + break; + } + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + else + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x19999999, 0x99999999))) // 2^64 - 1 = 18446744073709551615 + if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || s.Peek() > '5')) { + d = static_cast(i64); + useDouble = true; + break; + } + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + } + + // Force double for big integer + if (useDouble) { + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + d = d * 10 + (s.TakePush() - '0'); + } + } + + // Parse frac = decimal-point 1*DIGIT + int expFrac = 0; + size_t decimalPosition; + if (Consume(s, '.')) { + decimalPosition = s.Length(); + + if (RAPIDJSON_UNLIKELY(!(s.Peek() >= '0' && s.Peek() <= '9'))) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissFraction, s.Tell()); + + if (!useDouble) { +#if RAPIDJSON_64BIT + // Use i64 to store significand in 64-bit architecture + if (!use64bit) + i64 = i; + + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (i64 > RAPIDJSON_UINT64_C2(0x1FFFFF, 0xFFFFFFFF)) // 2^53 - 1 for fast path + break; + else { + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + --expFrac; + if (i64 != 0) + significandDigit++; + } + } + + d = static_cast(i64); +#else + // Use double to store significand in 32-bit architecture + d = static_cast(use64bit ? i64 : i); +#endif + useDouble = true; + } + + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (significandDigit < 17) { + d = d * 10.0 + (s.TakePush() - '0'); + --expFrac; + if (RAPIDJSON_LIKELY(d > 0.0)) + significandDigit++; + } + else + s.TakePush(); + } + } + else + decimalPosition = s.Length(); // decimal position at the end of integer. + + // Parse exp = e [ minus / plus ] 1*DIGIT + int exp = 0; + if (Consume(s, 'e') || Consume(s, 'E')) { + if (!useDouble) { + d = static_cast(use64bit ? i64 : i); + useDouble = true; + } + + bool expMinus = false; + if (Consume(s, '+')) + ; + else if (Consume(s, '-')) + expMinus = true; + + if (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = static_cast(s.Take() - '0'); + if (expMinus) { + // (exp + expFrac) must not underflow int => we're detecting when -exp gets + // dangerously close to INT_MIN (a pessimistic next digit 9 would push it into + // underflow territory): + // + // -(exp * 10 + 9) + expFrac >= INT_MIN + // <=> exp <= (expFrac - INT_MIN - 9) / 10 + RAPIDJSON_ASSERT(expFrac <= 0); + int maxExp = (expFrac + 2147483639) / 10; + + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = exp * 10 + static_cast(s.Take() - '0'); + if (RAPIDJSON_UNLIKELY(exp > maxExp)) { + while (RAPIDJSON_UNLIKELY(s.Peek() >= '0' && s.Peek() <= '9')) // Consume the rest of exponent + s.Take(); + } + } + } + else { // positive exp + int maxExp = 308 - expFrac; + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = exp * 10 + static_cast(s.Take() - '0'); + if (RAPIDJSON_UNLIKELY(exp > maxExp)) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); + } + } + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissExponent, s.Tell()); + + if (expMinus) + exp = -exp; + } + + // Finish parsing, call event according to the type of number. + bool cont = true; + + if (parseFlags & kParseNumbersAsStringsFlag) { + if (parseFlags & kParseInsituFlag) { + s.Pop(); // Pop stack no matter if it will be used or not. + typename InputStream::Ch* head = is.PutBegin(); + const size_t length = s.Tell() - startOffset; + RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); + // unable to insert the \0 character here, it will erase the comma after this number + const typename TargetEncoding::Ch* const str = reinterpret_cast(head); + cont = handler.RawNumber(str, SizeType(length), false); + } + else { + SizeType numCharsToCopy = static_cast(s.Length()); + StringStream srcStream(s.Pop()); + StackStream dstStream(stack_); + while (numCharsToCopy--) { + Transcoder, TargetEncoding>::Transcode(srcStream, dstStream); + } + dstStream.Put('\0'); + const typename TargetEncoding::Ch* str = dstStream.Pop(); + const SizeType length = static_cast(dstStream.Length()) - 1; + cont = handler.RawNumber(str, SizeType(length), true); + } + } + else { + size_t length = s.Length(); + const char* decimal = s.Pop(); // Pop stack no matter if it will be used or not. + + if (useDouble) { + int p = exp + expFrac; + if (parseFlags & kParseFullPrecisionFlag) + d = internal::StrtodFullPrecision(d, p, decimal, length, decimalPosition, exp); + else + d = internal::StrtodNormalPrecision(d, p); + + // Use > max, instead of == inf, to fix bogus warning -Wfloat-equal + if (d > (std::numeric_limits::max)()) { + // Overflow + // TODO: internal::StrtodX should report overflow (or underflow) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); + } + + cont = handler.Double(minus ? -d : d); + } + else if (useNanOrInf) { + cont = handler.Double(d); + } + else { + if (use64bit) { + if (minus) + cont = handler.Int64(static_cast(~i64 + 1)); + else + cont = handler.Uint64(i64); + } + else { + if (minus) + cont = handler.Int(static_cast(~i + 1)); + else + cont = handler.Uint(i); + } + } + } + if (RAPIDJSON_UNLIKELY(!cont)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, startOffset); + } + + // Parse any JSON value + template + void ParseValue(InputStream& is, Handler& handler) { + switch (is.Peek()) { + case 'n': ParseNull (is, handler); break; + case 't': ParseTrue (is, handler); break; + case 'f': ParseFalse (is, handler); break; + case '"': ParseString(is, handler); break; + case '{': ParseObject(is, handler); break; + case '[': ParseArray (is, handler); break; + default : + ParseNumber(is, handler); + break; + + } + } + + // Iterative Parsing + + // States + enum IterativeParsingState { + IterativeParsingFinishState = 0, // sink states at top + IterativeParsingErrorState, // sink states at top + IterativeParsingStartState, + + // Object states + IterativeParsingObjectInitialState, + IterativeParsingMemberKeyState, + IterativeParsingMemberValueState, + IterativeParsingObjectFinishState, + + // Array states + IterativeParsingArrayInitialState, + IterativeParsingElementState, + IterativeParsingArrayFinishState, + + // Single value state + IterativeParsingValueState, + + // Delimiter states (at bottom) + IterativeParsingElementDelimiterState, + IterativeParsingMemberDelimiterState, + IterativeParsingKeyValueDelimiterState, + + cIterativeParsingStateCount + }; + + // Tokens + enum Token { + LeftBracketToken = 0, + RightBracketToken, + + LeftCurlyBracketToken, + RightCurlyBracketToken, + + CommaToken, + ColonToken, + + StringToken, + FalseToken, + TrueToken, + NullToken, + NumberToken, + + kTokenCount + }; + + RAPIDJSON_FORCEINLINE Token Tokenize(Ch c) const { + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define N NumberToken +#define N16 N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N + // Maps from ASCII to Token + static const unsigned char tokenMap[256] = { + N16, // 00~0F + N16, // 10~1F + N, N, StringToken, N, N, N, N, N, N, N, N, N, CommaToken, N, N, N, // 20~2F + N, N, N, N, N, N, N, N, N, N, ColonToken, N, N, N, N, N, // 30~3F + N16, // 40~4F + N, N, N, N, N, N, N, N, N, N, N, LeftBracketToken, N, RightBracketToken, N, N, // 50~5F + N, N, N, N, N, N, FalseToken, N, N, N, N, N, N, N, NullToken, N, // 60~6F + N, N, N, N, TrueToken, N, N, N, N, N, N, LeftCurlyBracketToken, N, RightCurlyBracketToken, N, N, // 70~7F + N16, N16, N16, N16, N16, N16, N16, N16 // 80~FF + }; +#undef N +#undef N16 +//!@endcond + + if (sizeof(Ch) == 1 || static_cast(c) < 256) + return static_cast(tokenMap[static_cast(c)]); + else + return NumberToken; + } + + RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) const { + // current state x one lookahead token -> new state + static const char G[cIterativeParsingStateCount][kTokenCount] = { + // Finish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Error(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Start + { + IterativeParsingArrayInitialState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingValueState, // String + IterativeParsingValueState, // False + IterativeParsingValueState, // True + IterativeParsingValueState, // Null + IterativeParsingValueState // Number + }, + // ObjectInitial + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberKeyState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // MemberKey + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingKeyValueDelimiterState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // MemberValue + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingMemberDelimiterState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // ObjectFinish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // ArrayInitial + { + IterativeParsingArrayInitialState, // Left bracket(push Element state) + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push Element state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingElementState, // String + IterativeParsingElementState, // False + IterativeParsingElementState, // True + IterativeParsingElementState, // Null + IterativeParsingElementState // Number + }, + // Element + { + IterativeParsingErrorState, // Left bracket + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingElementDelimiterState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // ArrayFinish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Single Value (sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // ElementDelimiter + { + IterativeParsingArrayInitialState, // Left bracket(push Element state) + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push Element state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingElementState, // String + IterativeParsingElementState, // False + IterativeParsingElementState, // True + IterativeParsingElementState, // Null + IterativeParsingElementState // Number + }, + // MemberDelimiter + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberKeyState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // KeyValueDelimiter + { + IterativeParsingArrayInitialState, // Left bracket(push MemberValue state) + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push MemberValue state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberValueState, // String + IterativeParsingMemberValueState, // False + IterativeParsingMemberValueState, // True + IterativeParsingMemberValueState, // Null + IterativeParsingMemberValueState // Number + }, + }; // End of G + + return static_cast(G[state][token]); + } + + // Make an advance in the token stream and state based on the candidate destination state which was returned by Transit(). + // May return a new state on state pop. + template + RAPIDJSON_FORCEINLINE IterativeParsingState Transit(IterativeParsingState src, Token token, IterativeParsingState dst, InputStream& is, Handler& handler) { + (void)token; + + switch (dst) { + case IterativeParsingErrorState: + return dst; + + case IterativeParsingObjectInitialState: + case IterativeParsingArrayInitialState: + { + // Push the state(Element or MemeberValue) if we are nested in another array or value of member. + // In this way we can get the correct state on ObjectFinish or ArrayFinish by frame pop. + IterativeParsingState n = src; + if (src == IterativeParsingArrayInitialState || src == IterativeParsingElementDelimiterState) + n = IterativeParsingElementState; + else if (src == IterativeParsingKeyValueDelimiterState) + n = IterativeParsingMemberValueState; + // Push current state. + *stack_.template Push(1) = n; + // Initialize and push the member/element count. + *stack_.template Push(1) = 0; + // Call handler + bool hr = (dst == IterativeParsingObjectInitialState) ? handler.StartObject() : handler.StartArray(); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return dst; + } + } + + case IterativeParsingMemberKeyState: + ParseString(is, handler, true); + if (HasParseError()) + return IterativeParsingErrorState; + else + return dst; + + case IterativeParsingKeyValueDelimiterState: + RAPIDJSON_ASSERT(token == ColonToken); + is.Take(); + return dst; + + case IterativeParsingMemberValueState: + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return dst; + + case IterativeParsingElementState: + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return dst; + + case IterativeParsingMemberDelimiterState: + case IterativeParsingElementDelimiterState: + is.Take(); + // Update member/element count. + *stack_.template Top() = *stack_.template Top() + 1; + return dst; + + case IterativeParsingObjectFinishState: + { + // Transit from delimiter is only allowed when trailing commas are enabled + if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingMemberDelimiterState) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorObjectMissName, is.Tell()); + return IterativeParsingErrorState; + } + // Get member count. + SizeType c = *stack_.template Pop(1); + // If the object is not empty, count the last member. + if (src == IterativeParsingMemberValueState) + ++c; + // Restore the state. + IterativeParsingState n = static_cast(*stack_.template Pop(1)); + // Transit to Finish state if this is the topmost scope. + if (n == IterativeParsingStartState) + n = IterativeParsingFinishState; + // Call handler + bool hr = handler.EndObject(c); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return n; + } + } + + case IterativeParsingArrayFinishState: + { + // Transit from delimiter is only allowed when trailing commas are enabled + if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingElementDelimiterState) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorValueInvalid, is.Tell()); + return IterativeParsingErrorState; + } + // Get element count. + SizeType c = *stack_.template Pop(1); + // If the array is not empty, count the last element. + if (src == IterativeParsingElementState) + ++c; + // Restore the state. + IterativeParsingState n = static_cast(*stack_.template Pop(1)); + // Transit to Finish state if this is the topmost scope. + if (n == IterativeParsingStartState) + n = IterativeParsingFinishState; + // Call handler + bool hr = handler.EndArray(c); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return n; + } + } + + default: + // This branch is for IterativeParsingValueState actually. + // Use `default:` rather than + // `case IterativeParsingValueState:` is for code coverage. + + // The IterativeParsingStartState is not enumerated in this switch-case. + // It is impossible for that case. And it can be caught by following assertion. + + // The IterativeParsingFinishState is not enumerated in this switch-case either. + // It is a "derivative" state which cannot triggered from Predict() directly. + // Therefore it cannot happen here. And it can be caught by following assertion. + RAPIDJSON_ASSERT(dst == IterativeParsingValueState); + + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return IterativeParsingFinishState; + } + } + + template + void HandleError(IterativeParsingState src, InputStream& is) { + if (HasParseError()) { + // Error flag has been set. + return; + } + + switch (src) { + case IterativeParsingStartState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentEmpty, is.Tell()); return; + case IterativeParsingFinishState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotSingular, is.Tell()); return; + case IterativeParsingObjectInitialState: + case IterativeParsingMemberDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); return; + case IterativeParsingMemberKeyState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); return; + case IterativeParsingMemberValueState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); return; + case IterativeParsingKeyValueDelimiterState: + case IterativeParsingArrayInitialState: + case IterativeParsingElementDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); return; + default: RAPIDJSON_ASSERT(src == IterativeParsingElementState); RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); return; + } + } + + RAPIDJSON_FORCEINLINE bool IsIterativeParsingDelimiterState(IterativeParsingState s) const { + return s >= IterativeParsingElementDelimiterState; + } + + RAPIDJSON_FORCEINLINE bool IsIterativeParsingCompleteState(IterativeParsingState s) const { + return s <= IterativeParsingErrorState; + } + + template + ParseResult IterativeParse(InputStream& is, Handler& handler) { + parseResult_.Clear(); + ClearStackOnExit scope(*this); + IterativeParsingState state = IterativeParsingStartState; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + while (is.Peek() != '\0') { + Token t = Tokenize(is.Peek()); + IterativeParsingState n = Predict(state, t); + IterativeParsingState d = Transit(state, t, n, is, handler); + + if (d == IterativeParsingErrorState) { + HandleError(state, is); + break; + } + + state = d; + + // Do not further consume streams if a root JSON has been parsed. + if ((parseFlags & kParseStopWhenDoneFlag) && state == IterativeParsingFinishState) + break; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + + // Handle the end of file. + if (state != IterativeParsingFinishState) + HandleError(state, is); + + return parseResult_; + } + + static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string. + internal::Stack stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing. + ParseResult parseResult_; + IterativeParsingState state_; +}; // class GenericReader + +//! Reader with UTF8 encoding and default allocator. +typedef GenericReader, UTF8<> > Reader; + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_READER_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/schema.h b/libraries/libstratosphere/include/stratosphere/rapidjson/schema.h new file mode 100644 index 000000000..11f716096 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/schema.h @@ -0,0 +1,2644 @@ +// Tencent is pleased to support the open source community by making RapidJSON available-> +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip-> All rights reserved-> +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License-> You may obtain a copy of the License at +// +// http://opensource->org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied-> See the License for the +// specific language governing permissions and limitations under the License-> + +#ifndef RAPIDJSON_SCHEMA_H_ +#define RAPIDJSON_SCHEMA_H_ + +#include "document.h" +#include "pointer.h" +#include "stringbuffer.h" +#include "error/en.h" +#include // abs, floor + +#if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX) +#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 1 +#else +#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 0 +#endif + +#if !RAPIDJSON_SCHEMA_USE_INTERNALREGEX && defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)) +#define RAPIDJSON_SCHEMA_USE_STDREGEX 1 +#else +#define RAPIDJSON_SCHEMA_USE_STDREGEX 0 +#endif + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX +#include "internal/regex.h" +#elif RAPIDJSON_SCHEMA_USE_STDREGEX +#include +#endif + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX || RAPIDJSON_SCHEMA_USE_STDREGEX +#define RAPIDJSON_SCHEMA_HAS_REGEX 1 +#else +#define RAPIDJSON_SCHEMA_HAS_REGEX 0 +#endif + +#ifndef RAPIDJSON_SCHEMA_VERBOSE +#define RAPIDJSON_SCHEMA_VERBOSE 0 +#endif + +#if RAPIDJSON_SCHEMA_VERBOSE +#include "stringbuffer.h" +#endif + +RAPIDJSON_DIAG_PUSH + +#if defined(__GNUC__) +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_OFF(weak-vtables) +RAPIDJSON_DIAG_OFF(exit-time-destructors) +RAPIDJSON_DIAG_OFF(c++98-compat-pedantic) +RAPIDJSON_DIAG_OFF(variadic-macros) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Verbose Utilities + +#if RAPIDJSON_SCHEMA_VERBOSE + +namespace internal { + +inline void PrintInvalidKeyword(const char* keyword) { + printf("Fail keyword: %s\n", keyword); +} + +inline void PrintInvalidKeyword(const wchar_t* keyword) { + wprintf(L"Fail keyword: %ls\n", keyword); +} + +inline void PrintInvalidDocument(const char* document) { + printf("Fail document: %s\n\n", document); +} + +inline void PrintInvalidDocument(const wchar_t* document) { + wprintf(L"Fail document: %ls\n\n", document); +} + +inline void PrintValidatorPointers(unsigned depth, const char* s, const char* d) { + printf("S: %*s%s\nD: %*s%s\n\n", depth * 4, " ", s, depth * 4, " ", d); +} + +inline void PrintValidatorPointers(unsigned depth, const wchar_t* s, const wchar_t* d) { + wprintf(L"S: %*ls%ls\nD: %*ls%ls\n\n", depth * 4, L" ", s, depth * 4, L" ", d); +} + +} // namespace internal + +#endif // RAPIDJSON_SCHEMA_VERBOSE + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_INVALID_KEYWORD_RETURN + +#if RAPIDJSON_SCHEMA_VERBOSE +#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) internal::PrintInvalidKeyword(keyword) +#else +#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) +#endif + +#define RAPIDJSON_INVALID_KEYWORD_RETURN(code)\ +RAPIDJSON_MULTILINEMACRO_BEGIN\ + context.invalidCode = code;\ + context.invalidKeyword = SchemaType::GetValidateErrorKeyword(code).GetString();\ + RAPIDJSON_INVALID_KEYWORD_VERBOSE(context.invalidKeyword);\ + return false;\ +RAPIDJSON_MULTILINEMACRO_END + +/////////////////////////////////////////////////////////////////////////////// +// ValidateFlag + +/*! \def RAPIDJSON_VALIDATE_DEFAULT_FLAGS + \ingroup RAPIDJSON_CONFIG + \brief User-defined kValidateDefaultFlags definition. + + User can define this as any \c ValidateFlag combinations. +*/ +#ifndef RAPIDJSON_VALIDATE_DEFAULT_FLAGS +#define RAPIDJSON_VALIDATE_DEFAULT_FLAGS kValidateNoFlags +#endif + +//! Combination of validate flags +/*! \see + */ +enum ValidateFlag { + kValidateNoFlags = 0, //!< No flags are set. + kValidateContinueOnErrorFlag = 1, //!< Don't stop after first validation error. + kValidateDefaultFlags = RAPIDJSON_VALIDATE_DEFAULT_FLAGS //!< Default validate flags. Can be customized by defining RAPIDJSON_VALIDATE_DEFAULT_FLAGS +}; + +/////////////////////////////////////////////////////////////////////////////// +// Forward declarations + +template +class GenericSchemaDocument; + +namespace internal { + +template +class Schema; + +/////////////////////////////////////////////////////////////////////////////// +// ISchemaValidator + +class ISchemaValidator { +public: + virtual ~ISchemaValidator() {} + virtual bool IsValid() const = 0; + virtual void SetValidateFlags(unsigned flags) = 0; + virtual unsigned GetValidateFlags() const = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// ISchemaStateFactory + +template +class ISchemaStateFactory { +public: + virtual ~ISchemaStateFactory() {} + virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&, const bool inheritContinueOnErrors) = 0; + virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0; + virtual void* CreateHasher() = 0; + virtual uint64_t GetHashCode(void* hasher) = 0; + virtual void DestroryHasher(void* hasher) = 0; + virtual void* MallocState(size_t size) = 0; + virtual void FreeState(void* p) = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// IValidationErrorHandler + +template +class IValidationErrorHandler { +public: + typedef typename SchemaType::Ch Ch; + typedef typename SchemaType::SValue SValue; + + virtual ~IValidationErrorHandler() {} + + virtual void NotMultipleOf(int64_t actual, const SValue& expected) = 0; + virtual void NotMultipleOf(uint64_t actual, const SValue& expected) = 0; + virtual void NotMultipleOf(double actual, const SValue& expected) = 0; + virtual void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void AboveMaximum(double actual, const SValue& expected, bool exclusive) = 0; + virtual void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void BelowMinimum(double actual, const SValue& expected, bool exclusive) = 0; + + virtual void TooLong(const Ch* str, SizeType length, SizeType expected) = 0; + virtual void TooShort(const Ch* str, SizeType length, SizeType expected) = 0; + virtual void DoesNotMatch(const Ch* str, SizeType length) = 0; + + virtual void DisallowedItem(SizeType index) = 0; + virtual void TooFewItems(SizeType actualCount, SizeType expectedCount) = 0; + virtual void TooManyItems(SizeType actualCount, SizeType expectedCount) = 0; + virtual void DuplicateItems(SizeType index1, SizeType index2) = 0; + + virtual void TooManyProperties(SizeType actualCount, SizeType expectedCount) = 0; + virtual void TooFewProperties(SizeType actualCount, SizeType expectedCount) = 0; + virtual void StartMissingProperties() = 0; + virtual void AddMissingProperty(const SValue& name) = 0; + virtual bool EndMissingProperties() = 0; + virtual void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void DisallowedProperty(const Ch* name, SizeType length) = 0; + + virtual void StartDependencyErrors() = 0; + virtual void StartMissingDependentProperties() = 0; + virtual void AddMissingDependentProperty(const SValue& targetName) = 0; + virtual void EndMissingDependentProperties(const SValue& sourceName) = 0; + virtual void AddDependencySchemaError(const SValue& souceName, ISchemaValidator* subvalidator) = 0; + virtual bool EndDependencyErrors() = 0; + + virtual void DisallowedValue(const ValidateErrorCode code) = 0; + virtual void StartDisallowedType() = 0; + virtual void AddExpectedType(const typename SchemaType::ValueType& expectedType) = 0; + virtual void EndDisallowedType(const typename SchemaType::ValueType& actualType) = 0; + virtual void NotAllOf(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void NoneOf(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void NotOneOf(ISchemaValidator** subvalidators, SizeType count, bool matched) = 0; + virtual void Disallowed() = 0; +}; + + +/////////////////////////////////////////////////////////////////////////////// +// Hasher + +// For comparison of compound value +template +class Hasher { +public: + typedef typename Encoding::Ch Ch; + + Hasher(Allocator* allocator = 0, size_t stackCapacity = kDefaultSize) : stack_(allocator, stackCapacity) {} + + bool Null() { return WriteType(kNullType); } + bool Bool(bool b) { return WriteType(b ? kTrueType : kFalseType); } + bool Int(int i) { Number n; n.u.i = i; n.d = static_cast(i); return WriteNumber(n); } + bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } + bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast(i); return WriteNumber(n); } + bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } + bool Double(double d) { + Number n; + if (d < 0) n.u.i = static_cast(d); + else n.u.u = static_cast(d); + n.d = d; + return WriteNumber(n); + } + + bool RawNumber(const Ch* str, SizeType len, bool) { + WriteBuffer(kNumberType, str, len * sizeof(Ch)); + return true; + } + + bool String(const Ch* str, SizeType len, bool) { + WriteBuffer(kStringType, str, len * sizeof(Ch)); + return true; + } + + bool StartObject() { return true; } + bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); } + bool EndObject(SizeType memberCount) { + uint64_t h = Hash(0, kObjectType); + uint64_t* kv = stack_.template Pop(memberCount * 2); + for (SizeType i = 0; i < memberCount; i++) + h ^= Hash(kv[i * 2], kv[i * 2 + 1]); // Use xor to achieve member order insensitive + *stack_.template Push() = h; + return true; + } + + bool StartArray() { return true; } + bool EndArray(SizeType elementCount) { + uint64_t h = Hash(0, kArrayType); + uint64_t* e = stack_.template Pop(elementCount); + for (SizeType i = 0; i < elementCount; i++) + h = Hash(h, e[i]); // Use hash to achieve element order sensitive + *stack_.template Push() = h; + return true; + } + + bool IsValid() const { return stack_.GetSize() == sizeof(uint64_t); } + + uint64_t GetHashCode() const { + RAPIDJSON_ASSERT(IsValid()); + return *stack_.template Top(); + } + +private: + static const size_t kDefaultSize = 256; + struct Number { + union U { + uint64_t u; + int64_t i; + }u; + double d; + }; + + bool WriteType(Type type) { return WriteBuffer(type, 0, 0); } + + bool WriteNumber(const Number& n) { return WriteBuffer(kNumberType, &n, sizeof(n)); } + + bool WriteBuffer(Type type, const void* data, size_t len) { + // FNV-1a from http://isthe.com/chongo/tech/comp/fnv/ + uint64_t h = Hash(RAPIDJSON_UINT64_C2(0x84222325, 0xcbf29ce4), type); + const unsigned char* d = static_cast(data); + for (size_t i = 0; i < len; i++) + h = Hash(h, d[i]); + *stack_.template Push() = h; + return true; + } + + static uint64_t Hash(uint64_t h, uint64_t d) { + static const uint64_t kPrime = RAPIDJSON_UINT64_C2(0x00000100, 0x000001b3); + h ^= d; + h *= kPrime; + return h; + } + + Stack stack_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// SchemaValidationContext + +template +struct SchemaValidationContext { + typedef Schema SchemaType; + typedef ISchemaStateFactory SchemaValidatorFactoryType; + typedef IValidationErrorHandler ErrorHandlerType; + typedef typename SchemaType::ValueType ValueType; + typedef typename ValueType::Ch Ch; + + enum PatternValidatorType { + kPatternValidatorOnly, + kPatternValidatorWithProperty, + kPatternValidatorWithAdditionalProperty + }; + + SchemaValidationContext(SchemaValidatorFactoryType& f, ErrorHandlerType& eh, const SchemaType* s) : + factory(f), + error_handler(eh), + schema(s), + valueSchema(), + invalidKeyword(), + invalidCode(), + hasher(), + arrayElementHashCodes(), + validators(), + validatorCount(), + patternPropertiesValidators(), + patternPropertiesValidatorCount(), + patternPropertiesSchemas(), + patternPropertiesSchemaCount(), + valuePatternValidatorType(kPatternValidatorOnly), + propertyExist(), + inArray(false), + valueUniqueness(false), + arrayUniqueness(false) + { + } + + ~SchemaValidationContext() { + if (hasher) + factory.DestroryHasher(hasher); + if (validators) { + for (SizeType i = 0; i < validatorCount; i++) + factory.DestroySchemaValidator(validators[i]); + factory.FreeState(validators); + } + if (patternPropertiesValidators) { + for (SizeType i = 0; i < patternPropertiesValidatorCount; i++) + factory.DestroySchemaValidator(patternPropertiesValidators[i]); + factory.FreeState(patternPropertiesValidators); + } + if (patternPropertiesSchemas) + factory.FreeState(patternPropertiesSchemas); + if (propertyExist) + factory.FreeState(propertyExist); + } + + SchemaValidatorFactoryType& factory; + ErrorHandlerType& error_handler; + const SchemaType* schema; + const SchemaType* valueSchema; + const Ch* invalidKeyword; + ValidateErrorCode invalidCode; + void* hasher; // Only validator access + void* arrayElementHashCodes; // Only validator access this + ISchemaValidator** validators; + SizeType validatorCount; + ISchemaValidator** patternPropertiesValidators; + SizeType patternPropertiesValidatorCount; + const SchemaType** patternPropertiesSchemas; + SizeType patternPropertiesSchemaCount; + PatternValidatorType valuePatternValidatorType; + PatternValidatorType objectPatternValidatorType; + SizeType arrayElementIndex; + bool* propertyExist; + bool inArray; + bool valueUniqueness; + bool arrayUniqueness; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Schema + +template +class Schema { +public: + typedef typename SchemaDocumentType::ValueType ValueType; + typedef typename SchemaDocumentType::AllocatorType AllocatorType; + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename EncodingType::Ch Ch; + typedef SchemaValidationContext Context; + typedef Schema SchemaType; + typedef GenericValue SValue; + typedef IValidationErrorHandler ErrorHandler; + friend class GenericSchemaDocument; + + Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) : + allocator_(allocator), + uri_(schemaDocument->GetURI(), *allocator), + pointer_(p, allocator), + typeless_(schemaDocument->GetTypeless()), + enum_(), + enumCount_(), + not_(), + type_((1 << kTotalSchemaType) - 1), // typeless + validatorCount_(), + notValidatorIndex_(), + properties_(), + additionalPropertiesSchema_(), + patternProperties_(), + patternPropertyCount_(), + propertyCount_(), + minProperties_(), + maxProperties_(SizeType(~0)), + additionalProperties_(true), + hasDependencies_(), + hasRequired_(), + hasSchemaDependencies_(), + additionalItemsSchema_(), + itemsList_(), + itemsTuple_(), + itemsTupleCount_(), + minItems_(), + maxItems_(SizeType(~0)), + additionalItems_(true), + uniqueItems_(false), + pattern_(), + minLength_(0), + maxLength_(~SizeType(0)), + exclusiveMinimum_(false), + exclusiveMaximum_(false), + defaultValueLength_(0) + { + typedef typename ValueType::ConstValueIterator ConstValueIterator; + typedef typename ValueType::ConstMemberIterator ConstMemberIterator; + + if (!value.IsObject()) + return; + + if (const ValueType* v = GetMember(value, GetTypeString())) { + type_ = 0; + if (v->IsString()) + AddType(*v); + else if (v->IsArray()) + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) + AddType(*itr); + } + + if (const ValueType* v = GetMember(value, GetEnumString())) { + if (v->IsArray() && v->Size() > 0) { + enum_ = static_cast(allocator_->Malloc(sizeof(uint64_t) * v->Size())); + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) { + typedef Hasher > EnumHasherType; + char buffer[256u + 24]; + MemoryPoolAllocator<> hasherAllocator(buffer, sizeof(buffer)); + EnumHasherType h(&hasherAllocator, 256); + itr->Accept(h); + enum_[enumCount_++] = h.GetHashCode(); + } + } + } + + if (schemaDocument) { + AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document); + AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document); + AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document); + } + + if (const ValueType* v = GetMember(value, GetNotString())) { + schemaDocument->CreateSchema(¬_, p.Append(GetNotString(), allocator_), *v, document); + notValidatorIndex_ = validatorCount_; + validatorCount_++; + } + + // Object + + const ValueType* properties = GetMember(value, GetPropertiesString()); + const ValueType* required = GetMember(value, GetRequiredString()); + const ValueType* dependencies = GetMember(value, GetDependenciesString()); + { + // Gather properties from properties/required/dependencies + SValue allProperties(kArrayType); + + if (properties && properties->IsObject()) + for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) + AddUniqueElement(allProperties, itr->name); + + if (required && required->IsArray()) + for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) + if (itr->IsString()) + AddUniqueElement(allProperties, *itr); + + if (dependencies && dependencies->IsObject()) + for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { + AddUniqueElement(allProperties, itr->name); + if (itr->value.IsArray()) + for (ConstValueIterator i = itr->value.Begin(); i != itr->value.End(); ++i) + if (i->IsString()) + AddUniqueElement(allProperties, *i); + } + + if (allProperties.Size() > 0) { + propertyCount_ = allProperties.Size(); + properties_ = static_cast(allocator_->Malloc(sizeof(Property) * propertyCount_)); + for (SizeType i = 0; i < propertyCount_; i++) { + new (&properties_[i]) Property(); + properties_[i].name = allProperties[i]; + properties_[i].schema = typeless_; + } + } + } + + if (properties && properties->IsObject()) { + PointerType q = p.Append(GetPropertiesString(), allocator_); + for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) { + SizeType index; + if (FindPropertyIndex(itr->name, &index)) + schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document); + } + } + + if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) { + PointerType q = p.Append(GetPatternPropertiesString(), allocator_); + patternProperties_ = static_cast(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount())); + patternPropertyCount_ = 0; + + for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) { + new (&patternProperties_[patternPropertyCount_]) PatternProperty(); + patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name); + schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, q.Append(itr->name, allocator_), itr->value, document); + patternPropertyCount_++; + } + } + + if (required && required->IsArray()) + for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) + if (itr->IsString()) { + SizeType index; + if (FindPropertyIndex(*itr, &index)) { + properties_[index].required = true; + hasRequired_ = true; + } + } + + if (dependencies && dependencies->IsObject()) { + PointerType q = p.Append(GetDependenciesString(), allocator_); + hasDependencies_ = true; + for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { + SizeType sourceIndex; + if (FindPropertyIndex(itr->name, &sourceIndex)) { + if (itr->value.IsArray()) { + properties_[sourceIndex].dependencies = static_cast(allocator_->Malloc(sizeof(bool) * propertyCount_)); + std::memset(properties_[sourceIndex].dependencies, 0, sizeof(bool)* propertyCount_); + for (ConstValueIterator targetItr = itr->value.Begin(); targetItr != itr->value.End(); ++targetItr) { + SizeType targetIndex; + if (FindPropertyIndex(*targetItr, &targetIndex)) + properties_[sourceIndex].dependencies[targetIndex] = true; + } + } + else if (itr->value.IsObject()) { + hasSchemaDependencies_ = true; + schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document); + properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_; + validatorCount_++; + } + } + } + } + + if (const ValueType* v = GetMember(value, GetAdditionalPropertiesString())) { + if (v->IsBool()) + additionalProperties_ = v->GetBool(); + else if (v->IsObject()) + schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document); + } + + AssignIfExist(minProperties_, value, GetMinPropertiesString()); + AssignIfExist(maxProperties_, value, GetMaxPropertiesString()); + + // Array + if (const ValueType* v = GetMember(value, GetItemsString())) { + PointerType q = p.Append(GetItemsString(), allocator_); + if (v->IsObject()) // List validation + schemaDocument->CreateSchema(&itemsList_, q, *v, document); + else if (v->IsArray()) { // Tuple validation + itemsTuple_ = static_cast(allocator_->Malloc(sizeof(const Schema*) * v->Size())); + SizeType index = 0; + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++) + schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document); + } + } + + AssignIfExist(minItems_, value, GetMinItemsString()); + AssignIfExist(maxItems_, value, GetMaxItemsString()); + + if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) { + if (v->IsBool()) + additionalItems_ = v->GetBool(); + else if (v->IsObject()) + schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document); + } + + AssignIfExist(uniqueItems_, value, GetUniqueItemsString()); + + // String + AssignIfExist(minLength_, value, GetMinLengthString()); + AssignIfExist(maxLength_, value, GetMaxLengthString()); + + if (const ValueType* v = GetMember(value, GetPatternString())) + pattern_ = CreatePattern(*v); + + // Number + if (const ValueType* v = GetMember(value, GetMinimumString())) + if (v->IsNumber()) + minimum_.CopyFrom(*v, *allocator_); + + if (const ValueType* v = GetMember(value, GetMaximumString())) + if (v->IsNumber()) + maximum_.CopyFrom(*v, *allocator_); + + AssignIfExist(exclusiveMinimum_, value, GetExclusiveMinimumString()); + AssignIfExist(exclusiveMaximum_, value, GetExclusiveMaximumString()); + + if (const ValueType* v = GetMember(value, GetMultipleOfString())) + if (v->IsNumber() && v->GetDouble() > 0.0) + multipleOf_.CopyFrom(*v, *allocator_); + + // Default + if (const ValueType* v = GetMember(value, GetDefaultValueString())) + if (v->IsString()) + defaultValueLength_ = v->GetStringLength(); + + } + + ~Schema() { + AllocatorType::Free(enum_); + if (properties_) { + for (SizeType i = 0; i < propertyCount_; i++) + properties_[i].~Property(); + AllocatorType::Free(properties_); + } + if (patternProperties_) { + for (SizeType i = 0; i < patternPropertyCount_; i++) + patternProperties_[i].~PatternProperty(); + AllocatorType::Free(patternProperties_); + } + AllocatorType::Free(itemsTuple_); +#if RAPIDJSON_SCHEMA_HAS_REGEX + if (pattern_) { + pattern_->~RegexType(); + AllocatorType::Free(pattern_); + } +#endif + } + + const SValue& GetURI() const { + return uri_; + } + + const PointerType& GetPointer() const { + return pointer_; + } + + bool BeginValue(Context& context) const { + if (context.inArray) { + if (uniqueItems_) + context.valueUniqueness = true; + + if (itemsList_) + context.valueSchema = itemsList_; + else if (itemsTuple_) { + if (context.arrayElementIndex < itemsTupleCount_) + context.valueSchema = itemsTuple_[context.arrayElementIndex]; + else if (additionalItemsSchema_) + context.valueSchema = additionalItemsSchema_; + else if (additionalItems_) + context.valueSchema = typeless_; + else { + context.error_handler.DisallowedItem(context.arrayElementIndex); + // Must set valueSchema for when kValidateContinueOnErrorFlag is set, else reports spurious type error + context.valueSchema = typeless_; + // Must bump arrayElementIndex for when kValidateContinueOnErrorFlag is set + context.arrayElementIndex++; + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAdditionalItems); + } + } + else + context.valueSchema = typeless_; + + context.arrayElementIndex++; + } + return true; + } + + RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const { + if (context.patternPropertiesValidatorCount > 0) { + bool otherValid = false; + SizeType count = context.patternPropertiesValidatorCount; + if (context.objectPatternValidatorType != Context::kPatternValidatorOnly) + otherValid = context.patternPropertiesValidators[--count]->IsValid(); + + bool patternValid = true; + for (SizeType i = 0; i < count; i++) + if (!context.patternPropertiesValidators[i]->IsValid()) { + patternValid = false; + break; + } + + if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) { + if (!patternValid) { + context.error_handler.PropertyViolations(context.patternPropertiesValidators, count); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties); + } + } + else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) { + if (!patternValid || !otherValid) { + context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties); + } + } + else if (!patternValid && !otherValid) { // kPatternValidatorWithAdditionalProperty) + context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties); + } + } + + // For enums only check if we have a hasher + if (enum_ && context.hasher) { + const uint64_t h = context.factory.GetHashCode(context.hasher); + for (SizeType i = 0; i < enumCount_; i++) + if (enum_[i] == h) + goto foundEnum; + context.error_handler.DisallowedValue(kValidateErrorEnum); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorEnum); + foundEnum:; + } + + if (allOf_.schemas) + for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++) + if (!context.validators[i]->IsValid()) { + context.error_handler.NotAllOf(&context.validators[allOf_.begin], allOf_.count); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAllOf); + } + + if (anyOf_.schemas) { + for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++) + if (context.validators[i]->IsValid()) + goto foundAny; + context.error_handler.NoneOf(&context.validators[anyOf_.begin], anyOf_.count); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAnyOf); + foundAny:; + } + + if (oneOf_.schemas) { + bool oneValid = false; + for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++) + if (context.validators[i]->IsValid()) { + if (oneValid) { + context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count, true); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorOneOfMatch); + } else + oneValid = true; + } + if (!oneValid) { + context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count, false); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorOneOf); + } + } + + if (not_ && context.validators[notValidatorIndex_]->IsValid()) { + context.error_handler.Disallowed(); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorNot); + } + + return true; + } + + bool Null(Context& context) const { + if (!(type_ & (1 << kNullSchemaType))) { + DisallowedType(context, GetNullString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + return CreateParallelValidator(context); + } + + bool Bool(Context& context, bool) const { + if (!(type_ & (1 << kBooleanSchemaType))) { + DisallowedType(context, GetBooleanString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + return CreateParallelValidator(context); + } + + bool Int(Context& context, int i) const { + if (!CheckInt(context, i)) + return false; + return CreateParallelValidator(context); + } + + bool Uint(Context& context, unsigned u) const { + if (!CheckUint(context, u)) + return false; + return CreateParallelValidator(context); + } + + bool Int64(Context& context, int64_t i) const { + if (!CheckInt(context, i)) + return false; + return CreateParallelValidator(context); + } + + bool Uint64(Context& context, uint64_t u) const { + if (!CheckUint(context, u)) + return false; + return CreateParallelValidator(context); + } + + bool Double(Context& context, double d) const { + if (!(type_ & (1 << kNumberSchemaType))) { + DisallowedType(context, GetNumberString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d)) + return false; + + if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d)) + return false; + + if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d)) + return false; + + return CreateParallelValidator(context); + } + + bool String(Context& context, const Ch* str, SizeType length, bool) const { + if (!(type_ & (1 << kStringSchemaType))) { + DisallowedType(context, GetStringString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + if (minLength_ != 0 || maxLength_ != SizeType(~0)) { + SizeType count; + if (internal::CountStringCodePoint(str, length, &count)) { + if (count < minLength_) { + context.error_handler.TooShort(str, length, minLength_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinLength); + } + if (count > maxLength_) { + context.error_handler.TooLong(str, length, maxLength_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxLength); + } + } + } + + if (pattern_ && !IsPatternMatch(pattern_, str, length)) { + context.error_handler.DoesNotMatch(str, length); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPattern); + } + + return CreateParallelValidator(context); + } + + bool StartObject(Context& context) const { + if (!(type_ & (1 << kObjectSchemaType))) { + DisallowedType(context, GetObjectString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + if (hasDependencies_ || hasRequired_) { + context.propertyExist = static_cast(context.factory.MallocState(sizeof(bool) * propertyCount_)); + std::memset(context.propertyExist, 0, sizeof(bool) * propertyCount_); + } + + if (patternProperties_) { // pre-allocate schema array + SizeType count = patternPropertyCount_ + 1; // extra for valuePatternValidatorType + context.patternPropertiesSchemas = static_cast(context.factory.MallocState(sizeof(const SchemaType*) * count)); + context.patternPropertiesSchemaCount = 0; + std::memset(context.patternPropertiesSchemas, 0, sizeof(SchemaType*) * count); + } + + return CreateParallelValidator(context); + } + + bool Key(Context& context, const Ch* str, SizeType len, bool) const { + if (patternProperties_) { + context.patternPropertiesSchemaCount = 0; + for (SizeType i = 0; i < patternPropertyCount_; i++) + if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len)) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema; + context.valueSchema = typeless_; + } + } + + SizeType index = 0; + if (FindPropertyIndex(ValueType(str, len).Move(), &index)) { + if (context.patternPropertiesSchemaCount > 0) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema; + context.valueSchema = typeless_; + context.valuePatternValidatorType = Context::kPatternValidatorWithProperty; + } + else + context.valueSchema = properties_[index].schema; + + if (context.propertyExist) + context.propertyExist[index] = true; + + return true; + } + + if (additionalPropertiesSchema_) { + if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_; + context.valueSchema = typeless_; + context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty; + } + else + context.valueSchema = additionalPropertiesSchema_; + return true; + } + else if (additionalProperties_) { + context.valueSchema = typeless_; + return true; + } + + if (context.patternPropertiesSchemaCount == 0) { // patternProperties are not additional properties + // Must set valueSchema for when kValidateContinueOnErrorFlag is set, else reports spurious type error + context.valueSchema = typeless_; + context.error_handler.DisallowedProperty(str, len); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAdditionalProperties); + } + + return true; + } + + bool EndObject(Context& context, SizeType memberCount) const { + if (hasRequired_) { + context.error_handler.StartMissingProperties(); + for (SizeType index = 0; index < propertyCount_; index++) + if (properties_[index].required && !context.propertyExist[index]) + if (properties_[index].schema->defaultValueLength_ == 0 ) + context.error_handler.AddMissingProperty(properties_[index].name); + if (context.error_handler.EndMissingProperties()) + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorRequired); + } + + if (memberCount < minProperties_) { + context.error_handler.TooFewProperties(memberCount, minProperties_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinProperties); + } + + if (memberCount > maxProperties_) { + context.error_handler.TooManyProperties(memberCount, maxProperties_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxProperties); + } + + if (hasDependencies_) { + context.error_handler.StartDependencyErrors(); + for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) { + const Property& source = properties_[sourceIndex]; + if (context.propertyExist[sourceIndex]) { + if (source.dependencies) { + context.error_handler.StartMissingDependentProperties(); + for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++) + if (source.dependencies[targetIndex] && !context.propertyExist[targetIndex]) + context.error_handler.AddMissingDependentProperty(properties_[targetIndex].name); + context.error_handler.EndMissingDependentProperties(source.name); + } + else if (source.dependenciesSchema) { + ISchemaValidator* dependenciesValidator = context.validators[source.dependenciesValidatorIndex]; + if (!dependenciesValidator->IsValid()) + context.error_handler.AddDependencySchemaError(source.name, dependenciesValidator); + } + } + } + if (context.error_handler.EndDependencyErrors()) + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorDependencies); + } + + return true; + } + + bool StartArray(Context& context) const { + context.arrayElementIndex = 0; + context.inArray = true; // Ensure we note that we are in an array + + if (!(type_ & (1 << kArraySchemaType))) { + DisallowedType(context, GetArrayString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + return CreateParallelValidator(context); + } + + bool EndArray(Context& context, SizeType elementCount) const { + context.inArray = false; + + if (elementCount < minItems_) { + context.error_handler.TooFewItems(elementCount, minItems_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinItems); + } + + if (elementCount > maxItems_) { + context.error_handler.TooManyItems(elementCount, maxItems_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxItems); + } + + return true; + } + + static const ValueType& GetValidateErrorKeyword(ValidateErrorCode validateErrorCode) { + switch (validateErrorCode) { + case kValidateErrorMultipleOf: return GetMultipleOfString(); + case kValidateErrorMaximum: return GetMaximumString(); + case kValidateErrorExclusiveMaximum: return GetMaximumString(); // Same + case kValidateErrorMinimum: return GetMinimumString(); + case kValidateErrorExclusiveMinimum: return GetMinimumString(); // Same + + case kValidateErrorMaxLength: return GetMaxLengthString(); + case kValidateErrorMinLength: return GetMinLengthString(); + case kValidateErrorPattern: return GetPatternString(); + + case kValidateErrorMaxItems: return GetMaxItemsString(); + case kValidateErrorMinItems: return GetMinItemsString(); + case kValidateErrorUniqueItems: return GetUniqueItemsString(); + case kValidateErrorAdditionalItems: return GetAdditionalItemsString(); + + case kValidateErrorMaxProperties: return GetMaxPropertiesString(); + case kValidateErrorMinProperties: return GetMinPropertiesString(); + case kValidateErrorRequired: return GetRequiredString(); + case kValidateErrorAdditionalProperties: return GetAdditionalPropertiesString(); + case kValidateErrorPatternProperties: return GetPatternPropertiesString(); + case kValidateErrorDependencies: return GetDependenciesString(); + + case kValidateErrorEnum: return GetEnumString(); + case kValidateErrorType: return GetTypeString(); + + case kValidateErrorOneOf: return GetOneOfString(); + case kValidateErrorOneOfMatch: return GetOneOfString(); // Same + case kValidateErrorAllOf: return GetAllOfString(); + case kValidateErrorAnyOf: return GetAnyOfString(); + case kValidateErrorNot: return GetNotString(); + + default: return GetNullString(); + } + } + + + // Generate functions for string literal according to Ch +#define RAPIDJSON_STRING_(name, ...) \ + static const ValueType& Get##name##String() {\ + static const Ch s[] = { __VA_ARGS__, '\0' };\ + static const ValueType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1));\ + return v;\ + } + + RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l') + RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n') + RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't') + RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y') + RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g') + RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r') + RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r') + RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e') + RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm') + RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f') + RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f') + RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f') + RAPIDJSON_STRING_(Not, 'n', 'o', 't') + RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd') + RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's') + RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h') + RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h') + RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n') + RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f') + RAPIDJSON_STRING_(DefaultValue, 'd', 'e', 'f', 'a', 'u', 'l', 't') + +#undef RAPIDJSON_STRING_ + +private: + enum SchemaValueType { + kNullSchemaType, + kBooleanSchemaType, + kObjectSchemaType, + kArraySchemaType, + kStringSchemaType, + kNumberSchemaType, + kIntegerSchemaType, + kTotalSchemaType + }; + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX + typedef internal::GenericRegex RegexType; +#elif RAPIDJSON_SCHEMA_USE_STDREGEX + typedef std::basic_regex RegexType; +#else + typedef char RegexType; +#endif + + struct SchemaArray { + SchemaArray() : schemas(), count() {} + ~SchemaArray() { AllocatorType::Free(schemas); } + const SchemaType** schemas; + SizeType begin; // begin index of context.validators + SizeType count; + }; + + template + void AddUniqueElement(V1& a, const V2& v) { + for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr) + if (*itr == v) + return; + V1 c(v, *allocator_); + a.PushBack(c, *allocator_); + } + + static const ValueType* GetMember(const ValueType& value, const ValueType& name) { + typename ValueType::ConstMemberIterator itr = value.FindMember(name); + return itr != value.MemberEnd() ? &(itr->value) : 0; + } + + static void AssignIfExist(bool& out, const ValueType& value, const ValueType& name) { + if (const ValueType* v = GetMember(value, name)) + if (v->IsBool()) + out = v->GetBool(); + } + + static void AssignIfExist(SizeType& out, const ValueType& value, const ValueType& name) { + if (const ValueType* v = GetMember(value, name)) + if (v->IsUint64() && v->GetUint64() <= SizeType(~0)) + out = static_cast(v->GetUint64()); + } + + void AssignIfExist(SchemaArray& out, SchemaDocumentType& schemaDocument, const PointerType& p, const ValueType& value, const ValueType& name, const ValueType& document) { + if (const ValueType* v = GetMember(value, name)) { + if (v->IsArray() && v->Size() > 0) { + PointerType q = p.Append(name, allocator_); + out.count = v->Size(); + out.schemas = static_cast(allocator_->Malloc(out.count * sizeof(const Schema*))); + memset(out.schemas, 0, sizeof(Schema*)* out.count); + for (SizeType i = 0; i < out.count; i++) + schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document); + out.begin = validatorCount_; + validatorCount_ += out.count; + } + } + } + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX + template + RegexType* CreatePattern(const ValueType& value) { + if (value.IsString()) { + RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), allocator_); + if (!r->IsValid()) { + r->~RegexType(); + AllocatorType::Free(r); + r = 0; + } + return r; + } + return 0; + } + + static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) { + GenericRegexSearch rs(*pattern); + return rs.Search(str); + } +#elif RAPIDJSON_SCHEMA_USE_STDREGEX + template + RegexType* CreatePattern(const ValueType& value) { + if (value.IsString()) { + RegexType *r = static_cast(allocator_->Malloc(sizeof(RegexType))); + try { + return new (r) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript); + } + catch (const std::regex_error&) { + AllocatorType::Free(r); + } + } + return 0; + } + + static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType length) { + std::match_results r; + return std::regex_search(str, str + length, r, *pattern); + } +#else + template + RegexType* CreatePattern(const ValueType&) { return 0; } + + static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; } +#endif // RAPIDJSON_SCHEMA_USE_STDREGEX + + void AddType(const ValueType& type) { + if (type == GetNullString() ) type_ |= 1 << kNullSchemaType; + else if (type == GetBooleanString()) type_ |= 1 << kBooleanSchemaType; + else if (type == GetObjectString() ) type_ |= 1 << kObjectSchemaType; + else if (type == GetArrayString() ) type_ |= 1 << kArraySchemaType; + else if (type == GetStringString() ) type_ |= 1 << kStringSchemaType; + else if (type == GetIntegerString()) type_ |= 1 << kIntegerSchemaType; + else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType); + } + + bool CreateParallelValidator(Context& context) const { + if (enum_ || context.arrayUniqueness) + context.hasher = context.factory.CreateHasher(); + + if (validatorCount_) { + RAPIDJSON_ASSERT(context.validators == 0); + context.validators = static_cast(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_)); + context.validatorCount = validatorCount_; + + // Always return after first failure for these sub-validators + if (allOf_.schemas) + CreateSchemaValidators(context, allOf_, false); + + if (anyOf_.schemas) + CreateSchemaValidators(context, anyOf_, false); + + if (oneOf_.schemas) + CreateSchemaValidators(context, oneOf_, false); + + if (not_) + context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_, false); + + if (hasSchemaDependencies_) { + for (SizeType i = 0; i < propertyCount_; i++) + if (properties_[i].dependenciesSchema) + context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema, false); + } + } + + return true; + } + + void CreateSchemaValidators(Context& context, const SchemaArray& schemas, const bool inheritContinueOnErrors) const { + for (SizeType i = 0; i < schemas.count; i++) + context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i], inheritContinueOnErrors); + } + + // O(n) + bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const { + SizeType len = name.GetStringLength(); + const Ch* str = name.GetString(); + for (SizeType index = 0; index < propertyCount_; index++) + if (properties_[index].name.GetStringLength() == len && + (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0)) + { + *outIndex = index; + return true; + } + return false; + } + + bool CheckInt(Context& context, int64_t i) const { + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) { + DisallowedType(context, GetIntegerString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + if (!minimum_.IsNull()) { + if (minimum_.IsInt64()) { + if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) { + context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); + } + } + else if (minimum_.IsUint64()) { + context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); // i <= max(int64_t) < minimum.GetUint64() + } + else if (!CheckDoubleMinimum(context, static_cast(i))) + return false; + } + + if (!maximum_.IsNull()) { + if (maximum_.IsInt64()) { + if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) { + context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); + } + } + else if (maximum_.IsUint64()) { } + /* do nothing */ // i <= max(int64_t) < maximum_.GetUint64() + else if (!CheckDoubleMaximum(context, static_cast(i))) + return false; + } + + if (!multipleOf_.IsNull()) { + if (multipleOf_.IsUint64()) { + if (static_cast(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) { + context.error_handler.NotMultipleOf(i, multipleOf_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf); + } + } + else if (!CheckDoubleMultipleOf(context, static_cast(i))) + return false; + } + + return true; + } + + bool CheckUint(Context& context, uint64_t i) const { + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) { + DisallowedType(context, GetIntegerString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + if (!minimum_.IsNull()) { + if (minimum_.IsUint64()) { + if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) { + context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); + } + } + else if (minimum_.IsInt64()) + /* do nothing */; // i >= 0 > minimum.Getint64() + else if (!CheckDoubleMinimum(context, static_cast(i))) + return false; + } + + if (!maximum_.IsNull()) { + if (maximum_.IsUint64()) { + if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) { + context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); + } + } + else if (maximum_.IsInt64()) { + context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); // i >= 0 > maximum_ + } + else if (!CheckDoubleMaximum(context, static_cast(i))) + return false; + } + + if (!multipleOf_.IsNull()) { + if (multipleOf_.IsUint64()) { + if (i % multipleOf_.GetUint64() != 0) { + context.error_handler.NotMultipleOf(i, multipleOf_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf); + } + } + else if (!CheckDoubleMultipleOf(context, static_cast(i))) + return false; + } + + return true; + } + + bool CheckDoubleMinimum(Context& context, double d) const { + if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) { + context.error_handler.BelowMinimum(d, minimum_, exclusiveMinimum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); + } + return true; + } + + bool CheckDoubleMaximum(Context& context, double d) const { + if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) { + context.error_handler.AboveMaximum(d, maximum_, exclusiveMaximum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); + } + return true; + } + + bool CheckDoubleMultipleOf(Context& context, double d) const { + double a = std::abs(d), b = std::abs(multipleOf_.GetDouble()); + double q = std::floor(a / b); + double r = a - q * b; + if (r > 0.0) { + context.error_handler.NotMultipleOf(d, multipleOf_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf); + } + return true; + } + + void DisallowedType(Context& context, const ValueType& actualType) const { + ErrorHandler& eh = context.error_handler; + eh.StartDisallowedType(); + + if (type_ & (1 << kNullSchemaType)) eh.AddExpectedType(GetNullString()); + if (type_ & (1 << kBooleanSchemaType)) eh.AddExpectedType(GetBooleanString()); + if (type_ & (1 << kObjectSchemaType)) eh.AddExpectedType(GetObjectString()); + if (type_ & (1 << kArraySchemaType)) eh.AddExpectedType(GetArrayString()); + if (type_ & (1 << kStringSchemaType)) eh.AddExpectedType(GetStringString()); + + if (type_ & (1 << kNumberSchemaType)) eh.AddExpectedType(GetNumberString()); + else if (type_ & (1 << kIntegerSchemaType)) eh.AddExpectedType(GetIntegerString()); + + eh.EndDisallowedType(actualType); + } + + struct Property { + Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {} + ~Property() { AllocatorType::Free(dependencies); } + SValue name; + const SchemaType* schema; + const SchemaType* dependenciesSchema; + SizeType dependenciesValidatorIndex; + bool* dependencies; + bool required; + }; + + struct PatternProperty { + PatternProperty() : schema(), pattern() {} + ~PatternProperty() { + if (pattern) { + pattern->~RegexType(); + AllocatorType::Free(pattern); + } + } + const SchemaType* schema; + RegexType* pattern; + }; + + AllocatorType* allocator_; + SValue uri_; + PointerType pointer_; + const SchemaType* typeless_; + uint64_t* enum_; + SizeType enumCount_; + SchemaArray allOf_; + SchemaArray anyOf_; + SchemaArray oneOf_; + const SchemaType* not_; + unsigned type_; // bitmask of kSchemaType + SizeType validatorCount_; + SizeType notValidatorIndex_; + + Property* properties_; + const SchemaType* additionalPropertiesSchema_; + PatternProperty* patternProperties_; + SizeType patternPropertyCount_; + SizeType propertyCount_; + SizeType minProperties_; + SizeType maxProperties_; + bool additionalProperties_; + bool hasDependencies_; + bool hasRequired_; + bool hasSchemaDependencies_; + + const SchemaType* additionalItemsSchema_; + const SchemaType* itemsList_; + const SchemaType** itemsTuple_; + SizeType itemsTupleCount_; + SizeType minItems_; + SizeType maxItems_; + bool additionalItems_; + bool uniqueItems_; + + RegexType* pattern_; + SizeType minLength_; + SizeType maxLength_; + + SValue minimum_; + SValue maximum_; + SValue multipleOf_; + bool exclusiveMinimum_; + bool exclusiveMaximum_; + + SizeType defaultValueLength_; +}; + +template +struct TokenHelper { + RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { + *documentStack.template Push() = '/'; + char buffer[21]; + size_t length = static_cast((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer); + for (size_t i = 0; i < length; i++) + *documentStack.template Push() = static_cast(buffer[i]); + } +}; + +// Partial specialized version for char to prevent buffer copying. +template +struct TokenHelper { + RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { + if (sizeof(SizeType) == 4) { + char *buffer = documentStack.template Push(1 + 10); // '/' + uint + *buffer++ = '/'; + const char* end = internal::u32toa(index, buffer); + documentStack.template Pop(static_cast(10 - (end - buffer))); + } + else { + char *buffer = documentStack.template Push(1 + 20); // '/' + uint64 + *buffer++ = '/'; + const char* end = internal::u64toa(index, buffer); + documentStack.template Pop(static_cast(20 - (end - buffer))); + } + } +}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// IGenericRemoteSchemaDocumentProvider + +template +class IGenericRemoteSchemaDocumentProvider { +public: + typedef typename SchemaDocumentType::Ch Ch; + + virtual ~IGenericRemoteSchemaDocumentProvider() {} + virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericSchemaDocument + +//! JSON schema document. +/*! + A JSON schema document is a compiled version of a JSON schema. + It is basically a tree of internal::Schema. + + \note This is an immutable class (i.e. its instance cannot be modified after construction). + \tparam ValueT Type of JSON value (e.g. \c Value ), which also determine the encoding. + \tparam Allocator Allocator type for allocating memory of this document. +*/ +template +class GenericSchemaDocument { +public: + typedef ValueT ValueType; + typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProviderType; + typedef Allocator AllocatorType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename EncodingType::Ch Ch; + typedef internal::Schema SchemaType; + typedef GenericPointer PointerType; + typedef GenericValue URIType; + friend class internal::Schema; + template + friend class GenericSchemaValidator; + + //! Constructor. + /*! + Compile a JSON document into schema document. + + \param document A JSON document as source. + \param uri The base URI of this schema document for purposes of violation reporting. + \param uriLength Length of \c name, in code points. + \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null. + \param allocator An optional allocator instance for allocating memory. Can be null. + */ + explicit GenericSchemaDocument(const ValueType& document, const Ch* uri = 0, SizeType uriLength = 0, + IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) : + remoteProvider_(remoteProvider), + allocator_(allocator), + ownAllocator_(), + root_(), + typeless_(), + schemaMap_(allocator, kInitialSchemaMapSize), + schemaRef_(allocator, kInitialSchemaRefSize) + { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + + Ch noUri[1] = {0}; + uri_.SetString(uri ? uri : noUri, uriLength, *allocator_); + + typeless_ = static_cast(allocator_->Malloc(sizeof(SchemaType))); + new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), allocator_); + + // Generate root schema, it will call CreateSchema() to create sub-schemas, + // And call AddRefSchema() if there are $ref. + CreateSchemaRecursive(&root_, PointerType(), document, document); + + // Resolve $ref + while (!schemaRef_.Empty()) { + SchemaRefEntry* refEntry = schemaRef_.template Pop(1); + if (const SchemaType* s = GetSchema(refEntry->target)) { + if (refEntry->schema) + *refEntry->schema = s; + + // Create entry in map if not exist + if (!GetSchema(refEntry->source)) { + new (schemaMap_.template Push()) SchemaEntry(refEntry->source, const_cast(s), false, allocator_); + } + } + else if (refEntry->schema) + *refEntry->schema = typeless_; + + refEntry->~SchemaRefEntry(); + } + + RAPIDJSON_ASSERT(root_ != 0); + + schemaRef_.ShrinkToFit(); // Deallocate all memory for ref + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericSchemaDocument(GenericSchemaDocument&& rhs) RAPIDJSON_NOEXCEPT : + remoteProvider_(rhs.remoteProvider_), + allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + root_(rhs.root_), + typeless_(rhs.typeless_), + schemaMap_(std::move(rhs.schemaMap_)), + schemaRef_(std::move(rhs.schemaRef_)), + uri_(std::move(rhs.uri_)) + { + rhs.remoteProvider_ = 0; + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.typeless_ = 0; + } +#endif + + //! Destructor + ~GenericSchemaDocument() { + while (!schemaMap_.Empty()) + schemaMap_.template Pop(1)->~SchemaEntry(); + + if (typeless_) { + typeless_->~SchemaType(); + Allocator::Free(typeless_); + } + + RAPIDJSON_DELETE(ownAllocator_); + } + + const URIType& GetURI() const { return uri_; } + + //! Get the root schema. + const SchemaType& GetRoot() const { return *root_; } + +private: + //! Prohibit copying + GenericSchemaDocument(const GenericSchemaDocument&); + //! Prohibit assignment + GenericSchemaDocument& operator=(const GenericSchemaDocument&); + + struct SchemaRefEntry { + SchemaRefEntry(const PointerType& s, const PointerType& t, const SchemaType** outSchema, Allocator *allocator) : source(s, allocator), target(t, allocator), schema(outSchema) {} + PointerType source; + PointerType target; + const SchemaType** schema; + }; + + struct SchemaEntry { + SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {} + ~SchemaEntry() { + if (owned) { + schema->~SchemaType(); + Allocator::Free(schema); + } + } + PointerType pointer; + SchemaType* schema; + bool owned; + }; + + void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) { + if (schema) + *schema = typeless_; + + if (v.GetType() == kObjectType) { + const SchemaType* s = GetSchema(pointer); + if (!s) + CreateSchema(schema, pointer, v, document); + + for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr) + CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document); + } + else if (v.GetType() == kArrayType) + for (SizeType i = 0; i < v.Size(); i++) + CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document); + } + + void CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) { + RAPIDJSON_ASSERT(pointer.IsValid()); + if (v.IsObject()) { + if (!HandleRefSchema(pointer, schema, v, document)) { + SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_); + new (schemaMap_.template Push()) SchemaEntry(pointer, s, true, allocator_); + if (schema) + *schema = s; + } + } + } + + bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document) { + static const Ch kRefString[] = { '$', 'r', 'e', 'f', '\0' }; + static const ValueType kRefValue(kRefString, 4); + + typename ValueType::ConstMemberIterator itr = v.FindMember(kRefValue); + if (itr == v.MemberEnd()) + return false; + + if (itr->value.IsString()) { + SizeType len = itr->value.GetStringLength(); + if (len > 0) { + const Ch* s = itr->value.GetString(); + SizeType i = 0; + while (i < len && s[i] != '#') // Find the first # + i++; + + if (i > 0) { // Remote reference, resolve immediately + if (remoteProvider_) { + if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i)) { + PointerType pointer(&s[i], len - i, allocator_); + if (pointer.IsValid()) { + if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) { + if (schema) + *schema = sc; + new (schemaMap_.template Push()) SchemaEntry(source, const_cast(sc), false, allocator_); + return true; + } + } + } + } + } + else if (s[i] == '#') { // Local reference, defer resolution + PointerType pointer(&s[i], len - i, allocator_); + if (pointer.IsValid()) { + if (const ValueType* nv = pointer.Get(document)) + if (HandleRefSchema(source, schema, *nv, document)) + return true; + + new (schemaRef_.template Push()) SchemaRefEntry(source, pointer, schema, allocator_); + return true; + } + } + } + } + return false; + } + + const SchemaType* GetSchema(const PointerType& pointer) const { + for (const SchemaEntry* target = schemaMap_.template Bottom(); target != schemaMap_.template End(); ++target) + if (pointer == target->pointer) + return target->schema; + return 0; + } + + PointerType GetPointer(const SchemaType* schema) const { + for (const SchemaEntry* target = schemaMap_.template Bottom(); target != schemaMap_.template End(); ++target) + if (schema == target->schema) + return target->pointer; + return PointerType(); + } + + const SchemaType* GetTypeless() const { return typeless_; } + + static const size_t kInitialSchemaMapSize = 64; + static const size_t kInitialSchemaRefSize = 64; + + IRemoteSchemaDocumentProviderType* remoteProvider_; + Allocator *allocator_; + Allocator *ownAllocator_; + const SchemaType* root_; //!< Root schema. + SchemaType* typeless_; + internal::Stack schemaMap_; // Stores created Pointer -> Schemas + internal::Stack schemaRef_; // Stores Pointer from $ref and schema which holds the $ref + URIType uri_; +}; + +//! GenericSchemaDocument using Value type. +typedef GenericSchemaDocument SchemaDocument; +//! IGenericRemoteSchemaDocumentProvider using SchemaDocument. +typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProvider; + +/////////////////////////////////////////////////////////////////////////////// +// GenericSchemaValidator + +//! JSON Schema Validator. +/*! + A SAX style JSON schema validator. + It uses a \c GenericSchemaDocument to validate SAX events. + It delegates the incoming SAX events to an output handler. + The default output handler does nothing. + It can be reused multiple times by calling \c Reset(). + + \tparam SchemaDocumentType Type of schema document. + \tparam OutputHandler Type of output handler. Default handler does nothing. + \tparam StateAllocator Allocator for storing the internal validation states. +*/ +template < + typename SchemaDocumentType, + typename OutputHandler = BaseReaderHandler, + typename StateAllocator = CrtAllocator> +class GenericSchemaValidator : + public internal::ISchemaStateFactory, + public internal::ISchemaValidator, + public internal::IValidationErrorHandler { +public: + typedef typename SchemaDocumentType::SchemaType SchemaType; + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename SchemaType::EncodingType EncodingType; + typedef typename SchemaType::SValue SValue; + typedef typename EncodingType::Ch Ch; + typedef GenericStringRef StringRefType; + typedef GenericValue ValueType; + + //! Constructor without output handler. + /*! + \param schemaDocument The schema document to conform to. + \param allocator Optional allocator for storing internal validation states. + \param schemaStackCapacity Optional initial capacity of schema path stack. + \param documentStackCapacity Optional initial capacity of document path stack. + */ + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(schemaDocument.GetRoot()), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + outputHandler_(0), + error_(kObjectType), + currentError_(), + missingDependents_(), + valid_(true), + flags_(kValidateDefaultFlags) +#if RAPIDJSON_SCHEMA_VERBOSE + , depth_(0) +#endif + { + } + + //! Constructor with output handler. + /*! + \param schemaDocument The schema document to conform to. + \param allocator Optional allocator for storing internal validation states. + \param schemaStackCapacity Optional initial capacity of schema path stack. + \param documentStackCapacity Optional initial capacity of document path stack. + */ + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + OutputHandler& outputHandler, + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(schemaDocument.GetRoot()), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + outputHandler_(&outputHandler), + error_(kObjectType), + currentError_(), + missingDependents_(), + valid_(true), + flags_(kValidateDefaultFlags) +#if RAPIDJSON_SCHEMA_VERBOSE + , depth_(0) +#endif + { + } + + //! Destructor. + ~GenericSchemaValidator() { + Reset(); + RAPIDJSON_DELETE(ownStateAllocator_); + } + + //! Reset the internal states. + void Reset() { + while (!schemaStack_.Empty()) + PopSchema(); + documentStack_.Clear(); + ResetError(); + } + + //! Reset the error state. + void ResetError() { + error_.SetObject(); + currentError_.SetNull(); + missingDependents_.SetNull(); + valid_ = true; + } + + //! Implementation of ISchemaValidator + void SetValidateFlags(unsigned flags) { + flags_ = flags; + } + virtual unsigned GetValidateFlags() const { + return flags_; + } + + //! Checks whether the current state is valid. + // Implementation of ISchemaValidator + virtual bool IsValid() const { + if (!valid_) return false; + if (GetContinueOnErrors() && !error_.ObjectEmpty()) return false; + return true; + } + + //! Gets the error object. + ValueType& GetError() { return error_; } + const ValueType& GetError() const { return error_; } + + //! Gets the JSON pointer pointed to the invalid schema. + // If reporting all errors, the stack will be empty. + PointerType GetInvalidSchemaPointer() const { + return schemaStack_.Empty() ? PointerType() : CurrentSchema().GetPointer(); + } + + //! Gets the keyword of invalid schema. + // If reporting all errors, the stack will be empty, so return "errors". + const Ch* GetInvalidSchemaKeyword() const { + if (!schemaStack_.Empty()) return CurrentContext().invalidKeyword; + if (GetContinueOnErrors() && !error_.ObjectEmpty()) return (const Ch*)GetErrorsString(); + return 0; + } + + //! Gets the error code of invalid schema. + // If reporting all errors, the stack will be empty, so return kValidateErrors. + ValidateErrorCode GetInvalidSchemaCode() const { + if (!schemaStack_.Empty()) return CurrentContext().invalidCode; + if (GetContinueOnErrors() && !error_.ObjectEmpty()) return kValidateErrors; + return kValidateErrorNone; + } + + //! Gets the JSON pointer pointed to the invalid value. + // If reporting all errors, the stack will be empty. + PointerType GetInvalidDocumentPointer() const { + if (documentStack_.Empty()) { + return PointerType(); + } + else { + return PointerType(documentStack_.template Bottom(), documentStack_.GetSize() / sizeof(Ch)); + } + } + + void NotMultipleOf(int64_t actual, const SValue& expected) { + AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected); + } + void NotMultipleOf(uint64_t actual, const SValue& expected) { + AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected); + } + void NotMultipleOf(double actual, const SValue& expected) { + AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected); + } + void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMaximumString : 0); + } + void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMaximumString : 0); + } + void AboveMaximum(double actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMaximumString : 0); + } + void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMinimumString : 0); + } + void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMinimumString : 0); + } + void BelowMinimum(double actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMinimumString : 0); + } + + void TooLong(const Ch* str, SizeType length, SizeType expected) { + AddNumberError(kValidateErrorMaxLength, + ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move()); + } + void TooShort(const Ch* str, SizeType length, SizeType expected) { + AddNumberError(kValidateErrorMinLength, + ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move()); + } + void DoesNotMatch(const Ch* str, SizeType length) { + currentError_.SetObject(); + currentError_.AddMember(GetActualString(), ValueType(str, length, GetStateAllocator()).Move(), GetStateAllocator()); + AddCurrentError(kValidateErrorPattern); + } + + void DisallowedItem(SizeType index) { + currentError_.SetObject(); + currentError_.AddMember(GetDisallowedString(), ValueType(index).Move(), GetStateAllocator()); + AddCurrentError(kValidateErrorAdditionalItems, true); + } + void TooFewItems(SizeType actualCount, SizeType expectedCount) { + AddNumberError(kValidateErrorMinItems, + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void TooManyItems(SizeType actualCount, SizeType expectedCount) { + AddNumberError(kValidateErrorMaxItems, + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void DuplicateItems(SizeType index1, SizeType index2) { + ValueType duplicates(kArrayType); + duplicates.PushBack(index1, GetStateAllocator()); + duplicates.PushBack(index2, GetStateAllocator()); + currentError_.SetObject(); + currentError_.AddMember(GetDuplicatesString(), duplicates, GetStateAllocator()); + AddCurrentError(kValidateErrorUniqueItems, true); + } + + void TooManyProperties(SizeType actualCount, SizeType expectedCount) { + AddNumberError(kValidateErrorMaxProperties, + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void TooFewProperties(SizeType actualCount, SizeType expectedCount) { + AddNumberError(kValidateErrorMinProperties, + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void StartMissingProperties() { + currentError_.SetArray(); + } + void AddMissingProperty(const SValue& name) { + currentError_.PushBack(ValueType(name, GetStateAllocator()).Move(), GetStateAllocator()); + } + bool EndMissingProperties() { + if (currentError_.Empty()) + return false; + ValueType error(kObjectType); + error.AddMember(GetMissingString(), currentError_, GetStateAllocator()); + currentError_ = error; + AddCurrentError(kValidateErrorRequired); + return true; + } + void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) { + for (SizeType i = 0; i < count; ++i) + MergeError(static_cast(subvalidators[i])->GetError()); + } + void DisallowedProperty(const Ch* name, SizeType length) { + currentError_.SetObject(); + currentError_.AddMember(GetDisallowedString(), ValueType(name, length, GetStateAllocator()).Move(), GetStateAllocator()); + AddCurrentError(kValidateErrorAdditionalProperties, true); + } + + void StartDependencyErrors() { + currentError_.SetObject(); + } + void StartMissingDependentProperties() { + missingDependents_.SetArray(); + } + void AddMissingDependentProperty(const SValue& targetName) { + missingDependents_.PushBack(ValueType(targetName, GetStateAllocator()).Move(), GetStateAllocator()); + } + void EndMissingDependentProperties(const SValue& sourceName) { + if (!missingDependents_.Empty()) { + // Create equivalent 'required' error + ValueType error(kObjectType); + ValidateErrorCode code = kValidateErrorRequired; + error.AddMember(GetMissingString(), missingDependents_.Move(), GetStateAllocator()); + AddErrorCode(error, code); + AddErrorInstanceLocation(error, false); + // When appending to a pointer ensure its allocator is used + PointerType schemaRef = GetInvalidSchemaPointer().Append(SchemaType::GetValidateErrorKeyword(kValidateErrorDependencies), &GetInvalidSchemaPointer().GetAllocator()); + AddErrorSchemaLocation(error, schemaRef.Append(sourceName.GetString(), sourceName.GetStringLength(), &GetInvalidSchemaPointer().GetAllocator())); + ValueType wrapper(kObjectType); + wrapper.AddMember(ValueType(SchemaType::GetValidateErrorKeyword(code), GetStateAllocator()).Move(), error, GetStateAllocator()); + currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(), wrapper, GetStateAllocator()); + } + } + void AddDependencySchemaError(const SValue& sourceName, ISchemaValidator* subvalidator) { + currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(), + static_cast(subvalidator)->GetError(), GetStateAllocator()); + } + bool EndDependencyErrors() { + if (currentError_.ObjectEmpty()) + return false; + ValueType error(kObjectType); + error.AddMember(GetErrorsString(), currentError_, GetStateAllocator()); + currentError_ = error; + AddCurrentError(kValidateErrorDependencies); + return true; + } + + void DisallowedValue(const ValidateErrorCode code = kValidateErrorEnum) { + currentError_.SetObject(); + AddCurrentError(code); + } + void StartDisallowedType() { + currentError_.SetArray(); + } + void AddExpectedType(const typename SchemaType::ValueType& expectedType) { + currentError_.PushBack(ValueType(expectedType, GetStateAllocator()).Move(), GetStateAllocator()); + } + void EndDisallowedType(const typename SchemaType::ValueType& actualType) { + ValueType error(kObjectType); + error.AddMember(GetExpectedString(), currentError_, GetStateAllocator()); + error.AddMember(GetActualString(), ValueType(actualType, GetStateAllocator()).Move(), GetStateAllocator()); + currentError_ = error; + AddCurrentError(kValidateErrorType); + } + void NotAllOf(ISchemaValidator** subvalidators, SizeType count) { + // Treat allOf like oneOf and anyOf to match https://rapidjson.org/md_doc_schema.html#allOf-anyOf-oneOf + AddErrorArray(kValidateErrorAllOf, subvalidators, count); + //for (SizeType i = 0; i < count; ++i) { + // MergeError(static_cast(subvalidators[i])->GetError()); + //} + } + void NoneOf(ISchemaValidator** subvalidators, SizeType count) { + AddErrorArray(kValidateErrorAnyOf, subvalidators, count); + } + void NotOneOf(ISchemaValidator** subvalidators, SizeType count, bool matched = false) { + AddErrorArray(matched ? kValidateErrorOneOfMatch : kValidateErrorOneOf, subvalidators, count); + } + void Disallowed() { + currentError_.SetObject(); + AddCurrentError(kValidateErrorNot); + } + +#define RAPIDJSON_STRING_(name, ...) \ + static const StringRefType& Get##name##String() {\ + static const Ch s[] = { __VA_ARGS__, '\0' };\ + static const StringRefType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1)); \ + return v;\ + } + + RAPIDJSON_STRING_(InstanceRef, 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 'R', 'e', 'f') + RAPIDJSON_STRING_(SchemaRef, 's', 'c', 'h', 'e', 'm', 'a', 'R', 'e', 'f') + RAPIDJSON_STRING_(Expected, 'e', 'x', 'p', 'e', 'c', 't', 'e', 'd') + RAPIDJSON_STRING_(Actual, 'a', 'c', 't', 'u', 'a', 'l') + RAPIDJSON_STRING_(Disallowed, 'd', 'i', 's', 'a', 'l', 'l', 'o', 'w', 'e', 'd') + RAPIDJSON_STRING_(Missing, 'm', 'i', 's', 's', 'i', 'n', 'g') + RAPIDJSON_STRING_(Errors, 'e', 'r', 'r', 'o', 'r', 's') + RAPIDJSON_STRING_(ErrorCode, 'e', 'r', 'r', 'o', 'r', 'C', 'o', 'd', 'e') + RAPIDJSON_STRING_(ErrorMessage, 'e', 'r', 'r', 'o', 'r', 'M', 'e', 's', 's', 'a', 'g', 'e') + RAPIDJSON_STRING_(Duplicates, 'd', 'u', 'p', 'l', 'i', 'c', 'a', 't', 'e', 's') + +#undef RAPIDJSON_STRING_ + +#if RAPIDJSON_SCHEMA_VERBOSE +#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \ +RAPIDJSON_MULTILINEMACRO_BEGIN\ + *documentStack_.template Push() = '\0';\ + documentStack_.template Pop(1);\ + internal::PrintInvalidDocument(documentStack_.template Bottom());\ +RAPIDJSON_MULTILINEMACRO_END +#else +#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() +#endif + +#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\ + if (!valid_) return false; \ + if ((!BeginValue() && !GetContinueOnErrors()) || (!CurrentSchema().method arg1 && !GetContinueOnErrors())) {\ + RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_();\ + return valid_ = false;\ + } + +#define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\ + for (Context* context = schemaStack_.template Bottom(); context != schemaStack_.template End(); context++) {\ + if (context->hasher)\ + static_cast(context->hasher)->method arg2;\ + if (context->validators)\ + for (SizeType i_ = 0; i_ < context->validatorCount; i_++)\ + static_cast(context->validators[i_])->method arg2;\ + if (context->patternPropertiesValidators)\ + for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\ + static_cast(context->patternPropertiesValidators[i_])->method arg2;\ + } + +#define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\ + valid_ = (EndValue() || GetContinueOnErrors()) && (!outputHandler_ || outputHandler_->method arg2);\ + return valid_; + +#define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \ + RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\ + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\ + RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2) + + bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext()), ( )); } + bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); } + bool Int(int i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), i), (i)); } + bool Uint(unsigned u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), u), (u)); } + bool Int64(int64_t i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64, (CurrentContext(), i), (i)); } + bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); } + bool Double(double d) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); } + bool RawNumber(const Ch* str, SizeType length, bool copy) + { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } + bool String(const Ch* str, SizeType length, bool copy) + { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } + + bool StartObject() { + RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext())); + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ()); + return valid_ = !outputHandler_ || outputHandler_->StartObject(); + } + + bool Key(const Ch* str, SizeType len, bool copy) { + if (!valid_) return false; + AppendToken(str, len); + if (!CurrentSchema().Key(CurrentContext(), str, len, copy) && !GetContinueOnErrors()) return valid_ = false; + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy)); + return valid_ = !outputHandler_ || outputHandler_->Key(str, len, copy); + } + + bool EndObject(SizeType memberCount) { + if (!valid_) return false; + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount)); + if (!CurrentSchema().EndObject(CurrentContext(), memberCount) && !GetContinueOnErrors()) return valid_ = false; + RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount)); + } + + bool StartArray() { + RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext())); + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ()); + return valid_ = !outputHandler_ || outputHandler_->StartArray(); + } + + bool EndArray(SizeType elementCount) { + if (!valid_) return false; + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount)); + if (!CurrentSchema().EndArray(CurrentContext(), elementCount) && !GetContinueOnErrors()) return valid_ = false; + RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount)); + } + +#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_ +#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_ +#undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_ +#undef RAPIDJSON_SCHEMA_HANDLE_VALUE_ + + // Implementation of ISchemaStateFactory + virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root, const bool inheritContinueOnErrors) { + ISchemaValidator* sv = new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, documentStack_.template Bottom(), documentStack_.GetSize(), +#if RAPIDJSON_SCHEMA_VERBOSE + depth_ + 1, +#endif + &GetStateAllocator()); + sv->SetValidateFlags(inheritContinueOnErrors ? GetValidateFlags() : GetValidateFlags() & ~(unsigned)kValidateContinueOnErrorFlag); + return sv; + } + + virtual void DestroySchemaValidator(ISchemaValidator* validator) { + GenericSchemaValidator* v = static_cast(validator); + v->~GenericSchemaValidator(); + StateAllocator::Free(v); + } + + virtual void* CreateHasher() { + return new (GetStateAllocator().Malloc(sizeof(HasherType))) HasherType(&GetStateAllocator()); + } + + virtual uint64_t GetHashCode(void* hasher) { + return static_cast(hasher)->GetHashCode(); + } + + virtual void DestroryHasher(void* hasher) { + HasherType* h = static_cast(hasher); + h->~HasherType(); + StateAllocator::Free(h); + } + + virtual void* MallocState(size_t size) { + return GetStateAllocator().Malloc(size); + } + + virtual void FreeState(void* p) { + StateAllocator::Free(p); + } + +private: + typedef typename SchemaType::Context Context; + typedef GenericValue, StateAllocator> HashCodeArray; + typedef internal::Hasher HasherType; + + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + const SchemaType& root, + const char* basePath, size_t basePathSize, +#if RAPIDJSON_SCHEMA_VERBOSE + unsigned depth, +#endif + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(root), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + outputHandler_(0), + error_(kObjectType), + currentError_(), + missingDependents_(), + valid_(true), + flags_(kValidateDefaultFlags) +#if RAPIDJSON_SCHEMA_VERBOSE + , depth_(depth) +#endif + { + if (basePath && basePathSize) + memcpy(documentStack_.template Push(basePathSize), basePath, basePathSize); + } + + StateAllocator& GetStateAllocator() { + if (!stateAllocator_) + stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator)(); + return *stateAllocator_; + } + + bool GetContinueOnErrors() const { + return flags_ & kValidateContinueOnErrorFlag; + } + + bool BeginValue() { + if (schemaStack_.Empty()) + PushSchema(root_); + else { + if (CurrentContext().inArray) + internal::TokenHelper, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex); + + if (!CurrentSchema().BeginValue(CurrentContext()) && !GetContinueOnErrors()) + return false; + + SizeType count = CurrentContext().patternPropertiesSchemaCount; + const SchemaType** sa = CurrentContext().patternPropertiesSchemas; + typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType; + bool valueUniqueness = CurrentContext().valueUniqueness; + RAPIDJSON_ASSERT(CurrentContext().valueSchema); + PushSchema(*CurrentContext().valueSchema); + + if (count > 0) { + CurrentContext().objectPatternValidatorType = patternValidatorType; + ISchemaValidator**& va = CurrentContext().patternPropertiesValidators; + SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount; + va = static_cast(MallocState(sizeof(ISchemaValidator*) * count)); + for (SizeType i = 0; i < count; i++) + va[validatorCount++] = CreateSchemaValidator(*sa[i], true); // Inherit continueOnError + } + + CurrentContext().arrayUniqueness = valueUniqueness; + } + return true; + } + + bool EndValue() { + if (!CurrentSchema().EndValue(CurrentContext()) && !GetContinueOnErrors()) + return false; + +#if RAPIDJSON_SCHEMA_VERBOSE + GenericStringBuffer sb; + schemaDocument_->GetPointer(&CurrentSchema()).Stringify(sb); + + *documentStack_.template Push() = '\0'; + documentStack_.template Pop(1); + internal::PrintValidatorPointers(depth_, sb.GetString(), documentStack_.template Bottom()); +#endif + void* hasher = CurrentContext().hasher; + uint64_t h = hasher && CurrentContext().arrayUniqueness ? static_cast(hasher)->GetHashCode() : 0; + + PopSchema(); + + if (!schemaStack_.Empty()) { + Context& context = CurrentContext(); + // Only check uniqueness if there is a hasher + if (hasher && context.valueUniqueness) { + HashCodeArray* a = static_cast(context.arrayElementHashCodes); + if (!a) + CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType); + for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr) + if (itr->GetUint64() == h) { + DuplicateItems(static_cast(itr - a->Begin()), a->Size()); + // Cleanup before returning if continuing + if (GetContinueOnErrors()) { + a->PushBack(h, GetStateAllocator()); + while (!documentStack_.Empty() && *documentStack_.template Pop(1) != '/'); + } + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorUniqueItems); + } + a->PushBack(h, GetStateAllocator()); + } + } + + // Remove the last token of document pointer + while (!documentStack_.Empty() && *documentStack_.template Pop(1) != '/') + ; + + return true; + } + + void AppendToken(const Ch* str, SizeType len) { + documentStack_.template Reserve(1 + len * 2); // worst case all characters are escaped as two characters + *documentStack_.template PushUnsafe() = '/'; + for (SizeType i = 0; i < len; i++) { + if (str[i] == '~') { + *documentStack_.template PushUnsafe() = '~'; + *documentStack_.template PushUnsafe() = '0'; + } + else if (str[i] == '/') { + *documentStack_.template PushUnsafe() = '~'; + *documentStack_.template PushUnsafe() = '1'; + } + else + *documentStack_.template PushUnsafe() = str[i]; + } + } + + RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push()) Context(*this, *this, &schema); } + + RAPIDJSON_FORCEINLINE void PopSchema() { + Context* c = schemaStack_.template Pop(1); + if (HashCodeArray* a = static_cast(c->arrayElementHashCodes)) { + a->~HashCodeArray(); + StateAllocator::Free(a); + } + c->~Context(); + } + + void AddErrorInstanceLocation(ValueType& result, bool parent) { + GenericStringBuffer sb; + PointerType instancePointer = GetInvalidDocumentPointer(); + ((parent && instancePointer.GetTokenCount() > 0) + ? PointerType(instancePointer.GetTokens(), instancePointer.GetTokenCount() - 1) + : instancePointer).StringifyUriFragment(sb); + ValueType instanceRef(sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch)), + GetStateAllocator()); + result.AddMember(GetInstanceRefString(), instanceRef, GetStateAllocator()); + } + + void AddErrorSchemaLocation(ValueType& result, PointerType schema = PointerType()) { + GenericStringBuffer sb; + SizeType len = CurrentSchema().GetURI().GetStringLength(); + if (len) memcpy(sb.Push(len), CurrentSchema().GetURI().GetString(), len * sizeof(Ch)); + if (schema.GetTokenCount()) schema.StringifyUriFragment(sb); + else GetInvalidSchemaPointer().StringifyUriFragment(sb); + ValueType schemaRef(sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch)), + GetStateAllocator()); + result.AddMember(GetSchemaRefString(), schemaRef, GetStateAllocator()); + } + + void AddErrorCode(ValueType& result, const ValidateErrorCode code) { + result.AddMember(GetErrorCodeString(), code, GetStateAllocator()); + } + + void AddError(ValueType& keyword, ValueType& error) { + typename ValueType::MemberIterator member = error_.FindMember(keyword); + if (member == error_.MemberEnd()) + error_.AddMember(keyword, error, GetStateAllocator()); + else { + if (member->value.IsObject()) { + ValueType errors(kArrayType); + errors.PushBack(member->value, GetStateAllocator()); + member->value = errors; + } + member->value.PushBack(error, GetStateAllocator()); + } + } + + void AddCurrentError(const ValidateErrorCode code, bool parent = false) { + AddErrorCode(currentError_, code); + AddErrorInstanceLocation(currentError_, parent); + AddErrorSchemaLocation(currentError_); + AddError(ValueType(SchemaType::GetValidateErrorKeyword(code), GetStateAllocator(), false).Move(), currentError_); + } + + void MergeError(ValueType& other) { + for (typename ValueType::MemberIterator it = other.MemberBegin(), end = other.MemberEnd(); it != end; ++it) { + AddError(it->name, it->value); + } + } + + void AddNumberError(const ValidateErrorCode code, ValueType& actual, const SValue& expected, + const typename SchemaType::ValueType& (*exclusive)() = 0) { + currentError_.SetObject(); + currentError_.AddMember(GetActualString(), actual, GetStateAllocator()); + currentError_.AddMember(GetExpectedString(), ValueType(expected, GetStateAllocator()).Move(), GetStateAllocator()); + if (exclusive) + currentError_.AddMember(ValueType(exclusive(), GetStateAllocator()).Move(), true, GetStateAllocator()); + AddCurrentError(code); + } + + void AddErrorArray(const ValidateErrorCode code, + ISchemaValidator** subvalidators, SizeType count) { + ValueType errors(kArrayType); + for (SizeType i = 0; i < count; ++i) + errors.PushBack(static_cast(subvalidators[i])->GetError(), GetStateAllocator()); + currentError_.SetObject(); + currentError_.AddMember(GetErrorsString(), errors, GetStateAllocator()); + AddCurrentError(code); + } + + const SchemaType& CurrentSchema() const { return *schemaStack_.template Top()->schema; } + Context& CurrentContext() { return *schemaStack_.template Top(); } + const Context& CurrentContext() const { return *schemaStack_.template Top(); } + + static const size_t kDefaultSchemaStackCapacity = 1024; + static const size_t kDefaultDocumentStackCapacity = 256; + const SchemaDocumentType* schemaDocument_; + const SchemaType& root_; + StateAllocator* stateAllocator_; + StateAllocator* ownStateAllocator_; + internal::Stack schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *) + internal::Stack documentStack_; //!< stack to store the current path of validating document (Ch) + OutputHandler* outputHandler_; + ValueType error_; + ValueType currentError_; + ValueType missingDependents_; + bool valid_; + unsigned flags_; +#if RAPIDJSON_SCHEMA_VERBOSE + unsigned depth_; +#endif +}; + +typedef GenericSchemaValidator SchemaValidator; + +/////////////////////////////////////////////////////////////////////////////// +// SchemaValidatingReader + +//! A helper class for parsing with validation. +/*! + This helper class is a functor, designed as a parameter of \ref GenericDocument::Populate(). + + \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept. + \tparam SourceEncoding Encoding of the input stream. + \tparam SchemaDocumentType Type of schema document. + \tparam StackAllocator Allocator type for stack. +*/ +template < + unsigned parseFlags, + typename InputStream, + typename SourceEncoding, + typename SchemaDocumentType = SchemaDocument, + typename StackAllocator = CrtAllocator> +class SchemaValidatingReader { +public: + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename InputStream::Ch Ch; + typedef GenericValue ValueType; + + //! Constructor + /*! + \param is Input stream. + \param sd Schema document. + */ + SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), invalidSchemaCode_(kValidateErrorNone), error_(kObjectType), isValid_(true) {} + + template + bool operator()(Handler& handler) { + GenericReader reader; + GenericSchemaValidator validator(sd_, handler); + parseResult_ = reader.template Parse(is_, validator); + + isValid_ = validator.IsValid(); + if (isValid_) { + invalidSchemaPointer_ = PointerType(); + invalidSchemaKeyword_ = 0; + invalidDocumentPointer_ = PointerType(); + error_.SetObject(); + } + else { + invalidSchemaPointer_ = validator.GetInvalidSchemaPointer(); + invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword(); + invalidSchemaCode_ = validator.GetInvalidSchemaCode(); + invalidDocumentPointer_ = validator.GetInvalidDocumentPointer(); + error_.CopyFrom(validator.GetError(), allocator_); + } + + return parseResult_; + } + + const ParseResult& GetParseResult() const { return parseResult_; } + bool IsValid() const { return isValid_; } + const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; } + const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; } + const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; } + const ValueType& GetError() const { return error_; } + ValidateErrorCode GetInvalidSchemaCode() const { return invalidSchemaCode_; } + +private: + InputStream& is_; + const SchemaDocumentType& sd_; + + ParseResult parseResult_; + PointerType invalidSchemaPointer_; + const Ch* invalidSchemaKeyword_; + PointerType invalidDocumentPointer_; + ValidateErrorCode invalidSchemaCode_; + StackAllocator allocator_; + ValueType error_; + bool isValid_; +}; + +RAPIDJSON_NAMESPACE_END +RAPIDJSON_DIAG_POP + +#endif // RAPIDJSON_SCHEMA_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/stream.h b/libraries/libstratosphere/include/stratosphere/rapidjson/stream.h new file mode 100644 index 000000000..1fd70915c --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/stream.h @@ -0,0 +1,223 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#include "rapidjson.h" + +#ifndef RAPIDJSON_STREAM_H_ +#define RAPIDJSON_STREAM_H_ + +#include "encodings.h" + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Stream + +/*! \class rapidjson::Stream + \brief Concept for reading and writing characters. + + For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd(). + + For write-only stream, only need to implement Put() and Flush(). + +\code +concept Stream { + typename Ch; //!< Character type of the stream. + + //! Read the current character from stream without moving the read cursor. + Ch Peek() const; + + //! Read the current character from stream and moving the read cursor to next character. + Ch Take(); + + //! Get the current read cursor. + //! \return Number of characters read from start. + size_t Tell(); + + //! Begin writing operation at the current read pointer. + //! \return The begin writer pointer. + Ch* PutBegin(); + + //! Write a character. + void Put(Ch c); + + //! Flush the buffer. + void Flush(); + + //! End the writing operation. + //! \param begin The begin write pointer returned by PutBegin(). + //! \return Number of characters written. + size_t PutEnd(Ch* begin); +} +\endcode +*/ + +//! Provides additional information for stream. +/*! + By using traits pattern, this type provides a default configuration for stream. + For custom stream, this type can be specialized for other configuration. + See TEST(Reader, CustomStringStream) in readertest.cpp for example. +*/ +template +struct StreamTraits { + //! Whether to make local copy of stream for optimization during parsing. + /*! + By default, for safety, streams do not use local copy optimization. + Stream that can be copied fast should specialize this, like StreamTraits. + */ + enum { copyOptimization = 0 }; +}; + +//! Reserve n characters for writing to a stream. +template +inline void PutReserve(Stream& stream, size_t count) { + (void)stream; + (void)count; +} + +//! Write character to a stream, presuming buffer is reserved. +template +inline void PutUnsafe(Stream& stream, typename Stream::Ch c) { + stream.Put(c); +} + +//! Put N copies of a character to a stream. +template +inline void PutN(Stream& stream, Ch c, size_t n) { + PutReserve(stream, n); + for (size_t i = 0; i < n; i++) + PutUnsafe(stream, c); +} + +/////////////////////////////////////////////////////////////////////////////// +// GenericStreamWrapper + +//! A Stream Wrapper +/*! \tThis string stream is a wrapper for any stream by just forwarding any + \treceived message to the origin stream. + \note implements Stream concept +*/ + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4702) // unreachable code +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +template > +class GenericStreamWrapper { +public: + typedef typename Encoding::Ch Ch; + GenericStreamWrapper(InputStream& is): is_(is) {} + + Ch Peek() const { return is_.Peek(); } + Ch Take() { return is_.Take(); } + size_t Tell() { return is_.Tell(); } + Ch* PutBegin() { return is_.PutBegin(); } + void Put(Ch ch) { is_.Put(ch); } + void Flush() { is_.Flush(); } + size_t PutEnd(Ch* ch) { return is_.PutEnd(ch); } + + // wrapper for MemoryStream + const Ch* Peek4() const { return is_.Peek4(); } + + // wrapper for AutoUTFInputStream + UTFType GetType() const { return is_.GetType(); } + bool HasBOM() const { return is_.HasBOM(); } + +protected: + InputStream& is_; +}; + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_POP +#endif + +/////////////////////////////////////////////////////////////////////////////// +// StringStream + +//! Read-only string stream. +/*! \note implements Stream concept +*/ +template +struct GenericStringStream { + typedef typename Encoding::Ch Ch; + + GenericStringStream(const Ch *src) : src_(src), head_(src) {} + + Ch Peek() const { return *src_; } + Ch Take() { return *src_++; } + size_t Tell() const { return static_cast(src_ - head_); } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + const Ch* src_; //!< Current read position. + const Ch* head_; //!< Original head of the string. +}; + +template +struct StreamTraits > { + enum { copyOptimization = 1 }; +}; + +//! String stream with UTF8 encoding. +typedef GenericStringStream > StringStream; + +/////////////////////////////////////////////////////////////////////////////// +// InsituStringStream + +//! A read-write string stream. +/*! This string stream is particularly designed for in-situ parsing. + \note implements Stream concept +*/ +template +struct GenericInsituStringStream { + typedef typename Encoding::Ch Ch; + + GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {} + + // Read + Ch Peek() { return *src_; } + Ch Take() { return *src_++; } + size_t Tell() { return static_cast(src_ - head_); } + + // Write + void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; } + + Ch* PutBegin() { return dst_ = src_; } + size_t PutEnd(Ch* begin) { return static_cast(dst_ - begin); } + void Flush() {} + + Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; } + void Pop(size_t count) { dst_ -= count; } + + Ch* src_; + Ch* dst_; + Ch* head_; +}; + +template +struct StreamTraits > { + enum { copyOptimization = 1 }; +}; + +//! Insitu string stream with UTF8 encoding. +typedef GenericInsituStringStream > InsituStringStream; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_STREAM_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/stringbuffer.h b/libraries/libstratosphere/include/stratosphere/rapidjson/stringbuffer.h new file mode 100644 index 000000000..82ad3ca6b --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/stringbuffer.h @@ -0,0 +1,121 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_STRINGBUFFER_H_ +#define RAPIDJSON_STRINGBUFFER_H_ + +#include "stream.h" +#include "internal/stack.h" + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS +#include // std::move +#endif + +#include "internal/stack.h" + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory output stream. +/*! + \tparam Encoding Encoding of the stream. + \tparam Allocator type for allocating memory buffer. + \note implements Stream concept +*/ +template +class GenericStringBuffer { +public: + typedef typename Encoding::Ch Ch; + + GenericStringBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericStringBuffer(GenericStringBuffer&& rhs) : stack_(std::move(rhs.stack_)) {} + GenericStringBuffer& operator=(GenericStringBuffer&& rhs) { + if (&rhs != this) + stack_ = std::move(rhs.stack_); + return *this; + } +#endif + + void Put(Ch c) { *stack_.template Push() = c; } + void PutUnsafe(Ch c) { *stack_.template PushUnsafe() = c; } + void Flush() {} + + void Clear() { stack_.Clear(); } + void ShrinkToFit() { + // Push and pop a null terminator. This is safe. + *stack_.template Push() = '\0'; + stack_.ShrinkToFit(); + stack_.template Pop(1); + } + + void Reserve(size_t count) { stack_.template Reserve(count); } + Ch* Push(size_t count) { return stack_.template Push(count); } + Ch* PushUnsafe(size_t count) { return stack_.template PushUnsafe(count); } + void Pop(size_t count) { stack_.template Pop(count); } + + const Ch* GetString() const { + // Push and pop a null terminator. This is safe. + *stack_.template Push() = '\0'; + stack_.template Pop(1); + + return stack_.template Bottom(); + } + + //! Get the size of string in bytes in the string buffer. + size_t GetSize() const { return stack_.GetSize(); } + + //! Get the length of string in Ch in the string buffer. + size_t GetLength() const { return stack_.GetSize() / sizeof(Ch); } + + static const size_t kDefaultCapacity = 256; + mutable internal::Stack stack_; + +private: + // Prohibit copy constructor & assignment operator. + GenericStringBuffer(const GenericStringBuffer&); + GenericStringBuffer& operator=(const GenericStringBuffer&); +}; + +//! String buffer with UTF8 encoding +typedef GenericStringBuffer > StringBuffer; + +template +inline void PutReserve(GenericStringBuffer& stream, size_t count) { + stream.Reserve(count); +} + +template +inline void PutUnsafe(GenericStringBuffer& stream, typename Encoding::Ch c) { + stream.PutUnsafe(c); +} + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(GenericStringBuffer >& stream, char c, size_t n) { + std::memset(stream.stack_.Push(n), c, n * sizeof(c)); +} + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_STRINGBUFFER_H_ diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson/writer.h b/libraries/libstratosphere/include/stratosphere/rapidjson/writer.h new file mode 100644 index 000000000..8b389219a --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/rapidjson/writer.h @@ -0,0 +1,710 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_WRITER_H_ +#define RAPIDJSON_WRITER_H_ + +#include "stream.h" +#include "internal/clzll.h" +#include "internal/meta.h" +#include "internal/stack.h" +#include "internal/strfunc.h" +#include "internal/dtoa.h" +#include "internal/itoa.h" +#include "stringbuffer.h" +#include // placement new + +#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) +#include +#pragma intrinsic(_BitScanForward) +#endif +#ifdef RAPIDJSON_SSE42 +#include +#elif defined(RAPIDJSON_SSE2) +#include +#elif defined(RAPIDJSON_NEON) +#include +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(unreachable-code) +RAPIDJSON_DIAG_OFF(c++98-compat) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// WriteFlag + +/*! \def RAPIDJSON_WRITE_DEFAULT_FLAGS + \ingroup RAPIDJSON_CONFIG + \brief User-defined kWriteDefaultFlags definition. + + User can define this as any \c WriteFlag combinations. +*/ +#ifndef RAPIDJSON_WRITE_DEFAULT_FLAGS +#define RAPIDJSON_WRITE_DEFAULT_FLAGS kWriteNoFlags +#endif + +//! Combination of writeFlags +enum WriteFlag { + kWriteNoFlags = 0, //!< No flags are set. + kWriteValidateEncodingFlag = 1, //!< Validate encoding of JSON strings. + kWriteNanAndInfFlag = 2, //!< Allow writing of Infinity, -Infinity and NaN. + kWriteDefaultFlags = RAPIDJSON_WRITE_DEFAULT_FLAGS //!< Default write flags. Can be customized by defining RAPIDJSON_WRITE_DEFAULT_FLAGS +}; + +//! JSON writer +/*! Writer implements the concept Handler. + It generates JSON text by events to an output os. + + User may programmatically calls the functions of a writer to generate JSON text. + + On the other side, a writer can also be passed to objects that generates events, + + for example Reader::Parse() and Document::Accept(). + + \tparam OutputStream Type of output stream. + \tparam SourceEncoding Encoding of source string. + \tparam TargetEncoding Encoding of output stream. + \tparam StackAllocator Type of allocator for allocating memory of stack. + \note implements Handler concept +*/ +template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> +class Writer { +public: + typedef typename SourceEncoding::Ch Ch; + + static const int kDefaultMaxDecimalPlaces = 324; + + //! Constructor + /*! \param os Output stream. + \param stackAllocator User supplied allocator. If it is null, it will create a private one. + \param levelDepth Initial capacity of stack. + */ + explicit + Writer(OutputStream& os, StackAllocator* stackAllocator = 0, size_t levelDepth = kDefaultLevelDepth) : + os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} + + explicit + Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) : + os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + Writer(Writer&& rhs) : + os_(rhs.os_), level_stack_(std::move(rhs.level_stack_)), maxDecimalPlaces_(rhs.maxDecimalPlaces_), hasRoot_(rhs.hasRoot_) { + rhs.os_ = 0; + } +#endif + + //! Reset the writer with a new stream. + /*! + This function reset the writer with a new stream and default settings, + in order to make a Writer object reusable for output multiple JSONs. + + \param os New output stream. + \code + Writer writer(os1); + writer.StartObject(); + // ... + writer.EndObject(); + + writer.Reset(os2); + writer.StartObject(); + // ... + writer.EndObject(); + \endcode + */ + void Reset(OutputStream& os) { + os_ = &os; + hasRoot_ = false; + level_stack_.Clear(); + } + + //! Checks whether the output is a complete JSON. + /*! + A complete JSON has a complete root object or array. + */ + bool IsComplete() const { + return hasRoot_ && level_stack_.Empty(); + } + + int GetMaxDecimalPlaces() const { + return maxDecimalPlaces_; + } + + //! Sets the maximum number of decimal places for double output. + /*! + This setting truncates the output with specified number of decimal places. + + For example, + + \code + writer.SetMaxDecimalPlaces(3); + writer.StartArray(); + writer.Double(0.12345); // "0.123" + writer.Double(0.0001); // "0.0" + writer.Double(1.234567890123456e30); // "1.234567890123456e30" (do not truncate significand for positive exponent) + writer.Double(1.23e-4); // "0.0" (do truncate significand for negative exponent) + writer.EndArray(); + \endcode + + The default setting does not truncate any decimal places. You can restore to this setting by calling + \code + writer.SetMaxDecimalPlaces(Writer::kDefaultMaxDecimalPlaces); + \endcode + */ + void SetMaxDecimalPlaces(int maxDecimalPlaces) { + maxDecimalPlaces_ = maxDecimalPlaces; + } + + /*!@name Implementation of Handler + \see Handler + */ + //@{ + + bool Null() { Prefix(kNullType); return EndValue(WriteNull()); } + bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return EndValue(WriteBool(b)); } + bool Int(int i) { Prefix(kNumberType); return EndValue(WriteInt(i)); } + bool Uint(unsigned u) { Prefix(kNumberType); return EndValue(WriteUint(u)); } + bool Int64(int64_t i64) { Prefix(kNumberType); return EndValue(WriteInt64(i64)); } + bool Uint64(uint64_t u64) { Prefix(kNumberType); return EndValue(WriteUint64(u64)); } + + //! Writes the given \c double value to the stream + /*! + \param d The value to be written. + \return Whether it is succeed. + */ + bool Double(double d) { Prefix(kNumberType); return EndValue(WriteDouble(d)); } + + bool RawNumber(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + Prefix(kNumberType); + return EndValue(WriteString(str, length)); + } + + bool String(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + Prefix(kStringType); + return EndValue(WriteString(str, length)); + } + +#if RAPIDJSON_HAS_STDSTRING + bool String(const std::basic_string& str) { + return String(str.data(), SizeType(str.size())); + } +#endif + + bool StartObject() { + Prefix(kObjectType); + new (level_stack_.template Push()) Level(false); + return WriteStartObject(); + } + + bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + +#if RAPIDJSON_HAS_STDSTRING + bool Key(const std::basic_string& str) + { + return Key(str.data(), SizeType(str.size())); + } +#endif + + bool EndObject(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); // not inside an Object + RAPIDJSON_ASSERT(!level_stack_.template Top()->inArray); // currently inside an Array, not Object + RAPIDJSON_ASSERT(0 == level_stack_.template Top()->valueCount % 2); // Object has a Key without a Value + level_stack_.template Pop(1); + return EndValue(WriteEndObject()); + } + + bool StartArray() { + Prefix(kArrayType); + new (level_stack_.template Push()) Level(true); + return WriteStartArray(); + } + + bool EndArray(SizeType elementCount = 0) { + (void)elementCount; + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); + RAPIDJSON_ASSERT(level_stack_.template Top()->inArray); + level_stack_.template Pop(1); + return EndValue(WriteEndArray()); + } + //@} + + /*! @name Convenience extensions */ + //@{ + + //! Simpler but slower overload. + bool String(const Ch* const& str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* const& str) { return Key(str, internal::StrLen(str)); } + + //@} + + //! Write a raw JSON value. + /*! + For user to write a stringified JSON as a value. + + \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range. + \param length Length of the json. + \param type Type of the root of json. + */ + bool RawValue(const Ch* json, size_t length, Type type) { + RAPIDJSON_ASSERT(json != 0); + Prefix(type); + return EndValue(WriteRawValue(json, length)); + } + + //! Flush the output stream. + /*! + Allows the user to flush the output stream immediately. + */ + void Flush() { + os_->Flush(); + } + + static const size_t kDefaultLevelDepth = 32; + +protected: + //! Information for each nested level + struct Level { + Level(bool inArray_) : valueCount(0), inArray(inArray_) {} + size_t valueCount; //!< number of values in this level + bool inArray; //!< true if in array, otherwise in object + }; + + bool WriteNull() { + PutReserve(*os_, 4); + PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l'); return true; + } + + bool WriteBool(bool b) { + if (b) { + PutReserve(*os_, 4); + PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'r'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'e'); + } + else { + PutReserve(*os_, 5); + PutUnsafe(*os_, 'f'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 's'); PutUnsafe(*os_, 'e'); + } + return true; + } + + bool WriteInt(int i) { + char buffer[11]; + const char* end = internal::i32toa(i, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (const char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteUint(unsigned u) { + char buffer[10]; + const char* end = internal::u32toa(u, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (const char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteInt64(int64_t i64) { + char buffer[21]; + const char* end = internal::i64toa(i64, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (const char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteUint64(uint64_t u64) { + char buffer[20]; + char* end = internal::u64toa(u64, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteDouble(double d) { + if (internal::Double(d).IsNanOrInf()) { + if (!(writeFlags & kWriteNanAndInfFlag)) + return false; + if (internal::Double(d).IsNan()) { + PutReserve(*os_, 3); + PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); + return true; + } + if (internal::Double(d).Sign()) { + PutReserve(*os_, 9); + PutUnsafe(*os_, '-'); + } + else + PutReserve(*os_, 8); + PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); + PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); + return true; + } + + char buffer[25]; + char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); + PutReserve(*os_, static_cast(end - buffer)); + for (char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteString(const Ch* str, SizeType length) { + static const typename OutputStream::Ch hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + static const char escape[256] = { +#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + //0 1 2 3 4 5 6 7 8 9 A B C D E F + 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'b', 't', 'n', 'u', 'f', 'r', 'u', 'u', // 00 + 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', // 10 + 0, 0, '"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20 + Z16, Z16, // 30~4F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, // 50 + Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 // 60~FF +#undef Z16 + }; + + if (TargetEncoding::supportUnicode) + PutReserve(*os_, 2 + length * 6); // "\uxxxx..." + else + PutReserve(*os_, 2 + length * 12); // "\uxxxx\uyyyy..." + + PutUnsafe(*os_, '\"'); + GenericStringStream is(str); + while (ScanWriteUnescapedString(is, length)) { + const Ch c = is.Peek(); + if (!TargetEncoding::supportUnicode && static_cast(c) >= 0x80) { + // Unicode escaping + unsigned codepoint; + if (RAPIDJSON_UNLIKELY(!SourceEncoding::Decode(is, &codepoint))) + return false; + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, 'u'); + if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) { + PutUnsafe(*os_, hexDigits[(codepoint >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint ) & 15]); + } + else { + RAPIDJSON_ASSERT(codepoint >= 0x010000 && codepoint <= 0x10FFFF); + // Surrogate pair + unsigned s = codepoint - 0x010000; + unsigned lead = (s >> 10) + 0xD800; + unsigned trail = (s & 0x3FF) + 0xDC00; + PutUnsafe(*os_, hexDigits[(lead >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(lead >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(lead >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(lead ) & 15]); + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, 'u'); + PutUnsafe(*os_, hexDigits[(trail >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(trail >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(trail >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(trail ) & 15]); + } + } + else if ((sizeof(Ch) == 1 || static_cast(c) < 256) && RAPIDJSON_UNLIKELY(escape[static_cast(c)])) { + is.Take(); + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, static_cast(escape[static_cast(c)])); + if (escape[static_cast(c)] == 'u') { + PutUnsafe(*os_, '0'); + PutUnsafe(*os_, '0'); + PutUnsafe(*os_, hexDigits[static_cast(c) >> 4]); + PutUnsafe(*os_, hexDigits[static_cast(c) & 0xF]); + } + } + else if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ? + Transcoder::Validate(is, *os_) : + Transcoder::TranscodeUnsafe(is, *os_)))) + return false; + } + PutUnsafe(*os_, '\"'); + return true; + } + + bool ScanWriteUnescapedString(GenericStringStream& is, size_t length) { + return RAPIDJSON_LIKELY(is.Tell() < length); + } + + bool WriteStartObject() { os_->Put('{'); return true; } + bool WriteEndObject() { os_->Put('}'); return true; } + bool WriteStartArray() { os_->Put('['); return true; } + bool WriteEndArray() { os_->Put(']'); return true; } + + bool WriteRawValue(const Ch* json, size_t length) { + PutReserve(*os_, length); + GenericStringStream is(json); + while (RAPIDJSON_LIKELY(is.Tell() < length)) { + RAPIDJSON_ASSERT(is.Peek() != '\0'); + if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ? + Transcoder::Validate(is, *os_) : + Transcoder::TranscodeUnsafe(is, *os_)))) + return false; + } + return true; + } + + void Prefix(Type type) { + (void)type; + if (RAPIDJSON_LIKELY(level_stack_.GetSize() != 0)) { // this value is not at root + Level* level = level_stack_.template Top(); + if (level->valueCount > 0) { + if (level->inArray) + os_->Put(','); // add comma if it is not the first element in array + else // in object + os_->Put((level->valueCount % 2 == 0) ? ',' : ':'); + } + if (!level->inArray && level->valueCount % 2 == 0) + RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name + level->valueCount++; + } + else { + RAPIDJSON_ASSERT(!hasRoot_); // Should only has one and only one root. + hasRoot_ = true; + } + } + + // Flush the value if it is the top level one. + bool EndValue(bool ret) { + if (RAPIDJSON_UNLIKELY(level_stack_.Empty())) // end of json text + Flush(); + return ret; + } + + OutputStream* os_; + internal::Stack level_stack_; + int maxDecimalPlaces_; + bool hasRoot_; + +private: + // Prohibit copy constructor & assignment operator. + Writer(const Writer&); + Writer& operator=(const Writer&); +}; + +// Full specialization for StringStream to prevent memory copying + +template<> +inline bool Writer::WriteInt(int i) { + char *buffer = os_->Push(11); + const char* end = internal::i32toa(i, buffer); + os_->Pop(static_cast(11 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteUint(unsigned u) { + char *buffer = os_->Push(10); + const char* end = internal::u32toa(u, buffer); + os_->Pop(static_cast(10 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteInt64(int64_t i64) { + char *buffer = os_->Push(21); + const char* end = internal::i64toa(i64, buffer); + os_->Pop(static_cast(21 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteUint64(uint64_t u) { + char *buffer = os_->Push(20); + const char* end = internal::u64toa(u, buffer); + os_->Pop(static_cast(20 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteDouble(double d) { + if (internal::Double(d).IsNanOrInf()) { + // Note: This code path can only be reached if (RAPIDJSON_WRITE_DEFAULT_FLAGS & kWriteNanAndInfFlag). + if (!(kWriteDefaultFlags & kWriteNanAndInfFlag)) + return false; + if (internal::Double(d).IsNan()) { + PutReserve(*os_, 3); + PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); + return true; + } + if (internal::Double(d).Sign()) { + PutReserve(*os_, 9); + PutUnsafe(*os_, '-'); + } + else + PutReserve(*os_, 8); + PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); + PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); + return true; + } + + char *buffer = os_->Push(25); + char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); + os_->Pop(static_cast(25 - (end - buffer))); + return true; +} + +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) +template<> +inline bool Writer::ScanWriteUnescapedString(StringStream& is, size_t length) { + if (length < 16) + return RAPIDJSON_LIKELY(is.Tell() < length); + + if (!RAPIDJSON_LIKELY(is.Tell() < length)) + return false; + + const char* p = is.src_; + const char* end = is.head_ + length; + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + const char* endAligned = reinterpret_cast(reinterpret_cast(end) & static_cast(~15)); + if (nextAligned > end) + return true; + + while (p != nextAligned) + if (*p < 0x20 || *p == '\"' || *p == '\\') { + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); + } + else + os_->PutUnsafe(*p++); + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (; p != endAligned; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + SizeType len; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + len = offset; +#else + len = static_cast(__builtin_ffs(r) - 1); +#endif + char* q = reinterpret_cast(os_->PushUnsafe(len)); + for (size_t i = 0; i < len; i++) + q[i] = p[i]; + + p += len; + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(os_->PushUnsafe(16)), s); + } + + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); +} +#elif defined(RAPIDJSON_NEON) +template<> +inline bool Writer::ScanWriteUnescapedString(StringStream& is, size_t length) { + if (length < 16) + return RAPIDJSON_LIKELY(is.Tell() < length); + + if (!RAPIDJSON_LIKELY(is.Tell() < length)) + return false; + + const char* p = is.src_; + const char* end = is.head_ + length; + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + const char* endAligned = reinterpret_cast(reinterpret_cast(end) & static_cast(~15)); + if (nextAligned > end) + return true; + + while (p != nextAligned) + if (*p < 0x20 || *p == '\"' || *p == '\\') { + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); + } + else + os_->PutUnsafe(*p++); + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (; p != endAligned; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + SizeType len = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + len = 8 + (lz >> 3); + escaped = true; + } + } else { + uint32_t lz = internal::clzll(low); + len = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + char* q = reinterpret_cast(os_->PushUnsafe(len)); + for (size_t i = 0; i < len; i++) + q[i] = p[i]; + + p += len; + break; + } + vst1q_u8(reinterpret_cast(os_->PushUnsafe(16)), s); + } + + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); +} +#endif // RAPIDJSON_NEON + +RAPIDJSON_NAMESPACE_END + +#if defined(_MSC_VER) || defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_RAPIDJSON_H_ From 6fc24d8883b925a89dc80683b73284ed1ee5af5f Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 8 Feb 2021 15:50:00 -0800 Subject: [PATCH 025/280] htc: implement service channel parsing (ReceiveReadyPacket) --- .../source/htclow/ctrl/htclow_json.cpp | 80 +++++++++ .../source/htclow/ctrl/htclow_json.hpp | 50 ++++++ .../ctrl/htclow_service_channel_parser.cpp | 156 ++++++++++++++++++ .../ctrl/htclow_service_channel_parser.hpp | 23 +++ .../include/vapours/util/util_string_util.hpp | 13 ++ 5 files changed, 322 insertions(+) create mode 100644 libraries/libstratosphere/source/htclow/ctrl/htclow_json.cpp create mode 100644 libraries/libstratosphere/source/htclow/ctrl/htclow_json.hpp create mode 100644 libraries/libstratosphere/source/htclow/ctrl/htclow_service_channel_parser.cpp create mode 100644 libraries/libstratosphere/source/htclow/ctrl/htclow_service_channel_parser.hpp diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_json.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_json.cpp new file mode 100644 index 000000000..df248060c --- /dev/null +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_json.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htclow_json.hpp" + +namespace ams::htclow::ctrl { + + namespace { + + constexpr const char ChannelKey[] = "Chan"; + constexpr const char ProtocolVersionKey[] = "Prot"; + + } + + bool JsonHandler::Key(const Ch *str, rapidjson::SizeType len, bool copy) { + if (m_state == State::ParseObject) { + if (!util::Strncmp(str, ChannelKey, sizeof(ChannelKey))) { + m_state = State::ParseServiceChannels; + } + if (!util::Strncmp(str, ProtocolVersionKey, sizeof(ProtocolVersionKey))) { + m_state = State::ParseProtocolVersion; + } + } + return true; + } + + bool JsonHandler::Uint(unsigned val) { + if (m_state == State::ParseProtocolVersion) { + *m_version = val; + } + return true; + } + + bool JsonHandler::String(const Ch *str, rapidjson::SizeType len, bool copy) { + if (m_state == State::ParseServiceChannelsArray && *m_num_strings < m_max_strings) { + m_strings[(*m_num_strings)++] = str; + } + return true; + } + + bool JsonHandler::StartObject() { + if (m_state == State::Begin) { + m_state = State::ParseObject; + } + return true; + } + + bool JsonHandler::EndObject(rapidjson::SizeType) { + m_state = State::End; + return true; + } + + bool JsonHandler::StartArray() { + if (m_state == State::ParseServiceChannels) { + m_state = State::ParseServiceChannelsArray; + } + return true; + } + + bool JsonHandler::EndArray(rapidjson::SizeType len) { + if (m_state == State::ParseServiceChannelsArray && len) { + m_state = State::ParseObject; + } + return true; + } + +} diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_json.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_json.hpp new file mode 100644 index 000000000..12dc23c4a --- /dev/null +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_json.hpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htclow::ctrl { + + class JsonHandler : public rapidjson::BaseReaderHandler, JsonHandler>{ + private: + enum class State { + Begin = 0, + ParseObject = 1, + ParseServiceChannels = 2, + ParseServiceChannelsArray = 3, + ParseProtocolVersion = 4, + End = 5, + }; + private: + State m_state; + s16 *m_version; + const char **m_strings; + int *m_num_strings; + int m_max_strings; + public: + JsonHandler(s16 *vers, const char **str, int *ns, int max) : m_state(State::Begin), m_version(vers), m_strings(str), m_num_strings(ns), m_max_strings(max) { /* ... */ } + + bool Key(const Ch *str, rapidjson::SizeType len, bool copy); + bool Uint(unsigned); + bool String(const Ch *, rapidjson::SizeType, bool); + + bool StartObject(); + bool EndObject(rapidjson::SizeType); + bool StartArray(); + bool EndArray(rapidjson::SizeType); + }; + +} diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_service_channel_parser.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_service_channel_parser.cpp new file mode 100644 index 000000000..2a64382ce --- /dev/null +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_service_channel_parser.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htclow_json.hpp" +#include "htclow_service_channel_parser.hpp" + +namespace ams::htclow::ctrl { + + namespace { + + void ParseBody(s16 *out_version, const char **out_channels, int *out_num_channels, int max_channels, void *str, size_t str_size) { + /* Create JSON handler. */ + JsonHandler json_handler(out_version, out_channels, out_num_channels, max_channels); + + /* Create reader. */ + rapidjson::Reader json_reader; + + /* Create stream. */ + rapidjson::InsituStringStream json_stream(static_cast(str)); + + /* Parse the json. */ + json_reader.Parse(json_stream, json_handler); + } + + constexpr bool IsNumericCharacter(char c) { + return '0' <= c && c <= '9'; + } + + constexpr bool IsValidCharacter(char c) { + return IsNumericCharacter(c) || c == ':'; + } + + bool IntegerToModuleId(ModuleId *out, int id) { + switch (id) { + case 0: + case 1: + case 3: + case 4: + *out = static_cast(id); + return true; + default: + return false; + } + } + + bool StringToChannel(impl::ChannelInternalType *out, char *str, size_t size) { + enum class State { + Begin, + ModuleId, + Sep1, + Reserved, + Sep2, + ChannelId + }; + + State state = State::Begin; + + const char * cur = nullptr; + + const char * const str_end = str + size; + while (str < str_end && IsValidCharacter(*str)) { + const char c = *str; + + switch (state) { + case State::Begin: + if (IsNumericCharacter(c)) { + cur = str; + state = State::ModuleId; + } + break; + case State::ModuleId: + if (c == ':') { + *str = 0; + if (!IntegerToModuleId(std::addressof(out->module_id), atoi(cur))) { + return false; + } + state = State::Sep1; + } else if (!IsNumericCharacter(c)) { + return false; + } + break; + case State::Sep1: + if (IsNumericCharacter(c)) { + cur = str; + state = State::Reserved; + } + break; + case State::Reserved: + if (c == ':') { + *str = 0; + out->reserved = 0; + state = State::Sep2; + } else if (!IsNumericCharacter(c)) { + return false; + } + break; + case State::Sep2: + if (IsNumericCharacter(c)) { + cur = str; + state = State::ChannelId; + } + break; + case State::ChannelId: + if (!IsNumericCharacter(c)) { + return false; + } + break; + } + + ++str; + } + + if (str != str_end) { + return false; + } + + out->channel_id = atoi(cur); + + return true; + } + + } + + void ParseServiceChannel(s16 *out_version, impl::ChannelInternalType *out_channels, int *out_num_channels, int max_channels, void *str, size_t str_size) { + /* Parse the JSON. */ + const char *channel_strs[0x20]; + int num_channels; + ParseBody(out_version, channel_strs, std::addressof(num_channels), util::size(channel_strs), str, str_size); + + /* Parse the channel strings. */ + char * const str_end = static_cast(str) + str_size; + int parsed_channels = 0; + for (auto i = 0; i < num_channels && i < max_channels; ++i) { + impl::ChannelInternalType channel; + if (StringToChannel(std::addressof(channel), const_cast(channel_strs[i]), util::Strnlen(channel_strs[i], str_end - channel_strs[i]))) { + out_channels[parsed_channels++] = channel; + } + } + + *out_num_channels = parsed_channels; + } + +} diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_service_channel_parser.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_service_channel_parser.hpp new file mode 100644 index 000000000..1b0c1ec8e --- /dev/null +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_service_channel_parser.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htclow::ctrl { + + void ParseServiceChannel(s16 *out_version, impl::ChannelInternalType *out_channels, int *out_num_channels, int max_channels, void *str, size_t str_size); + +} diff --git a/libraries/libvapours/include/vapours/util/util_string_util.hpp b/libraries/libvapours/include/vapours/util/util_string_util.hpp index 9abe8aaab..a70f0cae4 100644 --- a/libraries/libvapours/include/vapours/util/util_string_util.hpp +++ b/libraries/libvapours/include/vapours/util/util_string_util.hpp @@ -88,4 +88,17 @@ namespace ams::util { return static_cast(cur - src); } + template + constexpr int Strnlen(const T *str, int count) { + AMS_ASSERT(str != nullptr); + AMS_ASSERT(count >= 0); + + int length = 0; + while (count-- && *str++) { + ++length; + } + + return length; + } + } From 2cdfde6637801a7b02e5bdbe140fd2e4331c9c4c Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 8 Feb 2021 23:44:30 -0800 Subject: [PATCH 026/280] htc: add remaining worker receive thread logic --- .../source/htclow/ctrl/htclow_ctrl_packet.hpp | 2 +- .../ctrl/htclow_ctrl_packet_factory.cpp | 127 ++++++++++++++ .../ctrl/htclow_ctrl_packet_factory.hpp | 19 ++- .../htclow/ctrl/htclow_ctrl_send_buffer.cpp | 54 ++++++ .../htclow/ctrl/htclow_ctrl_send_buffer.hpp | 10 +- .../htclow/ctrl/htclow_ctrl_service.cpp | 160 +++++++++++++++++- .../htclow/ctrl/htclow_ctrl_service.hpp | 15 +- .../ctrl/htclow_ctrl_service_channels.hpp | 40 +++++ .../htclow/ctrl/htclow_ctrl_state_machine.cpp | 76 ++++++--- .../htclow/ctrl/htclow_ctrl_state_machine.hpp | 6 + .../vapours/results/htclow_results.hpp | 4 +- 11 files changed, 477 insertions(+), 36 deletions(-) create mode 100644 libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet_factory.cpp create mode 100644 libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_send_buffer.cpp create mode 100644 libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service_channels.hpp diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet.hpp index bb12eb3dd..0689920bf 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet.hpp @@ -39,7 +39,7 @@ namespace ams::htclow::ctrl { struct HtcctrlPacketHeader { u32 signature; - u32 offset; + u32 sequence_id; u32 reserved; u32 body_size; s16 version; diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet_factory.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet_factory.cpp new file mode 100644 index 000000000..7c18ed574 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet_factory.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htclow_ctrl_packet_factory.hpp" + +namespace ams::htclow::ctrl { + + std::unique_ptr HtcctrlPacketFactory::MakeSendPacketCommon(int body_size) { + /* Allocate memory for the packet. */ + if (void *buffer = m_allocator->Allocate(sizeof(HtcctrlPacket), alignof(HtcctrlPacket)); buffer != nullptr) { + /* Convert the buffer to a packet. */ + HtcctrlPacket *packet = static_cast(buffer); + + /* Construct the packet. */ + std::construct_at(packet, m_allocator, body_size + sizeof(HtcctrlPacketHeader)); + + /* Create the unique pointer. */ + std::unique_ptr ptr(packet, HtcctrlPacketDeleter{m_allocator}); + + /* Set packet header fields. */ + if (ptr && ptr->IsAllocationSucceeded()) { + HtcctrlPacketHeader *header = ptr->GetHeader(); + + header->signature = HtcctrlSignature; + header->sequence_id = m_sequence_id++; + header->reserved = 0; + header->body_size = body_size; + header->version = 1; + header->channel = {}; + header->share = 0; + } + + return ptr; + } else { + return std::unique_ptr(nullptr, HtcctrlPacketDeleter{m_allocator}); + } + } + + std::unique_ptr HtcctrlPacketFactory::MakeSuspendPacket() { + auto packet = this->MakeSendPacketCommon(0); + if (packet && packet->IsAllocationSucceeded()) { + packet->GetHeader()->packet_type = HtcctrlPacketType_SuspendFromTarget; + } + + return packet; + } + + std::unique_ptr HtcctrlPacketFactory::MakeResumePacket() { + auto packet = this->MakeSendPacketCommon(0); + if (packet && packet->IsAllocationSucceeded()) { + packet->GetHeader()->packet_type = HtcctrlPacketType_SuspendFromTarget; + } + + return packet; + } + + std::unique_ptr HtcctrlPacketFactory::MakeReadyPacket(const void *body, int body_size) { + auto packet = this->MakeSendPacketCommon(0); + if (packet && packet->IsAllocationSucceeded()) { + packet->GetHeader()->packet_type = HtcctrlPacketType_ReadyFromTarget; + + std::memcpy(packet->GetBody(), body, packet->GetBodySize()); + } + + return packet; + } + + std::unique_ptr HtcctrlPacketFactory::MakeInformationPacket(const void *body, int body_size) { + auto packet = this->MakeSendPacketCommon(0); + if (packet && packet->IsAllocationSucceeded()) { + packet->GetHeader()->packet_type = HtcctrlPacketType_InformationFromTarget; + + std::memcpy(packet->GetBody(), body, packet->GetBodySize()); + } + + return packet; + } + + std::unique_ptr HtcctrlPacketFactory::MakeDisconnectPacket() { + auto packet = this->MakeSendPacketCommon(0); + if (packet) { + packet->GetHeader()->packet_type = HtcctrlPacketType_DisconnectFromTarget; + } + + return packet; + } + + std::unique_ptr HtcctrlPacketFactory::MakeConnectPacket(const void *body, int body_size) { + auto packet = this->MakeSendPacketCommon(0); + if (packet && packet->IsAllocationSucceeded()) { + packet->GetHeader()->packet_type = HtcctrlPacketType_ConnectFromTarget; + + std::memcpy(packet->GetBody(), body, packet->GetBodySize()); + } + + return packet; + } + + std::unique_ptr HtcctrlPacketFactory::MakeBeaconResponsePacket(const void *body, int body_size) { + auto packet = this->MakeSendPacketCommon(0); + if (packet && packet->IsAllocationSucceeded()) { + packet->GetHeader()->packet_type = HtcctrlPacketType_BeaconResponse; + + std::memcpy(packet->GetBody(), body, packet->GetBodySize()); + } + + return packet; + } + + void HtcctrlPacketFactory::Delete(HtcctrlPacket *packet) { + HtcctrlPacketDeleter{m_allocator}(packet); + } + +} diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet_factory.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet_factory.hpp index 8670672d7..5ed6834d6 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet_factory.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet_factory.hpp @@ -15,26 +15,39 @@ */ #pragma once #include +#include "htclow_ctrl_packet.hpp" namespace ams::htclow::ctrl { class HtcctrlPacketFactory { private: mem::StandardAllocator *m_allocator; - u32 m_seed; + u32 m_sequence_id; public: HtcctrlPacketFactory(mem::StandardAllocator *allocator) : m_allocator(allocator) { /* Get the current time. */ const u64 time = os::GetSystemTick().GetInt64Value(); - /* Set the random seed. */ + /* Set a random sequence id. */ { util::TinyMT rng; rng.Initialize(reinterpret_cast(std::addressof(time)), sizeof(time) / sizeof(u32)); - m_seed = rng.GenerateRandomU32(); + m_sequence_id = rng.GenerateRandomU32(); } } + public: + std::unique_ptr MakeSuspendPacket(); + std::unique_ptr MakeResumePacket(); + std::unique_ptr MakeReadyPacket(const void *body, int body_size); + std::unique_ptr MakeInformationPacket(const void *body, int body_size); + std::unique_ptr MakeDisconnectPacket(); + std::unique_ptr MakeConnectPacket(const void *body, int body_size); + std::unique_ptr MakeBeaconResponsePacket(const void *body, int body_size); + + void Delete(HtcctrlPacket *packet); + private: + std::unique_ptr MakeSendPacketCommon(int body_size); }; } diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_send_buffer.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_send_buffer.cpp new file mode 100644 index 000000000..77de070c7 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_send_buffer.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htclow_ctrl_send_buffer.hpp" + +namespace ams::htclow::ctrl { + + bool HtcctrlSendBuffer::IsPriorPacket(HtcctrlPacketType packet_type) const { + return packet_type == HtcctrlPacketType_DisconnectFromTarget; + } + + bool HtcctrlSendBuffer::IsPosteriorPacket(HtcctrlPacketType packet_type) const { + switch (packet_type) { + case HtcctrlPacketType_ConnectFromTarget: + case HtcctrlPacketType_ReadyFromTarget: + case HtcctrlPacketType_SuspendFromTarget: + case HtcctrlPacketType_ResumeFromTarget: + case HtcctrlPacketType_BeaconResponse: + case HtcctrlPacketType_InformationFromTarget: + default: + return false; + } + } + + void HtcctrlSendBuffer::AddPacket(std::unique_ptr ptr) { + /* Get the packet. */ + HtcctrlPacket *packet = ptr.release(); + + /* Get the packet type. */ + const auto packet_type = packet->GetHeader()->packet_type; + + /* Add the packet to the appropriate list. */ + if (this->IsPriorPacket(packet_type)) { + m_prior_packet_list.push_back(*packet); + } else { + AMS_ABORT_UNLESS(this->IsPosteriorPacket(packet_type)); + m_posterior_packet_list.push_back(*packet); + } + } + +} diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_send_buffer.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_send_buffer.hpp index abe1cf239..d5c8c4798 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_send_buffer.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_send_buffer.hpp @@ -26,9 +26,15 @@ namespace ams::htclow::ctrl { using PacketList = util::IntrusiveListBaseTraits::ListType; private: HtcctrlPacketFactory *m_packet_factory; - PacketList m_packet_list; + PacketList m_prior_packet_list; + PacketList m_posterior_packet_list; + private: + bool IsPriorPacket(HtcctrlPacketType packet_type) const; + bool IsPosteriorPacket(HtcctrlPacketType packet_type) const; public: - HtcctrlSendBuffer(HtcctrlPacketFactory *pf) : m_packet_factory(pf), m_packet_list() { /* ... */ } + HtcctrlSendBuffer(HtcctrlPacketFactory *pf) : m_packet_factory(pf), m_prior_packet_list(), m_posterior_packet_list() { /* ... */ } + + void AddPacket(std::unique_ptr ptr); }; } diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp index 55bb00d02..e1fd3b7cc 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp @@ -17,6 +17,9 @@ #include "htclow_ctrl_service.hpp" #include "htclow_ctrl_state.hpp" #include "htclow_ctrl_state_machine.hpp" +#include "htclow_ctrl_packet_factory.hpp" +#include "htclow_service_channel_parser.hpp" +#include "htclow_ctrl_service_channels.hpp" #include "../mux/htclow_mux.hpp" namespace ams::htclow::ctrl { @@ -38,7 +41,7 @@ namespace ams::htclow::ctrl { HtcctrlService::HtcctrlService(HtcctrlPacketFactory *pf, HtcctrlStateMachine *sm, mux::Mux *mux) : m_settings_holder(), m_beacon_response(), m_1100(), m_packet_factory(pf), m_state_machine(sm), m_mux(mux), m_event(os::EventClearMode_ManualClear), - m_send_buffer(pf), m_mutex(), m_condvar(), m_2170(), m_version(ProtocolVersion) + m_send_buffer(pf), m_mutex(), m_condvar(), m_service_channels_packet(), m_version(ProtocolVersion) { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); @@ -110,8 +113,150 @@ namespace ams::htclow::ctrl { } Result HtcctrlService::ProcessReceivePacket(const HtcctrlPacketHeader &header, const void *body, size_t body_size) { - /* TODO */ - AMS_ABORT("HtcctrlService::ProcessReceivePacket"); + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + switch (header.packet_type) { + case HtcctrlPacketType_ConnectFromHost: + return this->ProcessReceiveConnectPacket(); + case HtcctrlPacketType_ReadyFromHost: + return this->ProcessReceiveReadyPacket(body, body_size); + case HtcctrlPacketType_SuspendFromHost: + return this->ProcessReceiveSuspendPacket(); + case HtcctrlPacketType_ResumeFromHost: + return this->ProcessReceiveResumePacket(); + case HtcctrlPacketType_DisconnectFromHost: + return this->ProcessReceiveDisconnectPacket(); + case HtcctrlPacketType_BeaconQuery: + return this->ProcessReceiveBeaconQueryPacket(); + default: + return this->ProcessReceiveUnexpectedPacket(); + } + } + + Result HtcctrlService::ProcessReceiveConnectPacket() { + /* Try to transition to sent connect state. */ + if (R_FAILED(this->SetState(HtcctrlState_SentConnectFromHost))) { + /* We couldn't transition to sent connect. */ + return this->ProcessReceiveUnexpectedPacket(); + } + + /* Send a connect packet. */ + m_send_buffer.AddPacket(m_packet_factory->MakeConnectPacket(m_beacon_response, util::Strnlen(m_beacon_response, sizeof(m_beacon_response)) + 1)); + + /* Signal our event. */ + m_event.Signal(); + + return ResultSuccess(); + } + + Result HtcctrlService::ProcessReceiveReadyPacket(const void *body, size_t body_size) { + /* Update our service channels. */ + this->UpdateServiceChannels(body, body_size); + + /* Check that our version is correct. */ + if (m_version < ProtocolVersion) { + return this->ProcessReceiveUnexpectedPacket(); + } + + /* Set our version. */ + m_version = ProtocolVersion; + m_mux->SetVersion(m_version); + + /* Set our state. */ + if (R_FAILED(this->SetState(HtcctrlState_SentReadyFromHost))) { + return this->ProcessReceiveUnexpectedPacket(); + } + + /* Ready ourselves. */ + this->TryReadyInternal(); + return ResultSuccess(); + } + + Result HtcctrlService::ProcessReceiveSuspendPacket() { + /* Try to set our state to enter sleep. */ + if (R_FAILED(this->SetState(HtcctrlState_EnterSleep))) { + /* We couldn't transition to sleep. */ + return this->ProcessReceiveUnexpectedPacket(); + } + + return ResultSuccess(); + } + + Result HtcctrlService::ProcessReceiveResumePacket() { + /* If our state is sent-resume, change to readied. */ + if (m_state_machine->GetHtcctrlState() != HtcctrlState_SentResumeFromTarget || R_FAILED(this->SetState(HtcctrlState_Ready))) { + /* We couldn't perform a valid resume transition. */ + return this->ProcessReceiveUnexpectedPacket(); + } + + return ResultSuccess(); + } + + Result HtcctrlService::ProcessReceiveDisconnectPacket() { + /* Set our state. */ + R_TRY(this->SetState(HtcctrlState_Disconnected)); + + return ResultSuccess(); + } + + Result HtcctrlService::ProcessReceiveBeaconQueryPacket() { + /* Send a beacon response packet. */ + m_send_buffer.AddPacket(m_packet_factory->MakeBeaconResponsePacket(m_beacon_response, util::Strnlen(m_beacon_response, sizeof(m_beacon_response)) + 1)); + + /* Signal our event. */ + m_event.Signal(); + + return ResultSuccess(); + } + + Result HtcctrlService::ProcessReceiveUnexpectedPacket() { + /* Set our state. */ + R_TRY(this->SetState(HtcctrlState_Error)); + + /* Send a disconnection packet. */ + m_send_buffer.AddPacket(m_packet_factory->MakeDisconnectPacket()); + + /* Signal our event. */ + m_event.Signal(); + + /* Return unexpected packet error. */ + return htclow::ResultHtcctrlReceiveUnexpectedPacket(); + } + + void HtcctrlService::UpdateServiceChannels(const void *body, size_t body_size) { + /* Copy the packet body to our member. */ + std::memcpy(m_service_channels_packet, body, body_size); + + /* Parse service channels. */ + impl::ChannelInternalType channels[10]; + int num_channels; + s16 version = m_version; + ctrl::ParseServiceChannel(std::addressof(version), channels, std::addressof(num_channels), util::size(channels), m_service_channels_packet, body_size); + + /* Update version. */ + m_version = version; + + /* Notify state machine of supported channels. */ + m_state_machine->NotifySupportedServiceChannels(channels, num_channels); + } + + void HtcctrlService::TryReadyInternal() { + /* If we can send ready, do so. */ + if (m_state_machine->IsPossibleToSendReady()) { + /* Print the channels. */ + char channel_str[0x100]; + this->PrintServiceChannels(channel_str, sizeof(channel_str)); + + /* Send a ready packet. */ + m_send_buffer.AddPacket(m_packet_factory->MakeReadyPacket(channel_str, util::Strnlen(channel_str, sizeof(channel_str)) + 1)); + + /* Signal our event. */ + m_event.Signal(); + + /* Set connecting checked in state machine. */ + m_state_machine->SetConnectingChecked(); + } } Result HtcctrlService::NotifyDriverConnected() { @@ -168,4 +313,13 @@ namespace ams::htclow::ctrl { m_condvar.Broadcast(); } + void HtcctrlService::PrintServiceChannels(char *dst, size_t dst_size) { + size_t ofs = 0; + ofs += util::SNPrintf(dst + ofs, dst_size - ofs, "{\r\n \"Chan\" : [\r\n \"%d:%d:%d\"", static_cast(ServiceChannels[0].module_id), ServiceChannels[0].reserved, static_cast(ServiceChannels[0].channel_id)); + for (size_t i = 1; i < util::size(ServiceChannels); ++i) { + ofs += util::SNPrintf(dst + ofs, dst_size - ofs, ",\r\n \"%d:%d:%d\"", static_cast(ServiceChannels[i].module_id), ServiceChannels[i].reserved, static_cast(ServiceChannels[i].channel_id)); + } + ofs += util::SNPrintf(dst + ofs, dst_size - ofs, "\r\n],\r\n \"Prot\" : %d\r\n}\r\n", ProtocolVersion); + } + } diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp index b39fb6f41..b94dc58a8 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp @@ -50,13 +50,26 @@ namespace ams::htclow::ctrl { HtcctrlSendBuffer m_send_buffer; os::SdkMutex m_mutex; os::SdkConditionVariable m_condvar; - u8 m_2170[0x1000]; + char m_service_channels_packet[0x1000]; s16 m_version; private: const char *GetConnectionType(impl::DriverType driver_type) const; void UpdateBeaconResponse(const char *connection); + Result ProcessReceiveConnectPacket(); + Result ProcessReceiveReadyPacket(const void *body, size_t body_size); + Result ProcessReceiveSuspendPacket(); + Result ProcessReceiveResumePacket(); + Result ProcessReceiveDisconnectPacket(); + Result ProcessReceiveBeaconQueryPacket(); + Result ProcessReceiveUnexpectedPacket(); + + void UpdateServiceChannels(const void *body, size_t body_size); + void TryReadyInternal(); + + void PrintServiceChannels(char *dst, size_t dst_size); + Result SetState(HtcctrlState state); void ReflectState(); public: diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service_channels.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service_channels.hpp new file mode 100644 index 000000000..bcaf32b9f --- /dev/null +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service_channels.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htclow::ctrl { + + constexpr inline const impl::ChannelInternalType ServiceChannels[] = { + { + .channel_id = 0, + .module_id = static_cast(1), + }, + { + .channel_id = 1, + .module_id = static_cast(3), + }, + { + .channel_id = 2, + .module_id = static_cast(3), + }, + { + .channel_id = 0, + .module_id = static_cast(4), + }, + }; + +} diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp index c80bd784b..1836dbeeb 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp @@ -15,33 +15,10 @@ */ #include #include "htclow_ctrl_state_machine.hpp" +#include "htclow_ctrl_service_channels.hpp" namespace ams::htclow::ctrl { - namespace { - - /* TODO: Real module id names */ - constexpr const impl::ChannelInternalType ServiceChannels[] = { - { - .channel_id = 0, - .module_id = static_cast(1), - }, - { - .channel_id = 1, - .module_id = static_cast(3), - }, - { - .channel_id = 2, - .module_id = static_cast(3), - }, - { - .channel_id = 0, - .module_id = static_cast(4), - }, - }; - - } - HtcctrlStateMachine::HtcctrlStateMachine() : m_map(), m_state(HtcctrlState_DriverDisconnected), m_prev_state(HtcctrlState_DriverDisconnected), m_mutex() { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); @@ -67,7 +44,7 @@ namespace ams::htclow::ctrl { std::scoped_lock lk(m_mutex); /* Check that the transition is allowed. */ - R_UNLESS(ctrl::IsStateTransitionAllowed(m_state, state), htclow::ResultStateTransitionNotAllowed()); + R_UNLESS(ctrl::IsStateTransitionAllowed(m_state, state), htclow::ResultHtcctrlStateTransitionNotAllowed()); /* Get the state pre-transition. */ const auto old_state = m_state; @@ -149,6 +126,13 @@ namespace ams::htclow::ctrl { } } + bool HtcctrlStateMachine::IsPossibleToSendReady() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + return m_state == HtcctrlState_SentReadyFromHost && this->AreServiceChannelsConnecting(); + } + bool HtcctrlStateMachine::IsUnsupportedServiceChannelToShutdown(const impl::ChannelInternalType &channel) { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); @@ -175,4 +159,46 @@ namespace ams::htclow::ctrl { } } + void HtcctrlStateMachine::SetConnectingChecked() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + for (auto &pair : m_map) { + pair.second.connect = ServiceChannelConnect_ConnectingChecked; + } + } + + void HtcctrlStateMachine::NotifySupportedServiceChannels(const impl::ChannelInternalType *channels, int num_channels) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + auto IsSupportedServiceChannel = [] ALWAYS_INLINE_LAMBDA (const impl::ChannelInternalType &channel, const impl::ChannelInternalType *supported, int num_supported) -> bool { + for (auto i = 0; i < num_supported; ++i) { + if (channel.module_id == supported[i].module_id && channel.channel_id == supported[i].channel_id) { + return true; + } + } + + return false; + }; + + for (auto &pair : m_map) { + if (IsSupportedServiceChannel(pair.first, channels, num_channels)) { + pair.second.support = ServiceChannelSupport_Suppported; + } else { + pair.second.support = ServiceChannelSupport_Unsupported; + } + } + } + + bool HtcctrlStateMachine::AreServiceChannelsConnecting() { + for (auto &pair : m_map) { + if (pair.second.connect != ServiceChannelConnect_Connecting) { + return false; + } + } + + return true; + } + } diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp index 6651eff6e..aed8d5068 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp @@ -64,13 +64,19 @@ namespace ams::htclow::ctrl { bool IsDisconnected(); bool IsSleeping(); + bool IsPossibleToSendReady(); bool IsUnsupportedServiceChannelToShutdown(const impl::ChannelInternalType &channel); bool IsConnectable(const impl::ChannelInternalType &channel); void SetNotConnecting(const impl::ChannelInternalType &channel); + void SetConnectingChecked(); + + void NotifySupportedServiceChannels(const impl::ChannelInternalType *channels, int num_channels); private: void SetStateWithoutCheckInternal(HtcctrlState state); + bool AreServiceChannelsConnecting(); + void ClearServiceChannelStates(); }; diff --git a/libraries/libvapours/include/vapours/results/htclow_results.hpp b/libraries/libvapours/include/vapours/results/htclow_results.hpp index 383d343c4..30e976b20 100644 --- a/libraries/libvapours/include/vapours/results/htclow_results.hpp +++ b/libraries/libvapours/include/vapours/results/htclow_results.hpp @@ -49,6 +49,8 @@ namespace ams::htclow { R_DEFINE_ERROR_RESULT(UsbDriverReceiveError, 1403); R_DEFINE_ERROR_RESULT(UsbDriverSendError, 1404); - R_DEFINE_ERROR_RESULT(StateTransitionNotAllowed, 2001); + R_DEFINE_ERROR_RESULT(HtcctrlError, 2000); /* TODO: Range? */ + R_DEFINE_ERROR_RESULT(HtcctrlStateTransitionNotAllowed, 2001); + R_DEFINE_ERROR_RESULT(HtcctrlReceiveUnexpectedPacket, 2002); } From 0977ee72ca810ae6e2fede6f7b7cbdb8e23bc011 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 8 Feb 2021 23:56:27 -0800 Subject: [PATCH 027/280] rapidjson: add customization point for allocation/asserts --- .../include/stratosphere/ams/ams_environment.hpp | 4 ++++ .../include/stratosphere/rapidjson.hpp | 9 ++++++++- .../source/ams/ams_environment_weak.cpp | 12 ++++++++++++ stratosphere/htc/source/htc_main.cpp | 15 +++++++++++++++ 4 files changed, 39 insertions(+), 1 deletion(-) diff --git a/libraries/libstratosphere/include/stratosphere/ams/ams_environment.hpp b/libraries/libstratosphere/include/stratosphere/ams/ams_environment.hpp index c237ed05f..13f20a5d5 100644 --- a/libraries/libstratosphere/include/stratosphere/ams/ams_environment.hpp +++ b/libraries/libstratosphere/include/stratosphere/ams/ams_environment.hpp @@ -29,4 +29,8 @@ namespace ams { void *Malloc(size_t size); void Free(void *ptr); + void *MallocForRapidJson(size_t size); + void *ReallocForRapidJson(void *ptr, size_t size); + void FreeForRapidJson(void *ptr); + } diff --git a/libraries/libstratosphere/include/stratosphere/rapidjson.hpp b/libraries/libstratosphere/include/stratosphere/rapidjson.hpp index 9a68b1674..fafc93671 100644 --- a/libraries/libstratosphere/include/stratosphere/rapidjson.hpp +++ b/libraries/libstratosphere/include/stratosphere/rapidjson.hpp @@ -14,8 +14,15 @@ * along with this program. If not, see . */ #pragma once +#include +#include -#define RAPIDJSON_NAMESPACE ams::rapidjson +#define RAPIDJSON_NAMESPACE ams::rapidjson +#define RAPIDJSON_ASSERT(x) AMS_ABORT_UNLESS(x) + +#define RAPIDJSON_MALLOC(size) ams::MallocForRapidJson(size) +#define RAPIDJSON_REALLOC(ptr, size) ams::ReallocForRapidJson(ptr, size) +#define RAPIDJSON_FREE(ptr) ams::FreeForRapidJson(ptr) #include #include diff --git a/libraries/libstratosphere/source/ams/ams_environment_weak.cpp b/libraries/libstratosphere/source/ams/ams_environment_weak.cpp index a4c36a6a8..89b68067a 100644 --- a/libraries/libstratosphere/source/ams/ams_environment_weak.cpp +++ b/libraries/libstratosphere/source/ams/ams_environment_weak.cpp @@ -25,6 +25,18 @@ namespace ams { return std::free(ptr); } + WEAK_SYMBOL void *MallocForRapidJson(size_t size) { + return std::malloc(size); + } + + WEAK_SYMBOL void *ReallocForRapidJson(void *ptr, size_t size) { + return std::realloc(ptr, size); + } + + WEAK_SYMBOL void FreeForRapidJson(void *ptr) { + return std::free(ptr); + } + } extern "C" { diff --git a/stratosphere/htc/source/htc_main.cpp b/stratosphere/htc/source/htc_main.cpp index 775dff411..e9797aca1 100644 --- a/stratosphere/htc/source/htc_main.cpp +++ b/stratosphere/htc/source/htc_main.cpp @@ -110,6 +110,21 @@ namespace ams { AMS_ABORT("ams::Free was called"); } + void *MallocForRapidJson(size_t size) { + AMS_ABORT("ams::MallocForRapidJson was called"); + } + + void *ReallocForRapidJson(void *ptr, size_t size) { + AMS_ABORT("ams::ReallocForRapidJson was called"); + } + + void FreeForRapidJson(void *ptr) { + if (ptr == nullptr) { + return; + } + AMS_ABORT("ams::FreeForRapidJson was called"); + } + } void *operator new(size_t size) { From df3d62df84b924345bf32b0de11beedb8b70ed5a Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 9 Feb 2021 01:05:43 -0800 Subject: [PATCH 028/280] htc: send logic for HtcctrlService, bugfixes (thanks @misson20000) --- .../ctrl/htclow_ctrl_packet_factory.cpp | 10 +-- .../htclow/ctrl/htclow_ctrl_send_buffer.cpp | 48 ++++++++++++ .../htclow/ctrl/htclow_ctrl_send_buffer.hpp | 5 ++ .../htclow/ctrl/htclow_ctrl_service.cpp | 74 +++++++++++++++++++ .../htclow/ctrl/htclow_ctrl_service.hpp | 11 +++ .../source/htclow/htclow_packet.hpp | 2 +- .../source/htclow/htclow_worker.cpp | 46 +++++++++++- .../source/htclow/htclow_worker.hpp | 3 +- .../source/htclow/mux/htclow_mux.cpp | 6 +- .../source/htclow/mux/htclow_mux.hpp | 7 +- 10 files changed, 197 insertions(+), 15 deletions(-) diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet_factory.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet_factory.cpp index 7c18ed574..d2851378d 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet_factory.cpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet_factory.cpp @@ -61,14 +61,14 @@ namespace ams::htclow::ctrl { std::unique_ptr HtcctrlPacketFactory::MakeResumePacket() { auto packet = this->MakeSendPacketCommon(0); if (packet && packet->IsAllocationSucceeded()) { - packet->GetHeader()->packet_type = HtcctrlPacketType_SuspendFromTarget; + packet->GetHeader()->packet_type = HtcctrlPacketType_ResumeFromTarget; } return packet; } std::unique_ptr HtcctrlPacketFactory::MakeReadyPacket(const void *body, int body_size) { - auto packet = this->MakeSendPacketCommon(0); + auto packet = this->MakeSendPacketCommon(body_size); if (packet && packet->IsAllocationSucceeded()) { packet->GetHeader()->packet_type = HtcctrlPacketType_ReadyFromTarget; @@ -79,7 +79,7 @@ namespace ams::htclow::ctrl { } std::unique_ptr HtcctrlPacketFactory::MakeInformationPacket(const void *body, int body_size) { - auto packet = this->MakeSendPacketCommon(0); + auto packet = this->MakeSendPacketCommon(body_size); if (packet && packet->IsAllocationSucceeded()) { packet->GetHeader()->packet_type = HtcctrlPacketType_InformationFromTarget; @@ -99,7 +99,7 @@ namespace ams::htclow::ctrl { } std::unique_ptr HtcctrlPacketFactory::MakeConnectPacket(const void *body, int body_size) { - auto packet = this->MakeSendPacketCommon(0); + auto packet = this->MakeSendPacketCommon(body_size); if (packet && packet->IsAllocationSucceeded()) { packet->GetHeader()->packet_type = HtcctrlPacketType_ConnectFromTarget; @@ -110,7 +110,7 @@ namespace ams::htclow::ctrl { } std::unique_ptr HtcctrlPacketFactory::MakeBeaconResponsePacket(const void *body, int body_size) { - auto packet = this->MakeSendPacketCommon(0); + auto packet = this->MakeSendPacketCommon(body_size); if (packet && packet->IsAllocationSucceeded()) { packet->GetHeader()->packet_type = HtcctrlPacketType_BeaconResponse; diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_send_buffer.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_send_buffer.cpp index 77de070c7..ac0ebae26 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_send_buffer.cpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_send_buffer.cpp @@ -15,6 +15,7 @@ */ #include #include "htclow_ctrl_send_buffer.hpp" +#include "htclow_ctrl_packet_factory.hpp" namespace ams::htclow::ctrl { @@ -30,6 +31,7 @@ namespace ams::htclow::ctrl { case HtcctrlPacketType_ResumeFromTarget: case HtcctrlPacketType_BeaconResponse: case HtcctrlPacketType_InformationFromTarget: + return true; default: return false; } @@ -51,4 +53,50 @@ namespace ams::htclow::ctrl { } } + void HtcctrlSendBuffer::RemovePacket(const HtcctrlPacketHeader &header) { + /* Get the packet type. */ + const auto packet_type = header.packet_type; + + /* Remove the front from the appropriate list. */ + HtcctrlPacket *packet; + if (this->IsPriorPacket(packet_type)) { + packet = std::addressof(m_prior_packet_list.front()); + m_prior_packet_list.pop_front(); + } else { + AMS_ABORT_UNLESS(this->IsPosteriorPacket(packet_type)); + packet = std::addressof(m_posterior_packet_list.front()); + m_posterior_packet_list.pop_front(); + } + + /* Delete the packet. */ + m_packet_factory->Delete(packet); + } + + bool HtcctrlSendBuffer::QueryNextPacket(HtcctrlPacketHeader *header, HtcctrlPacketBody *body, int *out_body_size) { + if (!m_prior_packet_list.empty()) { + this->CopyPacket(header, body, out_body_size, m_prior_packet_list.front()); + return true; + } else if (!m_posterior_packet_list.empty()) { + this->CopyPacket(header, body, out_body_size, m_posterior_packet_list.front()); + return true; + } else { + return false; + } + } + + void HtcctrlSendBuffer::CopyPacket(HtcctrlPacketHeader *header, HtcctrlPacketBody *body, int *out_body_size, const HtcctrlPacket &packet) { + /* Get the body size. */ + const int body_size = packet.GetBodySize(); + AMS_ASSERT(0 <= body_size && body_size <= static_cast(sizeof(*body))); + + /* Copy the header. */ + std::memcpy(header, packet.GetHeader(), sizeof(*header)); + + /* Copy the body. */ + std::memcpy(body, packet.GetBody(), body_size); + + /* Set the output body size. */ + *out_body_size = body_size; + } + } diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_send_buffer.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_send_buffer.hpp index d5c8c4798..a7ecd8af4 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_send_buffer.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_send_buffer.hpp @@ -31,10 +31,15 @@ namespace ams::htclow::ctrl { private: bool IsPriorPacket(HtcctrlPacketType packet_type) const; bool IsPosteriorPacket(HtcctrlPacketType packet_type) const; + + void CopyPacket(HtcctrlPacketHeader *header, HtcctrlPacketBody *body, int *out_body_size, const HtcctrlPacket &packet); public: HtcctrlSendBuffer(HtcctrlPacketFactory *pf) : m_packet_factory(pf), m_prior_packet_list(), m_posterior_packet_list() { /* ... */ } void AddPacket(std::unique_ptr ptr); + void RemovePacket(const HtcctrlPacketHeader &header); + + bool QueryNextPacket(HtcctrlPacketHeader *header, HtcctrlPacketBody *body, int *out_body_size); }; } diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp index e1fd3b7cc..724a6f734 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp @@ -224,6 +224,41 @@ namespace ams::htclow::ctrl { return htclow::ResultHtcctrlReceiveUnexpectedPacket(); } + void HtcctrlService::ProcessSendConnectPacket() { + /* Set our state. */ + const Result result = this->SetState(HtcctrlState_Connected); + R_ASSERT(result); + } + + void HtcctrlService::ProcessSendReadyPacket() { + /* Set our state. */ + if (m_state_machine->GetHtcctrlState() == HtcctrlState_SentReadyFromHost) { + const Result result = this->SetState(HtcctrlState_Ready); + R_ASSERT(result); + } + + /* Update channel states. */ + m_mux->UpdateChannelState(); + } + + void HtcctrlService::ProcessSendSuspendPacket() { + /* Set our state. */ + const Result result = this->SetState(HtcctrlState_SentSuspendFromTarget); + R_ASSERT(result); + } + + void HtcctrlService::ProcessSendResumePacket() { + /* Set our state. */ + const Result result = this->SetState(HtcctrlState_SentResumeFromTarget); + R_ASSERT(result); + } + + void HtcctrlService::ProcessSendDisconnectPacket() { + /* Set our state. */ + const Result result = this->SetState(HtcctrlState_Disconnected); + R_ASSERT(result); + } + void HtcctrlService::UpdateServiceChannels(const void *body, size_t body_size) { /* Copy the packet body to our member. */ std::memcpy(m_service_channels_packet, body, body_size); @@ -259,6 +294,45 @@ namespace ams::htclow::ctrl { } } + bool HtcctrlService::QuerySendPacket(HtcctrlPacketHeader *header, HtcctrlPacketBody *body, int *out_body_size) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + return m_send_buffer.QueryNextPacket(header, body, out_body_size); + } + + void HtcctrlService::RemovePacket(const HtcctrlPacketHeader &header) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Remove the packet from our buffer. */ + m_send_buffer.RemovePacket(header); + + /* Switch on the packet type. */ + switch (header.packet_type) { + case HtcctrlPacketType_ConnectFromTarget: + this->ProcessSendConnectPacket(); + break; + case HtcctrlPacketType_ReadyFromTarget: + this->ProcessSendReadyPacket(); + break; + case HtcctrlPacketType_SuspendFromTarget: + this->ProcessSendSuspendPacket(); + break; + case HtcctrlPacketType_ResumeFromTarget: + this->ProcessSendResumePacket(); + break; + case HtcctrlPacketType_DisconnectFromTarget: + this->ProcessSendDisconnectPacket(); + break; + case HtcctrlPacketType_BeaconResponse: + case HtcctrlPacketType_InformationFromTarget: + break; + default: + AMS_ABORT("Send unsupported packet 0x%04x\n", static_cast(header.packet_type)); + } + } + Result HtcctrlService::NotifyDriverConnected() { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp index b94dc58a8..608d8ada5 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp @@ -65,6 +65,12 @@ namespace ams::htclow::ctrl { Result ProcessReceiveBeaconQueryPacket(); Result ProcessReceiveUnexpectedPacket(); + void ProcessSendConnectPacket(); + void ProcessSendReadyPacket(); + void ProcessSendSuspendPacket(); + void ProcessSendResumePacket(); + void ProcessSendDisconnectPacket(); + void UpdateServiceChannels(const void *body, size_t body_size); void TryReadyInternal(); @@ -77,9 +83,14 @@ namespace ams::htclow::ctrl { void SetDriverType(impl::DriverType driver_type); + os::EventType *GetSendPacketEvent() { return m_event.GetBase(); } + Result CheckReceivedHeader(const HtcctrlPacketHeader &header) const; Result ProcessReceivePacket(const HtcctrlPacketHeader &header, const void *body, size_t body_size); + bool QuerySendPacket(HtcctrlPacketHeader *header, HtcctrlPacketBody *body, int *out_body_size); + void RemovePacket(const HtcctrlPacketHeader &header); + Result NotifyDriverConnected(); Result NotifyDriverDisconnected(); }; diff --git a/libraries/libstratosphere/source/htclow/htclow_packet.hpp b/libraries/libstratosphere/source/htclow/htclow_packet.hpp index 369753d1d..925cace68 100644 --- a/libraries/libstratosphere/source/htclow/htclow_packet.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_packet.hpp @@ -80,7 +80,7 @@ namespace ams::htclow { return m_packet_size - sizeof(HeaderType); } - u8 *GetBody() { + u8 *GetBody() const { if (this->GetBodySize() > 0) { return m_header + sizeof(HeaderType); } else { diff --git a/libraries/libstratosphere/source/htclow/htclow_worker.cpp b/libraries/libstratosphere/source/htclow/htclow_worker.cpp index d5bc43592..f04cd7eaa 100644 --- a/libraries/libstratosphere/source/htclow/htclow_worker.cpp +++ b/libraries/libstratosphere/source/htclow/htclow_worker.cpp @@ -83,7 +83,8 @@ namespace ams::htclow { Result Worker::ProcessReceive() { /* Forever receive packets. */ - u8 packet_header_storage[sizeof(m_packet_header)]; + constexpr size_t MaxPacketHeaderSize = std::max(sizeof(PacketHeader), sizeof(ctrl::HtcctrlPacketHeader)); + u8 packet_header_storage[MaxPacketHeaderSize]; while (true) { /* Receive the packet header. */ R_TRY(m_driver->Receive(packet_header_storage, sizeof(packet_header_storage))); @@ -134,8 +135,47 @@ namespace ams::htclow { } Result Worker::ProcessSend() { - /* TODO */ - AMS_ABORT("Worker::ProcessSend"); + /* Forever process packets. */ + while (true) { + const auto index = os::WaitAny(m_service->GetSendPacketEvent(), m_mux->GetSendPacketEvent(), m_event.GetBase()); + if (index == 0) { + /* HtcctrlService packet. */ + + /* Clear the packet event. */ + os::ClearEvent(m_service->GetSendPacketEvent()); + + /* While we have packets, send them. */ + auto *packet_header = reinterpret_cast(m_send_buffer); + auto *packet_body = reinterpret_cast(m_send_buffer + sizeof(*packet_header)); + int body_size; + while (m_service->QuerySendPacket(packet_header, packet_body, std::addressof(body_size))) { + m_service->RemovePacket(*packet_header); + R_TRY(m_driver->Send(packet_header, body_size + sizeof(*packet_header))); + } + } else if (index == 1) { + /* Mux packet. */ + + /* Clear the packet event. */ + os::ClearEvent(m_mux->GetSendPacketEvent()); + + /* While we have packets, send them. */ + auto *packet_header = reinterpret_cast(m_send_buffer); + auto *packet_body = reinterpret_cast(m_send_buffer + sizeof(*packet_header)); + int body_size; + while (m_mux->QuerySendPacket(packet_header, packet_body, std::addressof(body_size))) { + R_TRY(m_driver->Send(packet_header, body_size + sizeof(*packet_header))); + m_mux->RemovePacket(*packet_header); + } + } else { + /* Our event. */ + + /* Check if we're cancelled. */ + if (m_cancelled) { + return htclow::ResultCancelled(); + } + } + } + } } diff --git a/libraries/libstratosphere/source/htclow/htclow_worker.hpp b/libraries/libstratosphere/source/htclow/htclow_worker.hpp index 1e9529f1f..6efb0d6c9 100644 --- a/libraries/libstratosphere/source/htclow/htclow_worker.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_worker.hpp @@ -27,8 +27,7 @@ namespace ams::htclow { static_assert(sizeof(ctrl::HtcctrlPacketBody) <= sizeof(PacketBody)); private: u32 m_thread_stack_size; - u8 m_packet_header[sizeof(PacketHeader)]; - u8 m_send_packet_body[sizeof(PacketBody)]; + u8 m_send_buffer[sizeof(PacketHeader) + sizeof(PacketBody)]; u8 m_receive_packet_body[sizeof(PacketBody)]; mem::StandardAllocator *m_allocator; mux::Mux *m_mux; diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp index 9db51a24f..b42be9a7f 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp @@ -20,8 +20,8 @@ namespace ams::htclow::mux { Mux::Mux(PacketFactory *pf, ctrl::HtcctrlStateMachine *sm) - : m_packet_factory(pf), m_state_machine(sm), m_task_manager(), m_wake_event(os::EventClearMode_ManualClear), - m_channel_impl_map(pf, sm, std::addressof(m_task_manager), std::addressof(m_wake_event)), m_global_send_buffer(pf), + : m_packet_factory(pf), m_state_machine(sm), m_task_manager(), m_event(os::EventClearMode_ManualClear), + m_channel_impl_map(pf, sm, std::addressof(m_task_manager), std::addressof(m_event)), m_global_send_buffer(pf), m_mutex(), m_is_sleeping(false), m_version(ProtocolVersion) { /* ... */ @@ -98,7 +98,7 @@ namespace ams::htclow::mux { m_is_sleeping = true; } else { m_is_sleeping = false; - m_wake_event.Signal(); + m_event.Signal(); } } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp index 8a41df89e..4538a1455 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp @@ -26,7 +26,7 @@ namespace ams::htclow::mux { PacketFactory *m_packet_factory; ctrl::HtcctrlStateMachine *m_state_machine; TaskManager m_task_manager; - os::Event m_wake_event; + os::Event m_event; ChannelImplMap m_channel_impl_map; GlobalSendBuffer m_global_send_buffer; os::SdkMutex m_mutex; @@ -37,9 +37,14 @@ namespace ams::htclow::mux { void SetVersion(u16 version); + os::EventType *GetSendPacketEvent() { return m_event.GetBase(); } + Result CheckReceivedHeader(const PacketHeader &header) const; Result ProcessReceivePacket(const PacketHeader &header, const void *body, size_t body_size); + bool QuerySendPacket(PacketHeader *header, PacketBody *body, int *out_body_size); + void RemovePacket(const PacketHeader &header); + void UpdateChannelState(); void UpdateMuxState(); private: From 4ed665bcd341b721fad4ef644f0a4b98083a9f04 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 9 Feb 2021 03:21:45 -0800 Subject: [PATCH 029/280] htc: implement remaining worker thread send logic (for channel mux) --- .../source/htclow/mux/htclow_mux.cpp | 70 +++++++++++++- .../source/htclow/mux/htclow_mux.hpp | 10 +- .../htclow/mux/htclow_mux_channel_impl.cpp | 24 +++++ .../htclow/mux/htclow_mux_channel_impl.hpp | 8 +- .../mux/htclow_mux_channel_impl_map.hpp | 2 +- .../mux/htclow_mux_global_send_buffer.cpp | 52 ++++++++++ .../mux/htclow_mux_global_send_buffer.hpp | 5 + .../htclow/mux/htclow_mux_ring_buffer.cpp | 46 +++++++++ .../htclow/mux/htclow_mux_ring_buffer.hpp | 8 +- .../htclow/mux/htclow_mux_send_buffer.cpp | 95 +++++++++++++++++++ .../htclow/mux/htclow_mux_send_buffer.hpp | 12 +++ .../htclow/mux/htclow_mux_task_manager.cpp | 16 ++++ .../htclow/mux/htclow_mux_task_manager.hpp | 10 +- .../vapours/results/htclow_results.hpp | 1 + 14 files changed, 346 insertions(+), 13 deletions(-) create mode 100644 libraries/libstratosphere/source/htclow/mux/htclow_mux_global_send_buffer.cpp diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp index b42be9a7f..c32fac3cb 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp @@ -15,6 +15,7 @@ */ #include #include "htclow_mux.hpp" +#include "../htclow_packet_factory.hpp" #include "../ctrl/htclow_ctrl_state_machine.hpp" namespace ams::htclow::mux { @@ -22,7 +23,7 @@ namespace ams::htclow::mux { Mux::Mux(PacketFactory *pf, ctrl::HtcctrlStateMachine *sm) : m_packet_factory(pf), m_state_machine(sm), m_task_manager(), m_event(os::EventClearMode_ManualClear), m_channel_impl_map(pf, sm, std::addressof(m_task_manager), std::addressof(m_event)), m_global_send_buffer(pf), - m_mutex(), m_is_sleeping(false), m_version(ProtocolVersion) + m_mutex(), m_state(MuxState::Normal), m_version(ProtocolVersion) { /* ... */ } @@ -78,6 +79,50 @@ namespace ams::htclow::mux { } } + bool Mux::QuerySendPacket(PacketHeader *header, PacketBody *body, int *out_body_size) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Get our map. */ + auto &map = m_channel_impl_map.GetMap(); + + /* Iterate the map, checking for valid packet each time. */ + for (auto &pair : map) { + /* Get the current channel impl. */ + auto &channel_impl = m_channel_impl_map.GetChannelImpl(pair.second); + + /* Check for an error packet. */ + /* NOTE: it's unclear why Nintendo does this every iteration of the loop... */ + if (auto *error_packet = m_global_send_buffer.GetNextPacket(); error_packet != nullptr) { + std::memcpy(header, error_packet->GetHeader(), sizeof(*header)); + *out_body_size = 0; + return true; + } + + /* See if the channel has something for us to send. */ + if (channel_impl.QuerySendPacket(header, body, out_body_size)) { + return this->IsSendable(header->packet_type); + } + } + + return false; + } + + void Mux::RemovePacket(const PacketHeader &header) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Remove the packet from the appropriate source. */ + if (header.packet_type == PacketType_Error) { + m_global_send_buffer.RemovePacket(); + } else if (m_channel_impl_map.Exists(header.channel)) { + m_channel_impl_map[header.channel].RemovePacket(header); + } + + /* Notify the task manager. */ + m_task_manager.NotifySendReady(); + } + void Mux::UpdateChannelState() { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); @@ -95,9 +140,9 @@ namespace ams::htclow::mux { /* Update whether we're sleeping. */ if (m_state_machine->IsSleeping()) { - m_is_sleeping = true; + m_state = MuxState::Sleep; } else { - m_is_sleeping = false; + m_state = MuxState::Normal; m_event.Signal(); } } @@ -108,8 +153,23 @@ namespace ams::htclow::mux { } Result Mux::SendErrorPacket(impl::ChannelInternalType channel) { - /* TODO */ - AMS_ABORT("Mux::SendErrorPacket"); + /* Create and send the packet. */ + R_TRY(m_global_send_buffer.AddPacket(m_packet_factory->MakeErrorPacket(channel))); + + /* Signal our event. */ + m_event.Signal(); + + return ResultSuccess(); + } + + bool Mux::IsSendable(PacketType packet_type) const { + switch (m_state) { + case MuxState::Normal: + return true; + case MuxState::Sleep: + return false; + AMS_UNREACHABLE_DEFAULT_CASE(); + } } } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp index 4538a1455..48e1a3a8e 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp @@ -21,7 +21,13 @@ namespace ams::htclow::mux { + enum class MuxState { + Normal, + Sleep, + }; + class Mux { + private: private: PacketFactory *m_packet_factory; ctrl::HtcctrlStateMachine *m_state_machine; @@ -30,7 +36,7 @@ namespace ams::htclow::mux { ChannelImplMap m_channel_impl_map; GlobalSendBuffer m_global_send_buffer; os::SdkMutex m_mutex; - bool m_is_sleeping; + MuxState m_state; s16 m_version; public: Mux(PacketFactory *pf, ctrl::HtcctrlStateMachine *sm); @@ -51,6 +57,8 @@ namespace ams::htclow::mux { Result CheckChannelExist(impl::ChannelInternalType channel); Result SendErrorPacket(impl::ChannelInternalType channel); + + bool IsSendable(PacketType packet_type) const; }; } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp index 9e523c1ac..b475f4cf2 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp @@ -132,6 +132,30 @@ namespace ams::htclow::mux { return ResultSuccess(); } + bool ChannelImpl::QuerySendPacket(PacketHeader *header, PacketBody *body, int *out_body_size) { + /* Check our send buffer. */ + if (m_send_buffer.QueryNextPacket(header, body, out_body_size, m_next_max_data, m_total_send_size, m_share.has_value(), m_share.value_or(0))) { + /* Update tracking variables. */ + if (header->packet_type == PacketType_Data) { + m_cur_max_data = m_next_max_data; + } + + return true; + } else { + return false; + } + } + + void ChannelImpl::RemovePacket(const PacketHeader &header) { + /* Remove the packet. */ + m_send_buffer.RemovePacket(header); + + /* Check if the send buffer is now empty. */ + if (m_send_buffer.Empty()) { + m_task_manager->NotifySendBufferEmpty(m_channel); + } + } + void ChannelImpl::UpdateState() { /* Check if shutdown must be forced. */ if (m_state_machine->IsUnsupportedServiceChannelToShutdown(m_channel)) { diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp index bc78d4a6f..5b7ba3485 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp @@ -43,7 +43,9 @@ namespace ams::htclow::mux { RingBuffer m_receive_buffer; s16 m_version; ChannelConfig m_config; - /* TODO: tracking variables. */ + u64 m_total_send_size; + u64 m_next_max_data; + u64 m_cur_max_data; u64 m_offset; std::optional m_share; os::Event m_state_change_event; @@ -55,6 +57,10 @@ namespace ams::htclow::mux { Result ProcessReceivePacket(const PacketHeader &header, const void *body, size_t body_size); + bool QuerySendPacket(PacketHeader *header, PacketBody *body, int *out_body_size); + + void RemovePacket(const PacketHeader &header); + void UpdateState(); private: void ShutdownForce(); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp index 3b193975e..52e131c35 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp @@ -40,13 +40,13 @@ namespace ams::htclow::mux { public: ChannelImplMap(PacketFactory *pf, ctrl::HtcctrlStateMachine *sm, TaskManager *tm, os::Event *ev); + ChannelImpl &GetChannelImpl(int index); ChannelImpl &GetChannelImpl(impl::ChannelInternalType channel); bool Exists(impl::ChannelInternalType channel) const { return m_map.find(channel) != m_map.end(); } private: - ChannelImpl &GetChannelImpl(int index); public: MapType &GetMap() { return m_map; diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_global_send_buffer.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_global_send_buffer.cpp new file mode 100644 index 000000000..c825efbde --- /dev/null +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_global_send_buffer.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htclow_mux_global_send_buffer.hpp" +#include "../htclow_packet_factory.hpp" + +namespace ams::htclow::mux { + + Packet *GlobalSendBuffer::GetNextPacket() { + if (!m_packet_list.empty()) { + return std::addressof(m_packet_list.front()); + } else { + return nullptr; + } + } + + Result GlobalSendBuffer::AddPacket(std::unique_ptr ptr) { + /* Global send buffer only supports adding error packets. */ + R_UNLESS(ptr->GetHeader()->packet_type == PacketType_Error, htclow::ResultInvalidArgument()); + + /* Check if we already have an error packet for the channel. */ + for (const auto &packet : m_packet_list) { + R_SUCCEED_IF(packet.GetHeader()->channel == ptr->GetHeader()->channel); + } + + /* We don't, so push back a new one. */ + m_packet_list.push_back(*(ptr.release())); + + return ResultSuccess(); + } + + void GlobalSendBuffer::RemovePacket() { + auto *packet = std::addressof(m_packet_list.front()); + m_packet_list.pop_front(); + + m_packet_factory->Delete(packet); + } + +} diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_global_send_buffer.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_global_send_buffer.hpp index ce32dca3c..5e4319a2e 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_global_send_buffer.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_global_send_buffer.hpp @@ -33,6 +33,11 @@ namespace ams::htclow::mux { PacketList m_packet_list; public: GlobalSendBuffer(PacketFactory *pf) : m_packet_factory(pf), m_packet_list() { /* ... */ } + + Packet *GetNextPacket(); + + Result AddPacket(std::unique_ptr ptr); + void RemovePacket(); }; } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.cpp index bf76300cb..0e8903dcc 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.cpp @@ -45,4 +45,50 @@ namespace ams::htclow::mux { return ResultSuccess(); } + Result RingBuffer::Copy(void *dst, size_t size) { + /* Select buffer to discard from. */ + void *buffer = m_is_read_only ? m_read_only_buffer : m_buffer; + R_UNLESS(buffer != nullptr, htclow::ResultChannelBufferHasNotEnoughData()); + + /* Verify that we have enough data. */ + R_UNLESS(m_data_size >= size, htclow::ResultChannelBufferHasNotEnoughData()); + + /* Determine position and copy sizes. */ + const size_t pos = m_offset; + const size_t left = std::min(m_buffer_size - pos, size); + const size_t over = size - left; + + /* Copy. */ + if (left != 0) { + std::memcpy(dst, static_cast(buffer) + pos, left); + } + if (over != 0) { + std::memcpy(static_cast(dst) + left, buffer, over); + } + + /* Mark that we can discard. */ + m_can_discard = true; + + return ResultSuccess(); + } + + Result RingBuffer::Discard(size_t size) { + /* Select buffer to discard from. */ + void *buffer = m_is_read_only ? m_read_only_buffer : m_buffer; + R_UNLESS(buffer != nullptr, htclow::ResultChannelBufferHasNotEnoughData()); + + /* Verify that the data we're discarding has been read. */ + R_UNLESS(m_can_discard, htclow::ResultChannelCannotDiscard()); + + /* Verify that we have enough data. */ + R_UNLESS(m_data_size >= size, htclow::ResultChannelBufferHasNotEnoughData()); + + /* Discard. */ + m_offset = (m_offset + size) % m_buffer_size; + m_data_size -= size; + m_can_discard = false; + + return ResultSuccess(); + } + } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.hpp index 7cd451f89..2cae14183 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.hpp @@ -26,13 +26,17 @@ namespace ams::htclow::mux { size_t m_buffer_size; size_t m_data_size; size_t m_offset; - bool m_has_copied; + bool m_can_discard; public: - RingBuffer() : m_buffer(), m_read_only_buffer(), m_is_read_only(true), m_buffer_size(), m_data_size(), m_offset(), m_has_copied(false) { /* ... */ } + RingBuffer() : m_buffer(), m_read_only_buffer(), m_is_read_only(true), m_buffer_size(), m_data_size(), m_offset(), m_can_discard(false) { /* ... */ } size_t GetDataSize() { return m_data_size; } Result Write(const void *data, size_t size); + + Result Copy(void *dst, size_t size); + + Result Discard(size_t size); }; } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp index 0927eba75..8e5c510ce 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp @@ -19,11 +19,106 @@ namespace ams::htclow::mux { + bool SendBuffer::IsPriorPacket(PacketType packet_type) const { + return packet_type == PacketType_MaxData; + } + void SendBuffer::SetVersion(s16 version) { /* Set version. */ m_version = version; } + void SendBuffer::MakeDataPacketHeader(PacketHeader *header, int body_size, s16 version, u64 share, u32 offset) const { + /* Set all packet fields. */ + header->signature = HtcGen2Signature; + header->offset = offset; + header->reserved = 0; + header->version = version; + header->body_size = body_size; + header->channel = m_channel; + header->packet_type = PacketType_Data; + header->share = share; + } + + void SendBuffer::CopyPacket(PacketHeader *header, PacketBody *body, int *out_body_size, const Packet &packet) { + /* Get the body size. */ + const int body_size = packet.GetBodySize(); + AMS_ASSERT(0 <= body_size && body_size <= static_cast(sizeof(*body))); + + /* Copy the header. */ + std::memcpy(header, packet.GetHeader(), sizeof(*header)); + + /* Copy the body. */ + std::memcpy(body, packet.GetBody(), body_size); + + /* Set the output body size. */ + *out_body_size = body_size; + } + + bool SendBuffer::QueryNextPacket(PacketHeader *header, PacketBody *body, int *out_body_size, u64 max_data, u64 total_send_size, bool has_share, u64 share) { + /* Check for a max data packet. */ + if (!m_packet_list.empty()) { + this->CopyPacket(header, body, out_body_size, m_packet_list.front()); + return true; + } + + /* Check that we have data. */ + const auto ring_buffer_data_size = m_ring_buffer.GetDataSize(); + if (ring_buffer_data_size > 0) { + return false; + } + + /* Check that we're valid for flow control. */ + if (m_flow_control_enabled && !has_share) { + return false; + } + + /* Determine the sendable size. */ + const auto offset = total_send_size - ring_buffer_data_size; + const auto sendable_size = std::min(share - offset, ring_buffer_data_size); + if (sendable_size == 0) { + return false; + } + + /* We're additionally bound by the actual packet size. */ + const auto data_size = std::min(sendable_size, m_max_packet_size); + + /* Make data packet header. */ + this->MakeDataPacketHeader(header, data_size, m_version, max_data, share); + + /* Copy the data. */ + R_ABORT_UNLESS(m_ring_buffer.Copy(body, data_size)); + + /* Set output body size. */ + *out_body_size = data_size; + return true; + } + + void SendBuffer::RemovePacket(const PacketHeader &header) { + /* Get the packet type. */ + const auto packet_type = header.packet_type; + + if (this->IsPriorPacket(packet_type)) { + /* Packet will be using our list. */ + auto *packet = std::addressof(m_packet_list.front()); + m_packet_list.pop_front(); + m_packet_factory->Delete(packet); + } else { + /* Packet managed by ring buffer. */ + AMS_ABORT_UNLESS(packet_type == PacketType_Data); + + /* Discard the packet's data. */ + const Result result = m_ring_buffer.Discard(header.body_size); + if (!htclow::ResultChannelCannotDiscard::Includes(result)) { + R_ABORT_UNLESS(result); + } + } + } + + bool SendBuffer::Empty() { + return m_packet_list.empty() && m_ring_buffer.GetDataSize() == 0; + } + void SendBuffer::Clear() { while (!m_packet_list.empty()) { auto *packet = std::addressof(m_packet_list.front()); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp index 5d7af800f..2c7ee6b5d 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp @@ -37,11 +37,23 @@ namespace ams::htclow::mux { s16 m_version; bool m_flow_control_enabled; size_t m_max_packet_size; + private: + bool IsPriorPacket(PacketType packet_type) const; + + void MakeDataPacketHeader(PacketHeader *header, int body_size, s16 version, u64 share, u32 offset) const; + + void CopyPacket(PacketHeader *header, PacketBody *body, int *out_body_size, const Packet &packet); public: SendBuffer(impl::ChannelInternalType channel, PacketFactory *pf); void SetVersion(s16 version); + bool QueryNextPacket(PacketHeader *header, PacketBody *body, int *out_body_size, u64 max_data, u64 total_send_size, bool has_share, u64 share); + + void RemovePacket(const PacketHeader &header); + + bool Empty(); + void Clear(); }; diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp index 3e2046acf..c96b0535f 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp @@ -34,6 +34,22 @@ namespace ams::htclow::mux { } } + void TaskManager::NotifySendReady() { + for (auto i = 0; i < MaxTaskCount; ++i) { + if (m_valid[i] && m_tasks[i].type == TaskType_Send) { + this->CompleteTask(i, EventTrigger_SendReady); + } + } + } + + void TaskManager::NotifySendBufferEmpty(impl::ChannelInternalType channel) { + for (auto i = 0; i < MaxTaskCount; ++i) { + if (m_valid[i] && m_tasks[i].channel == channel && m_tasks[i].type == TaskType_Flush) { + this->CompleteTask(i, EventTrigger_SendBufferEmpty); + } + } + } + void TaskManager::NotifyConnectReady() { for (auto i = 0; i < MaxTaskCount; ++i) { if (m_valid[i] && m_tasks[i].type == TaskType_Connect) { diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp index 9c5c03423..5feac0803 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp @@ -21,9 +21,11 @@ namespace ams::htclow::mux { constexpr inline int MaxTaskCount = 0x80; enum EventTrigger : u8 { - EventTrigger_Disconnect = 1, - EventTrigger_ReceiveData = 2, - EventTrigger_ConnectReady = 11, + EventTrigger_Disconnect = 1, + EventTrigger_ReceiveData = 2, + EventTrigger_SendReady = 5, + EventTrigger_SendBufferEmpty = 10, + EventTrigger_ConnectReady = 11, }; class TaskManager { @@ -51,6 +53,8 @@ namespace ams::htclow::mux { void NotifyDisconnect(impl::ChannelInternalType channel); void NotifyReceiveData(impl::ChannelInternalType channel, size_t size); + void NotifySendReady(); + void NotifySendBufferEmpty(impl::ChannelInternalType channel); void NotifyConnectReady(); private: void CompleteTask(int index, EventTrigger trigger); diff --git a/libraries/libvapours/include/vapours/results/htclow_results.hpp b/libraries/libvapours/include/vapours/results/htclow_results.hpp index 30e976b20..6294e5977 100644 --- a/libraries/libvapours/include/vapours/results/htclow_results.hpp +++ b/libraries/libvapours/include/vapours/results/htclow_results.hpp @@ -39,6 +39,7 @@ namespace ams::htclow { R_DEFINE_ERROR_RESULT(ChannelStateTransitionError, 1104); R_DEFINE_ERROR_RESULT(ChannelReceiveBufferEmpty, 1106); R_DEFINE_ERROR_RESULT(ChannelSequenceIdNotMatched, 1107); + R_DEFINE_ERROR_RESULT(ChannelCannotDiscard, 1108); R_DEFINE_ERROR_RANGE(DriverError, 1200, 1999); R_DEFINE_ERROR_RESULT(DriverOpened, 1201); From 4408ad6a4797703284b1c8cd59fa2b4016fb91d0 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 9 Feb 2021 04:35:30 -0800 Subject: [PATCH 030/280] htc: module id names, skeleton rest of main --- .../htclow/htclow_module_types.hpp | 4 +- .../ctrl/htclow_ctrl_service_channels.hpp | 16 ++-- stratosphere/htc/source/htc_main.cpp | 77 ++++++++++++++++++- 3 files changed, 86 insertions(+), 11 deletions(-) diff --git a/libraries/libstratosphere/include/stratosphere/htclow/htclow_module_types.hpp b/libraries/libstratosphere/include/stratosphere/htclow/htclow_module_types.hpp index ef8c92e3f..f5a8817ef 100644 --- a/libraries/libstratosphere/include/stratosphere/htclow/htclow_module_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/htclow/htclow_module_types.hpp @@ -20,7 +20,9 @@ namespace ams::htclow { enum class ModuleId : u8 { - /* ... */ + Htcfs = 1, + Htcmisc = 3, + Htcs = 4, }; struct ModuleType { diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service_channels.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service_channels.hpp index bcaf32b9f..d42bb45c0 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service_channels.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service_channels.hpp @@ -20,20 +20,20 @@ namespace ams::htclow::ctrl { constexpr inline const impl::ChannelInternalType ServiceChannels[] = { { - .channel_id = 0, - .module_id = static_cast(1), + .channel_id = 0, /* TODO: htcfs::ChannelId? */ + .module_id = ModuleId::Htcfs, }, { - .channel_id = 1, - .module_id = static_cast(3), + .channel_id = 1, /* TODO: htcmisc::ServerChannelId? */ + .module_id = ModuleId::Htcmisc, }, { - .channel_id = 2, - .module_id = static_cast(3), + .channel_id = 2, /* TODO: htcmisc::ClientChannelId? */ + .module_id = ModuleId::Htcmisc, }, { - .channel_id = 0, - .module_id = static_cast(4), + .channel_id = 0, /* TODO: htcs::ChannelId? */ + .module_id = ModuleId::Htcs, }, }; diff --git a/stratosphere/htc/source/htc_main.cpp b/stratosphere/htc/source/htc_main.cpp index e9797aca1..17e0a5c03 100644 --- a/stratosphere/htc/source/htc_main.cpp +++ b/stratosphere/htc/source/htc_main.cpp @@ -153,6 +153,12 @@ namespace ams::htc { constexpr htclow::impl::DriverType DefaultHtclowDriverType = htclow::impl::DriverType::Usb; + constexpr inline size_t NumHtcsIpcThreads = 8; + + alignas(os::ThreadStackAlignment) u8 g_htc_ipc_thread_stack[4_KB]; + alignas(os::ThreadStackAlignment) u8 g_htcfs_ipc_thread_stack[4_KB]; + alignas(os::ThreadStackAlignment) u8 g_htcs_ipc_thread_stack[NumHtcsIpcThreads][4_KB]; + htclow::impl::DriverType GetHtclowDriverType() { /* Get the transport type. */ char transport[0x10]; @@ -178,6 +184,18 @@ namespace ams::htc { } } + void HtcIpcThreadFunction(void *arg) { + //htc::server::LoopHtcmiscServer(); + } + + void HtcfsIpcThreadFunction(void *arg) { + //htcfs::LoopHipcServer(); + } + + void HtcsIpcThreadFunction(void *arg) { + //htcs::server::LoopHipcServer(); + } + } } @@ -194,10 +212,65 @@ int main(int argc, char **argv) /* Initialize the htclow manager. */ htclow::HtclowManagerHolder::AddReference(); + ON_SCOPE_EXIT { htclow::HtclowManagerHolder::Release(); }; - /* TODO */ + /* Get the htclow manager. */ + auto *htclow_manager = htclow::HtclowManagerHolder::GetHtclowManager(); + + /* Initialize the htc misc server. */ + //htc::server::InitializeHtcmiscServer(htclow_manager); + + /* Create the htc misc ipc thread. */ + os::ThreadType htc_ipc_thread; + os::CreateThread(std::addressof(htc_ipc_thread), htc::HtcIpcThreadFunction, nullptr, htc::g_htc_ipc_thread_stack, sizeof(htc::g_htc_ipc_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(htc, HtcIpc)); + os::SetThreadNamePointer(std::addressof(htc_ipc_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, HtcIpc)); + + /* Initialize the htcfs server. */ + //htcfs::Initialize(); + //htcfs::RegisterHipcServer(); + + /* Create the htcfs ipc thread. */ + os::ThreadType htcfs_ipc_thread; + os::CreateThread(std::addressof(htcfs_ipc_thread), htc::HtcfsIpcThreadFunction, nullptr, htc::g_htcfs_ipc_thread_stack, sizeof(htc::g_htcfs_ipc_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(htc, HtcfsIpc)); + os::SetThreadNamePointer(std::addressof(htcfs_ipc_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, HtcfsIpc)); + + /* Initialize the htcs server. */ + //htcs::server::Initialize(); + //htcs::server::RegisterHipcServer(); + + /* Create the htcs ipc threads. */ + os::ThreadType htcs_ipc_threads[htc::NumHtcsIpcThreads]; + for (size_t i = 0; i < htc::NumHtcsIpcThreads; ++i) { + os::CreateThread(std::addressof(htcs_ipc_threads[i]), htc::HtcsIpcThreadFunction, nullptr, htc::g_htcs_ipc_thread_stack[i], sizeof(htc::g_htcs_ipc_thread_stack[i]), AMS_GET_SYSTEM_THREAD_PRIORITY(htc, HtcsIpc)); + os::SetThreadNamePointer(std::addressof(htcs_ipc_threads[i]), AMS_GET_SYSTEM_THREAD_NAME(htc, HtcsIpc)); + } + + /* Initialize psc. */ + //htc::server::InitializePowerStateMonitor(driver_type, htclow_manager); + + /* Start all threads. */ + os::StartThread(std::addressof(htc_ipc_thread)); + os::StartThread(std::addressof(htcfs_ipc_thread)); + for (size_t i = 0; i < htc::NumHtcsIpcThreads; ++i) { + os::StartThread(std::addressof(htcs_ipc_threads[i])); + } + + /* Loop psc monitor. */ + //htc::server::LoopMonitorPowerState(); + + /* Destroy all threads. */ + for (size_t i = 0; i < htc::NumHtcsIpcThreads; ++i) { + os::WaitThread(std::addressof(htcs_ipc_threads[i])); + os::DestroyThread(std::addressof(htcs_ipc_threads[i])); + } + os::WaitThread(std::addressof(htcfs_ipc_thread)); + os::DestroyThread(std::addressof(htcfs_ipc_thread)); + os::WaitThread(std::addressof(htc_ipc_thread)); + os::DestroyThread(std::addressof(htc_ipc_thread)); + + /* Finalize psc monitor. */ + //htc::server::FinalizePowerStateMonitor(); - /* Cleanup */ return 0; } From 889f144b27718d4ef8504bef6a55f6662a56b4e4 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 9 Feb 2021 05:37:54 -0800 Subject: [PATCH 031/280] htc: hook up creation of the htc manager service object --- .../libstratosphere/include/stratosphere.hpp | 1 + .../include/stratosphere/htc.hpp | 1 + .../htc/server/htc_htcmisc_hipc_server.hpp | 35 ++++++ .../include/stratosphere/tma.hpp | 18 ++++ .../stratosphere/tma/tma_i_htc_manager.hpp | 41 +++++++ .../htc/server/htc_htc_service_object.cpp | 102 ++++++++++++++++++ .../htc/server/htc_htc_service_object.hpp | 52 +++++++++ .../htc/server/htc_htcmisc_hipc_server.cpp | 89 +++++++++++++++ .../source/htc/server/htc_htcmisc_manager.cpp | 46 ++++++++ .../source/htc/server/htc_htcmisc_manager.hpp | 24 +++++ .../source/htclow/htclow_manager_impl.cpp | 10 +- stratosphere/htc/source/htc_main.cpp | 4 +- 12 files changed, 418 insertions(+), 5 deletions(-) create mode 100644 libraries/libstratosphere/include/stratosphere/htc/server/htc_htcmisc_hipc_server.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/tma.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/tma/tma_i_htc_manager.hpp create mode 100644 libraries/libstratosphere/source/htc/server/htc_htc_service_object.cpp create mode 100644 libraries/libstratosphere/source/htc/server/htc_htc_service_object.hpp create mode 100644 libraries/libstratosphere/source/htc/server/htc_htcmisc_hipc_server.cpp create mode 100644 libraries/libstratosphere/source/htc/server/htc_htcmisc_manager.cpp create mode 100644 libraries/libstratosphere/source/htc/server/htc_htcmisc_manager.hpp diff --git a/libraries/libstratosphere/include/stratosphere.hpp b/libraries/libstratosphere/include/stratosphere.hpp index a05c6edad..d032cab3c 100644 --- a/libraries/libstratosphere/include/stratosphere.hpp +++ b/libraries/libstratosphere/include/stratosphere.hpp @@ -82,6 +82,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libstratosphere/include/stratosphere/htc.hpp b/libraries/libstratosphere/include/stratosphere/htc.hpp index 999d1a35e..571efc768 100644 --- a/libraries/libstratosphere/include/stratosphere/htc.hpp +++ b/libraries/libstratosphere/include/stratosphere/htc.hpp @@ -15,3 +15,4 @@ */ #pragma once +#include diff --git a/libraries/libstratosphere/include/stratosphere/htc/server/htc_htcmisc_hipc_server.hpp b/libraries/libstratosphere/include/stratosphere/htc/server/htc_htcmisc_hipc_server.hpp new file mode 100644 index 000000000..42f3e3e5d --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htc/server/htc_htcmisc_hipc_server.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htclow { + + class HtclowManager; + +} + +namespace ams::htc::server { + + void InitializeHtcmiscServer(htclow::HtclowManager *htclow_manager); + void FinalizeHtcmiscServer(); + void LoopHtcmiscServer(); + void RequestStopHtcmiscServer(); + + class HtcmiscImpl; + HtcmiscImpl *GetHtcmiscImpl(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/tma.hpp b/libraries/libstratosphere/include/stratosphere/tma.hpp new file mode 100644 index 000000000..fbb181ebe --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/tma.hpp @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include diff --git a/libraries/libstratosphere/include/stratosphere/tma/tma_i_htc_manager.hpp b/libraries/libstratosphere/include/stratosphere/tma/tma_i_htc_manager.hpp new file mode 100644 index 000000000..f9175ab9f --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/tma/tma_i_htc_manager.hpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +/* NOTE: Minimum firmware version not enforced for any commands. */ +#define AMS_TMA_I_HTC_MANAGER_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 0, Result, GetEnvironmentVariable, (sf::Out out_size, const sf::OutBuffer &out, const sf::InBuffer &name), (out_size, out, name)) \ + AMS_SF_METHOD_INFO(C, H, 1, Result, GetEnvironmentVariableLength, (sf::Out out_size, const sf::InBuffer &name), (out_size, name)) \ + AMS_SF_METHOD_INFO(C, H, 2, Result, GetHostConnectionEvent, (sf::OutCopyHandle out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 3, Result, GetHostDisconnectionEvent, (sf::OutCopyHandle out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 4, Result, GetHostConnectionEventForSystem, (sf::OutCopyHandle out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 5, Result, GetHostDisconnectionEventForSystem, (sf::OutCopyHandle out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 6, Result, GetBridgeIpAddress, (const sf::OutBuffer &out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 7, Result, GetBridgePort, (const sf::OutBuffer &out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 8, Result, SetCradleAttached, (bool attached), (attached)) \ + AMS_SF_METHOD_INFO(C, H, 9, Result, GetBridgeSubnetMask, (const sf::OutBuffer &out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 10, Result, GetBridgeMacAddress, (const sf::OutBuffer &out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 11, Result, GetWorkingDirectoryPath, (const sf::OutBuffer &out, s32 max_len), (out, max_len)) \ + AMS_SF_METHOD_INFO(C, H, 12, Result, GetWorkingDirectoryPathSize, (sf::Out out_size), (out_size)) \ + AMS_SF_METHOD_INFO(C, H, 13, Result, RunOnHostStart, (sf::Out out_id, sf::OutCopyHandle out, const sf::InBuffer &args), (out_id, out, args)) \ + AMS_SF_METHOD_INFO(C, H, 14, Result, RunOnHostResults, (sf::Out out_result, u32 id), (out_result, id)) \ + AMS_SF_METHOD_INFO(C, H, 15, Result, SetBridgeIpAddress, (const sf::InBuffer &arg), (arg)) \ + AMS_SF_METHOD_INFO(C, H, 16, Result, SetBridgeSubnetMask, (const sf::InBuffer &arg), (arg)) \ + AMS_SF_METHOD_INFO(C, H, 17, Result, SetBridgePort, (const sf::InBuffer &arg), (arg)) + +AMS_SF_DEFINE_INTERFACE(ams::tma, IHtcManager, AMS_TMA_I_HTC_MANAGER_INTERFACE_INFO) diff --git a/libraries/libstratosphere/source/htc/server/htc_htc_service_object.cpp b/libraries/libstratosphere/source/htc/server/htc_htc_service_object.cpp new file mode 100644 index 000000000..b3f2a9525 --- /dev/null +++ b/libraries/libstratosphere/source/htc/server/htc_htc_service_object.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htc_htc_service_object.hpp" + +namespace ams::htc::server { + + HtcServiceObject::HtcServiceObject(htclow::HtclowManager *htclow_manager) { + AMS_ABORT("HtcServiceObject::HtcServiceObject"); + } + + + HtcmiscImpl *HtcServiceObject::GetHtcmiscImpl() { + AMS_ABORT("HtcServiceObject::GetHtcmiscImpl"); + } + + Result HtcServiceObject::GetEnvironmentVariable(sf::Out out_size, const sf::OutBuffer &out, const sf::InBuffer &name) { + AMS_ABORT("HtcServiceObject::GetEnvironmentVariable"); + } + + Result HtcServiceObject::GetEnvironmentVariableLength(sf::Out out_size, const sf::InBuffer &name) { + AMS_ABORT("HtcServiceObject::GetEnvironmentVariableLength"); + } + + Result HtcServiceObject::GetHostConnectionEvent(sf::OutCopyHandle out) { + AMS_ABORT("HtcServiceObject::GetHostConnectionEvent"); + } + + Result HtcServiceObject::GetHostDisconnectionEvent(sf::OutCopyHandle out) { + AMS_ABORT("HtcServiceObject::GetHostDisconnectionEvent"); + } + + Result HtcServiceObject::GetHostConnectionEventForSystem(sf::OutCopyHandle out) { + AMS_ABORT("HtcServiceObject::GetHostConnectionEventForSystem"); + } + + Result HtcServiceObject::GetHostDisconnectionEventForSystem(sf::OutCopyHandle out) { + AMS_ABORT("HtcServiceObject::GetHostDisconnectionEventForSystem"); + } + + Result HtcServiceObject::GetBridgeIpAddress(const sf::OutBuffer &out) { + AMS_ABORT("HtcServiceObject::GetBridgeIpAddress"); + } + + Result HtcServiceObject::GetBridgePort(const sf::OutBuffer &out) { + AMS_ABORT("HtcServiceObject::GetBridgePort"); + } + + Result HtcServiceObject::SetCradleAttached(bool attached) { + AMS_ABORT("HtcServiceObject::SetCradleAttached"); + } + + Result HtcServiceObject::GetBridgeSubnetMask(const sf::OutBuffer &out) { + AMS_ABORT("HtcServiceObject::GetBridgeSubnetMask"); + } + + Result HtcServiceObject::GetBridgeMacAddress(const sf::OutBuffer &out) { + AMS_ABORT("HtcServiceObject::GetBridgeMacAddress"); + } + + Result HtcServiceObject::GetWorkingDirectoryPath(const sf::OutBuffer &out, s32 max_len) { + AMS_ABORT("HtcServiceObject::GetWorkingDirectoryPath"); + } + + Result HtcServiceObject::GetWorkingDirectoryPathSize(sf::Out out_size) { + AMS_ABORT("HtcServiceObject::GetWorkingDirectoryPathSize"); + } + + Result HtcServiceObject::RunOnHostStart(sf::Out out_id, sf::OutCopyHandle out, const sf::InBuffer &args) { + AMS_ABORT("HtcServiceObject::RunOnHostStart"); + } + + Result HtcServiceObject::RunOnHostResults(sf::Out out_result, u32 id) { + AMS_ABORT("HtcServiceObject::RunOnHostResults"); + } + + Result HtcServiceObject::SetBridgeIpAddress(const sf::InBuffer &arg) { + AMS_ABORT("HtcServiceObject::SetBridgeIpAddress"); + } + + Result HtcServiceObject::SetBridgeSubnetMask(const sf::InBuffer &arg) { + AMS_ABORT("HtcServiceObject::SetBridgeSubnetMask"); + } + + Result HtcServiceObject::SetBridgePort(const sf::InBuffer &arg) { + AMS_ABORT("HtcServiceObject::SetBridgePort"); + } + +} diff --git a/libraries/libstratosphere/source/htc/server/htc_htc_service_object.hpp b/libraries/libstratosphere/source/htc/server/htc_htc_service_object.hpp new file mode 100644 index 000000000..0ff25a582 --- /dev/null +++ b/libraries/libstratosphere/source/htc/server/htc_htc_service_object.hpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "../../htclow/htclow_manager.hpp" + +namespace ams::htc::server { + + class HtcServiceObject { + private: + /* TODO */ + public: + HtcServiceObject(htclow::HtclowManager *htclow_manager); + public: + HtcmiscImpl *GetHtcmiscImpl(); + public: + Result GetEnvironmentVariable(sf::Out out_size, const sf::OutBuffer &out, const sf::InBuffer &name); + Result GetEnvironmentVariableLength(sf::Out out_size, const sf::InBuffer &name); + Result GetHostConnectionEvent(sf::OutCopyHandle out); + Result GetHostDisconnectionEvent(sf::OutCopyHandle out); + Result GetHostConnectionEventForSystem(sf::OutCopyHandle out); + Result GetHostDisconnectionEventForSystem(sf::OutCopyHandle out); + Result GetBridgeIpAddress(const sf::OutBuffer &out); + Result GetBridgePort(const sf::OutBuffer &out); + Result SetCradleAttached(bool attached); + Result GetBridgeSubnetMask(const sf::OutBuffer &out); + Result GetBridgeMacAddress(const sf::OutBuffer &out); + Result GetWorkingDirectoryPath(const sf::OutBuffer &out, s32 max_len); + Result GetWorkingDirectoryPathSize(sf::Out out_size); + Result RunOnHostStart(sf::Out out_id, sf::OutCopyHandle out, const sf::InBuffer &args); + Result RunOnHostResults(sf::Out out_result, u32 id); + Result SetBridgeIpAddress(const sf::InBuffer &arg); + Result SetBridgeSubnetMask(const sf::InBuffer &arg); + Result SetBridgePort(const sf::InBuffer &arg); + + }; + static_assert(tma::IsIHtcManager); + +} diff --git a/libraries/libstratosphere/source/htc/server/htc_htcmisc_hipc_server.cpp b/libraries/libstratosphere/source/htc/server/htc_htcmisc_hipc_server.cpp new file mode 100644 index 000000000..f9ebb2b86 --- /dev/null +++ b/libraries/libstratosphere/source/htc/server/htc_htcmisc_hipc_server.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htc_htc_service_object.hpp" +#include "htc_htcmisc_manager.hpp" + +namespace ams::htc::server { + + namespace { + + static constexpr inline size_t NumServers = 1; + static constexpr inline size_t MaxSessions = 30; + static constexpr inline sm::ServiceName ServiceName = sm::ServiceName::Encode("htc"); + + using ServerOptions = sf::hipc::DefaultServerManagerOptions; + using ServerManager = sf::hipc::ServerManager; + + constinit TYPED_STORAGE(ServerManager) g_server_manager_storage; + constinit ServerManager *g_server_manager = nullptr; + + constinit HtcmiscImpl *g_misc_impl = nullptr; + + } + + void InitializeHtcmiscServer(htclow::HtclowManager *htclow_manager) { + /* Check that we haven't already initialized. */ + AMS_ASSERT(g_server_manager == nullptr); + + /* Create the server manager. */ + std::construct_at(GetPointer(g_server_manager_storage)); + + /* Set the server manager pointer. */ + g_server_manager = GetPointer(g_server_manager_storage); + + /* Create and register the htc manager object. */ + HtcServiceObject *service_object; + R_ABORT_UNLESS(g_server_manager->RegisterObjectForServer(CreateHtcmiscManager(std::addressof(service_object), htclow_manager), ServiceName, MaxSessions)); + + /* Set the misc impl. */ + g_misc_impl = service_object->GetHtcmiscImpl(); + + /* Start the server. */ + g_server_manager->ResumeProcessing(); + } + + void FinalizeHtcmiscServer() { + /* Check that we've already initialized. */ + AMS_ASSERT(g_server_manager != nullptr); + + /* Clear the misc impl. */ + g_misc_impl = nullptr; + + /* Clear and destroy. */ + std::destroy_at(g_server_manager); + g_server_manager = nullptr; + } + + void LoopHtcmiscServer() { + /* Check that we've already initialized. */ + AMS_ASSERT(g_server_manager != nullptr); + + g_server_manager->LoopProcess(); + } + + void RequestStopHtcmiscServer() { + /* Check that we've already initialized. */ + AMS_ASSERT(g_server_manager != nullptr); + + g_server_manager->RequestStopProcessing(); + } + + HtcmiscImpl *GetHtcmiscImpl() { + return g_misc_impl; + } + +} diff --git a/libraries/libstratosphere/source/htc/server/htc_htcmisc_manager.cpp b/libraries/libstratosphere/source/htc/server/htc_htcmisc_manager.cpp new file mode 100644 index 000000000..304451bea --- /dev/null +++ b/libraries/libstratosphere/source/htc/server/htc_htcmisc_manager.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htc_htcmisc_manager.hpp" + +namespace ams::htc::server { + + namespace { + + + mem::StandardAllocator g_allocator; + sf::StandardAllocatorMemoryResource g_sf_resource(std::addressof(g_allocator)); + + alignas(os::MemoryPageSize) constinit u8 g_heap[util::AlignUp(sizeof(HtcServiceObject), os::MemoryPageSize) + 12_KB]; + + class StaticAllocatorInitializer { + public: + StaticAllocatorInitializer() { + g_allocator.Initialize(g_heap, sizeof(g_heap)); + } + } g_static_allocator_initializer; + + using ObjectFactory = sf::ObjectFactory; + + } + + sf::SharedPointer CreateHtcmiscManager(HtcServiceObject **out, htclow::HtclowManager *htclow_manager) { + auto obj = ObjectFactory::CreateSharedEmplaced(std::addressof(g_sf_resource), htclow_manager); + *out = std::addressof(obj.GetImpl()); + return obj; + } + +} diff --git a/libraries/libstratosphere/source/htc/server/htc_htcmisc_manager.hpp b/libraries/libstratosphere/source/htc/server/htc_htcmisc_manager.hpp new file mode 100644 index 000000000..8d36f90a4 --- /dev/null +++ b/libraries/libstratosphere/source/htc/server/htc_htcmisc_manager.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "htc_htc_service_object.hpp" + +namespace ams::htc::server { + + sf::SharedPointer CreateHtcmiscManager(HtcServiceObject **out, htclow::HtclowManager *htclow_manager); + +} diff --git a/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp b/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp index 865c82dcc..34e530bd6 100644 --- a/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp +++ b/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp @@ -52,8 +52,12 @@ namespace ams::htclow { return ResultSuccess(); } - //void HtclowManagerImpl::CloseDriver(); - // - //void HtclowManagerImpl::Disconnect(); + void HtclowManagerImpl::CloseDriver() { + AMS_ABORT("HtclowManagerImpl::CloseDriver"); + } + + void HtclowManagerImpl::Disconnect() { + AMS_ABORT("HtclowManagerImpl::Disconnect"); + } } diff --git a/stratosphere/htc/source/htc_main.cpp b/stratosphere/htc/source/htc_main.cpp index 17e0a5c03..e83193b81 100644 --- a/stratosphere/htc/source/htc_main.cpp +++ b/stratosphere/htc/source/htc_main.cpp @@ -185,7 +185,7 @@ namespace ams::htc { } void HtcIpcThreadFunction(void *arg) { - //htc::server::LoopHtcmiscServer(); + htc::server::LoopHtcmiscServer(); } void HtcfsIpcThreadFunction(void *arg) { @@ -218,7 +218,7 @@ int main(int argc, char **argv) auto *htclow_manager = htclow::HtclowManagerHolder::GetHtclowManager(); /* Initialize the htc misc server. */ - //htc::server::InitializeHtcmiscServer(htclow_manager); + htc::server::InitializeHtcmiscServer(htclow_manager); /* Create the htc misc ipc thread. */ os::ThreadType htc_ipc_thread; From 1963ae7ec0d2241da6e1fb39add7779fadae69f0 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 9 Feb 2021 06:16:43 -0800 Subject: [PATCH 032/280] htc: begin skeletoning types for HtcmiscImpl --- .../include/stratosphere/htc.hpp | 1 + .../htc/server/htc_htcmisc_channel_ids.hpp | 25 ++++++++++ .../stratosphere/htclow/htclow_types.hpp | 4 ++ .../htc/server/driver/htc_driver_manager.hpp | 29 ++++++++++++ .../htc/server/driver/htc_htclow_driver.hpp | 47 +++++++++++++++++++ .../source/htc/server/driver/htc_i_driver.hpp | 35 ++++++++++++++ .../htc/server/htc_htc_service_object.cpp | 7 +-- .../htc/server/htc_htc_service_object.hpp | 11 ++++- .../source/htc/server/htc_htcmisc_impl.hpp | 47 +++++++++++++++++++ .../source/htc/server/htc_observer.hpp | 37 +++++++++++++++ .../htc/server/rpc/htc_htcmisc_rpc_server.hpp | 35 ++++++++++++++ .../source/htc/server/rpc/htc_rpc_client.hpp | 44 +++++++++++++++++ .../ctrl/htclow_ctrl_service_channels.hpp | 4 +- 13 files changed, 320 insertions(+), 6 deletions(-) create mode 100644 libraries/libstratosphere/include/stratosphere/htc/server/htc_htcmisc_channel_ids.hpp create mode 100644 libraries/libstratosphere/source/htc/server/driver/htc_driver_manager.hpp create mode 100644 libraries/libstratosphere/source/htc/server/driver/htc_htclow_driver.hpp create mode 100644 libraries/libstratosphere/source/htc/server/driver/htc_i_driver.hpp create mode 100644 libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.hpp create mode 100644 libraries/libstratosphere/source/htc/server/htc_observer.hpp create mode 100644 libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.hpp create mode 100644 libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp diff --git a/libraries/libstratosphere/include/stratosphere/htc.hpp b/libraries/libstratosphere/include/stratosphere/htc.hpp index 571efc768..748ba3f6b 100644 --- a/libraries/libstratosphere/include/stratosphere/htc.hpp +++ b/libraries/libstratosphere/include/stratosphere/htc.hpp @@ -16,3 +16,4 @@ #pragma once #include +#include diff --git a/libraries/libstratosphere/include/stratosphere/htc/server/htc_htcmisc_channel_ids.hpp b/libraries/libstratosphere/include/stratosphere/htc/server/htc_htcmisc_channel_ids.hpp new file mode 100644 index 000000000..bba165b7e --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htc/server/htc_htcmisc_channel_ids.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::htc::server { + + constexpr inline htclow::ChannelId HtcmiscClientChannelId = 1; + constexpr inline htclow::ChannelId HtcmiscServerChannelId = 2; + +} diff --git a/libraries/libstratosphere/include/stratosphere/htclow/htclow_types.hpp b/libraries/libstratosphere/include/stratosphere/htclow/htclow_types.hpp index 8abd164a8..b7dacb118 100644 --- a/libraries/libstratosphere/include/stratosphere/htclow/htclow_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/htclow/htclow_types.hpp @@ -33,4 +33,8 @@ namespace ams::htclow { constexpr inline s16 ProtocolVersion = 5; + enum ReceiveOption { + /* ... */ + }; + } diff --git a/libraries/libstratosphere/source/htc/server/driver/htc_driver_manager.hpp b/libraries/libstratosphere/source/htc/server/driver/htc_driver_manager.hpp new file mode 100644 index 000000000..35757d778 --- /dev/null +++ b/libraries/libstratosphere/source/htc/server/driver/htc_driver_manager.hpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "htc_i_driver.hpp" + +namespace ams::htc::server::driver { + + class DriverManager { + private: + IDriver *m_driver; + public: + DriverManager(IDriver *driver) : m_driver(driver) { /* ... */ } + }; + +} diff --git a/libraries/libstratosphere/source/htc/server/driver/htc_htclow_driver.hpp b/libraries/libstratosphere/source/htc/server/driver/htc_htclow_driver.hpp new file mode 100644 index 000000000..9e600acdd --- /dev/null +++ b/libraries/libstratosphere/source/htc/server/driver/htc_htclow_driver.hpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "../../../htclow/htclow_manager.hpp" +#include "htc_i_driver.hpp" + +namespace ams::htc::server::driver { + + class HtclowDriver final : public IDriver { + private: + static constexpr size_t DefaultBufferSize = 128_KB; + private: + u8 m_default_receive_buffer[DefaultBufferSize]; + u8 m_default_send_buffer[DefaultBufferSize]; + htclow::HtclowManager *m_manager; + bool m_disconnection_emulation_enabled; + htclow::ModuleId m_module_id; + public: + HtclowDriver(htclow::HtclowManager *manager, htclow::ModuleId module_id) : m_manager(manager), m_disconnection_emulation_enabled(false), m_module_id(module_id) { /* ... */ } + public: + virtual void SetDisconnectionEmulationEnabled(bool en) override; + virtual Result Open(htclow::ChannelId channel) override; + virtual Result Open(htclow::ChannelId channel, void *receive_buffer, size_t receive_buffer_size, void *send_buffer, size_t send_buffer_size) override; + virtual void Close(htclow::ChannelId channel) override; + virtual Result Connect(htclow::ChannelId channel) override; + virtual void Shutdown(htclow::ChannelId channel) override; + virtual Result Send(s64 *out, const void *src, s64 src_size, htclow::ChannelId channel) override; + virtual Result Receive(s64 *out, void *dst, s64 dst_size, htclow::ChannelId channel, htclow::ReceiveOption option) override; + virtual htclow::ChannelState GetChannelState(htclow::ChannelId channel) override; + virtual os::EventType *GetChannelStateEvent(htclow::ChannelId channel) override; + }; + +} diff --git a/libraries/libstratosphere/source/htc/server/driver/htc_i_driver.hpp b/libraries/libstratosphere/source/htc/server/driver/htc_i_driver.hpp new file mode 100644 index 000000000..e1ca06a54 --- /dev/null +++ b/libraries/libstratosphere/source/htc/server/driver/htc_i_driver.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htc::server::driver { + + class IDriver { + public: + virtual void SetDisconnectionEmulationEnabled(bool en) = 0; + virtual Result Open(htclow::ChannelId channel) = 0; + virtual Result Open(htclow::ChannelId channel, void *receive_buffer, size_t receive_buffer_size, void *send_buffer, size_t send_buffer_size) = 0; + virtual void Close(htclow::ChannelId channel) = 0; + virtual Result Connect(htclow::ChannelId channel) = 0; + virtual void Shutdown(htclow::ChannelId channel) = 0; + virtual Result Send(s64 *out, const void *src, s64 src_size, htclow::ChannelId channel) = 0; + virtual Result Receive(s64 *out, void *dst, s64 dst_size, htclow::ChannelId channel, htclow::ReceiveOption option) = 0; + virtual htclow::ChannelState GetChannelState(htclow::ChannelId channel) = 0; + virtual os::EventType *GetChannelStateEvent(htclow::ChannelId channel) = 0; + }; + +} diff --git a/libraries/libstratosphere/source/htc/server/htc_htc_service_object.cpp b/libraries/libstratosphere/source/htc/server/htc_htc_service_object.cpp index b3f2a9525..be8e4c001 100644 --- a/libraries/libstratosphere/source/htc/server/htc_htc_service_object.cpp +++ b/libraries/libstratosphere/source/htc/server/htc_htc_service_object.cpp @@ -18,13 +18,14 @@ namespace ams::htc::server { - HtcServiceObject::HtcServiceObject(htclow::HtclowManager *htclow_manager) { - AMS_ABORT("HtcServiceObject::HtcServiceObject"); + HtcServiceObject::HtcServiceObject(htclow::HtclowManager *htclow_manager) : m_set(), m_misc_impl(htclow_manager), m_observer(m_misc_impl), m_mutex(){ + /* Initialize our set. */ + m_set.Initialize(MaxSetElements, m_set_memory, sizeof(m_set_memory)); } HtcmiscImpl *HtcServiceObject::GetHtcmiscImpl() { - AMS_ABORT("HtcServiceObject::GetHtcmiscImpl"); + return std::addressof(m_misc_impl); } Result HtcServiceObject::GetEnvironmentVariable(sf::Out out_size, const sf::OutBuffer &out, const sf::InBuffer &name) { diff --git a/libraries/libstratosphere/source/htc/server/htc_htc_service_object.hpp b/libraries/libstratosphere/source/htc/server/htc_htc_service_object.hpp index 0ff25a582..b478062a1 100644 --- a/libraries/libstratosphere/source/htc/server/htc_htc_service_object.hpp +++ b/libraries/libstratosphere/source/htc/server/htc_htc_service_object.hpp @@ -16,12 +16,21 @@ #pragma once #include #include "../../htclow/htclow_manager.hpp" +#include "htc_htcmisc_impl.hpp" +#include "htc_observer.hpp" namespace ams::htc::server { class HtcServiceObject { private: - /* TODO */ + static constexpr inline auto MaxSetElements = 0x48; + using Set = util::FixedSet; + private: + u8 m_set_memory[Set::GetRequiredMemorySize(MaxSetElements)]; + Set m_set; + HtcmiscImpl m_misc_impl; + Observer m_observer; + os::SdkMutex m_mutex; public: HtcServiceObject(htclow::HtclowManager *htclow_manager); public: diff --git a/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.hpp b/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.hpp new file mode 100644 index 000000000..d2cd85c1e --- /dev/null +++ b/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.hpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "../../htclow/htclow_manager.hpp" +#include "driver/htc_htclow_driver.hpp" +#include "driver/htc_driver_manager.hpp" +#include "rpc/htc_rpc_client.hpp" +#include "rpc/htc_htcmisc_rpc_server.hpp" + +namespace ams::htc::server { + + class HtcmiscImpl { + private: + driver::HtclowDriver m_htclow_driver; + driver::DriverManager m_driver_manager; + rpc::RpcClient m_rpc_client; + rpc::HtcmiscRpcServer m_rpc_server; + os::ThreadType m_client_thread; + os::ThreadType m_server_thread; + os::Event m_event_61200; + u8 m_61228; + os::Event m_event_61230; + bool m_client_connected; + bool m_server_connected; + u8 m_6125A; + os::SdkMutex m_connection_mutex; + public: + HtcmiscImpl(htclow::HtclowManager *htclow_manager); + public: + /* TODO */ + }; + +} diff --git a/libraries/libstratosphere/source/htc/server/htc_observer.hpp b/libraries/libstratosphere/source/htc/server/htc_observer.hpp new file mode 100644 index 000000000..bde6d56cf --- /dev/null +++ b/libraries/libstratosphere/source/htc/server/htc_observer.hpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "htc_htcmisc_impl.hpp" + +namespace ams::htc::server { + + class Observer { + private: + os::SystemEvent m_connect_event; + os::SystemEvent m_disconnect_event; + os::Event m_event_60; + os::ThreadType m_observer_thread; + const HtcmiscImpl &m_misc_impl; + bool m_thread_running; + bool m_stopped; + bool m_connected; + bool m_is_service_available; + public: + Observer(const HtcmiscImpl &misc_impl); + }; + +} diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.hpp new file mode 100644 index 000000000..71d028130 --- /dev/null +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "../driver/htc_i_driver.hpp" + +namespace ams::htc::server::rpc { + + class HtcmiscRpcServer { + private: + u64 m_00; + driver::IDriver *m_driver; + htclow::ChannelId m_channel_id; + void *m_receive_thread_stack; + os::ThreadType m_receive_thread; + bool m_cancelled; + bool m_thread_running; + public: + HtcmiscRpcServer(driver::IDriver *driver, htclow::ChannelId channel); + }; + +} diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp new file mode 100644 index 000000000..4ca916797 --- /dev/null +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "../driver/htc_i_driver.hpp" + +namespace ams::htc::server::rpc { + + class RpcClient { + private: + u64 m_00; + driver::IDriver *m_driver; + htclow::ChannelId m_channel_id; + void *m_receive_thread_stack; + void *m_send_thread_stack; + os::ThreadType m_receive_thread; + os::ThreadType m_send_thread; + os::SdkMutex *m_p_mutex; + /* TODO: m_task_id_free_list */ + /* TODO: m_task_table */ + /* TODO: m_3C0[0x48] */ + /* TODO: m_rpc_task_queue */ + bool m_cancelled; + bool m_thread_running; + os::EventType m_5F8_events[0x48]; + os::EventType m_1138_events[0x48]; + public: + RpcClient(driver::IDriver *driver, htclow::ChannelId channel); + }; + +} diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service_channels.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service_channels.hpp index d42bb45c0..96fd92fa6 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service_channels.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service_channels.hpp @@ -24,11 +24,11 @@ namespace ams::htclow::ctrl { .module_id = ModuleId::Htcfs, }, { - .channel_id = 1, /* TODO: htcmisc::ServerChannelId? */ + .channel_id = 1, /* TODO: htcmisc::ClientChannelId? */ .module_id = ModuleId::Htcmisc, }, { - .channel_id = 2, /* TODO: htcmisc::ClientChannelId? */ + .channel_id = 2, /* TODO: htcmisc::ServerChannelId? */ .module_id = ModuleId::Htcmisc, }, { From 4cb6c63516a5e83f180cf93ad973755b924c5516 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 9 Feb 2021 07:27:17 -0800 Subject: [PATCH 033/280] htc: implement HtclowDriver --- .../stratosphere/htclow/htclow_types.hpp | 4 +- .../htc/server/driver/htc_htclow_driver.cpp | 183 ++++++++++++++++++ .../htc/server/driver/htc_htclow_driver.hpp | 3 + .../vapours/results/htclow_results.hpp | 7 +- libraries/libvapours/include/vapours/util.hpp | 1 + .../include/vapours/util/util_int_util.hpp | 72 +++++++ 6 files changed, 267 insertions(+), 3 deletions(-) create mode 100644 libraries/libstratosphere/source/htc/server/driver/htc_htclow_driver.cpp create mode 100644 libraries/libvapours/include/vapours/util/util_int_util.hpp diff --git a/libraries/libstratosphere/include/stratosphere/htclow/htclow_types.hpp b/libraries/libstratosphere/include/stratosphere/htclow/htclow_types.hpp index b7dacb118..445456e9a 100644 --- a/libraries/libstratosphere/include/stratosphere/htclow/htclow_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/htclow/htclow_types.hpp @@ -34,7 +34,9 @@ namespace ams::htclow { constexpr inline s16 ProtocolVersion = 5; enum ReceiveOption { - /* ... */ + ReceiveOption_NonBlocking = 0, + ReceiveOption_ReceiveAnyData = 1, + ReceiveOption_ReceiveAllData = 2, }; } diff --git a/libraries/libstratosphere/source/htc/server/driver/htc_htclow_driver.cpp b/libraries/libstratosphere/source/htc/server/driver/htc_htclow_driver.cpp new file mode 100644 index 000000000..a7bf79be1 --- /dev/null +++ b/libraries/libstratosphere/source/htc/server/driver/htc_htclow_driver.cpp @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htc_htclow_driver.hpp" + +namespace ams::htc::server::driver { + + namespace { + + constexpr ALWAYS_INLINE htclow::impl::ChannelInternalType GetHtclowChannel(htclow::ChannelId channel_id, htclow::ModuleId module_id) { + return { + .channel_id = channel_id, + .reserved = 0, + .module_id = module_id, + }; + } + + } + + void HtclowDriver::WaitTask(u32 task_id) { + os::WaitEvent(m_manager->GeTaskEvent(task_id)); + } + + void HtclowDriver::SetDisconnectionEmulationEnabled(bool en) { + /* NOTE: Nintendo ignores the input, here. */ + m_disconnection_emulation_enabled = false; + } + + Result HtclowDriver::Open(htclow::ChannelId channel) { + /* Check the channel/module combination. */ + if (m_module_id == htclow::ModuleId::Htcmisc) { + AMS_ABORT_UNLESS(channel == HtcmiscClientChannelId ); + } else if (m_module_id == htclow::ModuleId::Htcs) { + AMS_ABORT_UNLESS(channel == 0); + } else { + AMS_ABORT("Unsupported channel"); + } + + return this->Open(channel, m_default_receive_buffer, sizeof(m_default_receive_buffer), m_default_send_buffer, sizeof(m_default_send_buffer)); + } + + Result HtclowDriver::Open(htclow::ChannelId channel, void *receive_buffer, size_t receive_buffer_size, void *send_buffer, size_t send_buffer_size) { + /* Open the channel. */ + R_TRY(m_manager->Open(GetHtclowChannel(channel, m_module_id))); + + /* Set the send/receive buffers. */ + m_manager->SetReceiveBuffer(receive_buffer, receive_buffer_size); + m_manager->SetSendBuffer(send_buffer, send_buffer_size); + + return ResultSuccess(); + } + + void HtclowDriver::Close(htclow::ChannelId channel) { + /* Close the channel. */ + const auto result = m_manager->Close(GetHtclowChannel(channel, m_module_id)); + R_ASSERT(result); + } + + Result HtclowDriver::Connect(htclow::ChannelId channel) { + /* Check if we should emulate disconnection. */ + R_UNLESS(!m_disconnection_emulation_enabled, htclow::ResultConnectionFailure()); + + /* Begin connecting. */ + u32 task_id; + R_TRY(m_manager->ConnectBegin(std::addressof(task_id), GetHtclowChannel(channel, m_module_id))); + + /* Wait for the task to complete. */ + this->WaitTask(task_id); + + /* Finish connecting. */ + R_TRY(m_manager->ConnectEnd(GetHtclowChannel(channel, m_module_id), task_id)); + + return ResultSuccess(); + } + + void HtclowDriver::Shutdown(htclow::ChannelId channel) { + /* Shut down the channel. */ + m_manager->Shutdown(GetHtclowChannel(channel, m_module_id)); + } + + Result HtclowDriver::Send(s64 *out, const void *src, s64 src_size, htclow::ChannelId channel) { + /* Check if we should emulate disconnection. */ + R_UNLESS(!m_disconnection_emulation_enabled, htclow::ResultConnectionFailure()); + + /* Validate that dst_size is okay. */ + R_UNLESS(util::IsIntValueRepresentable(src_size), htclow::ResultOverflow()); + + /* Repeatedly send until we're done. */ + size_t cur_send; + size_t sent; + for (sent = 0; sent < static_cast(src_size); sent += cur_send) { + /* Begin sending. */ + u32 task_id; + R_TRY(m_manager->SendBegin(std::addressof(task_id), std::addressof(cur_send), static_cast(src) + sent, static_cast(src_size) - sent, GetHtclowChannel(channel, m_module_id))); + + /* Wait for the task to complete. */ + this->WaitTask(task_id); + + /* Finish sending. */ + R_ABORT_UNLESS(m_manager->SendEnd(task_id)); + } + + /* Set the output sent size. */ + *out = static_cast(sent); + + return ResultSuccess(); + } + + Result HtclowDriver::ReceiveInternal(size_t *out, void *dst, size_t dst_size, htclow::ChannelId channel, htclow::ReceiveOption option) { + /* Begin receiving. */ + u32 task_id; + R_TRY(m_manager->ReceiveBegin(std::addressof(task_id), GetHtclowChannel(channel, m_module_id), option != htclow::ReceiveOption_NonBlocking)); + + /* Wait for the task to complete. */ + this->WaitTask(task_id); + + /* Finish receiving. */ + return m_manager->ReceiveEnd(out, dst, dst_size, GetHtclowChannel(channel, m_module_id), task_id); + } + + Result HtclowDriver::Receive(s64 *out, void *dst, s64 dst_size, htclow::ChannelId channel, htclow::ReceiveOption option) { + /* Check if we should emulate disconnection. */ + R_UNLESS(!m_disconnection_emulation_enabled, htclow::ResultConnectionFailure()); + + /* Validate that dst_size is okay. */ + R_UNLESS(util::IsIntValueRepresentable(dst_size), htclow::ResultOverflow()); + + /* Determine the minimum allowable receive size. */ + size_t min_size; + switch (option) { + case htclow::ReceiveOption_NonBlocking: min_size = 0; break; + case htclow::ReceiveOption_ReceiveAnyData: min_size = 1; break; + case htclow::ReceiveOption_ReceiveAllData: min_size = dst_size; break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Repeatedly receive. */ + size_t received = 0; + do { + size_t cur_received; + const Result result = this->ReceiveInternal(std::addressof(cur_received), static_cast(dst) + received, static_cast(src_size) - received, channel, option); + + if (R_FAILED(result)) { + if (htclow::ResultChannelReceiveBufferEmpty::Includes(result)) { + R_UNLESS(option != htclow::ReceiveOption_NonBlocking, htclow::ResultNonBlockingReceiveFailed()); + } + if (htclow::ResultChannelNotExist::Includes(result)) { + *out = received; + } + return result; + } + + received += cur_received; + } while (received < min_size); + + /* Set the output received size. */ + *out = static_cast(received); + + return ResultSuccess(); + } + + htclow::ChannelState HtclowDriver::GetChannelState(htclow::ChannelId channel) { + return m_manager->GetChannelState(GetHtclowChannel(channel, m_module_id)); + } + + os::EventType *HtclowDriver::GetChannelStateEvent(htclow::ChannelId channel) { + return m_manager->GetChannelStateEvent(GetHtclowChannel(channel, m_module_id)); + } + +} diff --git a/libraries/libstratosphere/source/htc/server/driver/htc_htclow_driver.hpp b/libraries/libstratosphere/source/htc/server/driver/htc_htclow_driver.hpp index 9e600acdd..a4d8588a8 100644 --- a/libraries/libstratosphere/source/htc/server/driver/htc_htclow_driver.hpp +++ b/libraries/libstratosphere/source/htc/server/driver/htc_htclow_driver.hpp @@ -31,6 +31,9 @@ namespace ams::htc::server::driver { htclow::ModuleId m_module_id; public: HtclowDriver(htclow::HtclowManager *manager, htclow::ModuleId module_id) : m_manager(manager), m_disconnection_emulation_enabled(false), m_module_id(module_id) { /* ... */ } + private: + void WaitTask(u32 task_id); + Result ReceiveInternal(size_t *out, void *dst, size_t dst_size, htclow::ChannelId channel, htclow::ReceiveOption option); public: virtual void SetDisconnectionEmulationEnabled(bool en) override; virtual Result Open(htclow::ChannelId channel) override; diff --git a/libraries/libvapours/include/vapours/results/htclow_results.hpp b/libraries/libvapours/include/vapours/results/htclow_results.hpp index 6294e5977..60e719b79 100644 --- a/libraries/libvapours/include/vapours/results/htclow_results.hpp +++ b/libraries/libvapours/include/vapours/results/htclow_results.hpp @@ -20,13 +20,16 @@ namespace ams::htclow { R_DEFINE_NAMESPACE_RESULT_MODULE(29); - R_DEFINE_ERROR_RESULT(UnknownDriverType, 3); - R_DEFINE_ERROR_RESULT(ChannelNotExist, 10); + R_DEFINE_ERROR_RESULT(ConnectionFailure, 1); + R_DEFINE_ERROR_RESULT(UnknownDriverType, 3); + R_DEFINE_ERROR_RESULT(NonBlockingReceiveFailed, 5); + R_DEFINE_ERROR_RESULT(ChannelNotExist, 10); R_DEFINE_ERROR_RESULT(InvalidChannelState, 200); R_DEFINE_ERROR_RESULT(InvalidChannelStateDisconnected, 201); R_DEFINE_ERROR_RANGE(InternalError, 1000, 2999); + R_DEFINE_ERROR_RESULT(Overflow, 1001); R_DEFINE_ERROR_RESULT(OutOfMemory, 1002); R_DEFINE_ERROR_RESULT(InvalidArgument, 1003); R_DEFINE_ERROR_RESULT(ProtocolError, 1004); diff --git a/libraries/libvapours/include/vapours/util.hpp b/libraries/libvapours/include/vapours/util.hpp index f0a9250e1..01cbeb5c5 100644 --- a/libraries/libvapours/include/vapours/util.hpp +++ b/libraries/libvapours/include/vapours/util.hpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libvapours/include/vapours/util/util_int_util.hpp b/libraries/libvapours/include/vapours/util/util_int_util.hpp new file mode 100644 index 000000000..77a1739b1 --- /dev/null +++ b/libraries/libvapours/include/vapours/util/util_int_util.hpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +namespace ams::util { + + namespace impl { + + template + constexpr ALWAYS_INLINE bool IsIntValueRepresentableImpl(From v) { + using ToLimit = std::numeric_limits; + using FromLimit = std::numeric_limits; + if constexpr (ToLimit::min() <= FromLimit::min() && FromLimit::max() <= ToLimit::max()) { + return true; + } else { + return ToLimit::min() <= v && v <= ToLimit::max(); + } + } + + template + constexpr ALWAYS_INLINE bool IsIntValueRepresentableImpl(From v) { + using ToLimit = std::numeric_limits; + using FromLimit = std::numeric_limits; + if constexpr (ToLimit::min() <= FromLimit::min() && FromLimit::max() <= ToLimit::max()) { + return true; + } else { + return ToLimit::min() <= v && v <= ToLimit::max(); + } + } + + template + constexpr ALWAYS_INLINE bool IsIntValueRepresentableImpl(From v) { + using UnsignedFrom = typename std::make_unsigned::type; + + if (v < 0) { + return false; + } else { + return IsIntValueRepresentableImpl(static_cast(v)); + } + } + + template + constexpr ALWAYS_INLINE bool IsIntValueRepresentableImpl(From v) { + using UnsignedTo = typename std::make_unsigned::type; + + return v <= static_cast(std::numeric_limits::max()); + } + + } + + template + constexpr ALWAYS_INLINE bool IsIntValueRepresentable(From v) { + return ::ams::util::impl::IsIntValueRepresentableImpl(v); + } + +} From 00ab210e66a9c448e640f76a2b22d75359928ffd Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 9 Feb 2021 07:51:02 -0800 Subject: [PATCH 034/280] mux: optimize many accesses to O(log(n)) vs Nintendo's O(log(n)^2) --- .../htc/server/driver/htc_htclow_driver.cpp | 2 +- .../source/htclow/mux/htclow_mux.cpp | 16 +++++++--------- .../htclow/mux/htclow_mux_channel_impl_map.hpp | 4 ++-- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/libraries/libstratosphere/source/htc/server/driver/htc_htclow_driver.cpp b/libraries/libstratosphere/source/htc/server/driver/htc_htclow_driver.cpp index a7bf79be1..aed7f9f2b 100644 --- a/libraries/libstratosphere/source/htc/server/driver/htc_htclow_driver.cpp +++ b/libraries/libstratosphere/source/htc/server/driver/htc_htclow_driver.cpp @@ -31,7 +31,7 @@ namespace ams::htc::server::driver { } void HtclowDriver::WaitTask(u32 task_id) { - os::WaitEvent(m_manager->GeTaskEvent(task_id)); + os::WaitEvent(m_manager->GetTaskEvent(task_id)); } void HtclowDriver::SetDisconnectionEmulationEnabled(bool en) { diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp index c32fac3cb..3b7ca50ac 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp @@ -35,7 +35,7 @@ namespace ams::htclow::mux { /* Set all entries in our map. */ /* NOTE: Nintendo does this highly inefficiently... */ for (auto &pair : m_channel_impl_map.GetMap()) { - m_channel_impl_map[pair.first].SetVersion(m_version); + m_channel_impl_map[pair.second].SetVersion(m_version); } } @@ -67,10 +67,8 @@ namespace ams::htclow::mux { std::scoped_lock lk(m_mutex); /* Process for the channel. */ - if (m_channel_impl_map.Exists(header.channel)) { - R_TRY(this->CheckChannelExist(header.channel)); - - return m_channel_impl_map[header.channel].ProcessReceivePacket(header, body, body_size); + if (auto it = m_channel_impl_map.GetMap().find(header.channel); it != m_channel_impl_map.GetMap().end()) { + return m_channel_impl_map[it->second].ProcessReceivePacket(header, body, body_size); } else { if (header.packet_type == PacketType_Data || header.packet_type == PacketType_MaxData) { this->SendErrorPacket(header.channel); @@ -89,7 +87,7 @@ namespace ams::htclow::mux { /* Iterate the map, checking for valid packet each time. */ for (auto &pair : map) { /* Get the current channel impl. */ - auto &channel_impl = m_channel_impl_map.GetChannelImpl(pair.second); + auto &channel_impl = m_channel_impl_map[pair.second]; /* Check for an error packet. */ /* NOTE: it's unclear why Nintendo does this every iteration of the loop... */ @@ -115,8 +113,8 @@ namespace ams::htclow::mux { /* Remove the packet from the appropriate source. */ if (header.packet_type == PacketType_Error) { m_global_send_buffer.RemovePacket(); - } else if (m_channel_impl_map.Exists(header.channel)) { - m_channel_impl_map[header.channel].RemovePacket(header); + } else if (auto it = m_channel_impl_map.GetMap().find(header.channel); it != m_channel_impl_map.GetMap().end()) { + m_channel_impl_map[it->second].RemovePacket(header); } /* Notify the task manager. */ @@ -130,7 +128,7 @@ namespace ams::htclow::mux { /* Update the state of all channels in our map. */ /* NOTE: Nintendo does this highly inefficiently... */ for (auto pair : m_channel_impl_map.GetMap()) { - m_channel_impl_map[pair.first].UpdateState(); + m_channel_impl_map[pair.second].UpdateState(); } } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp index 52e131c35..623c0a07d 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp @@ -52,8 +52,8 @@ namespace ams::htclow::mux { return m_map; } - ChannelImpl &operator[](impl::ChannelInternalType channel) { - return this->GetChannelImpl(channel); + ChannelImpl &operator[](int index) { + return this->GetChannelImpl(index); } }; From 968ce1249201323888537e5ffb839831f77082f7 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 9 Feb 2021 11:46:34 -0800 Subject: [PATCH 035/280] htc: optimize Mux::QuerySendPacket --- .../source/htclow/mux/htclow_mux.cpp | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp index 3b7ca50ac..69e6288ba 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp @@ -81,24 +81,20 @@ namespace ams::htclow::mux { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); - /* Get our map. */ - auto &map = m_channel_impl_map.GetMap(); + /* Check for an error packet. */ + /* NOTE: Nintendo checks this once per iteration of the below loop. */ + /* The extra checks are unnecessary, because we hold our mutex. */ + if (auto *error_packet = m_global_send_buffer.GetNextPacket(); error_packet != nullptr) { + std::memcpy(header, error_packet->GetHeader(), sizeof(*header)); + *out_body_size = 0; + return true; + } - /* Iterate the map, checking for valid packet each time. */ - for (auto &pair : map) { + /* Iterate the map, checking each channel for a valid valid packet. */ + for (auto &pair : m_channel_impl_map.GetMap()) { /* Get the current channel impl. */ - auto &channel_impl = m_channel_impl_map[pair.second]; - - /* Check for an error packet. */ - /* NOTE: it's unclear why Nintendo does this every iteration of the loop... */ - if (auto *error_packet = m_global_send_buffer.GetNextPacket(); error_packet != nullptr) { - std::memcpy(header, error_packet->GetHeader(), sizeof(*header)); - *out_body_size = 0; - return true; - } - /* See if the channel has something for us to send. */ - if (channel_impl.QuerySendPacket(header, body, out_body_size)) { + if (m_channel_impl_map[pair.second].QuerySendPacket(header, body, out_body_size)) { return this->IsSendable(header->packet_type); } } From e20c2450ce85f211d3ba57841b18a56abc346bad Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 9 Feb 2021 12:36:37 -0800 Subject: [PATCH 036/280] htc: declare and begin impl of HtclowManagerImpl interface --- .../htclow/htclow_channel_types.hpp | 2 + .../htclow/htclow_module_types.hpp | 1 + .../htc/server/driver/htc_htclow_driver.cpp | 6 +- .../htclow/driver/htclow_driver_manager.cpp | 5 + .../htclow/driver/htclow_driver_manager.hpp | 2 + .../htclow/htclow_default_channel_config.hpp | 28 +++++ .../source/htclow/htclow_manager.cpp | 96 ++++++++++++++++ .../source/htclow/htclow_manager.hpp | 39 +++++++ .../source/htclow/htclow_manager_impl.cpp | 108 ++++++++++++++++++ .../source/htclow/htclow_manager_impl.hpp | 38 ++++++ .../source/htclow/mux/htclow_mux.cpp | 23 ++++ .../source/htclow/mux/htclow_mux.hpp | 4 + .../htclow/mux/htclow_mux_channel_impl.cpp | 11 ++ .../htclow/mux/htclow_mux_channel_impl.hpp | 2 +- .../mux/htclow_mux_channel_impl_map.cpp | 24 ++++ .../mux/htclow_mux_channel_impl_map.hpp | 2 + .../htclow/mux/htclow_mux_task_manager.cpp | 8 ++ .../htclow/mux/htclow_mux_task_manager.hpp | 2 + .../vapours/results/htclow_results.hpp | 11 +- libraries/libvapours/include/vapours/util.hpp | 2 +- 20 files changed, 405 insertions(+), 9 deletions(-) create mode 100644 libraries/libstratosphere/source/htclow/htclow_default_channel_config.hpp diff --git a/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp b/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp index 685f78372..51601f9c4 100644 --- a/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp @@ -36,6 +36,8 @@ namespace ams::htclow { struct ChannelConfig { bool flow_control_enabled; + bool handshake_enabled; + size_t max_packet_size; }; constexpr bool IsStateTransitionAllowed(ChannelState from, ChannelState to) { diff --git a/libraries/libstratosphere/include/stratosphere/htclow/htclow_module_types.hpp b/libraries/libstratosphere/include/stratosphere/htclow/htclow_module_types.hpp index f5a8817ef..178c61dfa 100644 --- a/libraries/libstratosphere/include/stratosphere/htclow/htclow_module_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/htclow/htclow_module_types.hpp @@ -21,6 +21,7 @@ namespace ams::htclow { enum class ModuleId : u8 { Htcfs = 1, + Htcmisc = 3, Htcs = 4, }; diff --git a/libraries/libstratosphere/source/htc/server/driver/htc_htclow_driver.cpp b/libraries/libstratosphere/source/htc/server/driver/htc_htclow_driver.cpp index aed7f9f2b..ecc87a6c9 100644 --- a/libraries/libstratosphere/source/htc/server/driver/htc_htclow_driver.cpp +++ b/libraries/libstratosphere/source/htc/server/driver/htc_htclow_driver.cpp @@ -57,8 +57,8 @@ namespace ams::htc::server::driver { R_TRY(m_manager->Open(GetHtclowChannel(channel, m_module_id))); /* Set the send/receive buffers. */ - m_manager->SetReceiveBuffer(receive_buffer, receive_buffer_size); - m_manager->SetSendBuffer(send_buffer, send_buffer_size); + m_manager->SetReceiveBuffer(GetHtclowChannel(channel, m_module_id), receive_buffer, receive_buffer_size); + m_manager->SetSendBuffer(GetHtclowChannel(channel, m_module_id), send_buffer, send_buffer_size); return ResultSuccess(); } @@ -151,7 +151,7 @@ namespace ams::htc::server::driver { size_t received = 0; do { size_t cur_received; - const Result result = this->ReceiveInternal(std::addressof(cur_received), static_cast(dst) + received, static_cast(src_size) - received, channel, option); + const Result result = this->ReceiveInternal(std::addressof(cur_received), static_cast(dst) + received, static_cast(dst_size) - received, channel, option); if (R_FAILED(result)) { if (htclow::ResultChannelReceiveBufferEmpty::Includes(result)) { diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp index 40268bf2c..1b79aeb93 100644 --- a/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp +++ b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp @@ -59,4 +59,9 @@ namespace ams::htclow::driver { return m_open_driver; } + void DriverManager::SetDebugDriver(IDriver *driver) { + m_debug_driver = driver; + m_driver_type = impl::DriverType::Debug; + } + } diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp index 6a2e80aff..c1ae457fb 100644 --- a/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp +++ b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp @@ -35,6 +35,8 @@ namespace ams::htclow::driver { Result OpenDriver(impl::DriverType driver_type); IDriver *GetCurrentDriver(); + + void SetDebugDriver(IDriver *driver); }; } diff --git a/libraries/libstratosphere/source/htclow/htclow_default_channel_config.hpp b/libraries/libstratosphere/source/htclow/htclow_default_channel_config.hpp new file mode 100644 index 000000000..a442ef2ed --- /dev/null +++ b/libraries/libstratosphere/source/htclow/htclow_default_channel_config.hpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "htclow_packet.hpp" + +namespace ams::htclow { + + constexpr inline const ChannelConfig DefaultChannelConfig = { + .flow_control_enabled = true, + .handshake_enabled = true, + .max_packet_size = 0xE000 + sizeof(PacketHeader), + }; + +} diff --git a/libraries/libstratosphere/source/htclow/htclow_manager.cpp b/libraries/libstratosphere/source/htclow/htclow_manager.cpp index 44c670d29..398de260a 100644 --- a/libraries/libstratosphere/source/htclow/htclow_manager.cpp +++ b/libraries/libstratosphere/source/htclow/htclow_manager.cpp @@ -36,8 +36,104 @@ namespace ams::htclow { return m_impl->CloseDriver(); } + Result HtclowManager::Open(impl::ChannelInternalType channel) { + return m_impl->Open(channel); + } + + Result HtclowManager::Close(impl::ChannelInternalType channel) { + return m_impl->Close(channel); + } + + void HtclowManager::Resume() { + return m_impl->Resume(); + } + + void HtclowManager::Suspend() { + return m_impl->Suspend(); + } + + Result HtclowManager::ConnectBegin(u32 *out_task_id, impl::ChannelInternalType channel) { + return m_impl->ConnectBegin(out_task_id, channel); + } + + Result HtclowManager::ConnectEnd(impl::ChannelInternalType channel, u32 task_id) { + return m_impl->ConnectEnd(channel, task_id); + } + void HtclowManager::Disconnect() { return m_impl->Disconnect(); } + Result HtclowManager::FlushBegin(u32 *out_task_id, impl::ChannelInternalType channel) { + return m_impl->FlushBegin(out_task_id, channel); + } + + Result HtclowManager::FlushEnd(u32 task_id) { + return m_impl->FlushEnd(task_id); + } + + ChannelState HtclowManager::GetChannelState(impl::ChannelInternalType channel) { + return m_impl->GetChannelState(channel); + } + + os::EventType *HtclowManager::GetChannelStateEvent(impl::ChannelInternalType channel) { + return m_impl->GetChannelStateEvent(channel); + } + + impl::DriverType HtclowManager::GetDriverType() { + return m_impl->GetDriverType(); + } + + os::EventType *HtclowManager::GetTaskEvent(u32 task_id) { + return m_impl->GetTaskEvent(task_id); + } + + void HtclowManager::NotifyAsleep() { + return m_impl->NotifyAsleep(); + } + + void HtclowManager::NotifyAwake() { + return m_impl->NotifyAwake(); + } + + Result HtclowManager::ReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, bool blocking) { + return m_impl->ReceiveBegin(out_task_id, channel, blocking); + } + + Result HtclowManager::ReceiveEnd(size_t *out, void *dst, size_t dst_size, impl::ChannelInternalType channel, u32 task_id) { + return m_impl->ReceiveEnd(out, dst, dst_size, channel, task_id); + } + + Result HtclowManager::SendBegin(u32 *out_task_id, size_t *out, const void *src, size_t src_size, impl::ChannelInternalType channel) { + return m_impl->SendBegin(out_task_id, out, src, src_size, channel); + } + + Result HtclowManager::SendEnd(u32 task_id) { + return m_impl->SendEnd(task_id); + } + + void HtclowManager::SetConfig(impl::ChannelInternalType channel, const ChannelConfig &config) { + return m_impl->SetConfig(channel, config); + } + + void HtclowManager::SetDebugDriver(driver::IDriver *driver) { + return m_impl->SetDebugDriver(driver); + } + + void HtclowManager::SetReceiveBuffer(impl::ChannelInternalType channel, void *buf, size_t buf_size) { + return m_impl->SetReceiveBuffer(channel, buf, buf_size); + } + + void HtclowManager::SetSendBuffer(impl::ChannelInternalType channel, void *buf, size_t buf_size) { + return m_impl->SetSendBuffer(channel, buf, buf_size); + } + + void HtclowManager::SetSendBufferWithData(impl::ChannelInternalType channel, const void *buf, size_t buf_size) { + return m_impl->SetSendBufferWithData(channel, buf, buf_size); + } + + Result HtclowManager::Shutdown(impl::ChannelInternalType channel) { + return m_impl->Shutdown(channel); + } + } diff --git a/libraries/libstratosphere/source/htclow/htclow_manager.hpp b/libraries/libstratosphere/source/htclow/htclow_manager.hpp index 727422a25..1842d2e5c 100644 --- a/libraries/libstratosphere/source/htclow/htclow_manager.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_manager.hpp @@ -15,6 +15,7 @@ */ #pragma once #include +#include "driver/htclow_i_driver.hpp" namespace ams::htclow { @@ -31,7 +32,45 @@ namespace ams::htclow { Result OpenDriver(impl::DriverType driver_type); void CloseDriver(); + Result Open(impl::ChannelInternalType channel); + Result Close(impl::ChannelInternalType channel); + + void Resume(); + void Suspend(); + + Result ConnectBegin(u32 *out_task_id, impl::ChannelInternalType channel); + Result ConnectEnd(impl::ChannelInternalType channel, u32 task_id); + void Disconnect(); + + Result FlushBegin(u32 *out_task_id, impl::ChannelInternalType channel); + Result FlushEnd(u32 task_id); + + ChannelState GetChannelState(impl::ChannelInternalType channel); + os::EventType *GetChannelStateEvent(impl::ChannelInternalType channel); + + impl::DriverType GetDriverType(); + + os::EventType *GetTaskEvent(u32 task_id); + + void NotifyAsleep(); + void NotifyAwake(); + + Result ReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, bool blocking); + Result ReceiveEnd(size_t *out, void *dst, size_t dst_size, impl::ChannelInternalType channel, u32 task_id); + + Result SendBegin(u32 *out_task_id, size_t *out, const void *src, size_t src_size, impl::ChannelInternalType channel); + Result SendEnd(u32 task_id); + + void SetConfig(impl::ChannelInternalType channel, const ChannelConfig &config); + + void SetDebugDriver(driver::IDriver *driver); + + void SetReceiveBuffer(impl::ChannelInternalType channel, void *buf, size_t buf_size); + void SetSendBuffer(impl::ChannelInternalType channel, void *buf, size_t buf_size); + void SetSendBufferWithData(impl::ChannelInternalType channel, const void *buf, size_t buf_size); + + Result Shutdown(impl::ChannelInternalType channel); }; } diff --git a/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp b/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp index 34e530bd6..97c5b51fa 100644 --- a/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp +++ b/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp @@ -56,8 +56,116 @@ namespace ams::htclow { AMS_ABORT("HtclowManagerImpl::CloseDriver"); } + Result HtclowManagerImpl::Open(impl::ChannelInternalType channel) { + return m_mux.Open(channel); + } + + Result HtclowManagerImpl::Close(impl::ChannelInternalType channel) { + /* TODO: Used by HtclowDriver */ + AMS_ABORT("HtclowManagerImpl::Close"); + } + + void HtclowManagerImpl::Resume() { + AMS_ABORT("HtclowManagerImpl::Resume"); + } + + void HtclowManagerImpl::Suspend() { + AMS_ABORT("HtclowManagerImpl::Suspend"); + } + + Result HtclowManagerImpl::ConnectBegin(u32 *out_task_id, impl::ChannelInternalType channel) { + /* TODO: Used by HtclowDriver */ + AMS_ABORT("HtclowManagerImpl::ConnectBegin"); + } + + Result HtclowManagerImpl::ConnectEnd(impl::ChannelInternalType channel, u32 task_id) { + /* TODO: Used by HtclowDriver */ + AMS_ABORT("HtclowManagerImpl::ConnectEnd"); + } + void HtclowManagerImpl::Disconnect() { AMS_ABORT("HtclowManagerImpl::Disconnect"); } + Result HtclowManagerImpl::FlushBegin(u32 *out_task_id, impl::ChannelInternalType channel) { + AMS_ABORT("HtclowManagerImpl::FlushBegin"); + } + + Result HtclowManagerImpl::FlushEnd(u32 task_id) { + AMS_ABORT("HtclowManagerImpl::FlushEnd"); + } + + ChannelState HtclowManagerImpl::GetChannelState(impl::ChannelInternalType channel) { + /* TODO: Used by HtclowDriver */ + AMS_ABORT("HtclowManagerImpl::GetChannelState"); + } + + os::EventType *HtclowManagerImpl::GetChannelStateEvent(impl::ChannelInternalType channel) { + /* TODO: Used by HtclowDriver */ + AMS_ABORT("HtclowManagerImpl::GetChannelStateEvent"); + } + + impl::DriverType HtclowManagerImpl::GetDriverType() { + AMS_ABORT("HtclowManagerImpl::GetDriverType"); + } + + os::EventType *HtclowManagerImpl::GetTaskEvent(u32 task_id) { + return m_mux.GetTaskEvent(task_id); + } + + void HtclowManagerImpl::NotifyAsleep() { + AMS_ABORT("HtclowManagerImpl::NotifyAsleep"); + } + + void HtclowManagerImpl::NotifyAwake() { + AMS_ABORT("HtclowManagerImpl::NotifyAwake"); + } + + Result HtclowManagerImpl::ReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, bool blocking) { + /* TODO: Used by HtclowDriver */ + AMS_ABORT("HtclowManagerImpl::ReceiveBegin"); + } + + Result HtclowManagerImpl::ReceiveEnd(size_t *out, void *dst, size_t dst_size, impl::ChannelInternalType channel, u32 task_id) { + /* TODO: Used by HtclowDriver */ + AMS_ABORT("HtclowManagerImpl::ReceiveEnd"); + } + + Result HtclowManagerImpl::SendBegin(u32 *out_task_id, size_t *out, const void *src, size_t src_size, impl::ChannelInternalType channel) { + /* TODO: Used by HtclowDriver */ + AMS_ABORT("HtclowManagerImpl::SendBegin"); + } + + Result HtclowManagerImpl::SendEnd(u32 task_id) { + /* TODO: Used by HtclowDriver */ + AMS_ABORT("HtclowManagerImpl::SendEnd"); + } + + void HtclowManagerImpl::SetConfig(impl::ChannelInternalType channel, const ChannelConfig &config) { + AMS_ABORT("HtclowManagerImpl::SetConfig"); + } + + void HtclowManagerImpl::SetDebugDriver(driver::IDriver *driver) { + m_driver_manager.SetDebugDriver(driver); + } + + void HtclowManagerImpl::SetReceiveBuffer(impl::ChannelInternalType channel, void *buf, size_t buf_size) { + /* TODO: Used by HtclowDriver */ + AMS_ABORT("HtclowManagerImpl::SetReceiveBuffer"); + } + + void HtclowManagerImpl::SetSendBuffer(impl::ChannelInternalType channel, void *buf, size_t buf_size) { + /* TODO: Used by HtclowDriver */ + AMS_ABORT("HtclowManagerImpl::SetSendBuffer"); + } + + void HtclowManagerImpl::SetSendBufferWithData(impl::ChannelInternalType channel, const void *buf, size_t buf_size) { + AMS_ABORT("HtclowManagerImpl::SetSendBufferWithData"); + } + + Result HtclowManagerImpl::Shutdown(impl::ChannelInternalType channel) { + /* TODO: Used by HtclowDriver */ + AMS_ABORT("HtclowManagerImpl::Shutdown"); + } + } diff --git a/libraries/libstratosphere/source/htclow/htclow_manager_impl.hpp b/libraries/libstratosphere/source/htclow/htclow_manager_impl.hpp index 8a6f05d11..59c63aa02 100644 --- a/libraries/libstratosphere/source/htclow/htclow_manager_impl.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_manager_impl.hpp @@ -44,7 +44,45 @@ namespace ams::htclow { Result OpenDriver(impl::DriverType driver_type); void CloseDriver(); + Result Open(impl::ChannelInternalType channel); + Result Close(impl::ChannelInternalType channel); + + void Resume(); + void Suspend(); + + Result ConnectBegin(u32 *out_task_id, impl::ChannelInternalType channel); + Result ConnectEnd(impl::ChannelInternalType channel, u32 task_id); + void Disconnect(); + + Result FlushBegin(u32 *out_task_id, impl::ChannelInternalType channel); + Result FlushEnd(u32 task_id); + + ChannelState GetChannelState(impl::ChannelInternalType channel); + os::EventType *GetChannelStateEvent(impl::ChannelInternalType channel); + + impl::DriverType GetDriverType(); + + os::EventType *GetTaskEvent(u32 task_id); + + void NotifyAsleep(); + void NotifyAwake(); + + Result ReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, bool blocking); + Result ReceiveEnd(size_t *out, void *dst, size_t dst_size, impl::ChannelInternalType channel, u32 task_id); + + Result SendBegin(u32 *out_task_id, size_t *out, const void *src, size_t src_size, impl::ChannelInternalType channel); + Result SendEnd(u32 task_id); + + void SetConfig(impl::ChannelInternalType channel, const ChannelConfig &config); + + void SetDebugDriver(driver::IDriver *driver); + + void SetReceiveBuffer(impl::ChannelInternalType channel, void *buf, size_t buf_size); + void SetSendBuffer(impl::ChannelInternalType channel, void *buf, size_t buf_size); + void SetSendBufferWithData(impl::ChannelInternalType channel, const void *buf, size_t buf_size); + + Result Shutdown(impl::ChannelInternalType channel); }; } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp index 69e6288ba..a9e907511 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp @@ -166,4 +166,27 @@ namespace ams::htclow::mux { } } + Result Mux::Open(impl::ChannelInternalType channel) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Check that the channel doesn't already exist. */ + R_UNLESS(!m_channel_impl_map.Exists(channel), htclow::ResultChannelAlreadyExist()); + + /* Add the channel. */ + R_TRY(m_channel_impl_map.AddChannel(channel)); + + /* Set the channel version. */ + m_channel_impl_map.GetChannelImpl(channel).SetVersion(m_version); + + return ResultSuccess(); + } + + os::EventType *Mux::GetTaskEvent(u32 task_id) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + return m_task_manager.GetTaskEvent(task_id); + } + } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp index 48e1a3a8e..9829310bb 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp @@ -53,6 +53,10 @@ namespace ams::htclow::mux { void UpdateChannelState(); void UpdateMuxState(); + public: + Result Open(impl::ChannelInternalType channel); + + os::EventType *GetTaskEvent(u32 task_id); private: Result CheckChannelExist(impl::ChannelInternalType channel); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp index b475f4cf2..0c7c7390c 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp @@ -16,9 +16,20 @@ #include #include "htclow_mux_channel_impl.hpp" #include "../ctrl/htclow_ctrl_state_machine.hpp" +#include "../htclow_default_channel_config.hpp" namespace ams::htclow::mux { + ChannelImpl::ChannelImpl(impl::ChannelInternalType channel, PacketFactory *pf, ctrl::HtcctrlStateMachine *sm, TaskManager *tm, os::Event *ev) + : m_channel(channel), m_packet_factory(pf), m_state_machine(sm), m_task_manager(tm), m_event(ev), + m_send_buffer(m_channel, pf), m_receive_buffer(), m_version(ProtocolVersion), m_config(DefaultChannelConfig), + m_offset(0), m_total_send_size(0), m_next_max_data(0), m_cur_max_data(0), m_share(), + m_state_change_event(os::EventClearMode_ManualClear), m_state(ChannelState_Unconnectable) + + { + this->UpdateState(); + } + void ChannelImpl::SetVersion(s16 version) { /* Sanity check the version. */ AMS_ASSERT(version <= ProtocolVersion); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp index 5b7ba3485..c167343ea 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp @@ -43,10 +43,10 @@ namespace ams::htclow::mux { RingBuffer m_receive_buffer; s16 m_version; ChannelConfig m_config; + u64 m_offset; u64 m_total_send_size; u64 m_next_max_data; u64 m_cur_max_data; - u64 m_offset; std::optional m_share; os::Event m_state_change_event; ChannelState m_state; diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.cpp index cd3ccf955..9ed955bd6 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.cpp @@ -43,4 +43,28 @@ namespace ams::htclow::mux { return this->GetChannelImpl(it->second); } + Result ChannelImplMap::AddChannel(impl::ChannelInternalType channel) { + /* Find a free storage. */ + int idx; + for (idx = 0; idx < MaxChannelCount; ++idx) { + if (!m_storage_valid[idx]) { + break; + } + } + + /* Validate that the storage is free. */ + R_UNLESS(idx < MaxChannelCount, htclow::ResultOutOfResource()); + + /* Create the channel impl. */ + std::construct_at(GetPointer(m_channel_storage[idx]), channel, m_packet_factory, m_state_machine, m_task_manager, m_event); + + /* Mark the storage valid. */ + m_storage_valid[idx] = true; + + /* Insert into our map. */ + m_map.insert(std::pair{channel, idx}); + + return ResultSuccess(); + } + } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp index 623c0a07d..a949b8261 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp @@ -46,6 +46,8 @@ namespace ams::htclow::mux { bool Exists(impl::ChannelInternalType channel) const { return m_map.find(channel) != m_map.end(); } + + Result AddChannel(impl::ChannelInternalType channel); private: public: MapType &GetMap() { diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp index c96b0535f..ce5f67576 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp @@ -18,6 +18,14 @@ namespace ams::htclow::mux { + os::EventType *TaskManager::GetTaskEvent(u32 task_id) { + /* Check pre-conditions. */ + AMS_ASSERT(0 <= task_id && task_id < MaxTaskCount); + AMS_ASSERT(m_valid[task_id]); + + return std::addressof(m_tasks[task_id].event); + } + void TaskManager::NotifyDisconnect(impl::ChannelInternalType channel) { for (auto i = 0; i < MaxTaskCount; ++i) { if (m_valid[i] && m_tasks[i].channel == channel) { diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp index 5feac0803..bb27d3182 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp @@ -51,6 +51,8 @@ namespace ams::htclow::mux { public: TaskManager() : m_valid() { /* ... */ } + os::EventType *GetTaskEvent(u32 task_id); + void NotifyDisconnect(impl::ChannelInternalType channel); void NotifyReceiveData(impl::ChannelInternalType channel, size_t size); void NotifySendReady(); diff --git a/libraries/libvapours/include/vapours/results/htclow_results.hpp b/libraries/libvapours/include/vapours/results/htclow_results.hpp index 60e719b79..ef8976ff8 100644 --- a/libraries/libvapours/include/vapours/results/htclow_results.hpp +++ b/libraries/libvapours/include/vapours/results/htclow_results.hpp @@ -20,10 +20,13 @@ namespace ams::htclow { R_DEFINE_NAMESPACE_RESULT_MODULE(29); - R_DEFINE_ERROR_RESULT(ConnectionFailure, 1); - R_DEFINE_ERROR_RESULT(UnknownDriverType, 3); - R_DEFINE_ERROR_RESULT(NonBlockingReceiveFailed, 5); - R_DEFINE_ERROR_RESULT(ChannelNotExist, 10); + R_DEFINE_ERROR_RESULT(ConnectionFailure, 1); + R_DEFINE_ERROR_RESULT(UnknownDriverType, 3); + R_DEFINE_ERROR_RESULT(NonBlockingReceiveFailed, 5); + R_DEFINE_ERROR_RESULT(ChannelAlreadyExist, 9); + R_DEFINE_ERROR_RESULT(ChannelNotExist, 10); + + R_DEFINE_ERROR_RESULT(OutOfResource, 151); R_DEFINE_ERROR_RESULT(InvalidChannelState, 200); R_DEFINE_ERROR_RESULT(InvalidChannelStateDisconnected, 201); diff --git a/libraries/libvapours/include/vapours/util.hpp b/libraries/libvapours/include/vapours/util.hpp index 01cbeb5c5..a7cce0a9a 100644 --- a/libraries/libvapours/include/vapours/util.hpp +++ b/libraries/libvapours/include/vapours/util.hpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -39,7 +40,6 @@ #include #include #include -#include #include #include #include From 87165e0f08afe0579be7b0fdf61baaa17d3dcedb Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 9 Feb 2021 17:42:39 -0800 Subject: [PATCH 037/280] htc: implement remaining htclow::HtclowManagerImpl funcs (mux impls pending) --- .../htclow/driver/htclow_driver_manager.cpp | 8 ++ .../htclow/driver/htclow_driver_manager.hpp | 2 + .../source/htclow/htclow_manager_impl.cpp | 97 ++++++++++++------- .../source/htclow/mux/htclow_mux.cpp | 36 +++++++ .../source/htclow/mux/htclow_mux.hpp | 4 + .../htclow/mux/htclow_mux_channel_impl.cpp | 31 ++++++ .../htclow/mux/htclow_mux_channel_impl.hpp | 4 + .../htclow/mux/htclow_mux_ring_buffer.cpp | 23 +++++ .../htclow/mux/htclow_mux_ring_buffer.hpp | 3 + .../htclow/mux/htclow_mux_send_buffer.cpp | 12 +++ .../htclow/mux/htclow_mux_send_buffer.hpp | 4 + 11 files changed, 190 insertions(+), 34 deletions(-) diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp index 1b79aeb93..e08f2529d 100644 --- a/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp +++ b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp @@ -49,9 +49,17 @@ namespace ams::htclow::driver { return htclow::ResultUnknownDriverType(); } + /* Set the driver type. */ + m_driver_type = driver_type; + return ResultSuccess(); } + impl::DriverType DriverManager::GetDriverType() { + /* Lock ourselves. */ + return m_driver_type.value_or(impl::DriverType::Unknown); + } + IDriver *DriverManager::GetCurrentDriver() { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp index c1ae457fb..1b0d3d967 100644 --- a/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp +++ b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp @@ -34,6 +34,8 @@ namespace ams::htclow::driver { Result OpenDriver(impl::DriverType driver_type); + impl::DriverType GetDriverType(); + IDriver *GetCurrentDriver(); void SetDebugDriver(IDriver *driver); diff --git a/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp b/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp index 97c5b51fa..7ff313ac5 100644 --- a/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp +++ b/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp @@ -15,6 +15,7 @@ */ #include #include "htclow_manager_impl.hpp" +#include "htclow_default_channel_config.hpp" namespace ams::htclow { @@ -53,7 +54,24 @@ namespace ams::htclow { } void HtclowManagerImpl::CloseDriver() { - AMS_ABORT("HtclowManagerImpl::CloseDriver"); + /* Close the driver, if we're open. */ + if (m_is_driver_open) { + /* Cancel the driver. */ + m_driver_manager.Cancel(); + + /* Stop our listener. */ + m_listener.Cancel(); + m_listener.Wait(); + + /* Close the driver. */ + m_driver_manager.CloseDriver(); + + /* Set the driver type to unknown. */ + m_ctrl_service.SetDriverType(impl::DriverType::Unknown); + + /* Note the driver as closed. */ + m_is_driver_open = false; + } } Result HtclowManagerImpl::Open(impl::ChannelInternalType channel) { @@ -61,52 +79,70 @@ namespace ams::htclow { } Result HtclowManagerImpl::Close(impl::ChannelInternalType channel) { - /* TODO: Used by HtclowDriver */ - AMS_ABORT("HtclowManagerImpl::Close"); + return m_mux.Close(channel); } void HtclowManagerImpl::Resume() { - AMS_ABORT("HtclowManagerImpl::Resume"); + /* Get our driver. */ + auto *driver = m_driver_manager.GetCurrentDriver(); + + /* Resume our driver. */ + driver->Resume(); + + /* Start the listener. */ + m_listener.Start(driver); + + /* Resume our control service. */ + m_ctrl_service.Resume(); } void HtclowManagerImpl::Suspend() { - AMS_ABORT("HtclowManagerImpl::Suspend"); + /* Suspend our control service. */ + m_ctrl_service.Suspend(); + + /* Stop our listener. */ + m_listener.Cancel(); + m_listener.Wait(); + + /* Suspend our driver. */ + m_driver_manager.GetCurrentDriver()->Suspend(); } Result HtclowManagerImpl::ConnectBegin(u32 *out_task_id, impl::ChannelInternalType channel) { - /* TODO: Used by HtclowDriver */ - AMS_ABORT("HtclowManagerImpl::ConnectBegin"); + /* Begin connecting. */ + R_TRY(m_mux.ConnectBegin(out_task_id, channel)); + + /* Try to ready ourselves. */ + m_ctrl_service.TryReady(); + return ResultSuccess(); } Result HtclowManagerImpl::ConnectEnd(impl::ChannelInternalType channel, u32 task_id) { - /* TODO: Used by HtclowDriver */ - AMS_ABORT("HtclowManagerImpl::ConnectEnd"); + return m_mux.ConnectEnd(channel, task_id); } void HtclowManagerImpl::Disconnect() { - AMS_ABORT("HtclowManagerImpl::Disconnect"); + return m_ctrl_service.Disconnect(); } Result HtclowManagerImpl::FlushBegin(u32 *out_task_id, impl::ChannelInternalType channel) { - AMS_ABORT("HtclowManagerImpl::FlushBegin"); + return m_mux.FlushBegin(out_task_id, channel); } Result HtclowManagerImpl::FlushEnd(u32 task_id) { - AMS_ABORT("HtclowManagerImpl::FlushEnd"); + return m_mux.FlushEnd(task_id); } ChannelState HtclowManagerImpl::GetChannelState(impl::ChannelInternalType channel) { - /* TODO: Used by HtclowDriver */ - AMS_ABORT("HtclowManagerImpl::GetChannelState"); + return m_mux.GetChannelState(channel); } os::EventType *HtclowManagerImpl::GetChannelStateEvent(impl::ChannelInternalType channel) { - /* TODO: Used by HtclowDriver */ - AMS_ABORT("HtclowManagerImpl::GetChannelStateEvent"); + return m_mux.GetChannelStateEvent(channel); } impl::DriverType HtclowManagerImpl::GetDriverType() { - AMS_ABORT("HtclowManagerImpl::GetDriverType"); + return m_driver_manager.GetDriverType(); } os::EventType *HtclowManagerImpl::GetTaskEvent(u32 task_id) { @@ -114,31 +150,27 @@ namespace ams::htclow { } void HtclowManagerImpl::NotifyAsleep() { - AMS_ABORT("HtclowManagerImpl::NotifyAsleep"); + return m_ctrl_service.NotifyAsleep(); } void HtclowManagerImpl::NotifyAwake() { - AMS_ABORT("HtclowManagerImpl::NotifyAwake"); + return m_ctrl_service.NotifyAwake(); } Result HtclowManagerImpl::ReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, bool blocking) { - /* TODO: Used by HtclowDriver */ - AMS_ABORT("HtclowManagerImpl::ReceiveBegin"); + return m_mux.ReceiveBegin(out_task_id, channel, blocking); } Result HtclowManagerImpl::ReceiveEnd(size_t *out, void *dst, size_t dst_size, impl::ChannelInternalType channel, u32 task_id) { - /* TODO: Used by HtclowDriver */ - AMS_ABORT("HtclowManagerImpl::ReceiveEnd"); + return m_mux.ReceiveEnd(out, dst, dst_size, channel, task_id); } Result HtclowManagerImpl::SendBegin(u32 *out_task_id, size_t *out, const void *src, size_t src_size, impl::ChannelInternalType channel) { - /* TODO: Used by HtclowDriver */ - AMS_ABORT("HtclowManagerImpl::SendBegin"); + return m_mux.SendBegin(out_task_id, out, src, src_size, channel); } Result HtclowManagerImpl::SendEnd(u32 task_id) { - /* TODO: Used by HtclowDriver */ - AMS_ABORT("HtclowManagerImpl::SendEnd"); + return m_mux.SendEnd(task_id); } void HtclowManagerImpl::SetConfig(impl::ChannelInternalType channel, const ChannelConfig &config) { @@ -150,22 +182,19 @@ namespace ams::htclow { } void HtclowManagerImpl::SetReceiveBuffer(impl::ChannelInternalType channel, void *buf, size_t buf_size) { - /* TODO: Used by HtclowDriver */ - AMS_ABORT("HtclowManagerImpl::SetReceiveBuffer"); + return m_mux.SetReceiveBuffer(channel, buf, buf_size); } void HtclowManagerImpl::SetSendBuffer(impl::ChannelInternalType channel, void *buf, size_t buf_size) { - /* TODO: Used by HtclowDriver */ - AMS_ABORT("HtclowManagerImpl::SetSendBuffer"); + return m_mux.SetSendBuffer(channel, buf, buf_size, m_driver_manager.GetDriverType() == impl::DriverType::Usb ? sizeof(PacketBody) : DefaultChannelConfig.max_packet_size); } void HtclowManagerImpl::SetSendBufferWithData(impl::ChannelInternalType channel, const void *buf, size_t buf_size) { - AMS_ABORT("HtclowManagerImpl::SetSendBufferWithData"); + return m_mux.SetSendBufferWithData(channel, buf, buf_size, m_driver_manager.GetDriverType() == impl::DriverType::Usb ? sizeof(PacketBody) : DefaultChannelConfig.max_packet_size); } Result HtclowManagerImpl::Shutdown(impl::ChannelInternalType channel) { - /* TODO: Used by HtclowDriver */ - AMS_ABORT("HtclowManagerImpl::Shutdown"); + return m_mux.Shutdown(channel); } } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp index a9e907511..656ce104f 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp @@ -189,4 +189,40 @@ namespace ams::htclow::mux { return m_task_manager.GetTaskEvent(task_id); } + void Mux::SetSendBuffer(impl::ChannelInternalType channel, void *buf, size_t buf_size, size_t max_packet_size) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Find the channel. */ + auto it = m_channel_impl_map.GetMap().find(channel); + AMS_ABORT_UNLESS(it != m_channel_impl_map.GetMap().end()); + + /* Set the send buffer. */ + m_channel_impl_map[it->second].SetSendBuffer(buf, buf_size, max_packet_size); + } + + void Mux::SetSendBufferWithData(impl::ChannelInternalType channel, const void *buf, size_t buf_size, size_t max_packet_size) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Find the channel. */ + auto it = m_channel_impl_map.GetMap().find(channel); + AMS_ABORT_UNLESS(it != m_channel_impl_map.GetMap().end()); + + /* Set the send buffer. */ + m_channel_impl_map[it->second].SetSendBufferWithData(buf, buf_size, max_packet_size); + } + + void Mux::SetReceiveBuffer(impl::ChannelInternalType channel, void *buf, size_t buf_size) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Find the channel. */ + auto it = m_channel_impl_map.GetMap().find(channel); + AMS_ABORT_UNLESS(it != m_channel_impl_map.GetMap().end()); + + /* Set the send buffer. */ + m_channel_impl_map[it->second].SetReceiveBuffer(buf, buf_size); + } + } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp index 9829310bb..39d900aad 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp @@ -57,6 +57,10 @@ namespace ams::htclow::mux { Result Open(impl::ChannelInternalType channel); os::EventType *GetTaskEvent(u32 task_id); + + void SetSendBuffer(impl::ChannelInternalType channel, void *buf, size_t buf_size, size_t max_packet_size); + void SetReceiveBuffer(impl::ChannelInternalType channel, void *buf, size_t buf_size); + void SetSendBufferWithData(impl::ChannelInternalType channel, const void *buf, size_t buf_size, size_t max_packet_size); private: Result CheckChannelExist(impl::ChannelInternalType channel); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp index 0c7c7390c..c683aaa76 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp @@ -228,4 +228,35 @@ namespace ams::htclow::mux { } } + void ChannelImpl::SetSendBuffer(void *buf, size_t buf_size, size_t max_packet_size) { + /* Set buffer. */ + m_send_buffer.SetBuffer(buf, buf_size); + + /* Determine true max packet size. */ + if (m_config.flow_control_enabled) { + max_packet_size = std::min(max_packet_size, m_config.max_packet_size); + } + + /* Set max packet size. */ + m_send_buffer.SetMaxPacketSize(max_packet_size); + } + + void ChannelImpl::SetReceiveBuffer(void *buf, size_t buf_size) { + /* Set the buffer. */ + m_receive_buffer.Initialize(buf, buf_size); + } + + void ChannelImpl::SetSendBufferWithData(const void *buf, size_t buf_size, size_t max_packet_size) { + /* Set buffer. */ + m_send_buffer.SetReadOnlyBuffer(buf, buf_size); + + /* Determine true max packet size. */ + if (m_config.flow_control_enabled) { + max_packet_size = std::min(max_packet_size, m_config.max_packet_size); + } + + /* Set max packet size. */ + m_send_buffer.SetMaxPacketSize(max_packet_size); + } + } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp index c167343ea..27191d2e1 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp @@ -62,6 +62,10 @@ namespace ams::htclow::mux { void RemovePacket(const PacketHeader &header); void UpdateState(); + public: + void SetSendBuffer(void *buf, size_t buf_size, size_t max_packet_size); + void SetReceiveBuffer(void *buf, size_t buf_size); + void SetSendBufferWithData(const void *buf, size_t buf_size, size_t max_packet_size); private: void ShutdownForce(); void SetState(ChannelState state); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.cpp index 0e8903dcc..e27f36b60 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.cpp @@ -18,6 +18,29 @@ namespace ams::htclow::mux { + void RingBuffer::Initialize(void *buffer, size_t buffer_size) { + /* Validate pre-conditions. */ + AMS_ASSERT(m_buffer == nullptr); + AMS_ASSERT(m_read_only_buffer == nullptr); + + /* Set our fields. */ + m_buffer = buffer; + m_buffer_size = buffer_size; + m_is_read_only = false; + } + + void RingBuffer::InitializeForReadOnly(const void *buffer, size_t buffer_size) { + /* Validate pre-conditions. */ + AMS_ASSERT(m_buffer == nullptr); + AMS_ASSERT(m_read_only_buffer == nullptr); + + /* Set our fields. */ + m_read_only_buffer = const_cast(buffer); + m_buffer_size = buffer_size; + m_data_size = buffer_size; + m_is_read_only = true; + } + Result RingBuffer::Write(const void *data, size_t size) { /* Validate pre-conditions. */ AMS_ASSERT(!m_is_read_only); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.hpp index 2cae14183..1ed9c8d38 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.hpp @@ -30,6 +30,9 @@ namespace ams::htclow::mux { public: RingBuffer() : m_buffer(), m_read_only_buffer(), m_is_read_only(true), m_buffer_size(), m_data_size(), m_offset(), m_can_discard(false) { /* ... */ } + void Initialize(void *buffer, size_t buffer_size); + void InitializeForReadOnly(const void *buffer, size_t buffer_size); + size_t GetDataSize() { return m_data_size; } Result Write(const void *data, size_t size); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp index 8e5c510ce..b7961a8ba 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp @@ -115,6 +115,18 @@ namespace ams::htclow::mux { } } + void SendBuffer::SetBuffer(void *buffer, size_t buffer_size) { + m_ring_buffer.Initialize(buffer, buffer_size); + } + + void SendBuffer::SetReadOnlyBuffer(const void *buffer, size_t buffer_size) { + m_ring_buffer.InitializeForReadOnly(buffer, buffer_size); + } + + void SendBuffer::SetMaxPacketSize(size_t max_packet_size) { + m_max_packet_size = max_packet_size; + } + bool SendBuffer::Empty() { return m_packet_list.empty() && m_ring_buffer.GetDataSize() == 0; } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp index 2c7ee6b5d..65aa19fe5 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp @@ -52,6 +52,10 @@ namespace ams::htclow::mux { void RemovePacket(const PacketHeader &header); + void SetBuffer(void *buffer, size_t buffer_size); + void SetReadOnlyBuffer(const void *buffer, size_t buffer_size); + void SetMaxPacketSize(size_t max_packet_size); + bool Empty(); void Clear(); From 70aae4e27ab8df641a4420722af7bef331772e1a Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 9 Feb 2021 17:44:12 -0800 Subject: [PATCH 038/280] htc: fix driver manager c/p error --- .../source/htclow/driver/htclow_driver_manager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp index e08f2529d..8f951c9db 100644 --- a/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp +++ b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp @@ -57,6 +57,8 @@ namespace ams::htclow::driver { impl::DriverType DriverManager::GetDriverType() { /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + return m_driver_type.value_or(impl::DriverType::Unknown); } From 42cf3f50d799df4174c6c57952e143e4ccc3896e Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 9 Feb 2021 19:07:51 -0800 Subject: [PATCH 039/280] htc: implement mux side of connecting (and more) --- .../htclow/htclow_channel_types.hpp | 1 + .../htclow/ctrl/htclow_ctrl_service.cpp | 89 ++++++++++++++++++- .../htclow/ctrl/htclow_ctrl_service.hpp | 18 +++- .../htclow/ctrl/htclow_ctrl_state_machine.cpp | 17 ++++ .../htclow/ctrl/htclow_ctrl_state_machine.hpp | 3 + .../htclow/driver/htclow_driver_manager.cpp | 18 ++++ .../htclow/driver/htclow_driver_manager.hpp | 3 + .../htclow/htclow_default_channel_config.hpp | 7 +- .../source/htclow/htclow_listener.cpp | 28 ++++++ .../source/htclow/htclow_listener.hpp | 2 + .../source/htclow/htclow_worker.hpp | 3 +- .../source/htclow/mux/htclow_mux.cpp | 60 +++++++++++++ .../source/htclow/mux/htclow_mux.hpp | 18 ++++ .../htclow/mux/htclow_mux_channel_impl.cpp | 62 +++++++++++++ .../htclow/mux/htclow_mux_channel_impl.hpp | 6 +- .../mux/htclow_mux_channel_impl_map.cpp | 26 +++++- .../mux/htclow_mux_channel_impl_map.hpp | 1 + .../htclow/mux/htclow_mux_ring_buffer.cpp | 6 ++ .../htclow/mux/htclow_mux_ring_buffer.hpp | 3 + .../htclow/mux/htclow_mux_send_buffer.cpp | 16 ++++ .../htclow/mux/htclow_mux_send_buffer.hpp | 2 + .../htclow/mux/htclow_mux_task_manager.cpp | 83 +++++++++++++++++ .../htclow/mux/htclow_mux_task_manager.hpp | 11 ++- .../vapours/results/htclow_results.hpp | 3 +- 24 files changed, 474 insertions(+), 12 deletions(-) diff --git a/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp b/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp index 51601f9c4..cf24af32c 100644 --- a/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp @@ -37,6 +37,7 @@ namespace ams::htclow { struct ChannelConfig { bool flow_control_enabled; bool handshake_enabled; + u64 initial_counter_max_data; size_t max_packet_size; }; diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp index 724a6f734..c8fc4722c 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp @@ -40,7 +40,7 @@ namespace ams::htclow::ctrl { } HtcctrlService::HtcctrlService(HtcctrlPacketFactory *pf, HtcctrlStateMachine *sm, mux::Mux *mux) - : m_settings_holder(), m_beacon_response(), m_1100(), m_packet_factory(pf), m_state_machine(sm), m_mux(mux), m_event(os::EventClearMode_ManualClear), + : m_settings_holder(), m_beacon_response(), m_information_body(), m_packet_factory(pf), m_state_machine(sm), m_mux(mux), m_event(os::EventClearMode_ManualClear), m_send_buffer(pf), m_mutex(), m_condvar(), m_service_channels_packet(), m_version(ProtocolVersion) { /* Lock ourselves. */ @@ -78,6 +78,10 @@ namespace ams::htclow::ctrl { ); } + void HtcctrlService::UpdateInformationBody(const char *status) { + util::SNPrintf(m_information_body, sizeof(m_information_body), "{\r\n \"Status\" : \"%s\"\r\n}\r\n", status); + } + void HtcctrlService::SetDriverType(impl::DriverType driver_type) { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); @@ -333,6 +337,89 @@ namespace ams::htclow::ctrl { } } + void HtcctrlService::TryReady() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + this->TryReadyInternal(); + } + + void HtcctrlService::Disconnect() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + this->DisconnectInternal(); + } + + void HtcctrlService::Resume() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Send resume packet, if we can. */ + if (const auto state = m_state_machine->GetHtcctrlState(); state == HtcctrlState_Sleep || state == HtcctrlState_ExitSleep) { + /* Send a resume packet. */ + m_send_buffer.AddPacket(m_packet_factory->MakeResumePacket()); + + /* Signal our event. */ + m_event.Signal(); + } + } + + void HtcctrlService::Suspend() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* If we can, perform a suspend. */ + if (m_state_machine->GetHtcctrlState() == HtcctrlState_Ready) { + /* Send a suspend packet. */ + m_send_buffer.AddPacket(m_packet_factory->MakeSuspendPacket()); + + /* Signal our event. */ + m_event.Signal(); + + /* Wait for our state to transition. */ + for (auto state = m_state_machine->GetHtcctrlState(); state == HtcctrlState_Ready || state == HtcctrlState_SentSuspendFromTarget; state = m_state_machine->GetHtcctrlState()) { + m_condvar.Wait(m_mutex); + } + } else { + /* Otherwise, just disconnect. */ + this->DisconnectInternal(); + } + } + + void HtcctrlService::NotifyAwake() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Update our information. */ + this->UpdateInformationBody("Awake"); + + /* Send information to host. */ + this->SendInformation(); + } + + void HtcctrlService::NotifyAsleep() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Update our information. */ + this->UpdateInformationBody("Asleep"); + + /* Send information to host. */ + this->SendInformation(); + } + + void HtcctrlService::SendInformation() { + /* If we need information, send information. */ + if (m_state_machine->IsInformationNeeded()) { + /* Send an information packet. */ + m_send_buffer.AddPacket(m_packet_factory->MakeInformationPacket(m_information_body, util::Strnlen(m_information_body, sizeof(m_information_body)) + 1)); + + /* Signal our event. */ + m_event.Signal(); + } + } + Result HtcctrlService::NotifyDriverConnected() { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp index 608d8ada5..3f52ce242 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp @@ -42,7 +42,7 @@ namespace ams::htclow::ctrl { private: SettingsHolder m_settings_holder; char m_beacon_response[0x1000]; - u8 m_1100[0x1000]; + char m_information_body[0x1000]; HtcctrlPacketFactory *m_packet_factory; HtcctrlStateMachine *m_state_machine; mux::Mux *m_mux; @@ -56,6 +56,9 @@ namespace ams::htclow::ctrl { const char *GetConnectionType(impl::DriverType driver_type) const; void UpdateBeaconResponse(const char *connection); + void UpdateInformationBody(const char *status); + + void SendInformation(); Result ProcessReceiveConnectPacket(); Result ProcessReceiveReadyPacket(const void *body, size_t body_size); @@ -72,10 +75,12 @@ namespace ams::htclow::ctrl { void ProcessSendDisconnectPacket(); void UpdateServiceChannels(const void *body, size_t body_size); - void TryReadyInternal(); void PrintServiceChannels(char *dst, size_t dst_size); + void TryReadyInternal(); + void DisconnectInternal(); + Result SetState(HtcctrlState state); void ReflectState(); public: @@ -91,6 +96,15 @@ namespace ams::htclow::ctrl { bool QuerySendPacket(HtcctrlPacketHeader *header, HtcctrlPacketBody *body, int *out_body_size); void RemovePacket(const HtcctrlPacketHeader &header); + void TryReady(); + void Disconnect(); + + void Resume(); + void Suspend(); + + void NotifyAwake(); + void NotifyAsleep(); + Result NotifyDriverConnected(); Result NotifyDriverDisconnected(); }; diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp index 1836dbeeb..e9acb3fd2 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp @@ -57,6 +57,13 @@ namespace ams::htclow::ctrl { return ResultSuccess(); } + bool HtcctrlStateMachine::IsInformationNeeded() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + return !ctrl::IsDisconnected(m_state) && m_state != HtcctrlState_DriverConnected; + } + bool HtcctrlStateMachine::IsConnected() { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); @@ -149,6 +156,16 @@ namespace ams::htclow::ctrl { return ctrl::IsConnected(m_state) && (it == m_map.end() || it->second.connect != ServiceChannelConnect_ConnectingChecked); } + void HtcctrlStateMachine::SetConnecting(const impl::ChannelInternalType &channel) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + auto it = m_map.find(channel); + if (it != m_map.end() && it->second.connect != ServiceChannelConnect_ConnectingChecked) { + it->second.connect = ServiceChannelConnect_Connecting; + } + } + void HtcctrlStateMachine::SetNotConnecting(const impl::ChannelInternalType &channel) { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp index aed8d5068..1e9fd254f 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp @@ -58,6 +58,8 @@ namespace ams::htclow::ctrl { bool IsConnectedStatusChanged(); bool IsSleepingStatusChanged(); + bool IsInformationNeeded(); + bool IsConnected(); bool IsReadied(); bool IsUnconnectable(); @@ -68,6 +70,7 @@ namespace ams::htclow::ctrl { bool IsUnsupportedServiceChannelToShutdown(const impl::ChannelInternalType &channel); bool IsConnectable(const impl::ChannelInternalType &channel); + void SetConnecting(const impl::ChannelInternalType &channel); void SetNotConnecting(const impl::ChannelInternalType &channel); void SetConnectingChecked(); diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp index 8f951c9db..2f7220387 100644 --- a/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp +++ b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp @@ -55,6 +55,20 @@ namespace ams::htclow::driver { return ResultSuccess(); } + void DriverManager::CloseDriver() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Clear our driver type. */ + m_driver_type = std::nullopt; + + /* Close our driver. */ + if (m_open_driver != nullptr) { + m_open_driver->Close(); + m_open_driver = nullptr; + } + } + impl::DriverType DriverManager::GetDriverType() { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); @@ -69,6 +83,10 @@ namespace ams::htclow::driver { return m_open_driver; } + void DriverManager::Cancel() { + m_open_driver->CancelSendReceive(); + } + void DriverManager::SetDebugDriver(IDriver *driver) { m_debug_driver = driver; m_driver_type = impl::DriverType::Debug; diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp index 1b0d3d967..f19d46d1e 100644 --- a/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp +++ b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp @@ -33,11 +33,14 @@ namespace ams::htclow::driver { DriverManager() = default; Result OpenDriver(impl::DriverType driver_type); + void CloseDriver(); impl::DriverType GetDriverType(); IDriver *GetCurrentDriver(); + void Cancel(); + void SetDebugDriver(IDriver *driver); }; diff --git a/libraries/libstratosphere/source/htclow/htclow_default_channel_config.hpp b/libraries/libstratosphere/source/htclow/htclow_default_channel_config.hpp index a442ef2ed..27d58a82e 100644 --- a/libraries/libstratosphere/source/htclow/htclow_default_channel_config.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_default_channel_config.hpp @@ -20,9 +20,10 @@ namespace ams::htclow { constexpr inline const ChannelConfig DefaultChannelConfig = { - .flow_control_enabled = true, - .handshake_enabled = true, - .max_packet_size = 0xE000 + sizeof(PacketHeader), + .flow_control_enabled = true, + .handshake_enabled = true, + .initial_counter_max_data = 0, + .max_packet_size = 0xE000 + sizeof(PacketHeader), }; } diff --git a/libraries/libstratosphere/source/htclow/htclow_listener.cpp b/libraries/libstratosphere/source/htclow/htclow_listener.cpp index 278cfc7cb..a7dd94bea 100644 --- a/libraries/libstratosphere/source/htclow/htclow_listener.cpp +++ b/libraries/libstratosphere/source/htclow/htclow_listener.cpp @@ -55,6 +55,34 @@ namespace ams::htclow { os::StartThread(std::addressof(m_listen_thread)); } + void Listener::Cancel() { + /* Mark ourselves as cancelled. */ + m_cancelled = true; + + /* Cancel our worker. */ + m_worker->Cancel(); + + /* Signal our event. */ + m_event.Signal(); + + /* Cancel our driver. */ + m_driver->CancelSendReceive(); + } + + void Listener::Wait() { + /* Wait for our listen thread to exit. */ + os::WaitThread(std::addressof(m_listen_thread)); + + /* Destroy our listen thread. */ + os::DestroyThread(std::addressof(m_listen_thread)); + + /* Clear our driver. */ + m_driver = nullptr; + + /* Mark our thread as not running. */ + m_thread_running = false; + } + void Listener::ListenThread() { /* Check pre-conditions. */ AMS_ASSERT(m_driver != nullptr); diff --git a/libraries/libstratosphere/source/htclow/htclow_listener.hpp b/libraries/libstratosphere/source/htclow/htclow_listener.hpp index 17e4aa698..a2c1fdbb6 100644 --- a/libraries/libstratosphere/source/htclow/htclow_listener.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_listener.hpp @@ -42,6 +42,8 @@ namespace ams::htclow { Listener(mem::StandardAllocator *allocator, mux::Mux *mux, ctrl::HtcctrlService *ctrl_srv, Worker *worker); void Start(driver::IDriver *driver); + void Cancel(); + void Wait(); }; } diff --git a/libraries/libstratosphere/source/htclow/htclow_worker.hpp b/libraries/libstratosphere/source/htclow/htclow_worker.hpp index 6efb0d6c9..34a3ca8aa 100644 --- a/libraries/libstratosphere/source/htclow/htclow_worker.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_worker.hpp @@ -56,10 +56,9 @@ namespace ams::htclow { void SetDriver(driver::IDriver *driver); void Start(); + void Cancel(); void Wait(); private: - void Cancel(); - Result ProcessReceive(); Result ProcessSend(); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp index 656ce104f..2419e54b9 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp @@ -182,6 +182,58 @@ namespace ams::htclow::mux { return ResultSuccess(); } + Result Mux::Close(impl::ChannelInternalType channel) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* If we have the channel, close it. */ + if (auto it = m_channel_impl_map.GetMap().find(channel); it != m_channel_impl_map.GetMap().end()) { + /* Shut down the channel. */ + m_channel_impl_map[it->second].ShutdownForce(); + + /* Remove the channel. */ + R_ABORT_UNLESS(m_channel_impl_map.RemoveChannel(channel)); + } + + return ResultSuccess(); + } + + Result Mux::ConnectBegin(u32 *out_task_id, impl::ChannelInternalType channel) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Find the channel. */ + auto it = m_channel_impl_map.GetMap().find(channel); + R_UNLESS(it != m_channel_impl_map.GetMap().end(), htclow::ResultChannelNotExist()); + + /* Perform the connection. */ + return m_channel_impl_map[it->second].DoConnectBegin(out_task_id); + } + + Result Mux::ConnectEnd(impl::ChannelInternalType channel, u32 task_id) { + /* Get the trigger for the task. */ + const auto trigger = m_task_manager.GetTrigger(task_id); + + /* Free the task. */ + m_task_manager.FreeTask(task_id); + + /* Check that we didn't hit a disconnect. */ + R_UNLESS(trigger != EventTrigger_Disconnect, htclow::ResultInvalidChannelStateDisconnected()); + + /* Find the channel. */ + auto it = m_channel_impl_map.GetMap().find(channel); + R_UNLESS(it != m_channel_impl_map.GetMap().end(), htclow::ResultChannelNotExist()); + + /* Perform the disconnection. */ + return m_channel_impl_map[it->second].DoConnectEnd(); + } + + ChannelState Mux::GetChannelState(impl::ChannelInternalType channel); + os::EventType *Mux::GetChannelStateEvent(impl::ChannelInternalType channel); + + Result Mux::FlushBegin(u32 *out_task_id, impl::ChannelInternalType channel); + Result Mux::FlushEnd(u32 task_id); + os::EventType *Mux::GetTaskEvent(u32 task_id) { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); @@ -189,6 +241,12 @@ namespace ams::htclow::mux { return m_task_manager.GetTaskEvent(task_id); } + Result Mux::ReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, bool blocking); + Result Mux::ReceiveEnd(size_t *out, void *dst, size_t dst_size, impl::ChannelInternalType channel, u32 task_id); + + Result Mux::SendBegin(u32 *out_task_id, size_t *out, const void *src, size_t src_size, impl::ChannelInternalType channel); + Result Mux::SendEnd(u32 task_id); + void Mux::SetSendBuffer(impl::ChannelInternalType channel, void *buf, size_t buf_size, size_t max_packet_size) { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); @@ -225,4 +283,6 @@ namespace ams::htclow::mux { m_channel_impl_map[it->second].SetReceiveBuffer(buf, buf_size); } + Result Mux::Shutdown(impl::ChannelInternalType channel); + } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp index 39d900aad..b8c005e10 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp @@ -55,12 +55,30 @@ namespace ams::htclow::mux { void UpdateMuxState(); public: Result Open(impl::ChannelInternalType channel); + Result Close(impl::ChannelInternalType channel); + + Result ConnectBegin(u32 *out_task_id, impl::ChannelInternalType channel); + Result ConnectEnd(impl::ChannelInternalType channel, u32 task_id); + + ChannelState GetChannelState(impl::ChannelInternalType channel); + os::EventType *GetChannelStateEvent(impl::ChannelInternalType channel); + + Result FlushBegin(u32 *out_task_id, impl::ChannelInternalType channel); + Result FlushEnd(u32 task_id); os::EventType *GetTaskEvent(u32 task_id); + Result ReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, bool blocking); + Result ReceiveEnd(size_t *out, void *dst, size_t dst_size, impl::ChannelInternalType channel, u32 task_id); + + Result SendBegin(u32 *out_task_id, size_t *out, const void *src, size_t src_size, impl::ChannelInternalType channel); + Result SendEnd(u32 task_id); + void SetSendBuffer(impl::ChannelInternalType channel, void *buf, size_t buf_size, size_t max_packet_size); void SetReceiveBuffer(impl::ChannelInternalType channel, void *buf, size_t buf_size); void SetSendBufferWithData(impl::ChannelInternalType channel, const void *buf, size_t buf_size, size_t max_packet_size); + + Result Shutdown(impl::ChannelInternalType channel); private: Result CheckChannelExist(impl::ChannelInternalType channel); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp index c683aaa76..7d173e1b1 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp @@ -17,6 +17,7 @@ #include "htclow_mux_channel_impl.hpp" #include "../ctrl/htclow_ctrl_state_machine.hpp" #include "../htclow_default_channel_config.hpp" +#include "../htclow_packet_factory.hpp" namespace ams::htclow::mux { @@ -228,6 +229,67 @@ namespace ams::htclow::mux { } } + Result ChannelImpl::DoConnectBegin(u32 *out_task_id) { + /* Check our state. */ + R_TRY(this->CheckState({ChannelState_Connectable})); + + /* Set ourselves as connecting. */ + m_state_machine->SetConnecting(m_channel); + + /* Allocate a task. */ + u32 task_id; + R_TRY(m_task_manager->AllocateTask(std::addressof(task_id), m_channel)); + + /* Configure the task. */ + m_task_manager->ConfigureConnectTask(task_id); + + /* If we're ready, complete the task immediately. */ + if (m_state_machine->IsReadied()) { + m_task_manager->CompleteTask(task_id, EventTrigger_ConnectReady); + } + + /* Set the output task id. */ + *out_task_id = task_id; + return ResultSuccess(); + } + + Result ChannelImpl::DoConnectEnd() { + /* Check our state. */ + R_TRY(this->CheckState({ChannelState_Connectable})); + + /* Perform handshake, if we should. */ + if (m_config.handshake_enabled) { + /* Set our next max data. */ + m_next_max_data = m_receive_buffer.GetBufferSize(); + + /* Make a max data packet. */ + auto packet = m_packet_factory->MakeMaxDataPacket(m_channel, m_version, m_next_max_data); + R_UNLESS(packet, htclow::ResultOutOfMemory()); + + /* Send the packet. */ + m_send_buffer.AddPacket(std::move(packet)); + + /* Signal that we have an packet to send. */ + this->SignalSendPacketEvent(); + + /* Set our current max data. */ + m_cur_max_data = m_next_max_data; + } else { + /* Set our share. */ + m_share = m_config.initial_counter_max_data; + + /* If we're not empty, signal. */ + if (!m_send_buffer.Empty()) { + this->SignalSendPacketEvent(); + } + } + + /* Set our state as connected. */ + this->SetState(ChannelState_Connected); + + return ResultSuccess(); + } + void ChannelImpl::SetSendBuffer(void *buf, size_t buf_size, size_t max_packet_size) { /* Set buffer. */ m_send_buffer.SetBuffer(buf, buf_size); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp index 27191d2e1..cba3e28b7 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp @@ -61,13 +61,17 @@ namespace ams::htclow::mux { void RemovePacket(const PacketHeader &header); + void ShutdownForce(); + void UpdateState(); public: + Result DoConnectBegin(u32 *out_task_id); + Result DoConnectEnd(); + void SetSendBuffer(void *buf, size_t buf_size, size_t max_packet_size); void SetReceiveBuffer(void *buf, size_t buf_size); void SetSendBufferWithData(const void *buf, size_t buf_size, size_t max_packet_size); private: - void ShutdownForce(); void SetState(ChannelState state); void SetStateWithoutCheck(ChannelState state); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.cpp index 9ed955bd6..8cc55f7c3 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.cpp @@ -53,7 +53,7 @@ namespace ams::htclow::mux { } /* Validate that the storage is free. */ - R_UNLESS(idx < MaxChannelCount, htclow::ResultOutOfResource()); + R_UNLESS(idx < MaxChannelCount, htclow::ResultOutOfChannel()); /* Create the channel impl. */ std::construct_at(GetPointer(m_channel_storage[idx]), channel, m_packet_factory, m_state_machine, m_task_manager, m_event); @@ -67,4 +67,28 @@ namespace ams::htclow::mux { return ResultSuccess(); } + Result ChannelImplMap::RemoveChannel(impl::ChannelInternalType channel) { + /* Find the storage. */ + auto it = m_map.find(channel); + AMS_ASSERT(it != m_map.end()); + + /* Get the channel index. */ + const auto index = it->second; + AMS_ASSERT(0 <= index && index < MaxChannelCount); + + /* Get the channel impl. */ + auto *channel_impl = GetPointer(m_channel_storage[index]); + + /* Mark the storage as invalid. */ + m_storage_valid[index] = false; + + /* Erase the channel from the map. */ + m_map.erase(channel); + + /* Destroy the channel. */ + std::destroy_at(channel_impl); + + return ResultSuccess(); + } + } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp index a949b8261..dee89de16 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp @@ -48,6 +48,7 @@ namespace ams::htclow::mux { } Result AddChannel(impl::ChannelInternalType channel); + Result RemoveChannel(impl::ChannelInternalType channel); private: public: MapType &GetMap() { diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.cpp index e27f36b60..0dfbef6c3 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.cpp @@ -41,6 +41,12 @@ namespace ams::htclow::mux { m_is_read_only = true; } + void RingBuffer::Clear() { + m_data_size = 0; + m_offset = 0; + m_can_discard = false; + } + Result RingBuffer::Write(const void *data, size_t size) { /* Validate pre-conditions. */ AMS_ASSERT(!m_is_read_only); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.hpp index 1ed9c8d38..7e04b89cb 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.hpp @@ -33,6 +33,9 @@ namespace ams::htclow::mux { void Initialize(void *buffer, size_t buffer_size); void InitializeForReadOnly(const void *buffer, size_t buffer_size); + void Clear(); + + size_t GetBufferSize() { return m_buffer_size; } size_t GetDataSize() { return m_data_size; } Result Write(const void *data, size_t size); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp index b7961a8ba..2d52214b2 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp @@ -19,6 +19,11 @@ namespace ams::htclow::mux { + SendBuffer::~SendBuffer() { + m_ring_buffer.Clear(); + this->Clear(); + } + bool SendBuffer::IsPriorPacket(PacketType packet_type) const { return packet_type == PacketType_MaxData; } @@ -94,6 +99,17 @@ namespace ams::htclow::mux { return true; } + void SendBuffer::AddPacket(std::unique_ptr ptr) { + /* Get the packet. */ + auto *packet = ptr.release(); + + /* Check the packet type. */ + AMS_ABORT_UNLESS(this->IsPriorPacket(packet->GetHeader()->packet_type)); + + /* Add the packet. */ + m_packet_list.push_back(*packet); + } + void SendBuffer::RemovePacket(const PacketHeader &header) { /* Get the packet type. */ const auto packet_type = header.packet_type; diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp index 65aa19fe5..f35dea9fd 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp @@ -45,11 +45,13 @@ namespace ams::htclow::mux { void CopyPacket(PacketHeader *header, PacketBody *body, int *out_body_size, const Packet &packet); public: SendBuffer(impl::ChannelInternalType channel, PacketFactory *pf); + ~SendBuffer(); void SetVersion(s16 version); bool QueryNextPacket(PacketHeader *header, PacketBody *body, int *out_body_size, u64 max_data, u64 total_send_size, bool has_share, u64 share); + void AddPacket(std::unique_ptr ptr); void RemovePacket(const PacketHeader &header); void SetBuffer(void *buffer, size_t buffer_size); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp index ce5f67576..2f5b67698 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp @@ -26,6 +26,89 @@ namespace ams::htclow::mux { return std::addressof(m_tasks[task_id].event); } + EventTrigger TaskManager::GetTrigger(u32 task_id) { + /* Check pre-conditions. */ + AMS_ASSERT(0 <= task_id && task_id < MaxTaskCount); + AMS_ASSERT(m_valid[task_id]); + + return m_tasks[task_id].event_trigger; + } + + Result TaskManager::AllocateTask(u32 *out_task_id, impl::ChannelInternalType channel) { + /* Find a free task. */ + u32 task_id = 0; + for (task_id = 0; task_id < util::size(m_tasks); ++task_id) { + if (!m_valid[task_id]) { + break; + } + } + + /* Verify the task is free. */ + R_UNLESS(!m_valid[task_id], htclow::ResultOutOfTask()); + + /* Mark the task as allocated. */ + m_valid[task_id] = true; + + /* Setup the task. */ + m_tasks[task_id].channel = channel; + m_tasks[task_id].has_event_trigger = false; + os::InitializeEvent(std::addressof(m_tasks[task_id].event), false, os::EventClearMode_ManualClear); + + /* Return the task id. */ + *out_task_id = task_id; + return ResultSuccess(); + } + + void TaskManager::FreeTask(u32 task_id) { + /* Check pre-conditions. */ + AMS_ASSERT(0 <= task_id && task_id < MaxTaskCount); + + /* Invalidate the task. */ + if (m_valid[task_id]) { + os::FinalizeEvent(std::addressof(m_tasks[task_id].event)); + m_valid[task_id] = false; + } + } + + void TaskManager::ConfigureConnectTask(u32 task_id) { + /* Check pre-conditions. */ + AMS_ASSERT(0 <= task_id && task_id < MaxTaskCount); + AMS_ASSERT(m_valid[task_id]); + + /* Set the task type. */ + m_tasks[task_id].type = TaskType_Connect; + } + + void TaskManager::ConfigureFlushTask(u32 task_id) { + /* Check pre-conditions. */ + AMS_ASSERT(0 <= task_id && task_id < MaxTaskCount); + AMS_ASSERT(m_valid[task_id]); + + /* Set the task type. */ + m_tasks[task_id].type = TaskType_Flush; + } + + void TaskManager::ConfigureReceiveTask(u32 task_id, size_t size) { + /* Check pre-conditions. */ + AMS_ASSERT(0 <= task_id && task_id < MaxTaskCount); + AMS_ASSERT(m_valid[task_id]); + + /* Set the task type. */ + m_tasks[task_id].type = TaskType_Receive; + + /* Set the task size. */ + m_tasks[task_id].size = size; + } + + void TaskManager::ConfigureSendTask(u32 task_id) { + /* Check pre-conditions. */ + AMS_ASSERT(0 <= task_id && task_id < MaxTaskCount); + AMS_ASSERT(m_valid[task_id]); + + /* Set the task type. */ + m_tasks[task_id].type = TaskType_Send; + } + void TaskManager::NotifyDisconnect(impl::ChannelInternalType channel) { for (auto i = 0; i < MaxTaskCount; ++i) { if (m_valid[i] && m_tasks[i].channel == channel) { diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp index bb27d3182..69f9ae779 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp @@ -51,14 +51,23 @@ namespace ams::htclow::mux { public: TaskManager() : m_valid() { /* ... */ } + Result AllocateTask(u32 *out_task_id, impl::ChannelInternalType channel); + void FreeTask(u32 task_id); + os::EventType *GetTaskEvent(u32 task_id); + EventTrigger GetTrigger(u32 task_id); + + void ConfigureConnectTask(u32 task_id); + void ConfigureFlushTask(u32 task_id); + void ConfigureReceiveTask(u32 task_id, size_t size); + void ConfigureSendTask(u32 task_id); void NotifyDisconnect(impl::ChannelInternalType channel); void NotifyReceiveData(impl::ChannelInternalType channel, size_t size); void NotifySendReady(); void NotifySendBufferEmpty(impl::ChannelInternalType channel); void NotifyConnectReady(); - private: + void CompleteTask(int index, EventTrigger trigger); }; diff --git a/libraries/libvapours/include/vapours/results/htclow_results.hpp b/libraries/libvapours/include/vapours/results/htclow_results.hpp index ef8976ff8..16e65a413 100644 --- a/libraries/libvapours/include/vapours/results/htclow_results.hpp +++ b/libraries/libvapours/include/vapours/results/htclow_results.hpp @@ -26,7 +26,8 @@ namespace ams::htclow { R_DEFINE_ERROR_RESULT(ChannelAlreadyExist, 9); R_DEFINE_ERROR_RESULT(ChannelNotExist, 10); - R_DEFINE_ERROR_RESULT(OutOfResource, 151); + R_DEFINE_ERROR_RESULT(OutOfChannel, 151); + R_DEFINE_ERROR_RESULT(OutOfTask, 151); R_DEFINE_ERROR_RESULT(InvalidChannelState, 200); R_DEFINE_ERROR_RESULT(InvalidChannelStateDisconnected, 201); From b925344c3b78d4efbab4b4c1f20cceb9073e5d63 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 9 Feb 2021 19:51:52 -0800 Subject: [PATCH 040/280] htc: implement remainder of Mux/Tasks --- .../htc/server/driver/htc_htclow_driver.cpp | 5 +- .../source/htclow/htclow_manager_impl.cpp | 4 +- .../source/htclow/htclow_manager_impl.hpp | 2 +- .../source/htclow/mux/htclow_mux.cpp | 110 +++++++++++-- .../source/htclow/mux/htclow_mux.hpp | 2 +- .../htclow/mux/htclow_mux_channel_impl.cpp | 151 +++++++++++++++++- .../htclow/mux/htclow_mux_channel_impl.hpp | 14 +- .../htclow/mux/htclow_mux_ring_buffer.cpp | 10 ++ .../htclow/mux/htclow_mux_ring_buffer.hpp | 1 + .../htclow/mux/htclow_mux_send_buffer.cpp | 11 ++ .../htclow/mux/htclow_mux_send_buffer.hpp | 2 + .../htclow/mux/htclow_mux_task_manager.cpp | 2 +- .../htclow/mux/htclow_mux_task_manager.hpp | 1 + 13 files changed, 291 insertions(+), 24 deletions(-) diff --git a/libraries/libstratosphere/source/htc/server/driver/htc_htclow_driver.cpp b/libraries/libstratosphere/source/htc/server/driver/htc_htclow_driver.cpp index ecc87a6c9..051165840 100644 --- a/libraries/libstratosphere/source/htc/server/driver/htc_htclow_driver.cpp +++ b/libraries/libstratosphere/source/htc/server/driver/htc_htclow_driver.cpp @@ -120,9 +120,12 @@ namespace ams::htc::server::driver { } Result HtclowDriver::ReceiveInternal(size_t *out, void *dst, size_t dst_size, htclow::ChannelId channel, htclow::ReceiveOption option) { + /* Determine whether we're blocking. */ + const bool blocking = option != htclow::ReceiveOption_NonBlocking; + /* Begin receiving. */ u32 task_id; - R_TRY(m_manager->ReceiveBegin(std::addressof(task_id), GetHtclowChannel(channel, m_module_id), option != htclow::ReceiveOption_NonBlocking)); + R_TRY(m_manager->ReceiveBegin(std::addressof(task_id), GetHtclowChannel(channel, m_module_id), blocking ? 1 : 0)); /* Wait for the task to complete. */ this->WaitTask(task_id); diff --git a/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp b/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp index 7ff313ac5..7e9c61b7e 100644 --- a/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp +++ b/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp @@ -157,8 +157,8 @@ namespace ams::htclow { return m_ctrl_service.NotifyAwake(); } - Result HtclowManagerImpl::ReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, bool blocking) { - return m_mux.ReceiveBegin(out_task_id, channel, blocking); + Result HtclowManagerImpl::ReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, size_t size) { + return m_mux.ReceiveBegin(out_task_id, channel, size); } Result HtclowManagerImpl::ReceiveEnd(size_t *out, void *dst, size_t dst_size, impl::ChannelInternalType channel, u32 task_id) { diff --git a/libraries/libstratosphere/source/htclow/htclow_manager_impl.hpp b/libraries/libstratosphere/source/htclow/htclow_manager_impl.hpp index 59c63aa02..6839eab98 100644 --- a/libraries/libstratosphere/source/htclow/htclow_manager_impl.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_manager_impl.hpp @@ -68,7 +68,7 @@ namespace ams::htclow { void NotifyAsleep(); void NotifyAwake(); - Result ReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, bool blocking); + Result ReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, size_t size); Result ReceiveEnd(size_t *out, void *dst, size_t dst_size, impl::ChannelInternalType channel, u32 task_id); Result SendBegin(u32 *out_task_id, size_t *out, const void *src, size_t src_size, impl::ChannelInternalType channel); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp index 2419e54b9..3bb6b9d19 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp @@ -228,11 +228,44 @@ namespace ams::htclow::mux { return m_channel_impl_map[it->second].DoConnectEnd(); } - ChannelState Mux::GetChannelState(impl::ChannelInternalType channel); - os::EventType *Mux::GetChannelStateEvent(impl::ChannelInternalType channel); + ChannelState Mux::GetChannelState(impl::ChannelInternalType channel) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); - Result Mux::FlushBegin(u32 *out_task_id, impl::ChannelInternalType channel); - Result Mux::FlushEnd(u32 task_id); + return m_channel_impl_map.GetChannelImpl(channel).GetChannelState(); + } + + os::EventType *Mux::GetChannelStateEvent(impl::ChannelInternalType channel) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + return m_channel_impl_map.GetChannelImpl(channel).GetChannelStateEvent(); + } + + Result Mux::FlushBegin(u32 *out_task_id, impl::ChannelInternalType channel) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Find the channel. */ + auto it = m_channel_impl_map.GetMap().find(channel); + R_UNLESS(it != m_channel_impl_map.GetMap().end(), htclow::ResultChannelNotExist()); + + /* Perform the connection. */ + return m_channel_impl_map[it->second].DoFlush(out_task_id); + } + + Result Mux::FlushEnd(u32 task_id) { + /* Get the trigger for the task. */ + const auto trigger = m_task_manager.GetTrigger(task_id); + + /* Free the task. */ + m_task_manager.FreeTask(task_id); + + /* Check that we didn't hit a disconnect. */ + R_UNLESS(trigger != EventTrigger_Disconnect, htclow::ResultInvalidChannelStateDisconnected()); + + return ResultSuccess(); + } os::EventType *Mux::GetTaskEvent(u32 task_id) { /* Lock ourselves. */ @@ -241,11 +274,60 @@ namespace ams::htclow::mux { return m_task_manager.GetTaskEvent(task_id); } - Result Mux::ReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, bool blocking); - Result Mux::ReceiveEnd(size_t *out, void *dst, size_t dst_size, impl::ChannelInternalType channel, u32 task_id); + Result Mux::ReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, size_t size) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); - Result Mux::SendBegin(u32 *out_task_id, size_t *out, const void *src, size_t src_size, impl::ChannelInternalType channel); - Result Mux::SendEnd(u32 task_id); + /* Find the channel. */ + auto it = m_channel_impl_map.GetMap().find(channel); + R_UNLESS(it != m_channel_impl_map.GetMap().end(), htclow::ResultChannelNotExist()); + + /* Perform the connection. */ + return m_channel_impl_map[it->second].DoReceiveBegin(out_task_id, size); + } + + Result Mux::ReceiveEnd(size_t *out, void *dst, size_t dst_size, impl::ChannelInternalType channel, u32 task_id) { + /* Free the task. */ + m_task_manager.FreeTask(task_id); + + /* If we have data, perform the receive. */ + if (dst_size > 0) { + /* Find the channel. */ + auto it = m_channel_impl_map.GetMap().find(channel); + R_UNLESS(it != m_channel_impl_map.GetMap().end(), htclow::ResultChannelNotExist()); + + /* Perform the RECEIVE. */ + return m_channel_impl_map[it->second].DoReceiveEnd(out, dst, dst_size); + } else { + *out = 0; + return ResultSuccess(); + } + } + + Result Mux::SendBegin(u32 *out_task_id, size_t *out, const void *src, size_t src_size, impl::ChannelInternalType channel) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Find the channel. */ + auto it = m_channel_impl_map.GetMap().find(channel); + R_UNLESS(it != m_channel_impl_map.GetMap().end(), htclow::ResultChannelNotExist()); + + /* Perform the connection. */ + return m_channel_impl_map[it->second].DoSend(out_task_id, out, src, src_size); + } + + Result Mux::SendEnd(u32 task_id) { + /* Get the trigger for the task. */ + const auto trigger = m_task_manager.GetTrigger(task_id); + + /* Free the task. */ + m_task_manager.FreeTask(task_id); + + /* Check that we didn't hit a disconnect. */ + R_UNLESS(trigger != EventTrigger_Disconnect, htclow::ResultInvalidChannelStateDisconnected()); + + return ResultSuccess(); + } void Mux::SetSendBuffer(impl::ChannelInternalType channel, void *buf, size_t buf_size, size_t max_packet_size) { /* Lock ourselves. */ @@ -283,6 +365,16 @@ namespace ams::htclow::mux { m_channel_impl_map[it->second].SetReceiveBuffer(buf, buf_size); } - Result Mux::Shutdown(impl::ChannelInternalType channel); + Result Mux::Shutdown(impl::ChannelInternalType channel) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Find the channel. */ + auto it = m_channel_impl_map.GetMap().find(channel); + R_UNLESS(it != m_channel_impl_map.GetMap().end(), htclow::ResultChannelNotExist()); + + /* Perform the shutdown. */ + return m_channel_impl_map[it->second].DoShutdown(); + } } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp index b8c005e10..071efbe17 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp @@ -68,7 +68,7 @@ namespace ams::htclow::mux { os::EventType *GetTaskEvent(u32 task_id); - Result ReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, bool blocking); + Result ReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, size_t size); Result ReceiveEnd(size_t *out, void *dst, size_t dst_size, impl::ChannelInternalType channel, u32 task_id); Result SendBegin(u32 *out_task_id, size_t *out, const void *src, size_t src_size, impl::ChannelInternalType channel); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp index 7d173e1b1..4b8aa3a9a 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp @@ -24,7 +24,7 @@ namespace ams::htclow::mux { ChannelImpl::ChannelImpl(impl::ChannelInternalType channel, PacketFactory *pf, ctrl::HtcctrlStateMachine *sm, TaskManager *tm, os::Event *ev) : m_channel(channel), m_packet_factory(pf), m_state_machine(sm), m_task_manager(tm), m_event(ev), m_send_buffer(m_channel, pf), m_receive_buffer(), m_version(ProtocolVersion), m_config(DefaultChannelConfig), - m_offset(0), m_total_send_size(0), m_next_max_data(0), m_cur_max_data(0), m_share(), + m_offset(0), m_total_send_size(0), m_cur_max_data(0), m_prev_max_data(0), m_share(), m_state_change_event(os::EventClearMode_ManualClear), m_state(ChannelState_Unconnectable) { @@ -146,10 +146,10 @@ namespace ams::htclow::mux { bool ChannelImpl::QuerySendPacket(PacketHeader *header, PacketBody *body, int *out_body_size) { /* Check our send buffer. */ - if (m_send_buffer.QueryNextPacket(header, body, out_body_size, m_next_max_data, m_total_send_size, m_share.has_value(), m_share.value_or(0))) { + if (m_send_buffer.QueryNextPacket(header, body, out_body_size, m_cur_max_data, m_total_send_size, m_share.has_value(), m_share.value_or(0))) { /* Update tracking variables. */ if (header->packet_type == PacketType_Data) { - m_cur_max_data = m_next_max_data; + m_prev_max_data = m_cur_max_data; } return true; @@ -259,11 +259,11 @@ namespace ams::htclow::mux { /* Perform handshake, if we should. */ if (m_config.handshake_enabled) { - /* Set our next max data. */ - m_next_max_data = m_receive_buffer.GetBufferSize(); + /* Set our current max data. */ + m_cur_max_data = m_receive_buffer.GetBufferSize(); /* Make a max data packet. */ - auto packet = m_packet_factory->MakeMaxDataPacket(m_channel, m_version, m_next_max_data); + auto packet = m_packet_factory->MakeMaxDataPacket(m_channel, m_version, m_cur_max_data); R_UNLESS(packet, htclow::ResultOutOfMemory()); /* Send the packet. */ @@ -272,8 +272,8 @@ namespace ams::htclow::mux { /* Signal that we have an packet to send. */ this->SignalSendPacketEvent(); - /* Set our current max data. */ - m_cur_max_data = m_next_max_data; + /* Set our prev max data. */ + m_prev_max_data = m_cur_max_data; } else { /* Set our share. */ m_share = m_config.initial_counter_max_data; @@ -290,6 +290,141 @@ namespace ams::htclow::mux { return ResultSuccess(); } + Result ChannelImpl::DoFlush(u32 *out_task_id) { + /* Check our state. */ + R_TRY(this->CheckState({ChannelState_Connected})); + + /* Allocate a task. */ + u32 task_id; + R_TRY(m_task_manager->AllocateTask(std::addressof(task_id), m_channel)); + + /* Configure the task. */ + m_task_manager->ConfigureFlushTask(task_id); + + /* If we're already flushed, complete the task immediately. */ + if (m_send_buffer.Empty()) { + m_task_manager->CompleteTask(task_id, EventTrigger_SendBufferEmpty); + } + + /* Set the output task id. */ + *out_task_id = task_id; + return ResultSuccess(); + } + + Result ChannelImpl::DoReceiveBegin(u32 *out_task_id, size_t size) { + /* Check our state. */ + R_TRY(this->CheckState({ChannelState_Connected, ChannelState_Disconnected})); + + /* Allocate a task. */ + u32 task_id; + R_TRY(m_task_manager->AllocateTask(std::addressof(task_id), m_channel)); + + /* Configure the task. */ + m_task_manager->ConfigureReceiveTask(task_id, size); + + /* Check if the task is already complete. */ + if (m_receive_buffer.GetDataSize() >= size) { + m_task_manager->CompleteTask(task_id, EventTrigger_ReceiveData); + } else if (m_state == ChannelState_Disconnected) { + m_task_manager->CompleteTask(task_id, EventTrigger_Disconnect); + } + + /* Set the output task id. */ + *out_task_id = task_id; + return ResultSuccess(); + + } + Result ChannelImpl::DoReceiveEnd(size_t *out, void *dst, size_t dst_size) { + /* Check our state. */ + R_TRY(this->CheckState({ChannelState_Connected, ChannelState_Disconnected})); + + /* If we have nowhere to receive, we're done. */ + if (dst_size == 0) { + *out = 0; + return ResultSuccess(); + } + + /* Get the amount of receivable data. */ + const size_t receivable = m_receive_buffer.GetDataSize(); + const size_t received = std::min(dst_size, receivable); + + /* Read the data. */ + R_ABORT_UNLESS(m_receive_buffer.Read(dst, received)); + + /* Handle flow control, if we should. */ + if (m_config.flow_control_enabled) { + /* Read our fields. */ + const auto prev_max_data = m_prev_max_data; + const auto next_max_data = m_cur_max_data + received; + const auto max_packet_size = m_config.max_packet_size; + const auto offset = m_offset; + + /* Update our current max data. */ + m_cur_max_data = next_max_data; + + /* If we can, send a max data packet. */ + if (prev_max_data - offset < max_packet_size + sizeof(PacketHeader)) { + /* Make a max data packet. */ + auto packet = m_packet_factory->MakeMaxDataPacket(m_channel, m_version, next_max_data); + R_UNLESS(packet, htclow::ResultOutOfMemory()); + + /* Send the packet. */ + m_send_buffer.AddPacket(std::move(packet)); + + /* Signal that we have an packet to send. */ + this->SignalSendPacketEvent(); + + /* Set our prev max data. */ + m_prev_max_data = m_cur_max_data; + } + } + + /* Set the output size. */ + *out = received; + return ResultSuccess(); + } + + Result ChannelImpl::DoSend(u32 *out_task_id, size_t *out, const void *src, size_t src_size) { + /* Check our state. */ + R_TRY(this->CheckState({ChannelState_Connected})); + + /* Allocate a task. */ + u32 task_id; + R_TRY(m_task_manager->AllocateTask(std::addressof(task_id), m_channel)); + + /* Send the data. */ + const size_t sent = m_send_buffer.AddData(src, src_size); + + /* Add the size to our total. */ + m_total_send_size += sent; + + /* Signal our event. */ + this->SignalSendPacketEvent(); + + /* Configure the task. */ + m_task_manager->ConfigureSendTask(task_id); + + /* If we sent all the data, we're done. */ + if (sent == src_size) { + m_task_manager->CompleteTask(task_id, EventTrigger_SendComplete); + } + + /* Set the output. */ + *out_task_id = task_id; + *out = sent; + + return ResultSuccess(); + } + + Result ChannelImpl::DoShutdown() { + /* Check our state. */ + R_TRY(this->CheckState({ChannelState_Connected})); + + /* Set our state. */ + this->SetState(ChannelState_Disconnected); + return ResultSuccess(); + } + void ChannelImpl::SetSendBuffer(void *buf, size_t buf_size, size_t max_packet_size) { /* Set buffer. */ m_send_buffer.SetBuffer(buf, buf_size); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp index cba3e28b7..c476aab77 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp @@ -45,8 +45,8 @@ namespace ams::htclow::mux { ChannelConfig m_config; u64 m_offset; u64 m_total_send_size; - u64 m_next_max_data; u64 m_cur_max_data; + u64 m_prev_max_data; std::optional m_share; os::Event m_state_change_event; ChannelState m_state; @@ -55,6 +55,9 @@ namespace ams::htclow::mux { void SetVersion(s16 version); + ChannelState GetChannelState() { return m_state; } + os::EventType *GetChannelStateEvent() { return m_state_change_event.GetBase(); } + Result ProcessReceivePacket(const PacketHeader &header, const void *body, size_t body_size); bool QuerySendPacket(PacketHeader *header, PacketBody *body, int *out_body_size); @@ -68,6 +71,15 @@ namespace ams::htclow::mux { Result DoConnectBegin(u32 *out_task_id); Result DoConnectEnd(); + Result DoFlush(u32 *out_task_id); + + Result DoReceiveBegin(u32 *out_task_id, size_t size); + Result DoReceiveEnd(size_t *out, void *dst, size_t dst_size); + + Result DoSend(u32 *out_task_id, size_t *out, const void *src, size_t src_size); + + Result DoShutdown(); + void SetSendBuffer(void *buf, size_t buf_size, size_t max_packet_size); void SetReceiveBuffer(void *buf, size_t buf_size); void SetSendBufferWithData(const void *buf, size_t buf_size, size_t max_packet_size); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.cpp index 0dfbef6c3..a772ce93c 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.cpp @@ -47,6 +47,16 @@ namespace ams::htclow::mux { m_can_discard = false; } + Result RingBuffer::Read(void *dst, size_t size) { + /* Copy the data. */ + R_TRY(this->Copy(dst, size)); + + /* Discard. */ + R_TRY(this->Discard(size)); + + return ResultSuccess(); + } + Result RingBuffer::Write(const void *data, size_t size) { /* Validate pre-conditions. */ AMS_ASSERT(!m_is_read_only); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.hpp index 7e04b89cb..2d8ff4048 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.hpp @@ -38,6 +38,7 @@ namespace ams::htclow::mux { size_t GetBufferSize() { return m_buffer_size; } size_t GetDataSize() { return m_data_size; } + Result Read(void *dst, size_t size); Result Write(const void *data, size_t size); Result Copy(void *dst, size_t size); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp index 2d52214b2..c41a900e9 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp @@ -131,6 +131,17 @@ namespace ams::htclow::mux { } } + size_t SendBuffer::AddData(const void *data, size_t size) { + /* Determine how much to actually add. */ + size = std::min(size, m_ring_buffer.GetBufferSize() - m_ring_buffer.GetDataSize()); + + /* Write the data. */ + R_ABORT_UNLESS(m_ring_buffer.Write(data, size)); + + /* Return the size we wrote. */ + return size; + } + void SendBuffer::SetBuffer(void *buffer, size_t buffer_size) { m_ring_buffer.Initialize(buffer, buffer_size); } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp index f35dea9fd..665443459 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp @@ -54,6 +54,8 @@ namespace ams::htclow::mux { void AddPacket(std::unique_ptr ptr); void RemovePacket(const PacketHeader &header); + size_t AddData(const void *data, size_t size); + void SetBuffer(void *buffer, size_t buffer_size); void SetReadOnlyBuffer(const void *buffer, size_t buffer_size); void SetMaxPacketSize(size_t max_packet_size); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp index 2f5b67698..564271242 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp @@ -44,7 +44,7 @@ namespace ams::htclow::mux { } /* Verify the task is free. */ - R_UNLESS(!m_valid[task_id], htclow::ResultOutOfTask()); + R_UNLESS(task_id < util::size(m_tasks), htclow::ResultOutOfTask()); /* Mark the task as allocated. */ m_valid[task_id] = true; diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp index 69f9ae779..1444602d5 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp @@ -23,6 +23,7 @@ namespace ams::htclow::mux { enum EventTrigger : u8 { EventTrigger_Disconnect = 1, EventTrigger_ReceiveData = 2, + EventTrigger_SendComplete = 4, EventTrigger_SendReady = 5, EventTrigger_SendBufferEmpty = 10, EventTrigger_ConnectReady = 11, From 1f03b11dbcc5f3b549d7d99d35e40565186d77b6 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 9 Feb 2021 20:43:40 -0800 Subject: [PATCH 041/280] htc: skeleton constructors for htcmisc --- .../impl/ams_system_thread_definitions.hpp | 1 + .../source/htc/server/htc_htcmisc_impl.cpp | 87 +++++++++++++++++++ .../source/htc/server/htc_htcmisc_impl.hpp | 16 +++- .../source/htc/server/htc_observer.cpp | 75 ++++++++++++++++ .../source/htc/server/htc_observer.hpp | 11 ++- .../htc/server/rpc/htc_htcmisc_rpc_server.cpp | 40 +++++++++ .../source/htc/server/rpc/htc_rpc_client.cpp | 52 +++++++++++ .../source/htc/server/rpc/htc_rpc_client.hpp | 2 +- .../htclow/ctrl/htclow_ctrl_service.cpp | 16 ++++ .../htclow/ctrl/htclow_ctrl_state_machine.cpp | 7 ++ .../htclow/ctrl/htclow_ctrl_state_machine.hpp | 1 + .../htclow/mux/htclow_mux_send_buffer.cpp | 8 ++ 12 files changed, 310 insertions(+), 6 deletions(-) create mode 100644 libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp create mode 100644 libraries/libstratosphere/source/htc/server/htc_observer.cpp create mode 100644 libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.cpp create mode 100644 libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp diff --git a/libraries/libstratosphere/include/stratosphere/ams/impl/ams_system_thread_definitions.hpp b/libraries/libstratosphere/include/stratosphere/ams/impl/ams_system_thread_definitions.hpp index f08fa246f..1c3bca1a3 100644 --- a/libraries/libstratosphere/include/stratosphere/ams/impl/ams_system_thread_definitions.hpp +++ b/libraries/libstratosphere/include/stratosphere/ams/impl/ams_system_thread_definitions.hpp @@ -140,6 +140,7 @@ namespace ams::impl { AMS_DEFINE_SYSTEM_THREAD(10, htc, Htcmisc); AMS_DEFINE_SYSTEM_THREAD(10, htc, HtcmiscReceive); AMS_DEFINE_SYSTEM_THREAD(10, htc, HtcmiscSend); + AMS_DEFINE_SYSTEM_THREAD(10, htc, HtcObserver); AMS_DEFINE_SYSTEM_THREAD(10, tma, BridgePcieDriver); diff --git a/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp b/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp new file mode 100644 index 000000000..4fc86daf9 --- /dev/null +++ b/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htc_htcmisc_impl.hpp" + +namespace ams::htc::server { + + namespace { + + alignas(os::ThreadStackAlignment) u8 g_client_thread_stack[os::MemoryPageSize]; + alignas(os::ThreadStackAlignment) u8 g_server_thread_stack[os::MemoryPageSize]; + + } + + HtcmiscImpl::HtcmiscImpl(htclow::HtclowManager *htclow_manager) + : m_htclow_driver(htclow_manager, htclow::ModuleId::Htcmisc), + m_driver_manager(std::addressof(m_htclow_driver)), + m_rpc_client(std::addressof(m_htclow_driver), HtcmiscClientChannelId), + m_rpc_server(std::addressof(m_htclow_driver), HtcmiscServerChannelId), + m_cancel_event(os::EventClearMode_ManualClear), + m_cancelled(false), + m_connection_event(os::EventClearMode_ManualClear), + m_client_connected(false), + m_server_connected(false), + m_connected(false), + m_connection_mutex() + { + /* Create the client thread. */ + R_ABORT_UNLESS(os::CreateThread(std::addressof(m_client_thread), ClientThreadEntry, this, g_client_thread_stack, sizeof(g_client_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(htc, Htcmisc))); + + /* Set the client thread name. */ + os::SetThreadNamePointer(std::addressof(m_client_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, Htcmisc)); + + /* Start the client thread. */ + os::StartThread(std::addressof(m_client_thread)); + + /* Create the server thread. */ + R_ABORT_UNLESS(os::CreateThread(std::addressof(m_server_thread), ServerThreadEntry, this, g_server_thread_stack, sizeof(g_server_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(htc, Htcmisc))); + + /* Set the server thread name. */ + os::SetThreadNamePointer(std::addressof(m_server_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, Htcmisc)); + + /* Start the server thread. */ + os::StartThread(std::addressof(m_server_thread)); + } + + HtcmiscImpl::~HtcmiscImpl() { + /* Cancel ourselves. */ + this->Cancel(); + + /* Wait for our threads to be done, and destroy them. */ + os::WaitThread(std::addressof(m_client_thread)); + os::DestroyThread(std::addressof(m_client_thread)); + os::WaitThread(std::addressof(m_server_thread)); + os::DestroyThread(std::addressof(m_server_thread)); + } + + void HtcmiscImpl::Cancel() { + /* Set ourselves as cancelled. */ + m_cancelled = true; + + /* Signal our cancel event. */ + m_cancel_event.Signal(); + } + + void HtcmiscImpl::ClientThread() { + AMS_ABORT("HtcmiscImpl::ClientThread"); + } + + void HtcmiscImpl::ServerThread() { + AMS_ABORT("HtcmiscImpl::ServerThread"); + } + +} diff --git a/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.hpp b/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.hpp index d2cd85c1e..7cd407f1f 100644 --- a/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.hpp +++ b/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.hpp @@ -31,16 +31,24 @@ namespace ams::htc::server { rpc::HtcmiscRpcServer m_rpc_server; os::ThreadType m_client_thread; os::ThreadType m_server_thread; - os::Event m_event_61200; - u8 m_61228; - os::Event m_event_61230; + os::Event m_cancel_event; + bool m_cancelled; + os::Event m_connection_event; bool m_client_connected; bool m_server_connected; - u8 m_6125A; + bool m_connected; os::SdkMutex m_connection_mutex; + private: + static void ClientThreadEntry(void *arg) { static_cast(arg)->ClientThread(); } + static void ServerThreadEntry(void *arg) { static_cast(arg)->ServerThread(); } + + void ClientThread(); + void ServerThread(); public: HtcmiscImpl(htclow::HtclowManager *htclow_manager); + ~HtcmiscImpl(); public: + void Cancel(); /* TODO */ }; diff --git a/libraries/libstratosphere/source/htc/server/htc_observer.cpp b/libraries/libstratosphere/source/htc/server/htc_observer.cpp new file mode 100644 index 000000000..4e1c6622b --- /dev/null +++ b/libraries/libstratosphere/source/htc/server/htc_observer.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htc_observer.hpp" + +namespace ams::htc::server { + + Observer::Observer(const HtcmiscImpl &misc_impl) + : m_connect_event(os::EventClearMode_ManualClear, true), + m_disconnect_event(os::EventClearMode_ManualClear, true), + m_stop_event(os::EventClearMode_ManualClear), + m_misc_impl(misc_impl), + m_thread_running(false), + m_stopped(false), + m_connected(false), + m_is_service_available(false) + { + /* Initialize htcs library. */ + AMS_ABORT("htcs::impl::HtcsManagerHolder::AddReference();"); + + /* Update our event state. */ + this->UpdateEvent(); + + /* Start. */ + R_ABORT_UNLESS(this->Start()); + } + + Result Observer::Start() { + /* Check that we're not already running. */ + AMS_ASSERT(!m_thread_running); + + /* Create the thread. */ + R_TRY(os::CreateThread(std::addressof(m_observer_thread), ObserverThreadEntry, this, m_observer_thread_stack, sizeof(m_observer_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(htc, HtcObserver))); + + /* Set the thread name pointer. */ + os::SetThreadNamePointer(std::addressof(m_observer_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, HtcObserver)); + + /* Mark our thread as running. */ + m_thread_running = true; + m_stopped = false; + + /* Start our thread. */ + os::StartThread(std::addressof(m_observer_thread)); + + return ResultSuccess(); + } + + void Observer::UpdateEvent() { + if (m_connected && m_is_service_available) { + m_disconnect_event.Clear(); + m_connect_event.Signal(); + } else { + m_connect_event.Clear(); + m_disconnect_event.Signal(); + } + } + + void Observer::ObserverThreadBody() { + AMS_ABORT("Observer::ObserverThreadBody"); + } + +} diff --git a/libraries/libstratosphere/source/htc/server/htc_observer.hpp b/libraries/libstratosphere/source/htc/server/htc_observer.hpp index bde6d56cf..ea9bf2d2d 100644 --- a/libraries/libstratosphere/source/htc/server/htc_observer.hpp +++ b/libraries/libstratosphere/source/htc/server/htc_observer.hpp @@ -23,15 +23,24 @@ namespace ams::htc::server { private: os::SystemEvent m_connect_event; os::SystemEvent m_disconnect_event; - os::Event m_event_60; + os::Event m_stop_event; os::ThreadType m_observer_thread; const HtcmiscImpl &m_misc_impl; bool m_thread_running; bool m_stopped; bool m_connected; bool m_is_service_available; + alignas(os::ThreadStackAlignment) u8 m_observer_thread_stack[os::MemoryPageSize]; public: Observer(const HtcmiscImpl &misc_impl); + private: + static void ObserverThreadEntry(void *arg) { static_cast(arg)->ObserverThreadBody(); } + + void ObserverThreadBody(); + private: + Result Start(); + + void UpdateEvent(); }; } diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.cpp b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.cpp new file mode 100644 index 000000000..438df31e2 --- /dev/null +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htc_htcmisc_rpc_server.hpp" + +namespace ams::htc::server::rpc { + + namespace { + + constexpr inline size_t ReceiveThreadStackSize = os::MemoryPageSize; + + alignas(os::ThreadStackAlignment) constinit u8 g_receive_thread_stack[ReceiveThreadStackSize]; + + } + + HtcmiscRpcServer::HtcmiscRpcServer(driver::IDriver *driver, htclow::ChannelId channel) + : m_00(0), + m_driver(driver), + m_channel_id(channel), + m_receive_thread_stack(g_receive_thread_stack), + m_cancelled(false), + m_thread_running(false) + { + /* ... */ + } + +} diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp new file mode 100644 index 000000000..394252c8e --- /dev/null +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htc_rpc_client.hpp" + +namespace ams::htc::server::rpc { + + namespace { + + constexpr inline size_t ThreadStackSize = os::MemoryPageSize; + + alignas(os::ThreadStackAlignment) constinit u8 g_receive_thread_stack[ThreadStackSize]; + alignas(os::ThreadStackAlignment) constinit u8 g_send_thread_stack[ThreadStackSize]; + + constinit os::SdkMutex g_rpc_mutex; + + } + + RpcClient::RpcClient(driver::IDriver *driver, htclow::ChannelId channel) + : m_00(0), + m_driver(driver), + m_channel_id(channel), + m_receive_thread_stack(g_receive_thread_stack), + m_send_thread_stack(g_send_thread_stack), + m_mutex(g_rpc_mutex), + m_cancelled(false), + m_thread_running(false) + { + /* Initialize all events. */ + /* TODO: MaxTaskCount? */ + for (size_t i = 0; i < util::size(m_5F8_events); ++i) { + os::InitializeEvent(std::addressof(m_5F8_events[i]), false, os::EventClearMode_AutoClear); + os::InitializeEvent(std::addressof(m_1138_events[i]), false, os::EventClearMode_AutoClear); + } + + /* TODO: Clear all of m_3C0 array to zero. */ + } + +} diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp index 4ca916797..52811489f 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp @@ -28,7 +28,7 @@ namespace ams::htc::server::rpc { void *m_send_thread_stack; os::ThreadType m_receive_thread; os::ThreadType m_send_thread; - os::SdkMutex *m_p_mutex; + os::SdkMutex &m_mutex; /* TODO: m_task_id_free_list */ /* TODO: m_task_table */ /* TODO: m_3C0[0x48] */ diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp index c8fc4722c..ca1615a5f 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp @@ -351,6 +351,22 @@ namespace ams::htclow::ctrl { this->DisconnectInternal(); } + void HtcctrlService::DisconnectInternal() { + /* Disconnect, if we need to. */ + if (m_state_machine->IsDisconnectionNeeded()) { + /* Send a disconnect packet. */ + m_send_buffer.AddPacket(m_packet_factory->MakeDisconnectPacket()); + + /* Signal our event. */ + m_event.Signal(); + + /* Wait for us to be disconnected. */ + while (!m_state_machine->IsDisconnected()) { + m_condvar.Wait(m_mutex); + } + } + } + void HtcctrlService::Resume() { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp index e9acb3fd2..c1271c769 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp @@ -64,6 +64,13 @@ namespace ams::htclow::ctrl { return !ctrl::IsDisconnected(m_state) && m_state != HtcctrlState_DriverConnected; } + bool HtcctrlStateMachine::IsDisconnectionNeeded() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + return !ctrl::IsDisconnected(m_state) && m_state != HtcctrlState_Sleep && m_state != HtcctrlState_DriverConnected; + } + bool HtcctrlStateMachine::IsConnected() { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp index 1e9fd254f..4881d6f46 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp @@ -59,6 +59,7 @@ namespace ams::htclow::ctrl { bool IsSleepingStatusChanged(); bool IsInformationNeeded(); + bool IsDisconnectionNeeded(); bool IsConnected(); bool IsReadied(); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp index c41a900e9..3250bbbbb 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp @@ -16,9 +16,17 @@ #include #include "htclow_mux_channel_impl.hpp" #include "../htclow_packet_factory.hpp" +#include "../htclow_default_channel_config.hpp" namespace ams::htclow::mux { + SendBuffer::SendBuffer(impl::ChannelInternalType channel, PacketFactory *pf) + : m_channel(channel), m_packet_factory(pf), m_ring_buffer(), m_packet_list(), + m_version(ProtocolVersion), m_flow_control_enabled(true), m_max_packet_size(DefaultChannelConfig.max_packet_size) + { + /* ... */ + } + SendBuffer::~SendBuffer() { m_ring_buffer.Clear(); this->Clear(); From 0880cebc4d4a7b54deaba59a47e2cbbb4fa64e77 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 9 Feb 2021 22:45:49 -0800 Subject: [PATCH 042/280] htc: implement htcmisc rpc tasks --- .../htc/server/rpc/htc_htcmisc_rpc_tasks.cpp | 294 ++++++++++++++++++ .../htc/server/rpc/htc_htcmisc_rpc_tasks.hpp | 141 +++++++++ .../source/htc/server/rpc/htc_rpc_tasks.hpp | 97 ++++++ .../libvapours/include/vapours/results.hpp | 1 + .../include/vapours/results/htc_results.hpp | 28 ++ 5 files changed, 561 insertions(+) create mode 100644 libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_tasks.cpp create mode 100644 libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_tasks.hpp create mode 100644 libraries/libstratosphere/source/htc/server/rpc/htc_rpc_tasks.hpp create mode 100644 libraries/libvapours/include/vapours/results/htc_results.hpp diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_tasks.cpp b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_tasks.cpp new file mode 100644 index 000000000..95972a110 --- /dev/null +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_tasks.cpp @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htc_htcmisc_rpc_tasks.hpp" + +namespace ams::htc::server::rpc { + + Result GetEnvironmentVariableTask::SetArguments(const char *args, size_t size) { + /* Copy to our name. */ + const size_t copied = util::Strlcpy(m_name, args, sizeof(m_name)); + m_name_size = copied; + + /* Require that the size be correct. */ + + R_UNLESS(size == copied || size == copied + 1, htc::ResultUnknown()); + + return ResultSuccess(); + } + + void GetEnvironmentVariableTask::Complete(HtcmiscResult result, const char *data, size_t size) { + /* Sanity check input. */ + if (size < sizeof(m_value)) { + /* Convert the result. */ + switch (result) { + case HtcmiscResult::Success: + /* Copy to our value. */ + std::memcpy(m_value, data, size); + m_value[size] = '\x00'; + m_value_size = size + 1; + + m_result = ResultSuccess(); + break; + case HtcmiscResult::UnknownError: + m_result = htc::ResultUnknown(); + break; + case HtcmiscResult::UnsupportedVersion: + m_result = htc::ResultConnectionFailure(); + break; + case HtcmiscResult::InvalidRequest: + m_result = htc::ResultNotFound(); + break; + } + } else { + m_result = htc::ResultUnknown(); + } + + /* Complete the task. */ + Task::Complete(); + } + + Result GetEnvironmentVariableTask::GetResult(size_t *out, char *dst, size_t size) { + /* Check our task state. */ + AMS_ASSERT(this->GetTaskState() == RpcTaskState::Completed); + + /* Check that we succeeded. */ + R_TRY(m_result); + + /* Check that we can convert successfully. */ + R_UNLESS(util::IsIntValueRepresentable(size), htc::ResultUnknown()); + + /* Copy out. */ + const auto copied = util::Strlcpy(dst, m_value, size); + R_UNLESS(copied < static_cast(size), htc::ResultNotEnoughBuffer()); + + /* Set the output size. */ + *out = m_value_size; + + return ResultSuccess(); + } + + Result GetEnvironmentVariableTask::CreateRequest(size_t *out, char *data, size_t size, u32 task_id) { + /* Validate pre-conditions. */ + AMS_ASSERT(size >= sizeof(HtcmiscRpcPacket)); + + /* Create the packet. */ + auto *packet = reinterpret_cast(data); + *packet = { + .protocol = HtcmiscProtocol, + .version = HtcmiscMaxVersion, + .category = HtcmiscPacketCategory::Request, + .type = HtcmiscPacketType::GetEnvironmentVariable, + .body_size = this->GetNameSize(), + .task_id = task_id, + .params = { + /* ... */ + }, + }; + + /* Set the packet body. */ + std::memcpy(packet->data, this->GetName(), this->GetNameSize()); + + /* Set the output size. */ + *out = sizeof(*packet) + this->GetNameSize(); + + return ResultSuccess(); + } + + Result GetEnvironmentVariableTask::ProcessResponse(const char *data, size_t size) { + /* Convert the input to a packet. */ + auto *packet = reinterpret_cast(data); + + /* Process the packet. */ + this->Complete(static_cast(packet->params[0]), data + sizeof(*packet), size - sizeof(*packet)); + + /* Complete the task. */ + Task::Complete(); + + return ResultSuccess(); + } + + Result GetEnvironmentVariableLengthTask::SetArguments(const char *args, size_t size) { + /* Copy to our name. */ + const size_t copied = util::Strlcpy(m_name, args, sizeof(m_name)); + m_name_size = copied; + + /* Require that the size be correct. */ + + R_UNLESS(size == copied || size == copied + 1, htc::ResultUnknown()); + + return ResultSuccess(); + } + + void GetEnvironmentVariableLengthTask::Complete(HtcmiscResult result, const char *data, size_t size) { + /* Sanity check input. */ + if (size == sizeof(s64)) { + /* Convert the result. */ + switch (result) { + case HtcmiscResult::Success: + /* Copy to our value. */ + s64 tmp; + std::memcpy(std::addressof(tmp), data, sizeof(tmp)); + if (util::IsIntValueRepresentable(tmp)) { + m_value_size = static_cast(tmp); + } + + m_result = ResultSuccess(); + break; + case HtcmiscResult::UnknownError: + m_result = htc::ResultUnknown(); + break; + case HtcmiscResult::UnsupportedVersion: + m_result = htc::ResultConnectionFailure(); + break; + case HtcmiscResult::InvalidRequest: + m_result = htc::ResultNotFound(); + break; + } + } else { + m_result = htc::ResultUnknown(); + } + + /* Complete the task. */ + Task::Complete(); + } + + Result GetEnvironmentVariableLengthTask::GetResult(size_t *out) { + /* Check our task state. */ + AMS_ASSERT(this->GetTaskState() == RpcTaskState::Completed); + + /* Check that we succeeded. */ + R_TRY(m_result); + + /* Set the output size. */ + *out = m_value_size; + + return ResultSuccess(); + } + + Result GetEnvironmentVariableLengthTask::CreateRequest(size_t *out, char *data, size_t size, u32 task_id) { + /* Validate pre-conditions. */ + AMS_ASSERT(size >= sizeof(HtcmiscRpcPacket)); + + /* Create the packet. */ + auto *packet = reinterpret_cast(data); + *packet = { + .protocol = HtcmiscProtocol, + .version = HtcmiscMaxVersion, + .category = HtcmiscPacketCategory::Request, + .type = HtcmiscPacketType::GetEnvironmentVariableLength, + .body_size = this->GetNameSize(), + .task_id = task_id, + .params = { + /* ... */ + }, + }; + + /* Set the packet body. */ + std::memcpy(packet->data, this->GetName(), this->GetNameSize()); + + /* Set the output size. */ + *out = sizeof(*packet) + this->GetNameSize(); + + return ResultSuccess(); + } + + Result GetEnvironmentVariableLengthTask::ProcessResponse(const char *data, size_t size) { + /* Convert the input to a packet. */ + auto *packet = reinterpret_cast(data); + + /* Process the packet. */ + this->Complete(static_cast(packet->params[0]), data + sizeof(*packet), size - sizeof(*packet)); + + /* Complete the task. */ + Task::Complete(); + + return ResultSuccess(); + } + + Result RunOnHostTask::SetArguments(const char *args, size_t size) { + /* Verify command fits in our buffer. */ + R_UNLESS(size < sizeof(m_command), htc::ResultNotEnoughBuffer()); + + /* Set our command. */ + std::memcpy(m_command, args, size); + m_command_size = size; + + return ResultSuccess(); + } + + void RunOnHostTask::Complete(int host_result) { + /* Set our host result. */ + m_host_result = host_result; + + /* Signal. */ + m_system_event.Signal(); + + /* Complete the task. */ + Task::Complete(); + } + + Result RunOnHostTask::GetResult(int *out) { + *out = m_host_result; + return ResultSuccess(); + } + + void RunOnHostTask::Cancel(RpcTaskCancelReason reason) { + /* Cancel the task. */ + Task::Cancel(reason); + + /* Signal our event. */ + m_system_event.Signal(); + } + + Result RunOnHostTask::CreateRequest(size_t *out, char *data, size_t size, u32 task_id) { + /* Validate pre-conditions. */ + AMS_ASSERT(size >= sizeof(HtcmiscRpcPacket)); + + /* Create the packet. */ + auto *packet = reinterpret_cast(data); + *packet = { + .protocol = HtcmiscProtocol, + .version = HtcmiscMaxVersion, + .category = HtcmiscPacketCategory::Request, + .type = HtcmiscPacketType::RunOnHost, + .body_size = this->GetCommandSize(), + .task_id = task_id, + .params = { + /* ... */ + }, + }; + + /* Set the packet body. */ + std::memcpy(packet->data, this->GetCommand(), this->GetCommandSize()); + + /* Set the output size. */ + *out = sizeof(*packet) + this->GetCommandSize(); + + return ResultSuccess(); + } + + Result RunOnHostTask::ProcessResponse(const char *data, size_t size) { + this->Complete(reinterpret_cast(data)->params[0]); + return ResultSuccess(); + } + + os::SystemEventType *RunOnHostTask::GetSystemEvent() { + return m_system_event.GetBase(); + } + + +} diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_tasks.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_tasks.hpp new file mode 100644 index 000000000..d2015fb85 --- /dev/null +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_tasks.hpp @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "htc_rpc_tasks.hpp" + +namespace ams::htc::server::rpc { + + enum class HtcmiscTaskType { + GetEnvironmentVariable = 0, + GetEnvironmentVariableLength = 1, + SetTargetStatus = 2, + RunOnHost = 3, + }; + + enum class HtcmiscResult { + Success = 0, + UnknownError = 1, + UnsupportedVersion = 2, + InvalidRequest = 3, + }; + + enum class HtcmiscPacketCategory : s16 { + Request = 0, + Response = 1, + }; + + enum class HtcmiscPacketType : s16 { + GetMaxProtocolVersion = 0, + SetProtocolVersion = 1, + GetEnvironmentVariable = 16, + GetEnvironmentVariableLength = 17, + SetTargetStatus = 18, + RunOnHost = 19, + GetWorkingDirectory = 20, + GetWorkingDirectorySize = 21, + SetTargetName = 22, + }; + + constexpr inline s16 HtcmiscProtocol = 4; + constexpr inline s16 HtcmiscMaxVersion = 2; + + struct HtcmiscRpcPacket { + s16 protocol; + s16 version; + HtcmiscPacketCategory category; + HtcmiscPacketType type; + s64 body_size; + u32 task_id; + u64 params[5]; + u8 data[]; + }; + static_assert(sizeof(HtcmiscRpcPacket) == 0x40); + + class HtcmiscTask : public Task { + private: + HtcmiscTaskType m_task_type; + public: + HtcmiscTask(HtcmiscTaskType type) : m_task_type(type) { /* ... */ } + + HtcmiscTaskType GetTaskType() const { return m_task_type; } + }; + + class GetEnvironmentVariableTask : public HtcmiscTask { + private: + char m_name[0x800]; + int m_name_size; + Result m_result; + size_t m_value_size; + char m_value[0x8000]; + public: + GetEnvironmentVariableTask() : HtcmiscTask(HtcmiscTaskType::GetEnvironmentVariable) { /* ... */ } + + Result SetArguments(const char *args, size_t size); + void Complete(HtcmiscResult result, const char *data, size_t size); + Result GetResult(size_t *out, char *dst, size_t size); + + const char *GetName() const { return m_name; } + int GetNameSize() const { return m_name_size; } + public: + virtual Result ProcessResponse(const char *data, size_t size) override; + virtual Result CreateRequest(size_t *out, char *data, size_t size, u32 task_id) override; + }; + + class GetEnvironmentVariableLengthTask : public HtcmiscTask { + private: + char m_name[0x800]; + int m_name_size; + Result m_result; + size_t m_value_size; + public: + GetEnvironmentVariableLengthTask() : HtcmiscTask(HtcmiscTaskType::GetEnvironmentVariableLength) { /* ... */ } + + Result SetArguments(const char *args, size_t size); + void Complete(HtcmiscResult result, const char *data, size_t size); + Result GetResult(size_t *out); + + const char *GetName() const { return m_name; } + int GetNameSize() const { return m_name_size; } + public: + virtual Result ProcessResponse(const char *data, size_t size) override; + virtual Result CreateRequest(size_t *out, char *data, size_t size, u32 task_id) override; + }; + + class RunOnHostTask : public HtcmiscTask { + private: + char m_command[0x2000]; + int m_command_size; + Result m_result; + int m_host_result; + os::SystemEvent m_system_event; + public: + RunOnHostTask() : HtcmiscTask(HtcmiscTaskType::RunOnHost), m_system_event(os::EventClearMode_ManualClear, true) { /* ... */ } + + Result SetArguments(const char *args, size_t size); + void Complete(int host_result); + Result GetResult(int *out); + + const char *GetCommand() const { return m_command; } + int GetCommandSize() const { return m_command_size; } + public: + virtual void Cancel(RpcTaskCancelReason reason) override; + virtual Result ProcessResponse(const char *data, size_t size) override; + virtual Result CreateRequest(size_t *out, char *data, size_t size, u32 task_id) override; + virtual os::SystemEventType *GetSystemEvent() override; + }; + +} diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_tasks.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_tasks.hpp new file mode 100644 index 000000000..9b8ef2566 --- /dev/null +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_tasks.hpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htc::server::rpc { + + enum class RpcTaskCancelReason { + None = 0, + /* ... */ + }; + + enum class RpcTaskState { + Started = 0, + Completed = 1, + Cancelled = 2, + Notified = 3, + }; + + class Task { + private: + RpcTaskState m_state; + RpcTaskCancelReason m_cancel_reason; + os::Event m_event; + public: + Task() : m_state(RpcTaskState::Started), m_cancel_reason(RpcTaskCancelReason::None), m_event(os::EventClearMode_AutoClear) { /* ... */ } + virtual ~Task() { /* ... */ } + public: + void SetTaskState(RpcTaskState state) { m_state = state; } + RpcTaskState GetTaskState() const { return m_state; } + + RpcTaskCancelReason GetTaskCancelReason() const { return m_cancel_reason; } + + os::EventType *GetEvent() { return m_event.GetBase(); } + + void Notify() { + AMS_ASSERT(m_state == RpcTaskState::Started); + + m_state = RpcTaskState::Notified; + } + + void Complete() { + AMS_ASSERT(m_state == RpcTaskState::Started || m_state == RpcTaskState::Notified); + + m_state = RpcTaskState::Completed; + m_event.Signal(); + } + public: + virtual void Cancel(RpcTaskCancelReason reason) { + m_state = RpcTaskState::Cancelled; + m_cancel_reason = reason; + m_event.Signal(); + } + + virtual Result ProcessResponse(const char *data, size_t size) { + return ResultSuccess(); + } + + virtual Result CreateRequest(size_t *out, char *data, size_t size, u32 task_id) { + return ResultSuccess(); + } + + virtual Result ProcessNotification(const char *data, size_t size) { + return ResultSuccess(); + } + + virtual Result CreateNotification(size_t *out, char *data, size_t size, u32 task_id) { + return ResultSuccess(); + } + + virtual bool IsReceiveBufferRequired() { + return false; + } + + virtual bool IsSendBufferRequired() { + return false; + } + + virtual os::SystemEventType *GetSystemEvent() { + return nullptr; + } + }; + +} diff --git a/libraries/libvapours/include/vapours/results.hpp b/libraries/libvapours/include/vapours/results.hpp index 95c7f83e4..23b7e2e9a 100644 --- a/libraries/libvapours/include/vapours/results.hpp +++ b/libraries/libvapours/include/vapours/results.hpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libvapours/include/vapours/results/htc_results.hpp b/libraries/libvapours/include/vapours/results/htc_results.hpp new file mode 100644 index 000000000..152e6ed08 --- /dev/null +++ b/libraries/libvapours/include/vapours/results/htc_results.hpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htc { + + R_DEFINE_NAMESPACE_RESULT_MODULE(18); + + R_DEFINE_ERROR_RESULT(ConnectionFailure, 1); + R_DEFINE_ERROR_RESULT(NotFound, 2); + R_DEFINE_ERROR_RESULT(NotEnoughBuffer, 3); + R_DEFINE_ERROR_RESULT(Unknown, 1023); + +} From f5e98de1a39775b87b147b1b94446e193617b23f Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 9 Feb 2021 23:09:28 -0800 Subject: [PATCH 043/280] htc: add RpcTaskTable --- .../htc/server/rpc/htc_htcmisc_rpc_tasks.hpp | 10 +- .../source/htc/server/rpc/htc_rpc_client.cpp | 4 +- .../source/htc/server/rpc/htc_rpc_client.hpp | 9 +- .../htc/server/rpc/htc_rpc_task_table.hpp | 109 ++++++++++++++++++ .../source/htc/server/rpc/htc_rpc_tasks.hpp | 2 + 5 files changed, 126 insertions(+), 8 deletions(-) create mode 100644 libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_table.hpp diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_tasks.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_tasks.hpp index d2015fb85..983b36b4c 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_tasks.hpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_tasks.hpp @@ -39,8 +39,8 @@ namespace ams::htc::server::rpc { }; enum class HtcmiscPacketType : s16 { - GetMaxProtocolVersion = 0, - SetProtocolVersion = 1, + GetMaxProtocolVersion = 0, + SetProtocolVersion = 1, GetEnvironmentVariable = 16, GetEnvironmentVariableLength = 17, SetTargetStatus = 18, @@ -75,6 +75,8 @@ namespace ams::htc::server::rpc { }; class GetEnvironmentVariableTask : public HtcmiscTask { + public: + static constexpr inline HtcmiscTaskType TaskType = HtcmiscTaskType::GetEnvironmentVariable; private: char m_name[0x800]; int m_name_size; @@ -96,6 +98,8 @@ namespace ams::htc::server::rpc { }; class GetEnvironmentVariableLengthTask : public HtcmiscTask { + public: + static constexpr inline HtcmiscTaskType TaskType = HtcmiscTaskType::GetEnvironmentVariableLength; private: char m_name[0x800]; int m_name_size; @@ -116,6 +120,8 @@ namespace ams::htc::server::rpc { }; class RunOnHostTask : public HtcmiscTask { + public: + static constexpr inline HtcmiscTaskType TaskType = HtcmiscTaskType::RunOnHost; private: char m_command[0x2000]; int m_command_size; diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp index 394252c8e..5b9f4beae 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp @@ -36,12 +36,12 @@ namespace ams::htc::server::rpc { m_receive_thread_stack(g_receive_thread_stack), m_send_thread_stack(g_send_thread_stack), m_mutex(g_rpc_mutex), + m_task_table(), m_cancelled(false), m_thread_running(false) { /* Initialize all events. */ - /* TODO: MaxTaskCount? */ - for (size_t i = 0; i < util::size(m_5F8_events); ++i) { + for (size_t i = 0; i < MaxTaskCount; ++i) { os::InitializeEvent(std::addressof(m_5F8_events[i]), false, os::EventClearMode_AutoClear); os::InitializeEvent(std::addressof(m_1138_events[i]), false, os::EventClearMode_AutoClear); } diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp index 52811489f..d626b861f 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp @@ -16,6 +16,7 @@ #pragma once #include #include "../driver/htc_i_driver.hpp" +#include "htc_rpc_task_table.hpp" namespace ams::htc::server::rpc { @@ -30,13 +31,13 @@ namespace ams::htc::server::rpc { os::ThreadType m_send_thread; os::SdkMutex &m_mutex; /* TODO: m_task_id_free_list */ - /* TODO: m_task_table */ - /* TODO: m_3C0[0x48] */ + RpcTaskTable m_task_table; + /* TODO: m_3C0[MaxTaskCount] */ /* TODO: m_rpc_task_queue */ bool m_cancelled; bool m_thread_running; - os::EventType m_5F8_events[0x48]; - os::EventType m_1138_events[0x48]; + os::EventType m_5F8_events[MaxTaskCount]; + os::EventType m_1138_events[MaxTaskCount]; public: RpcClient(driver::IDriver *driver, htclow::ChannelId channel); }; diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_table.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_table.hpp new file mode 100644 index 000000000..5a22e7cf9 --- /dev/null +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_table.hpp @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "htc_rpc_tasks.hpp" +#include "htc_htcmisc_rpc_tasks.hpp" + +namespace ams::htc::server::rpc { + + /* For convenience. */ + template + concept IsTypeCheckableTask = std::derived_from && requires (T &t) { + { t.GetTaskType() } -> std::convertible_to; + }; + + static_assert(!IsTypeCheckableTask); + static_assert(IsTypeCheckableTask); + + class RpcTaskTable { + private: + /* TODO: How is this variable derived...? */ + /* Nintendo has a value of 0xE1D8, which is deeply magic. */ + static constexpr size_t MaxTaskSize = 0xA000; + using TaskStorage = typename std::aligned_storage::type; + private: + bool m_valid[MaxTaskCount]; + TaskStorage m_storages[MaxTaskCount]; + private: + template + ALWAYS_INLINE T *GetPointer(u32 index) { + static_assert(alignof(T) <= alignof(TaskStorage)); + static_assert(sizeof(T) <= sizeof(TaskStorage)); + return reinterpret_cast(std::addressof(m_storages[index])); + } + + ALWAYS_INLINE bool IsValid(u32 index) { + return index < MaxTaskCount && m_valid[index]; + } + public: + RpcTaskTable() : m_valid() { /* ... */ } + + template requires std::derived_from + T *New(u32 index) { + /* Sanity check input. */ + AMS_ASSERT(!this->IsValid(index)); + + /* Set valid. */ + m_valid[index] = true; + + /* Allocate the task. */ + T *task = this->GetPointer(index); + + /* Create the task. */ + std::construct_at(task); + + /* Return the task. */ + return task; + } + + template requires std::derived_from + T *Get(u32 index) { + /* Check that the task is valid. */ + if (!this->IsValid(index)) { + return false; + } + + /* Get the task pointer. */ + T *task = this->GetPointer(index); + + /* Type check the task. */ + if constexpr (IsTypeCheckableTask) { + if (task->GetTaskType() != T::TaskType) { + task = nullptr; + } + } + + /* Return the task. */ + return task; + } + + template requires std::derived_from + void Delete(u32 index) { + /* Check that the task is valid. */ + if (!this->IsValid(index)) { + return; + } + + /* Delete the task. */ + std::destroy_at(this->GetPointer(index)); + + /* Mark the task as invalid. */ + m_valid[index] = false; + } + }; + +} diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_tasks.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_tasks.hpp index 9b8ef2566..fb9f423ba 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_tasks.hpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_tasks.hpp @@ -18,6 +18,8 @@ namespace ams::htc::server::rpc { + constexpr inline size_t MaxTaskCount = 0x48; + enum class RpcTaskCancelReason { None = 0, /* ... */ From 82757cd1b47c15dbf8871f369a589c56c0bedfd2 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 9 Feb 2021 23:21:39 -0800 Subject: [PATCH 044/280] htc: nullptr != false --- .../source/htc/server/rpc/htc_rpc_task_table.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_table.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_table.hpp index 5a22e7cf9..5d5f54e09 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_table.hpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_table.hpp @@ -50,7 +50,7 @@ namespace ams::htc::server::rpc { return index < MaxTaskCount && m_valid[index]; } public: - RpcTaskTable() : m_valid() { /* ... */ } + constexpr RpcTaskTable() = default; template requires std::derived_from T *New(u32 index) { @@ -74,7 +74,7 @@ namespace ams::htc::server::rpc { T *Get(u32 index) { /* Check that the task is valid. */ if (!this->IsValid(index)) { - return false; + return nullptr; } /* Get the task pointer. */ From 1867c31f63f7e67ca331d110c1090b64befc9b5e Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 10 Feb 2021 00:59:46 -0800 Subject: [PATCH 045/280] htc: add RpcTaskQueue/RpcTaskIdFreeList --- .../source/htc/server/rpc/htc_rpc_client.cpp | 16 ++- .../source/htc/server/rpc/htc_rpc_client.hpp | 19 +++- .../server/rpc/htc_rpc_task_id_free_list.hpp | 61 +++++++++++ .../htc/server/rpc/htc_rpc_task_queue.hpp | 103 ++++++++++++++++++ .../htc/server/rpc/htc_rpc_task_table.hpp | 6 +- .../source/htc/server/rpc/htc_rpc_tasks.hpp | 20 +++- .../include/vapours/results/htc_results.hpp | 5 + 7 files changed, 214 insertions(+), 16 deletions(-) create mode 100644 libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_id_free_list.hpp create mode 100644 libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_queue.hpp diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp index 5b9f4beae..6888c5539 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp @@ -27,6 +27,9 @@ namespace ams::htc::server::rpc { constinit os::SdkMutex g_rpc_mutex; + constinit RpcTaskIdFreeList g_task_id_free_list; + constinit RpcTaskTable g_task_table; + } RpcClient::RpcClient(driver::IDriver *driver, htclow::ChannelId channel) @@ -36,17 +39,18 @@ namespace ams::htc::server::rpc { m_receive_thread_stack(g_receive_thread_stack), m_send_thread_stack(g_send_thread_stack), m_mutex(g_rpc_mutex), - m_task_table(), + m_task_id_free_list(g_task_id_free_list), + m_task_table(g_task_table), + m_task_active(), + m_task_queue(), m_cancelled(false), m_thread_running(false) { /* Initialize all events. */ - for (size_t i = 0; i < MaxTaskCount; ++i) { - os::InitializeEvent(std::addressof(m_5F8_events[i]), false, os::EventClearMode_AutoClear); - os::InitializeEvent(std::addressof(m_1138_events[i]), false, os::EventClearMode_AutoClear); + for (size_t i = 0; i < MaxRpcCount; ++i) { + os::InitializeEvent(std::addressof(m_receive_buffer_available_events[i]), false, os::EventClearMode_AutoClear); + os::InitializeEvent(std::addressof(m_send_buffer_available_events[i]), false, os::EventClearMode_AutoClear); } - - /* TODO: Clear all of m_3C0 array to zero. */ } } diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp index d626b861f..1a20439dc 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp @@ -17,10 +17,15 @@ #include #include "../driver/htc_i_driver.hpp" #include "htc_rpc_task_table.hpp" +#include "htc_rpc_task_queue.hpp" +#include "htc_rpc_task_id_free_list.hpp" namespace ams::htc::server::rpc { class RpcClient { + private: + /* TODO: where is this value coming from, again? */ + static constexpr size_t BufferSize = 0xE400; private: u64 m_00; driver::IDriver *m_driver; @@ -30,14 +35,16 @@ namespace ams::htc::server::rpc { os::ThreadType m_receive_thread; os::ThreadType m_send_thread; os::SdkMutex &m_mutex; - /* TODO: m_task_id_free_list */ - RpcTaskTable m_task_table; - /* TODO: m_3C0[MaxTaskCount] */ - /* TODO: m_rpc_task_queue */ + RpcTaskIdFreeList &m_task_id_free_list; + RpcTaskTable &m_task_table; + bool m_task_active[MaxRpcCount]; + RpcTaskQueue m_task_queue; bool m_cancelled; bool m_thread_running; - os::EventType m_5F8_events[MaxTaskCount]; - os::EventType m_1138_events[MaxTaskCount]; + os::EventType m_receive_buffer_available_events[MaxRpcCount]; + os::EventType m_send_buffer_available_events[MaxRpcCount]; + u8 m_receive_buffer[BufferSize]; + u8 m_send_buffer[BufferSize]; public: RpcClient(driver::IDriver *driver, htclow::ChannelId channel); }; diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_id_free_list.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_id_free_list.hpp new file mode 100644 index 000000000..ee75670c2 --- /dev/null +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_id_free_list.hpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "htc_rpc_tasks.hpp" +#include "htc_htcmisc_rpc_tasks.hpp" + +namespace ams::htc::server::rpc { + + class RpcTaskIdFreeList { + private: + u32 m_task_ids[MaxRpcCount]; + u32 m_offset; + u32 m_free_count; + public: + constexpr RpcTaskIdFreeList() : m_task_ids(), m_offset(0), m_free_count(MaxRpcCount) { + for (auto i = 0; i < static_cast(MaxRpcCount); ++i) { + m_task_ids[i] = i; + } + } + + Result Allocate(u32 *out) { + /* Check that we have free tasks. */ + R_UNLESS(m_free_count > 0, htc::ResultOutOfRpcTask()); + + /* Get index. */ + const auto index = m_offset; + m_free_count = (m_free_count + 1) % MaxRpcCount; + --m_free_count; + + /* Get the task id. */ + *out = m_task_ids[index]; + return ResultSuccess(); + } + + void Free(u32 task_id) { + /* Check pre-conditions. */ + AMS_ASSERT(m_free_count < static_cast(MaxRpcCount)); + + /* Determine index. */ + const auto index = ((m_free_count++) + m_offset) % MaxRpcCount; + + /* Set the task id. */ + m_task_ids[index] = task_id; + } + }; + +} diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_queue.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_queue.hpp new file mode 100644 index 000000000..c7c9175b4 --- /dev/null +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_queue.hpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "htc_rpc_tasks.hpp" +#include "htc_htcmisc_rpc_tasks.hpp" + +namespace ams::htc::server::rpc { + + class RpcTaskQueue { + private: + u32 m_task_ids[MaxRpcCount]; + PacketCategory m_task_categories[MaxRpcCount]; + int m_offset; + int m_count; + os::SdkConditionVariable m_cv; + os::SdkMutex m_mutex; + bool m_cancelled; + public: + constexpr RpcTaskQueue() = default; + + void Initialize() { + m_offset = 0; + m_count = 0; + m_cancelled = false; + } + + void Finalize() { + m_offset = 0; + m_count = 0; + } + + void Cancel() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Cancel ourselves. */ + m_cancelled = true; + + /* Signal to consumers/producers. */ + m_cv.Signal(); + } + + void Add(u32 task_id, PacketCategory category) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Check pre-conditions. */ + AMS_ASSERT(m_count < static_cast(MaxRpcCount)); + + /* Determine index. */ + const auto index = ((m_count++) + m_offset) % MaxRpcCount; + + /* Set task. */ + m_task_ids[index] = task_id; + m_task_categories[index] = category; + + /* Signal. */ + if (m_count > 0) { + m_cv.Signal(); + } + } + + Result Take(u32 *out_id, PacketCategory *out_category) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Wait until we can take. */ + while (m_count == 0 && !m_cancelled) { + m_cv.Wait(m_mutex); + } + + /* Check that we're not cancelled. */ + R_UNLESS(!m_cancelled, htc::ResultCancelled()); + + /* Determine index. */ + const auto index = m_offset; + + /* Advance the queue. */ + m_offset = (m_offset + 1) % MaxRpcCount; + --m_count; + + /* Return the task info. */ + *out_id = m_task_ids[index]; + *out_category = m_task_categories[index]; + return ResultSuccess(); + } + }; + +} diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_table.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_table.hpp index 5d5f54e09..ffb6a7c74 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_table.hpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_table.hpp @@ -36,8 +36,8 @@ namespace ams::htc::server::rpc { static constexpr size_t MaxTaskSize = 0xA000; using TaskStorage = typename std::aligned_storage::type; private: - bool m_valid[MaxTaskCount]; - TaskStorage m_storages[MaxTaskCount]; + bool m_valid[MaxRpcCount]; + TaskStorage m_storages[MaxRpcCount]; private: template ALWAYS_INLINE T *GetPointer(u32 index) { @@ -47,7 +47,7 @@ namespace ams::htc::server::rpc { } ALWAYS_INLINE bool IsValid(u32 index) { - return index < MaxTaskCount && m_valid[index]; + return index < MaxRpcCount && m_valid[index]; } public: constexpr RpcTaskTable() = default; diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_tasks.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_tasks.hpp index fb9f423ba..cd1358449 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_tasks.hpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_tasks.hpp @@ -18,7 +18,25 @@ namespace ams::htc::server::rpc { - constexpr inline size_t MaxTaskCount = 0x48; + constexpr inline size_t MaxRpcCount = 0x48; + + enum class PacketCategory : s16 { + Request = 0, + Response = 1, + Notification = 2, + }; + + struct RpcPacket { + s16 protocol; + s16 version; + PacketCategory category; + u16 type; + s64 body_size; + u32 task_id; + u64 params[5]; + u8 data[]; + }; + static_assert(sizeof(RpcPacket) == 0x40); enum class RpcTaskCancelReason { None = 0, diff --git a/libraries/libvapours/include/vapours/results/htc_results.hpp b/libraries/libvapours/include/vapours/results/htc_results.hpp index 152e6ed08..917ff4e8e 100644 --- a/libraries/libvapours/include/vapours/results/htc_results.hpp +++ b/libraries/libvapours/include/vapours/results/htc_results.hpp @@ -23,6 +23,11 @@ namespace ams::htc { R_DEFINE_ERROR_RESULT(ConnectionFailure, 1); R_DEFINE_ERROR_RESULT(NotFound, 2); R_DEFINE_ERROR_RESULT(NotEnoughBuffer, 3); + + R_DEFINE_ERROR_RESULT(Cancelled, 101); + R_DEFINE_ERROR_RESULT(Unknown, 1023); + R_DEFINE_ERROR_RESULT(OutOfRpcTask, 2102); + } From d60b1abed089c6294b297a5e68f40f741a172754 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 10 Feb 2021 01:29:40 -0800 Subject: [PATCH 046/280] htc: Implement HtcmiscImpl::ClientThread --- .../source/htc/server/htc_htcmisc_impl.cpp | 74 +++++++++++- .../source/htc/server/htc_htcmisc_impl.hpp | 5 + .../source/htc/server/rpc/htc_rpc_client.cpp | 109 ++++++++++++++++++ .../source/htc/server/rpc/htc_rpc_client.hpp | 15 +++ .../server/rpc/htc_rpc_task_id_free_list.hpp | 2 +- 5 files changed, 203 insertions(+), 2 deletions(-) diff --git a/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp b/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp index 4fc86daf9..fdbf6d159 100644 --- a/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp +++ b/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp @@ -77,11 +77,83 @@ namespace ams::htc::server { } void HtcmiscImpl::ClientThread() { - AMS_ABORT("HtcmiscImpl::ClientThread"); + /* Loop so long as we're not cancelled. */ + while (!m_cancelled) { + /* Open the rpc client. */ + m_rpc_client.Open(); + + /* Ensure we close, if something goes wrong. */ + auto client_guard = SCOPE_GUARD { m_rpc_client.Close(); }; + + /* Wait for the rpc client. */ + if (m_rpc_client.WaitAny(htclow::ChannelState_Connectable, m_cancel_event.GetBase()) != 0) { + break; + } + + /* Start the rpc client. */ + if (R_FAILED(m_rpc_client.Start())) { + break; + } + + /* We're connected! */ + this->SetClientConnectionEvent(true); + client_guard.Cancel(); + + /* We're connected, so we want to cleanup when we're done. */ + ON_SCOPE_EXIT { + m_rpc_client.Close(); + m_rpc_client.Cancel(); + m_rpc_client.Wait(); + }; + + /* Wait to become disconnected. */ + if (m_rpc_client.WaitAny(htclow::ChannelState_Disconnected, m_cancel_event.GetBase()) != 0) { + break; + } + + /* Set ourselves as disconnected. */ + this->SetClientConnectionEvent(false); + } + + /* Set ourselves as disconnected. */ + this->SetClientConnectionEvent(false); } void HtcmiscImpl::ServerThread() { AMS_ABORT("HtcmiscImpl::ServerThread"); } + void HtcmiscImpl::SetClientConnectionEvent(bool en) { + /* Lock ourselves. */ + std::scoped_lock lk(m_connection_mutex); + + /* Update our state. */ + if (m_client_connected != en) { + m_client_connected = en; + this->UpdateConnectionEvent(); + } + } + + void HtcmiscImpl::SetServerConnectionEvent(bool en) { + /* Lock ourselves. */ + std::scoped_lock lk(m_connection_mutex); + + /* Update our state. */ + if (m_server_connected != en) { + m_server_connected = en; + this->UpdateConnectionEvent(); + } + } + + void HtcmiscImpl::UpdateConnectionEvent() { + /* Determine if we're connected. */ + const bool connected = m_client_connected && m_server_connected; + + /* Update our state. */ + if (m_connected != connected) { + m_connected = connected; + m_connection_event.Signal(); + } + } + } diff --git a/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.hpp b/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.hpp index 7cd407f1f..c012a9078 100644 --- a/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.hpp +++ b/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.hpp @@ -47,6 +47,11 @@ namespace ams::htc::server { public: HtcmiscImpl(htclow::HtclowManager *htclow_manager); ~HtcmiscImpl(); + private: + void SetClientConnectionEvent(bool en); + void SetServerConnectionEvent(bool en); + + void UpdateConnectionEvent(); public: void Cancel(); /* TODO */ diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp index 6888c5539..f307c3d82 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp @@ -53,4 +53,113 @@ namespace ams::htc::server::rpc { } } + void RpcClient::Open() { + R_ABORT_UNLESS(m_driver->Open(m_channel_id)); + } + + void RpcClient::Close() { + m_driver->Close(m_channel_id); + } + + Result RpcClient::Start() { + /* Connect. */ + R_TRY(m_driver->Connect(m_channel_id)); + + /* Initialize our task queue. */ + m_task_queue.Initialize(); + + /* Create our threads. */ + R_ABORT_UNLESS(os::CreateThread(std::addressof(m_receive_thread), ReceiveThreadEntry, this, m_receive_thread_stack, ThreadStackSize, AMS_GET_SYSTEM_THREAD_PRIORITY(htc, HtcmiscReceive))); + R_ABORT_UNLESS(os::CreateThread(std::addressof(m_send_thread), SendThreadEntry, this, m_send_thread_stack, ThreadStackSize, AMS_GET_SYSTEM_THREAD_PRIORITY(htc, HtcmiscSend))); + + /* Set thread name pointers. */ + os::SetThreadNamePointer(std::addressof(m_receive_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, HtcmiscReceive)); + os::SetThreadNamePointer(std::addressof(m_send_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, HtcmiscSend)); + + /* Start threads. */ + os::StartThread(std::addressof(m_receive_thread)); + os::StartThread(std::addressof(m_send_thread)); + + /* Set initial state. */ + m_cancelled = false; + m_thread_running = true; + + /* Clear events. */ + for (size_t i = 0; i < MaxRpcCount; ++i) { + os::ClearEvent(std::addressof(m_receive_buffer_available_events[i])); + os::ClearEvent(std::addressof(m_send_buffer_available_events[i])); + } + + return ResultSuccess(); + } + + void RpcClient::Cancel() { + /* Set cancelled. */ + m_cancelled = true; + + /* Signal all events. */ + for (size_t i = 0; i < MaxRpcCount; ++i) { + os::SignalEvent(std::addressof(m_receive_buffer_available_events[i])); + os::SignalEvent(std::addressof(m_send_buffer_available_events[i])); + } + + /* Cancel our queue. */ + m_task_queue.Cancel(); + } + + void RpcClient::Wait() { + /* Wait for thread to not be running. */ + if (m_thread_running) { + os::WaitThread(std::addressof(m_receive_thread)); + os::WaitThread(std::addressof(m_send_thread)); + os::DestroyThread(std::addressof(m_receive_thread)); + os::DestroyThread(std::addressof(m_send_thread)); + } + m_thread_running = false; + + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Finalize the task queue. */ + m_task_queue.Finalize(); + + /* Cancel all tasks. */ + for (size_t i = 0; i < MaxRpcCount; ++i) { + if (m_task_active[i]) { + /* TODO: enum member */ + m_task_table.Get(i)->Cancel(static_cast(2)); + } + } + } + + int RpcClient::WaitAny(htclow::ChannelState state, os::EventType *event) { + /* Check if we're already signaled. */ + if (os::TryWaitEvent(event)) { + return 1; + } + if (m_driver->GetChannelState(m_channel_id) == state) { + return 0; + } + + /* Wait. */ + while (true) { + const auto idx = os::WaitAny(m_driver->GetChannelStateEvent(m_channel_id), event); + if (idx == 0) { + if (m_driver->GetChannelState(m_channel_id) == state) { + return 0; + } + } else { + return idx; + } + } + } + + void RpcClient::ReceiveThread() { + AMS_ABORT("RpcClient::ReceiveThread"); + } + + void RpcClient::SendThread() { + AMS_ABORT("RpcClient::SendThread"); + } + } diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp index 1a20439dc..e517025f6 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp @@ -45,8 +45,23 @@ namespace ams::htc::server::rpc { os::EventType m_send_buffer_available_events[MaxRpcCount]; u8 m_receive_buffer[BufferSize]; u8 m_send_buffer[BufferSize]; + private: + static void ReceiveThreadEntry(void *arg) { static_cast(arg)->ReceiveThread(); } + static void SendThreadEntry(void *arg) { static_cast(arg)->SendThread(); } + + void ReceiveThread(); + void SendThread(); public: RpcClient(driver::IDriver *driver, htclow::ChannelId channel); + public: + void Open(); + void Close(); + + Result Start(); + void Cancel(); + void Wait(); + + int WaitAny(htclow::ChannelState state, os::EventType *event); }; } diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_id_free_list.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_id_free_list.hpp index ee75670c2..434791ee8 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_id_free_list.hpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_id_free_list.hpp @@ -38,7 +38,7 @@ namespace ams::htc::server::rpc { /* Get index. */ const auto index = m_offset; - m_free_count = (m_free_count + 1) % MaxRpcCount; + m_offset = (m_offset + 1) % MaxRpcCount; --m_free_count; /* Get the task id. */ From 3be005b638e6c693aa80ae371077661dfaffd74c Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 10 Feb 2021 02:05:45 -0800 Subject: [PATCH 047/280] htc: Implement RpcClient::ReceiveThread + SendThread --- .../htc/server/rpc/htc_htcmisc_rpc_tasks.hpp | 2 +- .../source/htc/server/rpc/htc_rpc_client.cpp | 141 +++++++++++++++++- .../source/htc/server/rpc/htc_rpc_client.hpp | 12 +- .../htc/server/rpc/htc_rpc_task_table.hpp | 6 + .../source/htc/server/rpc/htc_rpc_tasks.hpp | 2 +- .../include/vapours/results/htc_results.hpp | 4 + 6 files changed, 157 insertions(+), 10 deletions(-) diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_tasks.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_tasks.hpp index 983b36b4c..20268570f 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_tasks.hpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_tasks.hpp @@ -61,7 +61,7 @@ namespace ams::htc::server::rpc { s64 body_size; u32 task_id; u64 params[5]; - u8 data[]; + char data[]; }; static_assert(sizeof(HtcmiscRpcPacket) == 0x40); diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp index f307c3d82..12c91ee74 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp @@ -154,12 +154,145 @@ namespace ams::htc::server::rpc { } } - void RpcClient::ReceiveThread() { - AMS_ABORT("RpcClient::ReceiveThread"); + Result RpcClient::ReceiveThread() { + /* Loop forever. */ + auto *header = reinterpret_cast(m_receive_buffer); + while (true) { + /* Try to receive a packet header. */ + R_TRY(this->ReceiveHeader(header)); + + /* Track how much we've received. */ + size_t received = sizeof(*header); + + /* If the packet has one, receive its body. */ + if (header->body_size > 0) { + /* Sanity check the task id. */ + AMS_ABORT_UNLESS(header->task_id < static_cast(MaxRpcCount)); + + /* Sanity check the body size. */ + AMS_ABORT_UNLESS(util::IsIntValueRepresentable(header->body_size)); + AMS_ABORT_UNLESS(static_cast(header->body_size) <= sizeof(m_receive_buffer) - received); + + /* Receive the body. */ + R_TRY(this->ReceiveBody(header->data, header->body_size)); + + /* Note that we received the body. */ + received += header->body_size; + } + + /* Acquire exclusive access to the task tables. */ + std::scoped_lock lk(m_mutex); + + /* Get the specified task. */ + Task *task = m_task_table.Get(header->task_id); + R_UNLESS(task != nullptr, htc::ResultInvalidTaskId()); + + /* If the task is canceled, free it. */ + if (task->GetTaskState() == RpcTaskState::Cancelled) { + m_task_active[header->task_id] = false; + m_task_table.Delete(header->task_id); + m_task_id_free_list.Free(header->task_id); + continue; + } + + /* Handle the packet. */ + switch (header->category) { + case PacketCategory::Response: + R_TRY(task->ProcessResponse(m_receive_buffer, received)); + break; + case PacketCategory::Notification: + R_TRY(task->ProcessNotification(m_receive_buffer, received)); + break; + default: + return htc::ResultInvalidCategory(); + } + + /* If we used the receive buffer, signal that we're done with it. */ + if (task->IsReceiveBufferRequired()) { + os::SignalEvent(std::addressof(m_receive_buffer_available_events[header->task_id])); + } + } } - void RpcClient::SendThread() { - AMS_ABORT("RpcClient::SendThread"); + Result RpcClient::ReceiveHeader(RpcPacket *header) { + /* Receive. */ + s64 received; + R_TRY(m_driver->Receive(std::addressof(received), reinterpret_cast(header), sizeof(*header), m_channel_id, htclow::ReceiveOption_ReceiveAllData)); + + /* Check size. */ + R_UNLESS(static_cast(received) == sizeof(*header), htc::ResultInvalidSize()); + + return ResultSuccess(); + } + + Result RpcClient::ReceiveBody(char *dst, size_t size) { + /* Receive. */ + s64 received; + R_TRY(m_driver->Receive(std::addressof(received), dst, size, m_channel_id, htclow::ReceiveOption_ReceiveAllData)); + + /* Check size. */ + R_UNLESS(static_cast(received) == size, htc::ResultInvalidSize()); + + return ResultSuccess(); + } + + Result RpcClient::SendThread() { + while (true) { + /* Get a task. */ + Task *task; + u32 task_id; + PacketCategory category; + do { + /* Dequeue a task. */ + R_TRY(m_task_queue.Take(std::addressof(task_id), std::addressof(category))); + + /* Get the task from the table. */ + std::scoped_lock lk(m_mutex); + + task = m_task_table.Get(task_id); + } while (task == nullptr); + + /* If required, wait for the send buffer to become available. */ + if (task->IsSendBufferRequired()) { + os::WaitEvent(std::addressof(m_send_buffer_available_events[task_id])); + + /* Check if we've been cancelled. */ + if (m_cancelled) { + break; + } + } + + /* Handle the task. */ + size_t packet_size; + switch (category) { + case PacketCategory::Request: + R_TRY(task->CreateRequest(std::addressof(packet_size), m_send_buffer, sizeof(m_send_buffer), task_id)); + break; + case PacketCategory::Notification: + R_TRY(task->CreateNotification(std::addressof(packet_size), m_send_buffer, sizeof(m_send_buffer), task_id)); + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Send the request. */ + R_TRY(this->SendRequest(m_send_buffer, packet_size)); + } + + return htc::ResultCancelled(); + } + + Result RpcClient::SendRequest(const char *src, size_t size) { + /* Sanity check our size. */ + AMS_ASSERT(util::IsIntValueRepresentable(size)); + + /* Send the data. */ + s64 sent; + R_TRY(m_driver->Send(std::addressof(sent), src, static_cast(size), m_channel_id)); + + /* Check that we sent the right amount. */ + R_UNLESS(sent == static_cast(size), htc::ResultInvalidSize()); + + return ResultSuccess(); } } diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp index e517025f6..5b7369b11 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp @@ -43,14 +43,14 @@ namespace ams::htc::server::rpc { bool m_thread_running; os::EventType m_receive_buffer_available_events[MaxRpcCount]; os::EventType m_send_buffer_available_events[MaxRpcCount]; - u8 m_receive_buffer[BufferSize]; - u8 m_send_buffer[BufferSize]; + char m_receive_buffer[BufferSize]; + char m_send_buffer[BufferSize]; private: static void ReceiveThreadEntry(void *arg) { static_cast(arg)->ReceiveThread(); } static void SendThreadEntry(void *arg) { static_cast(arg)->SendThread(); } - void ReceiveThread(); - void SendThread(); + Result ReceiveThread(); + Result SendThread(); public: RpcClient(driver::IDriver *driver, htclow::ChannelId channel); public: @@ -62,6 +62,10 @@ namespace ams::htc::server::rpc { void Wait(); int WaitAny(htclow::ChannelState state, os::EventType *event); + private: + Result ReceiveHeader(RpcPacket *header); + Result ReceiveBody(char *dst, size_t size); + Result SendRequest(const char *src, size_t size); }; } diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_table.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_table.hpp index ffb6a7c74..f283f9232 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_table.hpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_table.hpp @@ -104,6 +104,12 @@ namespace ams::htc::server::rpc { /* Mark the task as invalid. */ m_valid[index] = false; } + + void Delete(u32 index) { + if (this->IsValid(index)) { + this->Delete(index); + } + } }; } diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_tasks.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_tasks.hpp index cd1358449..848c1ee3c 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_tasks.hpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_tasks.hpp @@ -34,7 +34,7 @@ namespace ams::htc::server::rpc { s64 body_size; u32 task_id; u64 params[5]; - u8 data[]; + char data[]; }; static_assert(sizeof(RpcPacket) == 0x40); diff --git a/libraries/libvapours/include/vapours/results/htc_results.hpp b/libraries/libvapours/include/vapours/results/htc_results.hpp index 917ff4e8e..3025aacbc 100644 --- a/libraries/libvapours/include/vapours/results/htc_results.hpp +++ b/libraries/libvapours/include/vapours/results/htc_results.hpp @@ -28,6 +28,10 @@ namespace ams::htc { R_DEFINE_ERROR_RESULT(Unknown, 1023); + R_DEFINE_ERROR_RESULT(InvalidTaskId, 2003); + R_DEFINE_ERROR_RESULT(InvalidSize, 2011); + R_DEFINE_ERROR_RESULT(OutOfRpcTask, 2102); + R_DEFINE_ERROR_RESULT(InvalidCategory, 2123); } From 7485a1968a3085d0eed7aa08a53ef53af93cb678 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 10 Feb 2021 02:37:27 -0800 Subject: [PATCH 048/280] htc: implement HtcmiscImpl::ServerThread/HtcmiscRpcServer::ReceiveThread --- .../source/htc/server/htc_htcmisc_impl.cpp | 41 +++++- .../htc/server/rpc/htc_htcmisc_rpc_server.cpp | 139 ++++++++++++++++++ .../htc/server/rpc/htc_htcmisc_rpc_server.hpp | 27 ++++ 3 files changed, 206 insertions(+), 1 deletion(-) diff --git a/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp b/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp index fdbf6d159..e2ed1d157 100644 --- a/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp +++ b/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp @@ -120,7 +120,46 @@ namespace ams::htc::server { } void HtcmiscImpl::ServerThread() { - AMS_ABORT("HtcmiscImpl::ServerThread"); + /* Loop so long as we're not cancelled. */ + while (!m_cancelled) { + /* Open the rpc server. */ + m_rpc_server.Open(); + + /* Ensure we close, if something goes wrong. */ + auto server_guard = SCOPE_GUARD { m_rpc_server.Close(); }; + + /* Wait for the rpc server. */ + if (m_rpc_server.WaitAny(htclow::ChannelState_Connectable, m_cancel_event.GetBase()) != 0) { + break; + } + + /* Start the rpc server. */ + if (R_FAILED(m_rpc_server.Start())) { + break; + } + + /* We're connected! */ + this->SetServerConnectionEvent(true); + server_guard.Cancel(); + + /* We're connected, so we want to cleanup when we're done. */ + ON_SCOPE_EXIT { + m_rpc_server.Close(); + m_rpc_server.Cancel(); + m_rpc_server.Wait(); + }; + + /* Wait to become disconnected. */ + if (m_rpc_server.WaitAny(htclow::ChannelState_Disconnected, m_cancel_event.GetBase()) != 0) { + break; + } + + /* Set ourselves as disconnected. */ + this->SetServerConnectionEvent(false); + } + + /* Set ourselves as disconnected. */ + this->SetServerConnectionEvent(false); } void HtcmiscImpl::SetClientConnectionEvent(bool en) { diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.cpp b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.cpp index 438df31e2..5e28c1255 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.cpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.cpp @@ -37,4 +37,143 @@ namespace ams::htc::server::rpc { /* ... */ } + void HtcmiscRpcServer::Open() { + R_ABORT_UNLESS(m_driver->Open(m_channel_id, m_driver_receive_buffer, sizeof(m_driver_receive_buffer), m_driver_send_buffer, sizeof(m_driver_send_buffer))); + } + + void HtcmiscRpcServer::Close() { + m_driver->Close(m_channel_id); + } + + Result HtcmiscRpcServer::Start() { + /* Connect. */ + R_TRY(m_driver->Connect(m_channel_id)); + + /* Create our thread. */ + R_ABORT_UNLESS(os::CreateThread(std::addressof(m_receive_thread), ReceiveThreadEntry, this, m_receive_thread_stack, ReceiveThreadStackSize, AMS_GET_SYSTEM_THREAD_PRIORITY(htc, HtcmiscReceive))); + + /* Set thread name pointer. */ + os::SetThreadNamePointer(std::addressof(m_receive_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, HtcmiscReceive)); + + /* Start thread. */ + os::StartThread(std::addressof(m_receive_thread)); + + /* Set initial state. */ + m_cancelled = false; + m_thread_running = true; + + return ResultSuccess(); + } + + void HtcmiscRpcServer::Cancel() { + /* Set cancelled. */ + m_cancelled = true; + } + + void HtcmiscRpcServer::Wait() { + /* Wait for thread to not be running. */ + if (m_thread_running) { + os::WaitThread(std::addressof(m_receive_thread)); + os::DestroyThread(std::addressof(m_receive_thread)); + } + m_thread_running = false; + } + + int HtcmiscRpcServer::WaitAny(htclow::ChannelState state, os::EventType *event) { + /* Check if we're already signaled. */ + if (os::TryWaitEvent(event)) { + return 1; + } + if (m_driver->GetChannelState(m_channel_id) == state) { + return 0; + } + + /* Wait. */ + while (true) { + const auto idx = os::WaitAny(m_driver->GetChannelStateEvent(m_channel_id), event); + if (idx == 0) { + if (m_driver->GetChannelState(m_channel_id) == state) { + return 0; + } + } else { + return idx; + } + } + } + + Result HtcmiscRpcServer::ReceiveThread() { + /* Loop forever. */ + auto *header = reinterpret_cast(m_receive_buffer); + while (true) { + /* Try to receive a packet header. */ + R_TRY(this->ReceiveHeader(header)); + + /* Track how much we've received. */ + size_t received = sizeof(*header); + + /* If the packet has one, receive its body. */ + if (header->body_size > 0) { + /* Sanity check the body size. */ + AMS_ABORT_UNLESS(util::IsIntValueRepresentable(header->body_size)); + AMS_ABORT_UNLESS(static_cast(header->body_size) <= sizeof(m_receive_buffer) - received); + + /* Receive the body. */ + R_TRY(this->ReceiveBody(header->data, header->body_size)); + + /* Note that we received the body. */ + received += header->body_size; + } + + /* Check that the packet is a request packet. */ + R_UNLESS(header->category == HtcmiscPacketCategory::Request, htc::ResultInvalidCategory()); + + /* Handle specific requests. */ + if (header->type == HtcmiscPacketType::SetTargetName) { + R_TRY(this->ProcessSetTargetNameRequest(header->data, header->body_size, header->task_id)); + } + } + } + + Result HtcmiscRpcServer::ProcessSetTargetNameRequest(const char *name, size_t size, u32 task_id) { + /* TODO: we need to use settings::fwdbg::SetSettingsItemValue here, but this will require ams support for set:fd re-enable? */ + /* Needs some thought. */ + AMS_ABORT("HtcmiscRpcServer::ProcessSetTargetNameRequest"); + } + + Result HtcmiscRpcServer::ReceiveHeader(HtcmiscRpcPacket *header) { + /* Receive. */ + s64 received; + R_TRY(m_driver->Receive(std::addressof(received), reinterpret_cast(header), sizeof(*header), m_channel_id, htclow::ReceiveOption_ReceiveAllData)); + + /* Check size. */ + R_UNLESS(static_cast(received) == sizeof(*header), htc::ResultInvalidSize()); + + return ResultSuccess(); + } + + Result HtcmiscRpcServer::ReceiveBody(char *dst, size_t size) { + /* Receive. */ + s64 received; + R_TRY(m_driver->Receive(std::addressof(received), dst, size, m_channel_id, htclow::ReceiveOption_ReceiveAllData)); + + /* Check size. */ + R_UNLESS(static_cast(received) == size, htc::ResultInvalidSize()); + + return ResultSuccess(); + } + + Result HtcmiscRpcServer::SendRequest(const char *src, size_t size) { + /* Sanity check our size. */ + AMS_ASSERT(util::IsIntValueRepresentable(size)); + + /* Send the data. */ + s64 sent; + R_TRY(m_driver->Send(std::addressof(sent), src, static_cast(size), m_channel_id)); + + /* Check that we sent the right amount. */ + R_UNLESS(sent == static_cast(size), htc::ResultInvalidSize()); + + return ResultSuccess(); + } + } diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.hpp index 71d028130..598559afe 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.hpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.hpp @@ -16,10 +16,14 @@ #pragma once #include #include "../driver/htc_i_driver.hpp" +#include "htc_htcmisc_rpc_tasks.hpp" namespace ams::htc::server::rpc { class HtcmiscRpcServer { + private: + /* TODO: where is this value coming from, again? */ + static constexpr size_t BufferSize = 1_KB; private: u64 m_00; driver::IDriver *m_driver; @@ -28,8 +32,31 @@ namespace ams::htc::server::rpc { os::ThreadType m_receive_thread; bool m_cancelled; bool m_thread_running; + char m_receive_buffer[BufferSize]; + char m_send_buffer[BufferSize]; + u8 m_driver_receive_buffer[4_KB]; + u8 m_driver_send_buffer[4_KB]; + private: + static void ReceiveThreadEntry(void *arg) { static_cast(arg)->ReceiveThread(); } + + Result ReceiveThread(); public: HtcmiscRpcServer(driver::IDriver *driver, htclow::ChannelId channel); + public: + void Open(); + void Close(); + + Result Start(); + void Cancel(); + void Wait(); + + int WaitAny(htclow::ChannelState state, os::EventType *event); + private: + Result ProcessSetTargetNameRequest(const char *name, size_t size, u32 task_id); + + Result ReceiveHeader(HtcmiscRpcPacket *header); + Result ReceiveBody(char *dst, size_t size); + Result SendRequest(const char *src, size_t size); }; } From 79a3f442d658d63d7aec8855d7cd113ba5932c10 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 10 Feb 2021 03:14:26 -0800 Subject: [PATCH 049/280] htc: implement psc/pm loop --- .../htc/server/htc_power_state_control.cpp | 104 ++++++++++++++++++ stratosphere/htc/source/htc_main.cpp | 11 +- 2 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 libraries/libstratosphere/source/htc/server/htc_power_state_control.cpp diff --git a/libraries/libstratosphere/source/htc/server/htc_power_state_control.cpp b/libraries/libstratosphere/source/htc/server/htc_power_state_control.cpp new file mode 100644 index 000000000..cb2e2bfee --- /dev/null +++ b/libraries/libstratosphere/source/htc/server/htc_power_state_control.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "../../htclow/htclow_manager.hpp" + +namespace ams::htc::server { + + namespace { + + constinit htclow::HtclowManager *g_htclow_manager = nullptr; + + constexpr const psc::PmModuleId PscModuleDependencies[] = { psc::PmModuleId_Pcie, psc::PmModuleId_Usb }; + psc::PmModule g_pm_module; + + constinit bool g_is_asleep = false; + constinit bool g_is_suspended = false; + + } + + void InitializePowerStateMonitor(htclow::impl::DriverType driver_type, htclow::HtclowManager *htclow_manager) { + /* Set the htclow manager. */ + g_htclow_manager = htclow_manager; + + /* Initialize pm module. */ + R_ABORT_UNLESS(g_pm_module.Initialize(psc::PmModuleId_TmaHostIo, PscModuleDependencies, util::size(PscModuleDependencies), os::EventClearMode_AutoClear)); + + /* We're neither asleep nor suspended. */ + g_is_asleep = false; + g_is_suspended = false; + } + + void LoopMonitorPowerState() { + /* Get the psc module's event pointer. */ + auto *event = g_pm_module.GetEventPointer(); + while (true) { + /* Wait for a new power state event. */ + event->Wait(); + + /* Get the power state. */ + psc::PmState pm_state; + psc::PmFlagSet pm_flags; + R_ABORT_UNLESS(g_pm_module.GetRequest(std::addressof(pm_state), std::addressof(pm_flags))); + + /* Update sleeping state. */ + switch (pm_state) { + case psc::PmState_Awake: + if (g_is_asleep) { + g_htclow_manager->NotifyAwake(); + g_is_asleep = false; + } + break; + case psc::PmState_ReadyAwaken: + case psc::PmState_ReadySleep: + case psc::PmState_ReadySleepCritical: + case psc::PmState_ReadyAwakenCritical: + if (!g_is_asleep) { + g_htclow_manager->NotifyAsleep(); + g_is_asleep = true; + } + break; + default: + break; + } + + /* Update suspend state. */ + switch (pm_state) { + case psc::PmState_Awake: + case psc::PmState_ReadyAwaken: + if (g_is_suspended) { + g_htclow_manager->Resume(); + g_is_suspended = false; + } + break; + case psc::PmState_ReadySleep: + case psc::PmState_ReadySleepCritical: + case psc::PmState_ReadyAwakenCritical: + if (!g_is_suspended) { + g_htclow_manager->Suspend(); + g_is_suspended = true; + } + break; + default: + break; + } + + /* Acknowledge the pm request. */ + R_ABORT_UNLESS(g_pm_module.Acknowledge(pm_state, ResultSuccess())); + } + } + +} diff --git a/stratosphere/htc/source/htc_main.cpp b/stratosphere/htc/source/htc_main.cpp index e83193b81..92b39ac7e 100644 --- a/stratosphere/htc/source/htc_main.cpp +++ b/stratosphere/htc/source/htc_main.cpp @@ -198,6 +198,13 @@ namespace ams::htc { } + namespace server { + + void InitializePowerStateMonitor(htclow::impl::DriverType driver_type, htclow::HtclowManager *htclow_manager); + void LoopMonitorPowerState(); + + } + } int main(int argc, char **argv) @@ -246,7 +253,7 @@ int main(int argc, char **argv) } /* Initialize psc. */ - //htc::server::InitializePowerStateMonitor(driver_type, htclow_manager); + htc::server::InitializePowerStateMonitor(driver_type, htclow_manager); /* Start all threads. */ os::StartThread(std::addressof(htc_ipc_thread)); @@ -256,7 +263,7 @@ int main(int argc, char **argv) } /* Loop psc monitor. */ - //htc::server::LoopMonitorPowerState(); + htc::server::LoopMonitorPowerState(); /* Destroy all threads. */ for (size_t i = 0; i < htc::NumHtcsIpcThreads; ++i) { From 4d86863f2cd85672df0094b10f6c21f9f44f4562 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 10 Feb 2021 04:22:19 -0800 Subject: [PATCH 050/280] htc: ObserverThread (mostly), system now boots + works with htc in bg --- .../source/boot2/boot2_api.cpp | 4 +- .../source/htc/server/htc_htcmisc_impl.cpp | 11 +++++ .../source/htc/server/htc_htcmisc_impl.hpp | 7 ++- .../source/htc/server/htc_observer.cpp | 45 ++++++++++++++++++- .../libstratosphere/source/usb/usb_device.cpp | 2 +- stratosphere/htc/source/htc_main.cpp | 21 +++++++++ 6 files changed, 83 insertions(+), 7 deletions(-) diff --git a/libraries/libstratosphere/source/boot2/boot2_api.cpp b/libraries/libstratosphere/source/boot2/boot2_api.cpp index 2c085abdd..baa539d61 100644 --- a/libraries/libstratosphere/source/boot2/boot2_api.cpp +++ b/libraries/libstratosphere/source/boot2/boot2_api.cpp @@ -34,7 +34,7 @@ namespace ams::boot2 { constexpr size_t NumPreSdCardLaunchPrograms = util::size(PreSdCardLaunchPrograms); constexpr ncm::SystemProgramId AdditionalLaunchPrograms[] = { - ncm::SystemProgramId::Tma, /* tma */ + ncm::SystemProgramId::Htc, /* htc */ /* TODO: should we do boot!use_htc_gen2, with default to on in custom settings? */ ncm::SystemProgramId::Am, /* am */ ncm::SystemProgramId::NvServices, /* nvservices */ ncm::SystemProgramId::NvnFlinger, /* nvnflinger */ @@ -80,7 +80,7 @@ namespace ams::boot2 { constexpr size_t NumAdditionalLaunchPrograms = util::size(AdditionalLaunchPrograms); constexpr ncm::SystemProgramId AdditionalMaintenanceLaunchPrograms[] = { - ncm::SystemProgramId::Tma, /* tma */ + ncm::SystemProgramId::Htc, /* htc */ ncm::SystemProgramId::Am, /* am */ ncm::SystemProgramId::NvServices, /* nvservices */ ncm::SystemProgramId::NvnFlinger, /* nvnflinger */ diff --git a/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp b/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp index e2ed1d157..5bd78d167 100644 --- a/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp +++ b/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp @@ -195,4 +195,15 @@ namespace ams::htc::server { } } + os::EventType *HtcmiscImpl::GetConnectionEvent() const { + return m_connection_event.GetBase(); + } + + bool HtcmiscImpl::IsConnected() const { + /* Lock ourselves. */ + std::scoped_lock lk(m_connection_mutex); + + return m_connected; + } + } diff --git a/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.hpp b/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.hpp index c012a9078..3317bda4c 100644 --- a/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.hpp +++ b/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.hpp @@ -33,11 +33,11 @@ namespace ams::htc::server { os::ThreadType m_server_thread; os::Event m_cancel_event; bool m_cancelled; - os::Event m_connection_event; + mutable os::Event m_connection_event; bool m_client_connected; bool m_server_connected; bool m_connected; - os::SdkMutex m_connection_mutex; + mutable os::SdkMutex m_connection_mutex; private: static void ClientThreadEntry(void *arg) { static_cast(arg)->ClientThread(); } static void ServerThreadEntry(void *arg) { static_cast(arg)->ServerThread(); } @@ -47,6 +47,9 @@ namespace ams::htc::server { public: HtcmiscImpl(htclow::HtclowManager *htclow_manager); ~HtcmiscImpl(); + + os::EventType *GetConnectionEvent() const; + bool IsConnected() const; private: void SetClientConnectionEvent(bool en); void SetServerConnectionEvent(bool en); diff --git a/libraries/libstratosphere/source/htc/server/htc_observer.cpp b/libraries/libstratosphere/source/htc/server/htc_observer.cpp index 4e1c6622b..58fc965bb 100644 --- a/libraries/libstratosphere/source/htc/server/htc_observer.cpp +++ b/libraries/libstratosphere/source/htc/server/htc_observer.cpp @@ -29,7 +29,7 @@ namespace ams::htc::server { m_is_service_available(false) { /* Initialize htcs library. */ - AMS_ABORT("htcs::impl::HtcsManagerHolder::AddReference();"); + /* TODO: AMS_ABORT("htcs::impl::HtcsManagerHolder::AddReference();"); */ /* Update our event state. */ this->UpdateEvent(); @@ -69,7 +69,48 @@ namespace ams::htc::server { } void Observer::ObserverThreadBody() { - AMS_ABORT("Observer::ObserverThreadBody"); + /* When we're done observing, clear our state. */ + ON_SCOPE_EXIT { + m_connected = false; + m_is_service_available = false; + this->UpdateEvent(); + }; + + /* Get the events we're waiting on. */ + os::EventType * const stop_event = m_stop_event.GetBase(); + os::EventType * const conn_event = m_misc_impl.GetConnectionEvent(); + os::EventType * const htcs_event = nullptr /* TODO: htcs::impl::HtcsManagerHolder::GetHtcsManager()->GetServiceAvailabilityEvent() */; + + /* Loop until we're asked to stop. */ + while (!m_stopped) { + /* Wait for an event to be signaled. */ + const auto index = os::WaitAny(stop_event, conn_event /*, htcs_event */); + switch (index) { + case 0: + /* Stop event, just break out of the loop. */ + os::ClearEvent(stop_event); + break; + case 1: + /* Connection event, update our connection status. */ + os::ClearEvent(conn_event); + m_connected = m_misc_impl.IsConnected(); + break; + case 2: + /* Htcs event, update our service status. */ + os::ClearEvent(htcs_event); + m_is_service_available = false /* TODO: htcs::impl::HtcsManagerHolder::GetHtcsManager()->IsServiceAvailable() */; + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* If the event was our stop event, break. */ + if (index == 0) { + break; + } + + /* Update event status. */ + this->UpdateEvent(); + } } } diff --git a/libraries/libstratosphere/source/usb/usb_device.cpp b/libraries/libstratosphere/source/usb/usb_device.cpp index cbc52439c..8eda9f6be 100644 --- a/libraries/libstratosphere/source/usb/usb_device.cpp +++ b/libraries/libstratosphere/source/usb/usb_device.cpp @@ -274,7 +274,7 @@ namespace ams::usb { Result DsInterface::Finalize() { /* Validate that we have a service. */ - R_ABORT_UNLESS(m_interface != nullptr); + AMS_ABORT_UNLESS(m_interface != nullptr); /* We must be disabled. */ R_UNLESS(!m_client->m_is_enabled, usb::ResultResourceBusy()); diff --git a/stratosphere/htc/source/htc_main.cpp b/stratosphere/htc/source/htc_main.cpp index 92b39ac7e..d3ebba403 100644 --- a/stratosphere/htc/source/htc_main.cpp +++ b/stratosphere/htc/source/htc_main.cpp @@ -42,6 +42,25 @@ namespace ams { using namespace ams; +#define AMS_HTC_USE_FATAL_ERROR 1 + +#if AMS_HTC_USE_FATAL_ERROR + +extern "C" { + + /* Exception handling. */ + alignas(16) u8 __nx_exception_stack[ams::os::MemoryPageSize]; + u64 __nx_exception_stack_size = sizeof(__nx_exception_stack); + void __libnx_exception_handler(ThreadExceptionDump *ctx); + +} + +void __libnx_exception_handler(ThreadExceptionDump *ctx) { + ams::CrashHandler(ctx); +} + +#endif + namespace ams::htc { namespace { @@ -87,6 +106,8 @@ void __appInit(void) { sm::DoWithSession([&]() { R_ABORT_UNLESS(setsysInitialize()); + R_ABORT_UNLESS(setcalInitialize()); + R_ABORT_UNLESS(pscmInitialize()); R_ABORT_UNLESS(fsInitialize()); }); From cb5a706659f8280bde3706fbe2138576483fffb4 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 10 Feb 2021 06:40:11 -0800 Subject: [PATCH 051/280] htcs: add sf interface info/types --- .../include/stratosphere/htcs.hpp | 1 + .../include/stratosphere/htcs/htcs_types.hpp | 109 ++++++++++++++++++ .../include/stratosphere/sf/sf_buffers.hpp | 4 + .../include/stratosphere/tma.hpp | 1 + .../stratosphere/tma/tma_i_htcs_manager.hpp | 43 +++++++ .../include/stratosphere/tma/tma_i_socket.hpp | 50 ++++++++ 6 files changed, 208 insertions(+) create mode 100644 libraries/libstratosphere/include/stratosphere/htcs/htcs_types.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/tma/tma_i_htcs_manager.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/tma/tma_i_socket.hpp diff --git a/libraries/libstratosphere/include/stratosphere/htcs.hpp b/libraries/libstratosphere/include/stratosphere/htcs.hpp index 999d1a35e..345d42c5d 100644 --- a/libraries/libstratosphere/include/stratosphere/htcs.hpp +++ b/libraries/libstratosphere/include/stratosphere/htcs.hpp @@ -15,3 +15,4 @@ */ #pragma once +#include diff --git a/libraries/libstratosphere/include/stratosphere/htcs/htcs_types.hpp b/libraries/libstratosphere/include/stratosphere/htcs/htcs_types.hpp new file mode 100644 index 000000000..03cb7b893 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htcs/htcs_types.hpp @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htcs { + + using ssize_t = intptr_t; + using AddressFamilyType = u16; + + constexpr inline int PeerNameBufferLength = 32; + constexpr inline int PortNameBufferLength = 32; + + constexpr inline int SocketCountMax = 40; + constexpr inline int FdSetSize = SocketCountMax; + + struct HtcsPeerName { + char name[PeerNameBufferLength]; + }; + + struct HtcsPortName { + char name[PortNameBufferLength]; + }; + + struct SockAddrHtcs { + AddressFamilyType family; + HtcsPeerName peer_name; + HtcsPortName port_name; + }; + + struct TimeVal { + s64 tv_sec; + s64 tv_usec; + }; + + struct FdSet { + int fds[FdSetSize]; + }; + + enum SocketError { + HTCS_ENONE = 0, + HTCS_EACCES = 2, + HTCS_EADDRINUSE = 3, + HTCS_EADDRNOTAVAIL = 4, + HTCS_EAGAIN = 6, + HTCS_EALREADY = 7, + HTCS_EBADF = 8, + HTCS_EBUSY = 10, + HTCS_ECONNABORTED = 13, + HTCS_ECONNREFUSED = 14, + HTCS_ECONNRESET = 15, + HTCS_EDESTADDRREQ = 17, + HTCS_EFAULT = 21, + HTCS_EINPROGRESS = 26, + HTCS_EINTR = 27, + HTCS_EINVAL = 28, + HTCS_EIO = 29, + HTCS_EISCONN = 30, + HTCS_EMFILE = 33, + HTCS_EMSGSIZE = 35, + HTCS_ENETDOWN = 38, + HTCS_ENETRESET = 39, + HTCS_ENOBUFS = 42, + HTCS_ENOMEM = 49, + HTCS_ENOTCONN = 56, + HTCS_ETIMEDOUT = 76, + HTCS_EUNKNOWN = 79, + + HTCS_EWOULDBLOCK = HTCS_EAGAIN, + }; + + enum MessageFlag { + HTCS_MSG_PEEK = 1, + HTCS_MSG_WAITALL = 2, + }; + + enum ShutdownType { + HTCS_SHUT_RD = 0, + HTCS_SHUT_WR = 1, + HTCS_SHUT_RDWR = 2, + }; + + enum FcntlOperation { + HTCS_F_GETFL = 3, + HTCS_F_SETFL = 4, + }; + + enum FcntlFlag { + HTCS_O_NONBLOCK = 4, + }; + + enum AddressFamily { + HTCS_AF_HTCS = 0, + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/sf/sf_buffers.hpp b/libraries/libstratosphere/include/stratosphere/sf/sf_buffers.hpp index 24d9dc526..1ae3ec12c 100644 --- a/libraries/libstratosphere/include/stratosphere/sf/sf_buffers.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf/sf_buffers.hpp @@ -256,6 +256,8 @@ namespace ams::sf { using InNonSecureBuffer = typename impl::InBufferImpl; using InNonDeviceBuffer = typename impl::InBufferImpl; + using InNonSecureAutoSelectBuffer = typename impl::InBufferImpl; + using OutBuffer = typename impl::OutBufferImpl; using OutMapAliasBuffer = typename impl::OutBufferImpl; using OutPointerBuffer = typename impl::OutBufferImpl; @@ -263,6 +265,8 @@ namespace ams::sf { using OutNonSecureBuffer = typename impl::OutBufferImpl; using OutNonDeviceBuffer = typename impl::OutBufferImpl; + using OutNonSecureAutoSelectBuffer = typename impl::OutBufferImpl; + template using InArray = typename impl::InArrayImpl; template diff --git a/libraries/libstratosphere/include/stratosphere/tma.hpp b/libraries/libstratosphere/include/stratosphere/tma.hpp index fbb181ebe..2174693ea 100644 --- a/libraries/libstratosphere/include/stratosphere/tma.hpp +++ b/libraries/libstratosphere/include/stratosphere/tma.hpp @@ -16,3 +16,4 @@ #pragma once #include +#include diff --git a/libraries/libstratosphere/include/stratosphere/tma/tma_i_htcs_manager.hpp b/libraries/libstratosphere/include/stratosphere/tma/tma_i_htcs_manager.hpp new file mode 100644 index 000000000..066a1c597 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/tma/tma_i_htcs_manager.hpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include + +/* NOTE: Minimum firmware version not enforced for any commands. */ +#define AMS_TMA_I_HTCS_MANAGER_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 0, Result, Socket, (sf::Out out_err, sf::Out out_sock), (out_err, out_sock)) \ + AMS_SF_METHOD_INFO(C, H, 1, Result, Close, (sf::Out out_err, sf::Out out_res, s32 desc), (out_err, out_res, desc)) \ + AMS_SF_METHOD_INFO(C, H, 2, Result, Connect, (sf::Out out_err, sf::Out out_res, s32 desc, const htcs::SockAddrHtcs &address), (out_err, out_res, desc, address)) \ + AMS_SF_METHOD_INFO(C, H, 3, Result, Bind, (sf::Out out_err, sf::Out out_res, s32 desc, const htcs::SockAddrHtcs &address), (out_err, out_res, desc, address)) \ + AMS_SF_METHOD_INFO(C, H, 4, Result, Listen, (sf::Out out_err, sf::Out out_res, s32 desc, s32 backlog_count), (out_err, out_res, desc, backlog_count)) \ + AMS_SF_METHOD_INFO(C, H, 5, Result, Accept, (sf::Out out_err, sf::Out out_res, sf::Out out_address, s32 desc), (out_err, out_res, out_address, desc)) \ + AMS_SF_METHOD_INFO(C, H, 6, Result, Recv, (sf::Out out_err, sf::Out out_size, const sf::OutBuffer &buffer, s32 desc, s32 flags), (out_err, out_size, buffer, desc, flags)) \ + AMS_SF_METHOD_INFO(C, H, 7, Result, Send, (sf::Out out_err, sf::Out out_size, s32 desc, const sf::InBuffer &buffer, s32 flags), (out_err, out_size, desc, buffer, flags)) \ + AMS_SF_METHOD_INFO(C, H, 8, Result, Shutdown, (sf::Out out_err, sf::Out out_res, s32 desc, s32 how), (out_err, out_res, desc, how)) \ + AMS_SF_METHOD_INFO(C, H, 9, Result, Fcntl, (sf::Out out_err, sf::Out out_res, s32 desc, s32 command, s32 value), (out_err, out_res, desc, command, value)) \ + AMS_SF_METHOD_INFO(C, H, 10, Result, GetPeerNameAny, (sf::Out out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 11, Result, GetDefaultHostName, (sf::Out out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 12, Result, CreateSocketOld, (sf::Out out_err, sf::Out> out), (out_err, out)) \ + AMS_SF_METHOD_INFO(C, H, 13, Result, CreateSocket, (sf::Out out_err, sf::Out> out, bool enable_disconnection_emulation), (out_err, out, enable_disconnection_emulation)) \ + AMS_SF_METHOD_INFO(C, H, 100, Result, RegisterProcessId, (const sf::ClientProcessId &client_pid), (client_pid)) \ + AMS_SF_METHOD_INFO(C, H, 101, Result, MonitorManager, (const sf::ClientProcessId &client_pid), (client_pid)) \ + AMS_SF_METHOD_INFO(C, H, 130, Result, StartSelect, (sf::Out out_task_id, sf::OutCopyHandle out_event, const sf::InMapAliasArray &read_handles, const sf::InMapAliasArray &write_handles, const sf::InMapAliasArray &exception_handles, s64 tv_sec, s64 tv_usec), (out_task_id, out_event, read_handles, write_handles, exception_handles, tv_sec, tv_usec)) \ + AMS_SF_METHOD_INFO(C, H, 131, Result, EndSelect, (sf::Out out_err, sf::Out out_res, const sf::OutMapAliasArray &read_handles, const sf::OutMapAliasArray &write_handles, const sf::OutMapAliasArray &exception_handles, u32 task_id), (out_err, out_res, read_handles, write_handles, exception_handles, task_id)) + +AMS_SF_DEFINE_INTERFACE(ams::tma, IHtcsManager, AMS_TMA_I_HTCS_MANAGER_INTERFACE_INFO) diff --git a/libraries/libstratosphere/include/stratosphere/tma/tma_i_socket.hpp b/libraries/libstratosphere/include/stratosphere/tma/tma_i_socket.hpp new file mode 100644 index 000000000..42368f3e0 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/tma/tma_i_socket.hpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include + +/* NOTE: Minimum firmware version not enforced for any commands. */ +#define AMS_TMA_I_SOCKET_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 0, Result, Close, (sf::Out out_err, sf::Out out_res), (out_err, out_res)) \ + AMS_SF_METHOD_INFO(C, H, 1, Result, Connect, (sf::Out out_err, sf::Out out_res, const htcs::SockAddrHtcs &address), (out_err, out_res, address)) \ + AMS_SF_METHOD_INFO(C, H, 2, Result, Bind, (sf::Out out_err, sf::Out out_res, const htcs::SockAddrHtcs &address), (out_err, out_res, address)) \ + AMS_SF_METHOD_INFO(C, H, 3, Result, Listen, (sf::Out out_err, sf::Out out_res, s32 backlog_count), (out_err, out_res, backlog_count)) \ + AMS_SF_METHOD_INFO(C, H, 4, Result, Accept, (sf::Out out_err, sf::Out> out, sf::Out out_address), (out_err, out, out_address)) \ + AMS_SF_METHOD_INFO(C, H, 5, Result, Recv, (sf::Out out_err, sf::Out out_size, const sf::OutAutoSelectBuffer &buffer, s32 flags), (out_err, out_size, buffer, flags)) \ + AMS_SF_METHOD_INFO(C, H, 6, Result, Send, (sf::Out out_err, sf::Out out_size, const sf::InAutoSelectBuffer &buffer, s32 flags), (out_err, out_size, buffer, flags)) \ + AMS_SF_METHOD_INFO(C, H, 7, Result, Shutdown, (sf::Out out_err, sf::Out out_res, s32 how), (out_err, out_res, how)) \ + AMS_SF_METHOD_INFO(C, H, 8, Result, Fcntl, (sf::Out out_err, sf::Out out_res, s32 command, s32 value), (out_err, out_res, command, value)) \ + AMS_SF_METHOD_INFO(C, H, 9, Result, AcceptStart, (sf::Out out_task_id, sf::OutCopyHandle out_event), (out_task_id, out_event)) \ + AMS_SF_METHOD_INFO(C, H, 10, Result, AcceptResults, (sf::Out out_err, sf::Out> out, sf::Out out_address, u32 task_id), (out_err, out, out_address, task_id)) \ + AMS_SF_METHOD_INFO(C, H, 11, Result, RecvStart, (sf::Out out_task_id, sf::OutCopyHandle out_event, s32 mem_size, s32 flags), (out_task_id, out_event, mem_size, flags)) \ + AMS_SF_METHOD_INFO(C, H, 12, Result, RecvResults, (sf::Out out_err, sf::Out out_size, const sf::OutAutoSelectBuffer &buffer, u32 task_id), (out_err, out_size, buffer, task_id)) \ + AMS_SF_METHOD_INFO(C, H, 13, Result, RecvLargeStart, (sf::Out out_task_id, sf::OutCopyHandle out_event, s32 unaligned_size_start, s32 unaligned_size_end, s64 aligned_size, sf::CopyHandle mem_handle, s32 flags), (out_task_id, out_event, unaligned_size_start, unaligned_size_end, aligned_size, mem_handle, flags)) \ + AMS_SF_METHOD_INFO(C, H, 14, Result, SendStartOld, (sf::Out out_task_id, sf::OutCopyHandle out_event, const sf::InAutoSelectBuffer &buffer, s32 flags), (out_task_id, out_event, buffer, flags)) \ + AMS_SF_METHOD_INFO(C, H, 15, Result, SendLargeStart, (sf::Out out_task_id, sf::OutCopyHandle out_event, const sf::InAutoSelectBuffer &start_buffer, const sf::InAutoSelectBuffer &end_buffer, sf::CopyHandle mem_handle, s64 aligned_size, s32 flags), (out_task_id, out_event, start_buffer, end_buffer, mem_handle, aligned_size, flags)) \ + AMS_SF_METHOD_INFO(C, H, 16, Result, SendResults, (sf::Out out_err, sf::Out out_size, u32 task_id), (out_err, out_size, task_id)) \ + AMS_SF_METHOD_INFO(C, H, 17, Result, StartSend, (sf::Out out_task_id, sf::OutCopyHandle out_event, sf::Out out_max_size, s64 size, s32 flags), (out_task_id, out_event, out_max_size, size, flags)) \ + AMS_SF_METHOD_INFO(C, H, 18, Result, ContinueSendOld, (sf::Out out_size, sf::Out out_wait, const sf::InAutoSelectBuffer &buffer, u32 task_id), (out_size, out_wait, buffer, task_id)) \ + AMS_SF_METHOD_INFO(C, H, 19, Result, EndSend, (sf::Out out_err, sf::Out out_size, u32 task_id), (out_err, out_size, task_id)) \ + AMS_SF_METHOD_INFO(C, H, 20, Result, StartRecv, (sf::Out out_task_id, sf::OutCopyHandle out_event, s64 size, s32 flags), (out_task_id, out_event, size, flags)) \ + AMS_SF_METHOD_INFO(C, H, 21, Result, EndRecv, (sf::Out out_err, sf::Out out_size, const sf::OutAutoSelectBuffer &buffer, u32 task_id), (out_err, out_size, buffer, task_id)) \ + AMS_SF_METHOD_INFO(C, H, 22, Result, SendStart, (sf::Out out_task_id, sf::OutCopyHandle out_event, const sf::InNonSecureAutoSelectBuffer &buffer, s32 flags), (out_task_id, out_event, buffer, flags)) \ + AMS_SF_METHOD_INFO(C, H, 23, Result, ContinueSend, (sf::Out out_size, sf::Out out_wait, const sf::InNonSecureAutoSelectBuffer &buffer, u32 task_id), (out_size, out_wait, buffer, task_id)) \ + AMS_SF_METHOD_INFO(C, H, 130, Result, GetPrimitive, (sf::Out out), (out)) + +AMS_SF_DEFINE_INTERFACE(ams::tma, ISocket, AMS_TMA_I_SOCKET_INTERFACE_INFO) From 10255f7f5161ebe84b1a14725308b92b8d4cb862 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 10 Feb 2021 18:54:40 -0800 Subject: [PATCH 052/280] htc: skeleton HtcsManagerImpl, implement HtcsMonitor --- .../htclow/htclow_module_types.hpp | 32 ++++++ .../include/stratosphere/htcs.hpp | 2 + .../htcs/impl/htcs_channel_ids.hpp | 24 ++++ .../htcs/impl/htcs_manager_holder.hpp | 33 ++++++ .../htc/server/driver/htc_driver_manager.hpp | 2 + .../source/htc/server/htc_htcmisc_impl.cpp | 12 +- .../source/htc/server/htc_observer.cpp | 10 +- .../htc/server/rpc/htc_htcmisc_rpc_server.cpp | 2 +- .../htc/server/rpc/htc_htcmisc_rpc_server.hpp | 2 +- .../source/htc/server/rpc/htc_rpc_client.cpp | 49 ++++++++- .../source/htc/server/rpc/htc_rpc_client.hpp | 4 +- .../source/htcs/impl/htcs_manager.cpp | 39 +++++++ .../source/htcs/impl/htcs_manager.hpp | 37 +++++++ .../source/htcs/impl/htcs_manager_holder.cpp | 79 ++++++++++++++ .../source/htcs/impl/htcs_manager_impl.cpp | 49 +++++++++ .../source/htcs/impl/htcs_manager_impl.hpp | 46 ++++++++ .../source/htcs/impl/htcs_monitor.cpp | 103 ++++++++++++++++++ .../source/htcs/impl/htcs_monitor.hpp | 73 +++++++++++++ .../source/htcs/impl/htcs_service.hpp | 37 +++++++ .../impl/rpc/htcs_data_channel_manager.hpp | 34 ++++++ 20 files changed, 652 insertions(+), 17 deletions(-) create mode 100644 libraries/libstratosphere/include/stratosphere/htcs/impl/htcs_channel_ids.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/htcs/impl/htcs_manager_holder.hpp create mode 100644 libraries/libstratosphere/source/htcs/impl/htcs_manager.cpp create mode 100644 libraries/libstratosphere/source/htcs/impl/htcs_manager.hpp create mode 100644 libraries/libstratosphere/source/htcs/impl/htcs_manager_holder.cpp create mode 100644 libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.cpp create mode 100644 libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.hpp create mode 100644 libraries/libstratosphere/source/htcs/impl/htcs_monitor.cpp create mode 100644 libraries/libstratosphere/source/htcs/impl/htcs_monitor.hpp create mode 100644 libraries/libstratosphere/source/htcs/impl/htcs_service.hpp create mode 100644 libraries/libstratosphere/source/htcs/impl/rpc/htcs_data_channel_manager.hpp diff --git a/libraries/libstratosphere/include/stratosphere/htclow/htclow_module_types.hpp b/libraries/libstratosphere/include/stratosphere/htclow/htclow_module_types.hpp index 178c61dfa..3f039d153 100644 --- a/libraries/libstratosphere/include/stratosphere/htclow/htclow_module_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/htclow/htclow_module_types.hpp @@ -20,6 +20,8 @@ namespace ams::htclow { enum class ModuleId : u8 { + Unknown = 0, + Htcfs = 1, Htcmisc = 3, @@ -31,4 +33,34 @@ namespace ams::htclow { ModuleId _id; }; + constexpr void InitializeModule(ModuleType *out, ModuleId id) { + *out = { + ._is_initialized = true, + ._id = id, + }; + } + + constexpr void FinalizeModule(ModuleType *out) { + *out = { + ._is_initialized = false, + ._id = ModuleId::Unknown, + }; + } + + class Module final { + private: + ModuleType m_impl; + public: + constexpr explicit Module(ModuleId id) : m_impl() { + InitializeModule(std::addressof(m_impl), id); + } + + constexpr ~Module() { + FinalizeModule(std::addressof(m_impl)); + } + + ModuleType *GetBase() { return std::addressof(m_impl); } + const ModuleType *GetBase() const { return std::addressof(m_impl); } + }; + } diff --git a/libraries/libstratosphere/include/stratosphere/htcs.hpp b/libraries/libstratosphere/include/stratosphere/htcs.hpp index 345d42c5d..59881a770 100644 --- a/libraries/libstratosphere/include/stratosphere/htcs.hpp +++ b/libraries/libstratosphere/include/stratosphere/htcs.hpp @@ -16,3 +16,5 @@ #pragma once #include +#include +#include diff --git a/libraries/libstratosphere/include/stratosphere/htcs/impl/htcs_channel_ids.hpp b/libraries/libstratosphere/include/stratosphere/htcs/impl/htcs_channel_ids.hpp new file mode 100644 index 000000000..7406484ce --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htcs/impl/htcs_channel_ids.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::htcs::impl { + + constexpr inline htclow::ChannelId HtcsClientChannelId = 0; + +} diff --git a/libraries/libstratosphere/include/stratosphere/htcs/impl/htcs_manager_holder.hpp b/libraries/libstratosphere/include/stratosphere/htcs/impl/htcs_manager_holder.hpp new file mode 100644 index 000000000..b9868254c --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htcs/impl/htcs_manager_holder.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::htcs::impl { + + class HtcsManager; + + namespace HtcsManagerHolder { + + void AddReference(); + void Release(); + + HtcsManager *GetHtcsManager(); + + } + +} diff --git a/libraries/libstratosphere/source/htc/server/driver/htc_driver_manager.hpp b/libraries/libstratosphere/source/htc/server/driver/htc_driver_manager.hpp index 35757d778..072d30c9e 100644 --- a/libraries/libstratosphere/source/htc/server/driver/htc_driver_manager.hpp +++ b/libraries/libstratosphere/source/htc/server/driver/htc_driver_manager.hpp @@ -24,6 +24,8 @@ namespace ams::htc::server::driver { IDriver *m_driver; public: DriverManager(IDriver *driver) : m_driver(driver) { /* ... */ } + + IDriver *GetDriver() { return m_driver; } }; } diff --git a/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp b/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp index 5bd78d167..632c0e446 100644 --- a/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp +++ b/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp @@ -104,6 +104,7 @@ namespace ams::htc::server { m_rpc_client.Close(); m_rpc_client.Cancel(); m_rpc_client.Wait(); + this->SetClientConnectionEvent(false); }; /* Wait to become disconnected. */ @@ -111,12 +112,7 @@ namespace ams::htc::server { break; } - /* Set ourselves as disconnected. */ - this->SetClientConnectionEvent(false); } - - /* Set ourselves as disconnected. */ - this->SetClientConnectionEvent(false); } void HtcmiscImpl::ServerThread() { @@ -147,6 +143,7 @@ namespace ams::htc::server { m_rpc_server.Close(); m_rpc_server.Cancel(); m_rpc_server.Wait(); + this->SetServerConnectionEvent(false); }; /* Wait to become disconnected. */ @@ -154,12 +151,7 @@ namespace ams::htc::server { break; } - /* Set ourselves as disconnected. */ - this->SetServerConnectionEvent(false); } - - /* Set ourselves as disconnected. */ - this->SetServerConnectionEvent(false); } void HtcmiscImpl::SetClientConnectionEvent(bool en) { diff --git a/libraries/libstratosphere/source/htc/server/htc_observer.cpp b/libraries/libstratosphere/source/htc/server/htc_observer.cpp index 58fc965bb..c3f6fda84 100644 --- a/libraries/libstratosphere/source/htc/server/htc_observer.cpp +++ b/libraries/libstratosphere/source/htc/server/htc_observer.cpp @@ -15,6 +15,7 @@ */ #include #include "htc_observer.hpp" +#include "../../htcs/impl/htcs_manager.hpp" namespace ams::htc::server { @@ -29,7 +30,7 @@ namespace ams::htc::server { m_is_service_available(false) { /* Initialize htcs library. */ - /* TODO: AMS_ABORT("htcs::impl::HtcsManagerHolder::AddReference();"); */ + htcs::impl::HtcsManagerHolder::AddReference(); /* Update our event state. */ this->UpdateEvent(); @@ -76,10 +77,13 @@ namespace ams::htc::server { this->UpdateEvent(); }; + /* Get the htcs manager. */ + auto * const htcs_manager = htcs::impl::HtcsManagerHolder::GetHtcsManager(); + /* Get the events we're waiting on. */ os::EventType * const stop_event = m_stop_event.GetBase(); os::EventType * const conn_event = m_misc_impl.GetConnectionEvent(); - os::EventType * const htcs_event = nullptr /* TODO: htcs::impl::HtcsManagerHolder::GetHtcsManager()->GetServiceAvailabilityEvent() */; + os::EventType * const htcs_event = htcs_manager->GetServiceAvailabilityEvent(); /* Loop until we're asked to stop. */ while (!m_stopped) { @@ -98,7 +102,7 @@ namespace ams::htc::server { case 2: /* Htcs event, update our service status. */ os::ClearEvent(htcs_event); - m_is_service_available = false /* TODO: htcs::impl::HtcsManagerHolder::GetHtcsManager()->IsServiceAvailable() */; + m_is_service_available = htcs_manager->IsServiceAvailable(); break; AMS_UNREACHABLE_DEFAULT_CASE(); } diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.cpp b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.cpp index 5e28c1255..d0bd22eff 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.cpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.cpp @@ -27,7 +27,7 @@ namespace ams::htc::server::rpc { } HtcmiscRpcServer::HtcmiscRpcServer(driver::IDriver *driver, htclow::ChannelId channel) - : m_00(0), + : m_allocator(nullptr), m_driver(driver), m_channel_id(channel), m_receive_thread_stack(g_receive_thread_stack), diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.hpp index 598559afe..e26679f32 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.hpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.hpp @@ -25,7 +25,7 @@ namespace ams::htc::server::rpc { /* TODO: where is this value coming from, again? */ static constexpr size_t BufferSize = 1_KB; private: - u64 m_00; + mem::StandardAllocator *m_allocator; driver::IDriver *m_driver; htclow::ChannelId m_channel_id; void *m_receive_thread_stack; diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp index 12c91ee74..00cb9b352 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp @@ -33,7 +33,7 @@ namespace ams::htc::server::rpc { } RpcClient::RpcClient(driver::IDriver *driver, htclow::ChannelId channel) - : m_00(0), + : m_allocator(nullptr), m_driver(driver), m_channel_id(channel), m_receive_thread_stack(g_receive_thread_stack), @@ -53,6 +53,53 @@ namespace ams::htc::server::rpc { } } + RpcClient::RpcClient(mem::StandardAllocator *allocator, driver::IDriver *driver, htclow::ChannelId channel) + : m_allocator(allocator), + m_driver(driver), + m_channel_id(channel), + m_receive_thread_stack(m_allocator->Allocate(ThreadStackSize, os::ThreadStackAlignment)), + m_send_thread_stack(m_allocator->Allocate(ThreadStackSize, os::ThreadStackAlignment)), + m_mutex(g_rpc_mutex), + m_task_id_free_list(g_task_id_free_list), + m_task_table(g_task_table), + m_task_active(), + m_task_queue(), + m_cancelled(false), + m_thread_running(false) + { + /* Initialize all events. */ + for (size_t i = 0; i < MaxRpcCount; ++i) { + os::InitializeEvent(std::addressof(m_receive_buffer_available_events[i]), false, os::EventClearMode_AutoClear); + os::InitializeEvent(std::addressof(m_send_buffer_available_events[i]), false, os::EventClearMode_AutoClear); + } + } + + RpcClient::~RpcClient() { + /* Finalize all events. */ + for (size_t i = 0; i < MaxRpcCount; ++i) { + os::FinalizeEvent(std::addressof(m_receive_buffer_available_events[i])); + os::FinalizeEvent(std::addressof(m_send_buffer_available_events[i])); + } + + /* Free the thread stacks. */ + if (m_allocator != nullptr) { + m_allocator->Free(m_receive_thread_stack); + m_allocator->Free(m_send_thread_stack); + } + m_receive_thread_stack = nullptr; + m_send_thread_stack = nullptr; + + /* Free all tasks. */ + for (u32 i = 0; i < MaxRpcCount; ++i) { + if (m_task_active[i]) { + std::scoped_lock lk(m_mutex); + + m_task_table.Delete(i); + m_task_id_free_list.Free(i); + } + } + } + void RpcClient::Open() { R_ABORT_UNLESS(m_driver->Open(m_channel_id)); } diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp index 5b7369b11..1d8bf9fa3 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp @@ -27,7 +27,7 @@ namespace ams::htc::server::rpc { /* TODO: where is this value coming from, again? */ static constexpr size_t BufferSize = 0xE400; private: - u64 m_00; + mem::StandardAllocator *m_allocator; driver::IDriver *m_driver; htclow::ChannelId m_channel_id; void *m_receive_thread_stack; @@ -53,6 +53,8 @@ namespace ams::htc::server::rpc { Result SendThread(); public: RpcClient(driver::IDriver *driver, htclow::ChannelId channel); + RpcClient(mem::StandardAllocator *allocator, driver::IDriver *driver, htclow::ChannelId channel); + ~RpcClient(); public: void Open(); void Close(); diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_manager.cpp b/libraries/libstratosphere/source/htcs/impl/htcs_manager.cpp new file mode 100644 index 000000000..d6c6360ea --- /dev/null +++ b/libraries/libstratosphere/source/htcs/impl/htcs_manager.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcs_manager.hpp" +#include "htcs_manager_impl.hpp" + +namespace ams::htcs::impl { + + HtcsManager::HtcsManager(mem::StandardAllocator *allocator, htclow::HtclowManager *htclow_manager) : m_allocator(allocator), m_impl(static_cast(allocator->Allocate(sizeof(HtcsManagerImpl), alignof(HtcsManagerImpl)))) { + std::construct_at(m_impl, m_allocator, htclow_manager); + } + + HtcsManager::~HtcsManager() { + std::destroy_at(m_impl); + m_allocator->Free(m_impl); + } + + os::EventType *HtcsManager::GetServiceAvailabilityEvent() { + return m_impl->GetServiceAvailabilityEvent(); + } + + bool HtcsManager::IsServiceAvailable() { + return m_impl->IsServiceAvailable(); + } + +} diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_manager.hpp b/libraries/libstratosphere/source/htcs/impl/htcs_manager.hpp new file mode 100644 index 000000000..433fc8b7f --- /dev/null +++ b/libraries/libstratosphere/source/htcs/impl/htcs_manager.hpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "../../htclow/htclow_manager.hpp" + +namespace ams::htcs::impl { + + class HtcsManagerImpl; + + class HtcsManager { + private: + mem::StandardAllocator *m_allocator; + HtcsManagerImpl *m_impl; + public: + HtcsManager(mem::StandardAllocator *allocator, htclow::HtclowManager *htclow_manager); + ~HtcsManager(); + public: + os::EventType *GetServiceAvailabilityEvent(); + + bool IsServiceAvailable(); + }; + +} diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_manager_holder.cpp b/libraries/libstratosphere/source/htcs/impl/htcs_manager_holder.cpp new file mode 100644 index 000000000..1091e3288 --- /dev/null +++ b/libraries/libstratosphere/source/htcs/impl/htcs_manager_holder.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcs_manager.hpp" + +namespace ams::htcs::impl::HtcsManagerHolder { + + namespace { + + constinit os::SdkMutex g_holder_mutex; + constinit int g_holder_reference_count = 0; + + mem::StandardAllocator g_allocator; + + constinit HtcsManager *g_manager = nullptr; + + alignas(os::MemoryPageSize) u8 g_heap_buffer[416_KB]; + + } + + void AddReference() { + std::scoped_lock lk(g_holder_mutex); + + if ((g_holder_reference_count++) == 0) { + /* Add reference to the htclow manager. */ + htclow::HtclowManagerHolder::AddReference(); + + /* Initialize the allocator for the manager. */ + g_allocator.Initialize(g_heap_buffer, sizeof(g_heap_buffer)); + + /* Allocate the manager. */ + g_manager = static_cast(g_allocator.Allocate(sizeof(HtcsManager), alignof(HtcsManager))); + + /* Construct the manager. */ + std::construct_at(g_manager, std::addressof(g_allocator), htclow::HtclowManagerHolder::GetHtclowManager()); + } + + AMS_ASSERT(g_holder_reference_count > 0); + } + + void Release() { + std::scoped_lock lk(g_holder_mutex); + + AMS_ASSERT(g_holder_reference_count > 0); + + if ((--g_holder_reference_count) == 0) { + /* Destroy the manager. */ + std::destroy_at(g_manager); + g_allocator.Free(g_manager); + g_manager = nullptr; + + /* Finalize the allocator. */ + g_allocator.Finalize(); + + /* Release reference to the htclow manager. */ + htclow::HtclowManagerHolder::Release(); + } + } + + HtcsManager *GetHtcsManager() { + std::scoped_lock lk(g_holder_mutex); + + return g_manager; + } + +} diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.cpp b/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.cpp new file mode 100644 index 000000000..d6c71fb20 --- /dev/null +++ b/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcs_manager.hpp" +#include "htcs_manager_impl.hpp" + +namespace ams::htcs::impl { + + HtcsManagerImpl::HtcsManagerImpl(mem::StandardAllocator *allocator, htclow::HtclowManager *htclow_manager) + : m_allocator(allocator), + m_driver(htclow_manager, htclow::ModuleId::Htcs), + m_driver_manager(std::addressof(m_driver)), + m_rpc_client(m_allocator, std::addressof(m_driver), HtcsClientChannelId), + m_data_channel_manager(std::addressof(m_rpc_client), htclow_manager), + m_service(m_allocator, m_driver_manager.GetDriver(), std::addressof(m_rpc_client), std::addressof(m_data_channel_manager)), + m_monitor(m_allocator, m_driver_manager.GetDriver(), std::addressof(m_rpc_client), std::addressof(m_service)) + { + /* Start the monitor. */ + m_monitor.Start(); + } + + HtcsManagerImpl::~HtcsManagerImpl() { + /* Cancel our monitor. */ + m_monitor.Cancel(); + m_monitor.Wait(); + } + + os::EventType *HtcsManagerImpl::GetServiceAvailabilityEvent() { + return m_monitor.GetServiceAvailabilityEvent(); + } + + bool HtcsManagerImpl::IsServiceAvailable() { + return m_monitor.IsServiceAvailable(); + } + +} diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.hpp b/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.hpp new file mode 100644 index 000000000..38c0916ec --- /dev/null +++ b/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.hpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "../../htclow/htclow_manager.hpp" +#include "../../htc/server/driver/htc_htclow_driver.hpp" +#include "../../htc/server/driver/htc_driver_manager.hpp" +#include "../../htc/server/rpc/htc_rpc_client.hpp" +#include "rpc/htcs_data_channel_manager.hpp" +#include "htcs_service.hpp" +#include "htcs_monitor.hpp" + +namespace ams::htcs::impl { + + class HtcsManagerImpl { + private: + mem::StandardAllocator *m_allocator; + htc::server::driver::HtclowDriver m_driver; + htc::server::driver::DriverManager m_driver_manager; + htc::server::rpc::RpcClient m_rpc_client; + rpc::DataChannelManager m_data_channel_manager; + HtcsService m_service; + HtcsMonitor m_monitor; + public: + HtcsManagerImpl(mem::StandardAllocator *allocator, htclow::HtclowManager *htclow_manager); + ~HtcsManagerImpl(); + public: + os::EventType *GetServiceAvailabilityEvent(); + + bool IsServiceAvailable(); + }; + +} diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_monitor.cpp b/libraries/libstratosphere/source/htcs/impl/htcs_monitor.cpp new file mode 100644 index 000000000..cd949983b --- /dev/null +++ b/libraries/libstratosphere/source/htcs/impl/htcs_monitor.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcs_manager.hpp" +#include "htcs_manager_impl.hpp" + +namespace ams::htcs::impl { + + HtcsMonitor::HtcsMonitor(mem::StandardAllocator *allocator, htc::server::driver::IDriver *drv, htc::server::rpc::RpcClient *rc, HtcsService *srv) + : m_allocator(allocator), + m_driver(drv), + m_rpc_client(rc), + m_service(srv), + m_monitor_thread_stack(m_allocator->Allocate(os::MemoryPageSize, os::ThreadStackAlignment)), + m_mutex(), + m_cancel_event(os::EventClearMode_ManualClear), + m_service_availability_event(os::EventClearMode_ManualClear), + m_cancelled(false), + m_is_service_available(false) + { + /* ... */ + } + + HtcsMonitor::~HtcsMonitor() { + /* Free thread stack. */ + m_allocator->Free(m_monitor_thread_stack); + } + + void HtcsMonitor::Start() { + /* Create the monitor thread. */ + R_ABORT_UNLESS(os::CreateThread(std::addressof(m_monitor_thread), ThreadEntry, this, m_monitor_thread_stack, os::MemoryPageSize, AMS_GET_SYSTEM_THREAD_PRIORITY(htc, HtcsMonitor))); + + /* Set thread name. */ + os::SetThreadNamePointer(std::addressof(m_monitor_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, HtcsMonitor)); + + /* Start the monitor thread. */ + os::StartThread(std::addressof(m_monitor_thread)); + } + + void HtcsMonitor::Cancel() { + /* Cancel, and signal. */ + m_cancelled = true; + m_cancel_event.Signal(); + } + + void HtcsMonitor::Wait() { + /* Wait for the thread. */ + os::WaitThread(std::addressof(m_monitor_thread)); + os::DestroyThread(std::addressof(m_monitor_thread)); + } + + void HtcsMonitor::ThreadBody() { + /* Loop so long as we're not cancelled. */ + while (!m_cancelled) { + /* Open the rpc client. */ + m_rpc_client->Open(); + + /* Ensure we close, if something goes wrong. */ + auto client_guard = SCOPE_GUARD { m_rpc_client->Close(); }; + + /* Wait for the rpc server. */ + if (m_rpc_client->WaitAny(htclow::ChannelState_Connectable, m_cancel_event.GetBase()) != 0) { + break; + } + + /* Start the rpc client. */ + if (R_FAILED(m_rpc_client->Start())) { + continue; + } + + /* We're available! */ + this->SetServiceAvailability(true); + client_guard.Cancel(); + + /* We're available, so we want to cleanup when we're done. */ + ON_SCOPE_EXIT { + m_rpc_client->Close(); + m_rpc_client->Cancel(); + m_rpc_client->Wait(); + this->SetServiceAvailability(false); + }; + + /* Wait to become disconnected. */ + if (m_rpc_client->WaitAny(htclow::ChannelState_Disconnected, m_cancel_event.GetBase()) != 0) { + break; + } + } + } + +} diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_monitor.hpp b/libraries/libstratosphere/source/htcs/impl/htcs_monitor.hpp new file mode 100644 index 000000000..be215e5e2 --- /dev/null +++ b/libraries/libstratosphere/source/htcs/impl/htcs_monitor.hpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "../../htc/server/driver/htc_i_driver.hpp" +#include "../../htc/server/rpc/htc_rpc_client.hpp" +#include "htcs_service.hpp" + +namespace ams::htcs::impl { + + class HtcsMonitor { + private: + mem::StandardAllocator *m_allocator; + htc::server::driver::IDriver *m_driver; + htc::server::rpc::RpcClient *m_rpc_client; + HtcsService *m_service; + void *m_monitor_thread_stack; + os::ThreadType m_monitor_thread; + os::SdkMutex m_mutex; + os::Event m_cancel_event; + os::Event m_service_availability_event; + bool m_cancelled; + bool m_is_service_available; + private: + static void ThreadEntry(void *arg) { + static_cast(arg)->ThreadBody(); + } + + void ThreadBody(); + public: + HtcsMonitor(mem::StandardAllocator *allocator, htc::server::driver::IDriver *drv, htc::server::rpc::RpcClient *rc, HtcsService *srv); + ~HtcsMonitor(); + public: + void Start(); + void Cancel(); + void Wait(); + + os::EventType *GetServiceAvailabilityEvent() { return m_service_availability_event.GetBase(); } + + bool IsServiceAvailable() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Get availability. */ + return m_is_service_available; + } + private: + void SetServiceAvailability(bool available) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Set availability. */ + m_is_service_available = available; + + /* Signal availability change. */ + m_service_availability_event.Signal(); + } + }; + +} diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_service.hpp b/libraries/libstratosphere/source/htcs/impl/htcs_service.hpp new file mode 100644 index 000000000..9968b81ec --- /dev/null +++ b/libraries/libstratosphere/source/htcs/impl/htcs_service.hpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "../../htc/server/driver/htc_i_driver.hpp" +#include "../../htc/server/rpc/htc_rpc_client.hpp" +#include "rpc/htcs_data_channel_manager.hpp" + +namespace ams::htcs::impl { + + class HtcsService { + private: + mem::StandardAllocator *m_allocator; + htc::server::driver::IDriver *m_driver; + htc::server::rpc::RpcClient *m_rpc_client; + rpc::DataChannelManager *m_data_channel_manager; + public: + HtcsService(mem::StandardAllocator *allocator, htc::server::driver::IDriver *drv, htc::server::rpc::RpcClient *rc, rpc::DataChannelManager *dcm) + : m_allocator(allocator), m_driver(drv), m_rpc_client(rc), m_data_channel_manager(dcm) { /* ... */ } + public: + /* TODO */ + }; + +} diff --git a/libraries/libstratosphere/source/htcs/impl/rpc/htcs_data_channel_manager.hpp b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_data_channel_manager.hpp new file mode 100644 index 000000000..7d683e523 --- /dev/null +++ b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_data_channel_manager.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "../../../htclow/htclow_manager.hpp" +#include "../../../htc/server/rpc/htc_rpc_client.hpp" + +namespace ams::htcs::impl::rpc { + + class DataChannelManager { + private: + htc::server::rpc::RpcClient* m_rpc_client; + htclow::HtclowManager *m_htclow_manager; + htclow::Module m_module; + public: + DataChannelManager(htc::server::rpc::RpcClient *client, htclow::HtclowManager *htclow_manager) : m_rpc_client(client), m_htclow_manager(htclow_manager), m_module(htclow::ModuleId::Htcs) { /* ... */ } + public: + /* TODO */ + }; + +} From b8982411125b3e28d3f6d03cca838ade5babda65 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 10 Feb 2021 19:36:35 -0800 Subject: [PATCH 053/280] htcs: add hipc server/service object skeletons --- .../include/stratosphere/htcs.hpp | 1 + .../htcs/server/htcs_hipc_server.hpp | 26 ++++ .../source/htcs/server/htcs_hipc_server.cpp | 58 ++++++++ .../server/htcs_manager_service_object.cpp | 71 ++++++++++ .../server/htcs_manager_service_object.hpp | 44 ++++++ ...htcs_manager_service_object_reprecated.cpp | 74 ++++++++++ .../server/htcs_socket_service_object.cpp | 129 ++++++++++++++++++ .../server/htcs_socket_service_object.hpp | 58 ++++++++ stratosphere/htc/source/htc_main.cpp | 6 +- 9 files changed, 464 insertions(+), 3 deletions(-) create mode 100644 libraries/libstratosphere/include/stratosphere/htcs/server/htcs_hipc_server.hpp create mode 100644 libraries/libstratosphere/source/htcs/server/htcs_hipc_server.cpp create mode 100644 libraries/libstratosphere/source/htcs/server/htcs_manager_service_object.cpp create mode 100644 libraries/libstratosphere/source/htcs/server/htcs_manager_service_object.hpp create mode 100644 libraries/libstratosphere/source/htcs/server/htcs_manager_service_object_reprecated.cpp create mode 100644 libraries/libstratosphere/source/htcs/server/htcs_socket_service_object.cpp create mode 100644 libraries/libstratosphere/source/htcs/server/htcs_socket_service_object.hpp diff --git a/libraries/libstratosphere/include/stratosphere/htcs.hpp b/libraries/libstratosphere/include/stratosphere/htcs.hpp index 59881a770..cda430983 100644 --- a/libraries/libstratosphere/include/stratosphere/htcs.hpp +++ b/libraries/libstratosphere/include/stratosphere/htcs.hpp @@ -18,3 +18,4 @@ #include #include #include +#include diff --git a/libraries/libstratosphere/include/stratosphere/htcs/server/htcs_hipc_server.hpp b/libraries/libstratosphere/include/stratosphere/htcs/server/htcs_hipc_server.hpp new file mode 100644 index 000000000..20c39070a --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htcs/server/htcs_hipc_server.hpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htcs::server { + + void Initialize(); + + void RegisterHipcServer(); + void LoopHipcServer(); + +} diff --git a/libraries/libstratosphere/source/htcs/server/htcs_hipc_server.cpp b/libraries/libstratosphere/source/htcs/server/htcs_hipc_server.cpp new file mode 100644 index 000000000..26f4301f3 --- /dev/null +++ b/libraries/libstratosphere/source/htcs/server/htcs_hipc_server.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcs_manager_service_object.hpp" + +namespace ams::htcs::server { + + namespace { + + static constexpr inline size_t NumServers = 1; + static constexpr inline size_t MaxSessions = 63; + static constexpr inline sm::ServiceName ServiceName = sm::ServiceName::Encode("htcs"); + + struct ServerOptions { + static constexpr size_t PointerBufferSize = 0x80; + static constexpr size_t MaxDomains = 0x10; + static constexpr size_t MaxDomainObjects = 100; + }; + + using ServerManager = sf::hipc::ServerManager; + + /* Service object. */ + ServerManager g_server_manager; + + /* Service object. */ + constinit sf::UnmanagedServiceObject g_htcs_service_object; + + } + + void Initialize() { + /* Add a reference to the htcs manager. */ + htcs::impl::HtcsManagerHolder::AddReference(); + } + + void RegisterHipcServer() { + /* Register the service. */ + R_ABORT_UNLESS(g_server_manager.RegisterObjectForServer(g_htcs_service_object.GetShared(), ServiceName, MaxSessions)); + } + + void LoopHipcServer() { + /* Loop, servicing services. */ + g_server_manager.LoopProcess(); + } + +} diff --git a/libraries/libstratosphere/source/htcs/server/htcs_manager_service_object.cpp b/libraries/libstratosphere/source/htcs/server/htcs_manager_service_object.cpp new file mode 100644 index 000000000..a01a6df4f --- /dev/null +++ b/libraries/libstratosphere/source/htcs/server/htcs_manager_service_object.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcs_manager_service_object.hpp" +#include "htcs_socket_service_object.hpp" + +namespace ams::htcs::server { + + namespace { + + struct ServiceObjectAllocatorTag; + using ServiceObjectAllocator = ams::sf::ExpHeapStaticAllocator<32_KB, ServiceObjectAllocatorTag>; + using ServiceObjectFactory = ams::sf::ObjectFactory; + + class StaticAllocatorInitializer { + public: + StaticAllocatorInitializer() { + ServiceObjectAllocator::Initialize(lmem::CreateOption_ThreadSafe); + } + } g_static_allocator_initializer; + + } + + Result ManagerServiceObject::GetPeerNameAny(sf::Out out) { + AMS_ABORT("ManagerServiceObject::GetPeerNameAny"); + } + + Result ManagerServiceObject::GetDefaultHostName(sf::Out out) { + AMS_ABORT("ManagerServiceObject::GetDefaultHostName"); + } + + Result ManagerServiceObject::CreateSocketOld(sf::Out out_err, sf::Out> out) { + return this->CreateSocket(out_err, out, false); + } + + Result ManagerServiceObject::CreateSocket(sf::Out out_err, sf::Out> out, bool enable_disconnection_emulation) { + AMS_ABORT("ManagerServiceObject::CreateSocket"); + } + + Result ManagerServiceObject::RegisterProcessId(const sf::ClientProcessId &client_pid) { + /* NOTE: Nintend does nothing here. */ + return ResultSuccess(); + } + + Result ManagerServiceObject::MonitorManager(const sf::ClientProcessId &client_pid) { + /* NOTE: Nintend does nothing here. */ + return ResultSuccess(); + } + + Result ManagerServiceObject::StartSelect(sf::Out out_task_id, sf::OutCopyHandle out_event, const sf::InMapAliasArray &read_handles, const sf::InMapAliasArray &write_handles, const sf::InMapAliasArray &exception_handles, s64 tv_sec, s64 tv_usec) { + AMS_ABORT("ManagerServiceObject::StartSelect"); + } + + Result ManagerServiceObject::EndSelect(sf::Out out_err, sf::Out out_res, const sf::OutMapAliasArray &read_handles, const sf::OutMapAliasArray &write_handles, const sf::OutMapAliasArray &exception_handles, u32 task_id) { + AMS_ABORT("ManagerServiceObject::EndSelect"); + } + +} diff --git a/libraries/libstratosphere/source/htcs/server/htcs_manager_service_object.hpp b/libraries/libstratosphere/source/htcs/server/htcs_manager_service_object.hpp new file mode 100644 index 000000000..43e62d5b1 --- /dev/null +++ b/libraries/libstratosphere/source/htcs/server/htcs_manager_service_object.hpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htcs::server { + + class ManagerServiceObject : public sf::ISharedObject { + public: + Result Socket(sf::Out out_err, sf::Out out_sock); + Result Close(sf::Out out_err, sf::Out out_res, s32 desc); + Result Connect(sf::Out out_err, sf::Out out_res, s32 desc, const htcs::SockAddrHtcs &address); + Result Bind(sf::Out out_err, sf::Out out_res, s32 desc, const htcs::SockAddrHtcs &address); + Result Listen(sf::Out out_err, sf::Out out_res, s32 desc, s32 backlog_count); + Result Accept(sf::Out out_err, sf::Out out_res, sf::Out out_address, s32 desc); + Result Recv(sf::Out out_err, sf::Out out_size, const sf::OutBuffer &buffer, s32 desc, s32 flags); + Result Send(sf::Out out_err, sf::Out out_size, s32 desc, const sf::InBuffer &buffer, s32 flags); + Result Shutdown(sf::Out out_err, sf::Out out_res, s32 desc, s32 how); + Result Fcntl(sf::Out out_err, sf::Out out_res, s32 desc, s32 command, s32 value); + Result GetPeerNameAny(sf::Out out); + Result GetDefaultHostName(sf::Out out); + Result CreateSocketOld(sf::Out out_err, sf::Out> out); + Result CreateSocket(sf::Out out_err, sf::Out> out, bool enable_disconnection_emulation); + Result RegisterProcessId(const sf::ClientProcessId &client_pid); + Result MonitorManager(const sf::ClientProcessId &client_pid); + Result StartSelect(sf::Out out_task_id, sf::OutCopyHandle out_event, const sf::InMapAliasArray &read_handles, const sf::InMapAliasArray &write_handles, const sf::InMapAliasArray &exception_handles, s64 tv_sec, s64 tv_usec); + Result EndSelect(sf::Out out_err, sf::Out out_res, const sf::OutMapAliasArray &read_handles, const sf::OutMapAliasArray &write_handles, const sf::OutMapAliasArray &exception_handles, u32 task_id); + }; + static_assert(tma::IsIHtcsManager); + +} diff --git a/libraries/libstratosphere/source/htcs/server/htcs_manager_service_object_reprecated.cpp b/libraries/libstratosphere/source/htcs/server/htcs_manager_service_object_reprecated.cpp new file mode 100644 index 000000000..43dc60c0c --- /dev/null +++ b/libraries/libstratosphere/source/htcs/server/htcs_manager_service_object_reprecated.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcs_manager_service_object.hpp" +#include "htcs_socket_service_object.hpp" + +namespace ams::htcs::server { + + #define AMS_HTCS_MANAGER_DEPRECATED_API() AMS_ABORT("Deprecated IHtcsManager API %s was called.\n", AMS_CURRENT_FUNCTION_NAME) + + Result ManagerServiceObject::Socket(sf::Out out_err, sf::Out out_sock) { + /* NOTE: This is a deprecated API, and Nintendo aborts when it is called. */ + AMS_HTCS_MANAGER_DEPRECATED_API(); + } + + Result ManagerServiceObject::Close(sf::Out out_err, sf::Out out_res, s32 desc) { + /* NOTE: This is a deprecated API, and Nintendo aborts when it is called. */ + AMS_HTCS_MANAGER_DEPRECATED_API(); + } + + Result ManagerServiceObject::Connect(sf::Out out_err, sf::Out out_res, s32 desc, const htcs::SockAddrHtcs &address) { + /* NOTE: This is a deprecated API, and Nintendo aborts when it is called. */ + AMS_HTCS_MANAGER_DEPRECATED_API(); + } + + Result ManagerServiceObject::Bind(sf::Out out_err, sf::Out out_res, s32 desc, const htcs::SockAddrHtcs &address) { + /* NOTE: This is a deprecated API, and Nintendo aborts when it is called. */ + AMS_HTCS_MANAGER_DEPRECATED_API(); + } + + Result ManagerServiceObject::Listen(sf::Out out_err, sf::Out out_res, s32 desc, s32 backlog_count) { + /* NOTE: This is a deprecated API, and Nintendo aborts when it is called. */ + AMS_HTCS_MANAGER_DEPRECATED_API(); + } + + Result ManagerServiceObject::Accept(sf::Out out_err, sf::Out out_res, sf::Out out_address, s32 desc) { + /* NOTE: This is a deprecated API, and Nintendo aborts when it is called. */ + AMS_HTCS_MANAGER_DEPRECATED_API(); + } + + Result ManagerServiceObject::Recv(sf::Out out_err, sf::Out out_size, const sf::OutBuffer &buffer, s32 desc, s32 flags) { + /* NOTE: This is a deprecated API, and Nintendo aborts when it is called. */ + AMS_HTCS_MANAGER_DEPRECATED_API(); + } + + Result ManagerServiceObject::Send(sf::Out out_err, sf::Out out_size, s32 desc, const sf::InBuffer &buffer, s32 flags) { + /* NOTE: This is a deprecated API, and Nintendo aborts when it is called. */ + AMS_HTCS_MANAGER_DEPRECATED_API(); + } + + Result ManagerServiceObject::Shutdown(sf::Out out_err, sf::Out out_res, s32 desc, s32 how) { + /* NOTE: This is a deprecated API, and Nintendo aborts when it is called. */ + AMS_HTCS_MANAGER_DEPRECATED_API(); + } + + Result ManagerServiceObject::Fcntl(sf::Out out_err, sf::Out out_res, s32 desc, s32 command, s32 value) { + /* NOTE: This is a deprecated API, and Nintendo aborts when it is called. */ + AMS_HTCS_MANAGER_DEPRECATED_API(); + } + +} diff --git a/libraries/libstratosphere/source/htcs/server/htcs_socket_service_object.cpp b/libraries/libstratosphere/source/htcs/server/htcs_socket_service_object.cpp new file mode 100644 index 000000000..ebf277520 --- /dev/null +++ b/libraries/libstratosphere/source/htcs/server/htcs_socket_service_object.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcs_socket_service_object.hpp" + +namespace ams::htcs::server { + + SocketServiceObject::SocketServiceObject(ManagerServiceObject *manager, s32 desc) : m_manager(manager, true), m_desc(desc) { + /* ... */ + } + + SocketServiceObject::~SocketServiceObject() { + AMS_ABORT("SocketServiceObject::~SocketServiceObject"); + } + + Result SocketServiceObject::Close(sf::Out out_err, sf::Out out_res) { + AMS_ABORT("SocketServiceObject::Close"); + } + + Result SocketServiceObject::Connect(sf::Out out_err, sf::Out out_res, const htcs::SockAddrHtcs &address) { + AMS_ABORT("SocketServiceObject::Connect"); + } + + Result SocketServiceObject::Bind(sf::Out out_err, sf::Out out_res, const htcs::SockAddrHtcs &address) { + AMS_ABORT("SocketServiceObject::Bind"); + } + + Result SocketServiceObject::Listen(sf::Out out_err, sf::Out out_res, s32 backlog_count) { + AMS_ABORT("SocketServiceObject::Listen"); + } + + Result SocketServiceObject::Accept(sf::Out out_err, sf::Out> out, sf::Out out_address) { + AMS_ABORT("SocketServiceObject::Accept"); + } + + Result SocketServiceObject::Recv(sf::Out out_err, sf::Out out_size, const sf::OutAutoSelectBuffer &buffer, s32 flags) { + AMS_ABORT("SocketServiceObject::Recv"); + } + + Result SocketServiceObject::Send(sf::Out out_err, sf::Out out_size, const sf::InAutoSelectBuffer &buffer, s32 flags) { + AMS_ABORT("SocketServiceObject::Send"); + } + + Result SocketServiceObject::Shutdown(sf::Out out_err, sf::Out out_res, s32 how) { + AMS_ABORT("SocketServiceObject::Shutdown"); + } + + Result SocketServiceObject::Fcntl(sf::Out out_err, sf::Out out_res, s32 command, s32 value) { + AMS_ABORT("SocketServiceObject::Fcntl"); + } + + Result SocketServiceObject::AcceptStart(sf::Out out_task_id, sf::OutCopyHandle out_event) { + AMS_ABORT("SocketServiceObject::AcceptStart"); + } + + Result SocketServiceObject::AcceptResults(sf::Out out_err, sf::Out> out, sf::Out out_address, u32 task_id) { + AMS_ABORT("SocketServiceObject::AcceptResults"); + } + + Result SocketServiceObject::RecvStart(sf::Out out_task_id, sf::OutCopyHandle out_event, s32 mem_size, s32 flags) { + AMS_ABORT("SocketServiceObject::RecvStart"); + } + + Result SocketServiceObject::RecvResults(sf::Out out_err, sf::Out out_size, const sf::OutAutoSelectBuffer &buffer, u32 task_id) { + AMS_ABORT("SocketServiceObject::RecvResults"); + } + + Result SocketServiceObject::RecvLargeStart(sf::Out out_task_id, sf::OutCopyHandle out_event, s32 unaligned_size_start, s32 unaligned_size_end, s64 aligned_size, sf::CopyHandle mem_handle, s32 flags) { + AMS_ABORT("SocketServiceObject::RecvLargeStart"); + } + + Result SocketServiceObject::SendStartOld(sf::Out out_task_id, sf::OutCopyHandle out_event, const sf::InAutoSelectBuffer &buffer, s32 flags) { + AMS_ABORT("SocketServiceObject::SendStartOld"); + } + + Result SocketServiceObject::SendLargeStart(sf::Out out_task_id, sf::OutCopyHandle out_event, const sf::InAutoSelectBuffer &start_buffer, const sf::InAutoSelectBuffer &end_buffer, sf::CopyHandle mem_handle, s64 aligned_size, s32 flags) { + AMS_ABORT("SocketServiceObject::SendLargeStart"); + } + + Result SocketServiceObject::SendResults(sf::Out out_err, sf::Out out_size, u32 task_id) { + AMS_ABORT("SocketServiceObject::SendResults"); + } + + Result SocketServiceObject::StartSend(sf::Out out_task_id, sf::OutCopyHandle out_event, sf::Out out_max_size, s64 size, s32 flags) { + AMS_ABORT("SocketServiceObject::StartSend"); + } + + Result SocketServiceObject::ContinueSendOld(sf::Out out_size, sf::Out out_wait, const sf::InAutoSelectBuffer &buffer, u32 task_id) { + AMS_ABORT("SocketServiceObject::ContinueSendOld"); + } + + Result SocketServiceObject::EndSend(sf::Out out_err, sf::Out out_size, u32 task_id) { + AMS_ABORT("SocketServiceObject::EndSend"); + } + + Result SocketServiceObject::StartRecv(sf::Out out_task_id, sf::OutCopyHandle out_event, s64 size, s32 flags) { + AMS_ABORT("SocketServiceObject::StartRecv"); + } + + Result SocketServiceObject::EndRecv(sf::Out out_err, sf::Out out_size, const sf::OutAutoSelectBuffer &buffer, u32 task_id) { + AMS_ABORT("SocketServiceObject::EndRecv"); + } + + Result SocketServiceObject::SendStart(sf::Out out_task_id, sf::OutCopyHandle out_event, const sf::InNonSecureAutoSelectBuffer &buffer, s32 flags) { + AMS_ABORT("SocketServiceObject::SendStart"); + } + + Result SocketServiceObject::ContinueSend(sf::Out out_size, sf::Out out_wait, const sf::InNonSecureAutoSelectBuffer &buffer, u32 task_id) { + AMS_ABORT("SocketServiceObject::ContinueSend"); + } + + Result SocketServiceObject::GetPrimitive(sf::Out out) { + AMS_ABORT("SocketServiceObject::GetPrimitive"); + } + +} diff --git a/libraries/libstratosphere/source/htcs/server/htcs_socket_service_object.hpp b/libraries/libstratosphere/source/htcs/server/htcs_socket_service_object.hpp new file mode 100644 index 000000000..043af4030 --- /dev/null +++ b/libraries/libstratosphere/source/htcs/server/htcs_socket_service_object.hpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "htcs_manager_service_object.hpp" + +namespace ams::htcs::server { + + class SocketServiceObject { + private: + sf::SharedPointer m_manager; + s32 m_desc; + public: + SocketServiceObject(ManagerServiceObject *manager, s32 desc); + ~SocketServiceObject(); + public: + Result Close(sf::Out out_err, sf::Out out_res); + Result Connect(sf::Out out_err, sf::Out out_res, const htcs::SockAddrHtcs &address); + Result Bind(sf::Out out_err, sf::Out out_res, const htcs::SockAddrHtcs &address); + Result Listen(sf::Out out_err, sf::Out out_res, s32 backlog_count); + Result Accept(sf::Out out_err, sf::Out> out, sf::Out out_address); + Result Recv(sf::Out out_err, sf::Out out_size, const sf::OutAutoSelectBuffer &buffer, s32 flags); + Result Send(sf::Out out_err, sf::Out out_size, const sf::InAutoSelectBuffer &buffer, s32 flags); + Result Shutdown(sf::Out out_err, sf::Out out_res, s32 how); + Result Fcntl(sf::Out out_err, sf::Out out_res, s32 command, s32 value); + Result AcceptStart(sf::Out out_task_id, sf::OutCopyHandle out_event); + Result AcceptResults(sf::Out out_err, sf::Out> out, sf::Out out_address, u32 task_id); + Result RecvStart(sf::Out out_task_id, sf::OutCopyHandle out_event, s32 mem_size, s32 flags); + Result RecvResults(sf::Out out_err, sf::Out out_size, const sf::OutAutoSelectBuffer &buffer, u32 task_id); + Result RecvLargeStart(sf::Out out_task_id, sf::OutCopyHandle out_event, s32 unaligned_size_start, s32 unaligned_size_end, s64 aligned_size, sf::CopyHandle mem_handle, s32 flags); + Result SendStartOld(sf::Out out_task_id, sf::OutCopyHandle out_event, const sf::InAutoSelectBuffer &buffer, s32 flags); + Result SendLargeStart(sf::Out out_task_id, sf::OutCopyHandle out_event, const sf::InAutoSelectBuffer &start_buffer, const sf::InAutoSelectBuffer &end_buffer, sf::CopyHandle mem_handle, s64 aligned_size, s32 flags); + Result SendResults(sf::Out out_err, sf::Out out_size, u32 task_id); + Result StartSend(sf::Out out_task_id, sf::OutCopyHandle out_event, sf::Out out_max_size, s64 size, s32 flags); + Result ContinueSendOld(sf::Out out_size, sf::Out out_wait, const sf::InAutoSelectBuffer &buffer, u32 task_id); + Result EndSend(sf::Out out_err, sf::Out out_size, u32 task_id); + Result StartRecv(sf::Out out_task_id, sf::OutCopyHandle out_event, s64 size, s32 flags); + Result EndRecv(sf::Out out_err, sf::Out out_size, const sf::OutAutoSelectBuffer &buffer, u32 task_id); + Result SendStart(sf::Out out_task_id, sf::OutCopyHandle out_event, const sf::InNonSecureAutoSelectBuffer &buffer, s32 flags); + Result ContinueSend(sf::Out out_size, sf::Out out_wait, const sf::InNonSecureAutoSelectBuffer &buffer, u32 task_id); + Result GetPrimitive(sf::Out out); + }; + static_assert(tma::IsISocket); + +} diff --git a/stratosphere/htc/source/htc_main.cpp b/stratosphere/htc/source/htc_main.cpp index d3ebba403..4f4bd1c2a 100644 --- a/stratosphere/htc/source/htc_main.cpp +++ b/stratosphere/htc/source/htc_main.cpp @@ -214,7 +214,7 @@ namespace ams::htc { } void HtcsIpcThreadFunction(void *arg) { - //htcs::server::LoopHipcServer(); + htcs::server::LoopHipcServer(); } } @@ -263,8 +263,8 @@ int main(int argc, char **argv) os::SetThreadNamePointer(std::addressof(htcfs_ipc_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, HtcfsIpc)); /* Initialize the htcs server. */ - //htcs::server::Initialize(); - //htcs::server::RegisterHipcServer(); + htcs::server::Initialize(); + htcs::server::RegisterHipcServer(); /* Create the htcs ipc threads. */ os::ThreadType htcs_ipc_threads[htc::NumHtcsIpcThreads]; From dec06ff649a079552543d1d869a776a1866c7e74 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 10 Feb 2021 20:13:32 -0800 Subject: [PATCH 054/280] htc: add htcfs service api definitions --- .../include/stratosphere/tma.hpp | 1 + .../tma/tma_i_directory_accessor.hpp | 28 +++++++++++ .../stratosphere/tma/tma_i_file_accessor.hpp | 31 ++++++++++++ .../stratosphere/tma/tma_i_file_manager.hpp | 39 +++++++++++++++ .../include/stratosphere/tma/tma_path.hpp | 49 +++++++++++++++++++ 5 files changed, 148 insertions(+) create mode 100644 libraries/libstratosphere/include/stratosphere/tma/tma_i_directory_accessor.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/tma/tma_i_file_accessor.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/tma/tma_i_file_manager.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/tma/tma_path.hpp diff --git a/libraries/libstratosphere/include/stratosphere/tma.hpp b/libraries/libstratosphere/include/stratosphere/tma.hpp index 2174693ea..a643a7baf 100644 --- a/libraries/libstratosphere/include/stratosphere/tma.hpp +++ b/libraries/libstratosphere/include/stratosphere/tma.hpp @@ -17,3 +17,4 @@ #include #include +#include diff --git a/libraries/libstratosphere/include/stratosphere/tma/tma_i_directory_accessor.hpp b/libraries/libstratosphere/include/stratosphere/tma/tma_i_directory_accessor.hpp new file mode 100644 index 000000000..1347ac6c2 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/tma/tma_i_directory_accessor.hpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include + +/* NOTE: Minimum firmware version not enforced for any commands. */ +#define AMS_TMA_I_DIRECTORY_ACCESSOR_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 0, Result, GetEntryCount, (ams::sf::Out out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 1, Result, Read, (ams::sf::Out out, const ams::sf::OutMapAliasArray &out_entries), (out, out_entries)) \ + AMS_SF_METHOD_INFO(C, H, 2, Result, SetPriorityForDirectory, (s32 priority), (priority)) \ + AMS_SF_METHOD_INFO(C, H, 3, Result, GetPriorityForDirectory, (ams::sf::Out out), (out)) + +AMS_SF_DEFINE_INTERFACE(ams::tma, IDirectoryAccessor, AMS_TMA_I_DIRECTORY_ACCESSOR_INTERFACE_INFO) diff --git a/libraries/libstratosphere/include/stratosphere/tma/tma_i_file_accessor.hpp b/libraries/libstratosphere/include/stratosphere/tma/tma_i_file_accessor.hpp new file mode 100644 index 000000000..2cc088c60 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/tma/tma_i_file_accessor.hpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include + +/* NOTE: Minimum firmware version not enforced for any commands. */ +#define AMS_TMA_I_FILE_ACCESSOR_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 0, Result, ReadFile, (ams::sf::Out out, s64 offset, const ams::sf::OutNonSecureBuffer &buffer, ams::fs::ReadOption option), (out, offset, buffer, option)) \ + AMS_SF_METHOD_INFO(C, H, 1, Result, WriteFile, (s64 offset, const ams::sf::InNonSecureBuffer &buffer, ams::fs::WriteOption option), (offset, buffer, option)) \ + AMS_SF_METHOD_INFO(C, H, 2, Result, GetFileSize, (ams::sf::Out out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 3, Result, SetFileSize, (s64 size), (size)) \ + AMS_SF_METHOD_INFO(C, H, 4, Result, FlushFile, (), ()) \ + AMS_SF_METHOD_INFO(C, H, 5, Result, SetPriorityForFile, (s32 priority), (priority)) \ + AMS_SF_METHOD_INFO(C, H, 6, Result, GetPriorityForFile, (ams::sf::Out out), (out)) + +AMS_SF_DEFINE_INTERFACE(ams::tma, IFileAccessor, AMS_TMA_I_FILE_ACCESSOR_INTERFACE_INFO) diff --git a/libraries/libstratosphere/include/stratosphere/tma/tma_i_file_manager.hpp b/libraries/libstratosphere/include/stratosphere/tma/tma_i_file_manager.hpp new file mode 100644 index 000000000..43a7d77f3 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/tma/tma_i_file_manager.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include + +/* NOTE: Minimum firmware version not enforced for any commands. */ +#define AMS_TMA_I_FILE_MANAGER_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 0, Result, OpenFile, (sf::Out> out, const tma::Path &path, bool case_sensitive), (out, path, case_sensitive)) \ + AMS_SF_METHOD_INFO(C, H, 1, Result, FileExists, (sf::Out out, const tma::Path &path, bool case_sensitive), (out, path, case_sensitive)) \ + AMS_SF_METHOD_INFO(C, H, 2, Result, DeleteFile, (const tma::Path &path, bool case_sensitive), (path, case_sensitive)) \ + AMS_SF_METHOD_INFO(C, H, 3, Result, RenameFile, (const tma::Path &old_path, const tma::Path &new_path, bool case_sensitive), (old_path, new_path, case_sensitive)) \ + AMS_SF_METHOD_INFO(C, H, 4, Result, GetIOType, (sf::Out out, const tma::Path &path, bool case_sensitive), (out, path, case_sensitive)) \ + AMS_SF_METHOD_INFO(C, H, 5, Result, OpenDirectory, (sf::Out> out, const tma::Path &path, bool case_sensitive), (out, path, case_sensitive)) \ + AMS_SF_METHOD_INFO(C, H, 6, Result, DirectoryExists, (sf::Out out, const tma::Path &path, bool case_sensitive), (out, path, case_sensitive)) \ + AMS_SF_METHOD_INFO(C, H, 7, Result, CreateDirectory, (const tma::Path &path, bool case_sensitive), (path, case_sensitive)) \ + AMS_SF_METHOD_INFO(C, H, 8, Result, DeleteDirectory, (const tma::Path &path, bool case_sensitive), (path, case_sensitive)) \ + AMS_SF_METHOD_INFO(C, H, 9, Result, RenameDirectory, (const tma::Path &old_path, const tma::Path &new_path, bool case_sensitive), (old_path, new_path, case_sensitive)) \ + AMS_SF_METHOD_INFO(C, H, 10, Result, CreateFile, (const tma::Path &path, s64 size, bool case_sensitive), (path, size, case_sensitive)) \ + AMS_SF_METHOD_INFO(C, H, 11, Result, GetFileTimeStamp, (sf::Out out_create, sf::Out out_access, sf::Out out_modify, const tma::Path &path, bool case_sensitive), (out_create, out_access, out_modify, path, case_sensitive)) \ + AMS_SF_METHOD_INFO(C, H, 12, Result, GetCaseSensitivePath, (const tma::Path &path, const sf::OutBuffer &out), (path, out)) \ + AMS_SF_METHOD_INFO(C, H, 13, Result, GetDiskFreeSpaceExW, (sf::Out out_free, sf::Out out_total, sf::Out out_total_free, const tma::Path &path), (out_free, out_total, out_total_free, path)) + +AMS_SF_DEFINE_INTERFACE(ams::tma, IFileManager, AMS_TMA_I_FILE_MANAGER_INTERFACE_INFO) diff --git a/libraries/libstratosphere/include/stratosphere/tma/tma_path.hpp b/libraries/libstratosphere/include/stratosphere/tma/tma_path.hpp new file mode 100644 index 000000000..f6d91db1f --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/tma/tma_path.hpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include + +namespace ams::tma { + + struct Path : ams::sf::LargeData { + char str[fs::EntryNameLengthMax + 1]; + + static constexpr Path Encode(const char *p) { + Path path = {}; + for (size_t i = 0; i < sizeof(path) - 1; i++) { + path.str[i] = p[i]; + if (p[i] == '\x00') { + break; + } + } + return path; + } + + static constexpr size_t GetLength(const Path &path) { + size_t len = 0; + for (size_t i = 0; i < sizeof(path) - 1 && path.str[i] != '\x00'; i++) { + len++; + } + return len; + } + }; + + static_assert(util::is_pod::value && sizeof(Path) == fs::EntryNameLengthMax + 1); + +} \ No newline at end of file From 9fbbb9fadba912cc0bbb05b78cdf8b43e347c4ce Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 10 Feb 2021 22:52:12 -0800 Subject: [PATCH 055/280] htclow: add Channel wrapper class --- .../source/htclow/htclow_channel.cpp | 235 ++++++++++++++++++ .../source/htclow/htclow_channel.hpp | 58 +++++ .../source/htclow/htclow_manager.cpp | 12 +- .../source/htclow/htclow_manager.hpp | 5 +- .../source/htclow/htclow_manager_impl.cpp | 10 +- .../source/htclow/htclow_manager_impl.hpp | 3 + .../source/htclow/mux/htclow_mux.cpp | 44 ++++ .../source/htclow/mux/htclow_mux.hpp | 5 + .../htclow/mux/htclow_mux_channel_impl.cpp | 11 + .../htclow/mux/htclow_mux_channel_impl.hpp | 2 + .../htclow/mux/htclow_mux_send_buffer.cpp | 5 + .../htclow/mux/htclow_mux_send_buffer.hpp | 1 + .../vapours/results/htclow_results.hpp | 1 + 13 files changed, 388 insertions(+), 4 deletions(-) create mode 100644 libraries/libstratosphere/source/htclow/htclow_channel.cpp create mode 100644 libraries/libstratosphere/source/htclow/htclow_channel.hpp diff --git a/libraries/libstratosphere/source/htclow/htclow_channel.cpp b/libraries/libstratosphere/source/htclow/htclow_channel.cpp new file mode 100644 index 000000000..830369e25 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/htclow_channel.cpp @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htclow_channel.hpp" + +namespace ams::htclow { + + Result Channel::Open(const Module *module, ChannelId id) { + /* Check we're not already initialized. */ + AMS_ASSERT(!module->GetBase()->_is_initialized); + + /* Set our channel type. */ + m_channel = { + ._is_initialized = true, + ._module_id = module->GetBase()->_id, + ._channel_id = id, + }; + + /* Open the channel. */ + return m_manager->Open(impl::ConvertChannelType(m_channel)); + } + + void Channel::Close() { + m_manager->Close(impl::ConvertChannelType(m_channel)); + } + + ChannelState Channel::GetChannelState() { + return m_manager->GetChannelState(impl::ConvertChannelType(m_channel)); + } + + os::EventType *Channel::GetChannelStateEvent() { + return m_manager->GetChannelStateEvent(impl::ConvertChannelType(m_channel)); + } + + Result Channel::Connect() { + const auto channel = impl::ConvertChannelType(m_channel); + + /* Begin the flush. */ + u32 task_id; + R_TRY(m_manager->ConnectBegin(std::addressof(task_id), channel)); + + /* Wait for the task to finish. */ + this->WaitEvent(m_manager->GetTaskEvent(task_id), false); + + /* End the flush. */ + return m_manager->ConnectEnd(channel, task_id); + } + + Result Channel::Flush() { + /* Begin the flush. */ + u32 task_id; + R_TRY(m_manager->FlushBegin(std::addressof(task_id), impl::ConvertChannelType(m_channel))); + + /* Wait for the task to finish. */ + this->WaitEvent(m_manager->GetTaskEvent(task_id), true); + + /* End the flush. */ + return m_manager->FlushEnd(task_id); + } + + void Channel::Shutdown() { + m_manager->Shutdown(impl::ConvertChannelType(m_channel)); + } + + Result Channel::Receive(s64 *out, void *dst, s64 size, ReceiveOption option) { + /* Check pre-conditions. */ + AMS_ABORT_UNLESS(util::IsIntValueRepresentable(size)); + + /* Determine the minimum allowable receive size. */ + s64 min_size; + switch (option) { + case htclow::ReceiveOption_NonBlocking: min_size = 0; break; + case htclow::ReceiveOption_ReceiveAnyData: min_size = 1; break; + case htclow::ReceiveOption_ReceiveAllData: min_size = size; break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Repeatedly receive. */ + s64 received = 0; + do { + s64 cur_received; + const Result result = this->ReceiveInternal(std::addressof(cur_received), static_cast(dst) + received, size - received, option); + + if (R_FAILED(result)) { + if (htclow::ResultChannelReceiveBufferEmpty::Includes(result)) { + R_UNLESS(option != htclow::ReceiveOption_NonBlocking, htclow::ResultNonBlockingReceiveFailed()); + } + if (htclow::ResultChannelNotExist::Includes(result)) { + *out = received; + } + return result; + } + + received += static_cast(cur_received); + } while (received < min_size); + + /* Set output size. */ + AMS_ASSERT(received <= size); + *out = received; + + return ResultSuccess(); + } + + Result Channel::Send(s64 *out, const void *src, s64 size, ReceiveOption option) { + /* Check pre-conditions. */ + AMS_ASSERT(util::IsIntValueRepresentable(size)); + + /* Convert channel. */ + const auto channel = impl::ConvertChannelType(m_channel); + + /* Loop sending. */ + s64 total_sent; + size_t cur_sent; + for (total_sent = 0; total_sent < size; total_sent += cur_sent) { + AMS_ASSERT(util::IsIntValueRepresentable(size - total_sent)); + + /* Begin the send. */ + u32 task_id; + const auto begin_result = m_manager->SendBegin(std::addressof(task_id), std::addressof(cur_sent), static_cast(src) + total_sent, size - total_sent, channel); + if (R_FAILED(begin_result)) { + if (total_sent != 0) { + break; + } else { + return begin_result; + } + } + + /* Wait for the task to finish. */ + this->WaitEvent(m_manager->GetTaskEvent(task_id), true); + + /* Finish the send. */ + R_ABORT_UNLESS(m_manager->SendEnd(task_id)); + } + + /* Set output size. */ + AMS_ASSERT(total_sent <= size); + *out = total_sent; + + return ResultSuccess(); + } + + void Channel::SetConfig(const ChannelConfig &config) { + m_manager->SetConfig(impl::ConvertChannelType(m_channel), config); + } + + void Channel::SetReceiveBuffer(void *buf, size_t size) { + m_manager->SetReceiveBuffer(impl::ConvertChannelType(m_channel), buf, size); + } + + void Channel::SetSendBuffer(void *buf, size_t size) { + m_manager->SetSendBuffer(impl::ConvertChannelType(m_channel), buf, size); + } + + void Channel::SetSendBufferWithData(const void *buf, size_t size) { + m_manager->SetSendBufferWithData(impl::ConvertChannelType(m_channel), buf, size); + } + + Result Channel::WaitReceive(s64 size) { + /* Check pre-conditions. */ + AMS_ASSERT(util::IsIntValueRepresentable(size)); + + return this->WaitReceiveInternal(size, nullptr); + } + + Result Channel::WaitReceive(s64 size, os::EventType *event) { + /* Check pre-conditions. */ + AMS_ASSERT(util::IsIntValueRepresentable(size)); + AMS_ASSERT(event != nullptr); + + return this->WaitReceiveInternal(size, event); + } + + void Channel::WaitEvent(os::EventType *event, bool) { + return os::WaitEvent(event); + } + + Result Channel::ReceiveInternal(s64 *out, void *dst, s64 size, ReceiveOption option) { + const auto channel = impl::ConvertChannelType(m_channel); + const bool blocking = option != ReceiveOption_NonBlocking; + + /* Begin the receive. */ + u32 task_id; + R_TRY(m_manager->ReceiveBegin(std::addressof(task_id), channel, blocking ? 1 : 0)); + + /* Wait for the task to finish. */ + this->WaitEvent(m_manager->GetTaskEvent(task_id), false); + + /* Receive the data. */ + size_t received; + AMS_ASSERT(util::IsIntValueRepresentable(size)); + R_TRY(m_manager->ReceiveEnd(std::addressof(received), dst, static_cast(size), channel, task_id)); + + /* Set the output size. */ + AMS_ASSERT(util::IsIntValueRepresentable(received)); + *out = static_cast(received); + + return ResultSuccess(); + } + + Result Channel::WaitReceiveInternal(s64 size, os::EventType *event) { + const auto channel = impl::ConvertChannelType(m_channel); + + /* Begin the wait. */ + u32 task_id; + R_TRY(m_manager->WaitReceiveBegin(std::addressof(task_id), channel, size)); + + + /* Perform the wait. */ + if (event != nullptr) { + if (os::WaitAny(event, m_manager->GetTaskEvent(task_id)) == 0) { + m_manager->WaitReceiveEnd(task_id); + return htclow::ResultChannelWaitCancelled(); + } + } else { + this->WaitEvent(m_manager->GetTaskEvent(task_id), false); + } + + /* End the wait. */ + return m_manager->WaitReceiveEnd(task_id); + } + +} diff --git a/libraries/libstratosphere/source/htclow/htclow_channel.hpp b/libraries/libstratosphere/source/htclow/htclow_channel.hpp new file mode 100644 index 000000000..2d99cc7c4 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/htclow_channel.hpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "htclow_manager.hpp" + +namespace ams::htclow { + + class Channel final { + private: + HtclowManager *m_manager; + ChannelType m_channel; + public: + constexpr Channel(HtclowManager *manager) : m_manager(manager), m_channel() { /* ... */ } + constexpr ~Channel() { m_channel = {}; } + + ChannelType *GetBase() { return std::addressof(m_channel); } + public: + Result Open(const Module *module, ChannelId id); + void Close(); + + ChannelState GetChannelState(); + os::EventType *GetChannelStateEvent(); + + Result Connect(); + Result Flush(); + void Shutdown(); + + Result Receive(s64 *out, void *dst, s64 size, ReceiveOption option); + Result Send(s64 *out, const void *src, s64 size, ReceiveOption option); + + void SetConfig(const ChannelConfig &config); + void SetReceiveBuffer(void *buf, size_t size); + void SetSendBuffer(void *buf, size_t size); + void SetSendBufferWithData(const void *buf, size_t size); + + Result WaitReceive(s64 size); + Result WaitReceive(s64 size, os::EventType *event); + private: + void WaitEvent(os::EventType *event, bool); + Result ReceiveInternal(s64 *out, void *dst, s64 size, ReceiveOption option); + Result WaitReceiveInternal(s64 size, os::EventType *event); + }; + +} diff --git a/libraries/libstratosphere/source/htclow/htclow_manager.cpp b/libraries/libstratosphere/source/htclow/htclow_manager.cpp index 398de260a..66990a084 100644 --- a/libraries/libstratosphere/source/htclow/htclow_manager.cpp +++ b/libraries/libstratosphere/source/htclow/htclow_manager.cpp @@ -96,8 +96,8 @@ namespace ams::htclow { return m_impl->NotifyAwake(); } - Result HtclowManager::ReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, bool blocking) { - return m_impl->ReceiveBegin(out_task_id, channel, blocking); + Result HtclowManager::ReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, size_t size) { + return m_impl->ReceiveBegin(out_task_id, channel, size); } Result HtclowManager::ReceiveEnd(size_t *out, void *dst, size_t dst_size, impl::ChannelInternalType channel, u32 task_id) { @@ -112,6 +112,14 @@ namespace ams::htclow { return m_impl->SendEnd(task_id); } + Result HtclowManager::WaitReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, size_t size) { + return m_impl->WaitReceiveBegin(out_task_id, channel, size); + } + + Result HtclowManager::WaitReceiveEnd(u32 task_id) { + return m_impl->WaitReceiveEnd(task_id); + } + void HtclowManager::SetConfig(impl::ChannelInternalType channel, const ChannelConfig &config) { return m_impl->SetConfig(channel, config); } diff --git a/libraries/libstratosphere/source/htclow/htclow_manager.hpp b/libraries/libstratosphere/source/htclow/htclow_manager.hpp index 1842d2e5c..d01cc891a 100644 --- a/libraries/libstratosphere/source/htclow/htclow_manager.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_manager.hpp @@ -56,12 +56,15 @@ namespace ams::htclow { void NotifyAsleep(); void NotifyAwake(); - Result ReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, bool blocking); + Result ReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, size_t size); Result ReceiveEnd(size_t *out, void *dst, size_t dst_size, impl::ChannelInternalType channel, u32 task_id); Result SendBegin(u32 *out_task_id, size_t *out, const void *src, size_t src_size, impl::ChannelInternalType channel); Result SendEnd(u32 task_id); + Result WaitReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, size_t size); + Result WaitReceiveEnd(u32 task_id); + void SetConfig(impl::ChannelInternalType channel, const ChannelConfig &config); void SetDebugDriver(driver::IDriver *driver); diff --git a/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp b/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp index 7e9c61b7e..d4f6d2e8a 100644 --- a/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp +++ b/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp @@ -173,8 +173,16 @@ namespace ams::htclow { return m_mux.SendEnd(task_id); } + Result HtclowManagerImpl::WaitReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, size_t size) { + return m_mux.WaitReceiveBegin(out_task_id, channel, size); + } + + Result HtclowManagerImpl::WaitReceiveEnd(u32 task_id) { + return m_mux.WaitReceiveEnd(task_id); + } + void HtclowManagerImpl::SetConfig(impl::ChannelInternalType channel, const ChannelConfig &config) { - AMS_ABORT("HtclowManagerImpl::SetConfig"); + return m_mux.SetConfig(channel, config); } void HtclowManagerImpl::SetDebugDriver(driver::IDriver *driver) { diff --git a/libraries/libstratosphere/source/htclow/htclow_manager_impl.hpp b/libraries/libstratosphere/source/htclow/htclow_manager_impl.hpp index 6839eab98..ce7eb0aea 100644 --- a/libraries/libstratosphere/source/htclow/htclow_manager_impl.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_manager_impl.hpp @@ -74,6 +74,9 @@ namespace ams::htclow { Result SendBegin(u32 *out_task_id, size_t *out, const void *src, size_t src_size, impl::ChannelInternalType channel); Result SendEnd(u32 task_id); + Result WaitReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, size_t size); + Result WaitReceiveEnd(u32 task_id); + void SetConfig(impl::ChannelInternalType channel, const ChannelConfig &config); void SetDebugDriver(driver::IDriver *driver); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp index 3bb6b9d19..220d28c62 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp @@ -211,6 +211,9 @@ namespace ams::htclow::mux { } Result Mux::ConnectEnd(impl::ChannelInternalType channel, u32 task_id) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + /* Get the trigger for the task. */ const auto trigger = m_task_manager.GetTrigger(task_id); @@ -255,6 +258,9 @@ namespace ams::htclow::mux { } Result Mux::FlushEnd(u32 task_id) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + /* Get the trigger for the task. */ const auto trigger = m_task_manager.GetTrigger(task_id); @@ -287,6 +293,9 @@ namespace ams::htclow::mux { } Result Mux::ReceiveEnd(size_t *out, void *dst, size_t dst_size, impl::ChannelInternalType channel, u32 task_id) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + /* Free the task. */ m_task_manager.FreeTask(task_id); @@ -317,6 +326,9 @@ namespace ams::htclow::mux { } Result Mux::SendEnd(u32 task_id) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + /* Get the trigger for the task. */ const auto trigger = m_task_manager.GetTrigger(task_id); @@ -329,6 +341,38 @@ namespace ams::htclow::mux { return ResultSuccess(); } + Result Mux::WaitReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, size_t size) { + return this->ReceiveBegin(out_task_id, channel, size); + } + + Result Mux::WaitReceiveEnd(u32 task_id) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Get the trigger for the task. */ + const auto trigger = m_task_manager.GetTrigger(task_id); + + /* Free the task. */ + m_task_manager.FreeTask(task_id); + + /* Check that we didn't hit a disconnect. */ + R_UNLESS(trigger != EventTrigger_Disconnect, htclow::ResultInvalidChannelStateDisconnected()); + + return ResultSuccess(); + } + + void Mux::SetConfig(impl::ChannelInternalType channel, const ChannelConfig &config) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Find the channel. */ + auto it = m_channel_impl_map.GetMap().find(channel); + AMS_ABORT_UNLESS(it != m_channel_impl_map.GetMap().end()); + + /* Perform the connection. */ + return m_channel_impl_map[it->second].SetConfig(config); + } + void Mux::SetSendBuffer(impl::ChannelInternalType channel, void *buf, size_t buf_size, size_t max_packet_size) { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp index 071efbe17..c3686223d 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp @@ -74,6 +74,11 @@ namespace ams::htclow::mux { Result SendBegin(u32 *out_task_id, size_t *out, const void *src, size_t src_size, impl::ChannelInternalType channel); Result SendEnd(u32 task_id); + Result WaitReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, size_t size); + Result WaitReceiveEnd(u32 task_id); + + void SetConfig(impl::ChannelInternalType channel, const ChannelConfig &config); + void SetSendBuffer(impl::ChannelInternalType channel, void *buf, size_t buf_size, size_t max_packet_size); void SetReceiveBuffer(impl::ChannelInternalType channel, void *buf, size_t buf_size); void SetSendBufferWithData(impl::ChannelInternalType channel, const void *buf, size_t buf_size, size_t max_packet_size); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp index 4b8aa3a9a..b465eeadf 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp @@ -425,6 +425,17 @@ namespace ams::htclow::mux { return ResultSuccess(); } + void ChannelImpl::SetConfig(const ChannelConfig &config) { + /* Check our state. */ + R_ABORT_UNLESS(this->CheckState({ChannelState_Unconnectable, ChannelState_Connectable})); + + /* Set our config. */ + m_config = config; + + /* Set flow control for our send buffer. */ + m_send_buffer.SetFlowControlEnabled(m_config.flow_control_enabled); + } + void ChannelImpl::SetSendBuffer(void *buf, size_t buf_size, size_t max_packet_size) { /* Set buffer. */ m_send_buffer.SetBuffer(buf, buf_size); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp index c476aab77..5849b88a5 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp @@ -80,6 +80,8 @@ namespace ams::htclow::mux { Result DoShutdown(); + void SetConfig(const ChannelConfig &config); + void SetSendBuffer(void *buf, size_t buf_size, size_t max_packet_size); void SetReceiveBuffer(void *buf, size_t buf_size); void SetSendBufferWithData(const void *buf, size_t buf_size, size_t max_packet_size); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp index 3250bbbbb..214bc3032 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp @@ -41,6 +41,11 @@ namespace ams::htclow::mux { m_version = version; } + void SendBuffer::SetFlowControlEnabled(bool en) { + /* Set flow control enabled. */ + m_flow_control_enabled = en; + } + void SendBuffer::MakeDataPacketHeader(PacketHeader *header, int body_size, s16 version, u64 share, u32 offset) const { /* Set all packet fields. */ header->signature = HtcGen2Signature; diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp index 665443459..373cece1b 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp @@ -48,6 +48,7 @@ namespace ams::htclow::mux { ~SendBuffer(); void SetVersion(s16 version); + void SetFlowControlEnabled(bool en); bool QueryNextPacket(PacketHeader *header, PacketBody *body, int *out_body_size, u64 max_data, u64 total_send_size, bool has_share, u64 share); diff --git a/libraries/libvapours/include/vapours/results/htclow_results.hpp b/libraries/libvapours/include/vapours/results/htclow_results.hpp index 16e65a413..eb90e2b90 100644 --- a/libraries/libvapours/include/vapours/results/htclow_results.hpp +++ b/libraries/libvapours/include/vapours/results/htclow_results.hpp @@ -23,6 +23,7 @@ namespace ams::htclow { R_DEFINE_ERROR_RESULT(ConnectionFailure, 1); R_DEFINE_ERROR_RESULT(UnknownDriverType, 3); R_DEFINE_ERROR_RESULT(NonBlockingReceiveFailed, 5); + R_DEFINE_ERROR_RESULT(ChannelWaitCancelled, 8); R_DEFINE_ERROR_RESULT(ChannelAlreadyExist, 9); R_DEFINE_ERROR_RESULT(ChannelNotExist, 10); From 870b45f208846ad27cec938310bdd7c307b8f659 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 11 Feb 2021 05:47:41 -0800 Subject: [PATCH 056/280] htc: add htcfs server/service object skeletons --- .../include/stratosphere/htcfs.hpp | 1 + .../stratosphere/htcfs/htcfs_hipc_server.hpp | 32 ++++++ .../stratosphere/tma/tma_i_file_manager.hpp | 19 ++++ .../source/htcfs/htcfs_cache_manager.hpp | 103 ++++++++++++++++++ .../source/htcfs/htcfs_client.cpp | 47 ++++++++ .../source/htcfs/htcfs_client.hpp | 34 ++++++ .../source/htcfs/htcfs_client_impl.hpp | 53 +++++++++ .../htcfs/htcfs_directory_service_object.cpp | 45 ++++++++ .../htcfs/htcfs_directory_service_object.hpp | 35 ++++++ .../htcfs/htcfs_file_service_object.cpp | 56 ++++++++++ .../htcfs/htcfs_file_service_object.hpp | 38 +++++++ .../htcfs_file_system_service_object.cpp | 77 +++++++++++++ .../htcfs_file_system_service_object.hpp | 43 ++++++++ .../source/htcfs/htcfs_header_factory.hpp | 30 +++++ .../source/htcfs/htcfs_hipc_server.cpp | 64 +++++++++++ .../server/htcs_manager_service_object.cpp | 4 +- stratosphere/htc/source/htc_main.cpp | 6 +- 17 files changed, 682 insertions(+), 5 deletions(-) create mode 100644 libraries/libstratosphere/include/stratosphere/htcfs/htcfs_hipc_server.hpp create mode 100644 libraries/libstratosphere/source/htcfs/htcfs_cache_manager.hpp create mode 100644 libraries/libstratosphere/source/htcfs/htcfs_client.cpp create mode 100644 libraries/libstratosphere/source/htcfs/htcfs_client.hpp create mode 100644 libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp create mode 100644 libraries/libstratosphere/source/htcfs/htcfs_directory_service_object.cpp create mode 100644 libraries/libstratosphere/source/htcfs/htcfs_directory_service_object.hpp create mode 100644 libraries/libstratosphere/source/htcfs/htcfs_file_service_object.cpp create mode 100644 libraries/libstratosphere/source/htcfs/htcfs_file_service_object.hpp create mode 100644 libraries/libstratosphere/source/htcfs/htcfs_file_system_service_object.cpp create mode 100644 libraries/libstratosphere/source/htcfs/htcfs_file_system_service_object.hpp create mode 100644 libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp create mode 100644 libraries/libstratosphere/source/htcfs/htcfs_hipc_server.cpp diff --git a/libraries/libstratosphere/include/stratosphere/htcfs.hpp b/libraries/libstratosphere/include/stratosphere/htcfs.hpp index 999d1a35e..1e9368003 100644 --- a/libraries/libstratosphere/include/stratosphere/htcfs.hpp +++ b/libraries/libstratosphere/include/stratosphere/htcfs.hpp @@ -15,3 +15,4 @@ */ #pragma once +#include diff --git a/libraries/libstratosphere/include/stratosphere/htcfs/htcfs_hipc_server.hpp b/libraries/libstratosphere/include/stratosphere/htcfs/htcfs_hipc_server.hpp new file mode 100644 index 000000000..eb79d1c71 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htcfs/htcfs_hipc_server.hpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htclow { + + class HtclowManager; + +} + +namespace ams::htcfs { + + void Initialize(htclow::HtclowManager *htclow_manager); + + void RegisterHipcServer(); + void LoopHipcServer(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/tma/tma_i_file_manager.hpp b/libraries/libstratosphere/include/stratosphere/tma/tma_i_file_manager.hpp index 43a7d77f3..6bd0190cf 100644 --- a/libraries/libstratosphere/include/stratosphere/tma/tma_i_file_manager.hpp +++ b/libraries/libstratosphere/include/stratosphere/tma/tma_i_file_manager.hpp @@ -37,3 +37,22 @@ AMS_SF_METHOD_INFO(C, H, 13, Result, GetDiskFreeSpaceExW, (sf::Out out_free, sf::Out out_total, sf::Out out_total_free, const tma::Path &path), (out_free, out_total, out_total_free, path)) AMS_SF_DEFINE_INTERFACE(ams::tma, IFileManager, AMS_TMA_I_FILE_MANAGER_INTERFACE_INFO) + +/* Prior to system version 6.0.0, case sensitivity was not parameterized. */ +#define AMS_TMA_I_DEPRECATED_FILE_MANAGER_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 0, Result, OpenFile, (sf::Out> out, const tma::Path &path), (out, path)) \ + AMS_SF_METHOD_INFO(C, H, 1, Result, FileExists, (sf::Out out, const tma::Path &path), (out, path)) \ + AMS_SF_METHOD_INFO(C, H, 2, Result, DeleteFile, (const tma::Path &path), (path)) \ + AMS_SF_METHOD_INFO(C, H, 3, Result, RenameFile, (const tma::Path &old_path, const tma::Path &new_path), (old_path, new_path)) \ + AMS_SF_METHOD_INFO(C, H, 4, Result, GetIOType, (sf::Out out, const tma::Path &path), (out, path)) \ + AMS_SF_METHOD_INFO(C, H, 5, Result, OpenDirectory, (sf::Out> out, const tma::Path &path), (out, path)) \ + AMS_SF_METHOD_INFO(C, H, 6, Result, DirectoryExists, (sf::Out out, const tma::Path &path), (out, path)) \ + AMS_SF_METHOD_INFO(C, H, 7, Result, CreateDirectory, (const tma::Path &path), (path)) \ + AMS_SF_METHOD_INFO(C, H, 8, Result, DeleteDirectory, (const tma::Path &path), (path)) \ + AMS_SF_METHOD_INFO(C, H, 9, Result, RenameDirectory, (const tma::Path &old_path, const tma::Path &new_path), (old_path, new_path)) \ + AMS_SF_METHOD_INFO(C, H, 10, Result, CreateFile, (const tma::Path &path, s64 size), (path, size)) \ + AMS_SF_METHOD_INFO(C, H, 11, Result, GetFileTimeStamp, (sf::Out out_create, sf::Out out_access, sf::Out out_modify, const tma::Path &path), (out_create, out_access, out_modify, path)) \ + AMS_SF_METHOD_INFO(C, H, 12, Result, GetCaseSensitivePath, (const tma::Path &path, const sf::OutBuffer &out), (path, out)) \ + AMS_SF_METHOD_INFO(C, H, 13, Result, GetDiskFreeSpaceExW, (sf::Out out_free, sf::Out out_total, sf::Out out_total_free, const tma::Path &path), (out_free, out_total, out_total_free, path)) + +AMS_SF_DEFINE_INTERFACE(ams::tma, IDeprecatedFileManager, AMS_TMA_I_DEPRECATED_FILE_MANAGER_INTERFACE_INFO) diff --git a/libraries/libstratosphere/source/htcfs/htcfs_cache_manager.hpp b/libraries/libstratosphere/source/htcfs/htcfs_cache_manager.hpp new file mode 100644 index 000000000..deb65fc35 --- /dev/null +++ b/libraries/libstratosphere/source/htcfs/htcfs_cache_manager.hpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htcfs { + + class CacheManager { + private: + os::SdkMutex m_mutex; + void *m_cache; + size_t m_cache_size; + s64 m_cached_file_size; + size_t m_cached_data_size; + s32 m_cached_handle; + bool m_has_cached_handle; + public: + CacheManager(void *cache, size_t cache_size) : m_mutex(), m_cache(cache), m_cache_size(cache_size), m_cached_file_size(), m_cached_data_size(), m_cached_handle(), m_has_cached_handle() { /* ... */ } + public: + bool GetFileSize(s64 *out, s32 handle) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Get the cached size, if we match. */ + if (m_has_cached_handle && m_cached_handle == handle) { + *out = m_cached_file_size; + return true; + } else { + return false; + } + } + + void Invalidate() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Note that we have no handle. */ + m_has_cached_handle = false; + } + + void Record(s64 file_size, const void *data, s32 handle, size_t data_size) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Set our cached file size. */ + m_cached_file_size = file_size; + + /* Set our cached data size. */ + m_cached_data_size = std::min(m_cache_size, data_size); + + /* Copy the data. */ + std::memcpy(m_cache, data, m_cached_data_size); + + /* Set our cache handle. */ + m_cached_handle = handle; + + /* Note that we have cached data. */ + m_has_cached_handle = true; + } + + bool ReadFile(size_t *out, void *dst, s32 handle, size_t offset, size_t size) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Check that we have a cached file. */ + if (!m_has_cached_handle) { + return false; + } + + /* Check the file is our cached one. */ + if (handle != m_cached_handle) { + return false; + } + + /* Check that we can read data. */ + if (offset + size > m_cached_data_size) { + return false; + } + + /* Copy the cached data. */ + std::memcpy(dst, static_cast(m_cache) + offset, size); + + /* Set the output read size. */ + *out = size; + + return true; + } + }; + +} diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client.cpp b/libraries/libstratosphere/source/htcfs/htcfs_client.cpp new file mode 100644 index 000000000..0d871278d --- /dev/null +++ b/libraries/libstratosphere/source/htcfs/htcfs_client.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcfs_client.hpp" + +namespace ams::htcfs { + + namespace { + + constinit TYPED_STORAGE(Client) g_client_storage; + constinit bool g_initialized; + + } + + void InitializeClient(htclow::HtclowManager *manager) { + AMS_ASSERT(!g_initialized); + + std::construct_at(GetPointer(g_client_storage), manager); + } + + void FinalizeClient() { + AMS_ASSERT(g_initialized); + + std::destroy_at(GetPointer(g_client_storage)); + } + + Client &GetClient() { + AMS_ASSERT(g_initialized); + + return GetReference(g_client_storage); + } + + +} diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client.hpp b/libraries/libstratosphere/source/htcfs/htcfs_client.hpp new file mode 100644 index 000000000..974342703 --- /dev/null +++ b/libraries/libstratosphere/source/htcfs/htcfs_client.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "htcfs_client_impl.hpp" + +namespace ams::htcfs { + + class Client { + private: + ClientImpl m_impl; + public: + Client(htclow::HtclowManager *manager); + }; + + void InitializeClient(htclow::HtclowManager *manager); + void FinalizeClient(); + + Client &GetClient(); + +} diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp new file mode 100644 index 000000000..fd5508c62 --- /dev/null +++ b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "../htclow/htclow_manager.hpp" +#include "../htclow/htclow_channel.hpp" +#include "htcfs_cache_manager.hpp" +#include "htcfs_header_factory.hpp" + +namespace ams::htcfs { + + class ClientImpl { + private: + u8 m_receive_buffer[0x1C040]; + u8 m_send_buffer[0x1C040]; + u8 m_packet_buffer[0xE020]; + htclow::HtclowManager *m_htclow_manager; + CacheManager m_cache_manager; + HeaderFactory m_header_factory; + os::SdkMutex m_mutex; + htclow::Module m_module; + htclow::Channel m_channel; + htclow::Channel m_data_channel; + bool m_connected; + os::ThreadType m_monitor_thread; + os::Event m_event; + public: + ClientImpl(htclow::HtclowManager *manager); + + ~ClientImpl() { + this->Cancel(); + this->Wait(); + } + public: + void Start(); + void Cancel(); + void Wait(); + }; + +} diff --git a/libraries/libstratosphere/source/htcfs/htcfs_directory_service_object.cpp b/libraries/libstratosphere/source/htcfs/htcfs_directory_service_object.cpp new file mode 100644 index 000000000..1d0e53f80 --- /dev/null +++ b/libraries/libstratosphere/source/htcfs/htcfs_directory_service_object.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcfs_directory_service_object.hpp" + +namespace ams::htcfs { + + DirectoryServiceObject::DirectoryServiceObject(s32 handle) : m_handle(handle) { /* ... */ } + + DirectoryServiceObject::~DirectoryServiceObject() { + /* TODO */ + AMS_ABORT("htcfs::GetClient().CloseDirectory(m_handle);"); + } + + Result DirectoryServiceObject::GetEntryCount(ams::sf::Out out) { + AMS_ABORT("DirectoryServiceObject::GetEntryCount"); + } + + Result DirectoryServiceObject::Read(ams::sf::Out out, const ams::sf::OutMapAliasArray &out_entries) { + AMS_ABORT("DirectoryServiceObject::Read"); + } + + Result DirectoryServiceObject::SetPriorityForDirectory(s32 priority) { + AMS_ABORT("DirectoryServiceObject::SetPriorityForDirectory"); + } + + Result DirectoryServiceObject::GetPriorityForDirectory(ams::sf::Out out) { + AMS_ABORT("DirectoryServiceObject::GetPriorityForDirectory"); + } + + +} diff --git a/libraries/libstratosphere/source/htcfs/htcfs_directory_service_object.hpp b/libraries/libstratosphere/source/htcfs/htcfs_directory_service_object.hpp new file mode 100644 index 000000000..8e27f6254 --- /dev/null +++ b/libraries/libstratosphere/source/htcfs/htcfs_directory_service_object.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htcfs { + + class DirectoryServiceObject { + private: + s32 m_handle; + public: + explicit DirectoryServiceObject(s32 handle); + ~DirectoryServiceObject(); + public: + Result GetEntryCount(ams::sf::Out out); + Result Read(ams::sf::Out out, const ams::sf::OutMapAliasArray &out_entries); + Result SetPriorityForDirectory(s32 priority); + Result GetPriorityForDirectory(ams::sf::Out out); + }; + static_assert(tma::IsIDirectoryAccessor); + +} diff --git a/libraries/libstratosphere/source/htcfs/htcfs_file_service_object.cpp b/libraries/libstratosphere/source/htcfs/htcfs_file_service_object.cpp new file mode 100644 index 000000000..edae19ad0 --- /dev/null +++ b/libraries/libstratosphere/source/htcfs/htcfs_file_service_object.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcfs_file_service_object.hpp" + +namespace ams::htcfs { + + FileServiceObject::FileServiceObject(s32 handle) : m_handle(handle) { /* ... */ } + + FileServiceObject::~FileServiceObject() { + /* TODO */ + AMS_ABORT("htcfs::GetClient().CloseFile(m_handle);"); + } + + Result FileServiceObject::ReadFile(ams::sf::Out out, s64 offset, const ams::sf::OutNonSecureBuffer &buffer, ams::fs::ReadOption option) { + AMS_ABORT("FileServiceObject::ReadFile"); + } + + Result FileServiceObject::WriteFile(s64 offset, const ams::sf::InNonSecureBuffer &buffer, ams::fs::WriteOption option) { + AMS_ABORT("FileServiceObject::WriteFile"); + } + + Result FileServiceObject::GetFileSize(ams::sf::Out out) { + AMS_ABORT("FileServiceObject::GetFileSize"); + } + + Result FileServiceObject::SetFileSize(s64 size) { + AMS_ABORT("FileServiceObject::SetFileSize"); + } + + Result FileServiceObject::FlushFile() { + AMS_ABORT("FileServiceObject::FlushFile"); + } + + Result FileServiceObject::SetPriorityForFile(s32 priority) { + AMS_ABORT("FileServiceObject::SetPriorityForFile"); + } + + Result FileServiceObject::GetPriorityForFile(ams::sf::Out out) { + AMS_ABORT("FileServiceObject::GetPriorityForFile"); + } + +} diff --git a/libraries/libstratosphere/source/htcfs/htcfs_file_service_object.hpp b/libraries/libstratosphere/source/htcfs/htcfs_file_service_object.hpp new file mode 100644 index 000000000..b546efbb4 --- /dev/null +++ b/libraries/libstratosphere/source/htcfs/htcfs_file_service_object.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htcfs { + + class FileServiceObject { + private: + s32 m_handle; + public: + explicit FileServiceObject(s32 handle); + ~FileServiceObject(); + public: + Result ReadFile(ams::sf::Out out, s64 offset, const ams::sf::OutNonSecureBuffer &buffer, ams::fs::ReadOption option); + Result WriteFile(s64 offset, const ams::sf::InNonSecureBuffer &buffer, ams::fs::WriteOption option); + Result GetFileSize(ams::sf::Out out); + Result SetFileSize(s64 size); + Result FlushFile(); + Result SetPriorityForFile(s32 priority); + Result GetPriorityForFile(ams::sf::Out out); + }; + static_assert(tma::IsIFileAccessor); + +} diff --git a/libraries/libstratosphere/source/htcfs/htcfs_file_system_service_object.cpp b/libraries/libstratosphere/source/htcfs/htcfs_file_system_service_object.cpp new file mode 100644 index 000000000..e1e75af0d --- /dev/null +++ b/libraries/libstratosphere/source/htcfs/htcfs_file_system_service_object.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcfs_file_system_service_object.hpp" + +namespace ams::htcfs { + + Result FileSystemServiceObject::OpenFile(sf::Out> out, const tma::Path &path, bool case_sensitive) { + AMS_ABORT("FileSystemServiceObject::OpenFile"); + } + + Result FileSystemServiceObject::FileExists(sf::Out out, const tma::Path &path, bool case_sensitive) { + AMS_ABORT("FileSystemServiceObject::FileExists"); + } + + Result FileSystemServiceObject::DeleteFile(const tma::Path &path, bool case_sensitive) { + AMS_ABORT("FileSystemServiceObject::DeleteFile"); + } + + Result FileSystemServiceObject::RenameFile(const tma::Path &old_path, const tma::Path &new_path, bool case_sensitive) { + AMS_ABORT("FileSystemServiceObject::RenameFile"); + } + + Result FileSystemServiceObject::GetIOType(sf::Out out, const tma::Path &path, bool case_sensitive) { + AMS_ABORT("FileSystemServiceObject::GetIOType"); + } + + Result FileSystemServiceObject::OpenDirectory(sf::Out> out, const tma::Path &path, bool case_sensitive) { + AMS_ABORT("FileSystemServiceObject::OpenDirectory"); + } + + Result FileSystemServiceObject::DirectoryExists(sf::Out out, const tma::Path &path, bool case_sensitive) { + AMS_ABORT("FileSystemServiceObject::DirectoryExists"); + } + + Result FileSystemServiceObject::CreateDirectory(const tma::Path &path, bool case_sensitive) { + AMS_ABORT("FileSystemServiceObject::CreateDirectory"); + } + + Result FileSystemServiceObject::DeleteDirectory(const tma::Path &path, bool case_sensitive) { + AMS_ABORT("FileSystemServiceObject::DeleteDirectory"); + } + + Result FileSystemServiceObject::RenameDirectory(const tma::Path &old_path, const tma::Path &new_path, bool case_sensitive) { + AMS_ABORT("FileSystemServiceObject::RenameDirectory"); + } + + Result FileSystemServiceObject::CreateFile(const tma::Path &path, s64 size, bool case_sensitive) { + AMS_ABORT("FileSystemServiceObject::CreateFile"); + } + + Result FileSystemServiceObject::GetFileTimeStamp(sf::Out out_create, sf::Out out_access, sf::Out out_modify, const tma::Path &path, bool case_sensitive) { + AMS_ABORT("FileSystemServiceObject::GetFileTimeStamp"); + } + + Result FileSystemServiceObject::GetCaseSensitivePath(const tma::Path &path, const sf::OutBuffer &out) { + AMS_ABORT("FileSystemServiceObject::GetCaseSensitivePath"); + } + + Result FileSystemServiceObject::GetDiskFreeSpaceExW(sf::Out out_free, sf::Out out_total, sf::Out out_total_free, const tma::Path &path) { + AMS_ABORT("FileSystemServiceObject::GetDiskFreeSpaceExW"); + } + +} diff --git a/libraries/libstratosphere/source/htcfs/htcfs_file_system_service_object.hpp b/libraries/libstratosphere/source/htcfs/htcfs_file_system_service_object.hpp new file mode 100644 index 000000000..8c1546aa2 --- /dev/null +++ b/libraries/libstratosphere/source/htcfs/htcfs_file_system_service_object.hpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htcfs { + + constexpr inline bool DefaultCaseSensitivityForDeprecatedFileSystemServiceObject = false; + + class FileSystemServiceObject { + public: + Result OpenFile(sf::Out> out, const tma::Path &path, bool case_sensitive = DefaultCaseSensitivityForDeprecatedFileSystemServiceObject); + Result FileExists(sf::Out out, const tma::Path &path, bool case_sensitive = DefaultCaseSensitivityForDeprecatedFileSystemServiceObject); + Result DeleteFile(const tma::Path &path, bool case_sensitive = DefaultCaseSensitivityForDeprecatedFileSystemServiceObject); + Result RenameFile(const tma::Path &old_path, const tma::Path &new_path, bool case_sensitive = DefaultCaseSensitivityForDeprecatedFileSystemServiceObject); + Result GetIOType(sf::Out out, const tma::Path &path, bool case_sensitive = DefaultCaseSensitivityForDeprecatedFileSystemServiceObject); + Result OpenDirectory(sf::Out> out, const tma::Path &path, bool case_sensitive = DefaultCaseSensitivityForDeprecatedFileSystemServiceObject); + Result DirectoryExists(sf::Out out, const tma::Path &path, bool case_sensitive = DefaultCaseSensitivityForDeprecatedFileSystemServiceObject); + Result CreateDirectory(const tma::Path &path, bool case_sensitive = DefaultCaseSensitivityForDeprecatedFileSystemServiceObject); + Result DeleteDirectory(const tma::Path &path, bool case_sensitive = DefaultCaseSensitivityForDeprecatedFileSystemServiceObject); + Result RenameDirectory(const tma::Path &old_path, const tma::Path &new_path, bool case_sensitive = DefaultCaseSensitivityForDeprecatedFileSystemServiceObject); + Result CreateFile(const tma::Path &path, s64 size, bool case_sensitive = DefaultCaseSensitivityForDeprecatedFileSystemServiceObject); + Result GetFileTimeStamp(sf::Out out_create, sf::Out out_access, sf::Out out_modify, const tma::Path &path, bool case_sensitive = DefaultCaseSensitivityForDeprecatedFileSystemServiceObject); + Result GetCaseSensitivePath(const tma::Path &path, const sf::OutBuffer &out); + Result GetDiskFreeSpaceExW(sf::Out out_free, sf::Out out_total, sf::Out out_total_free, const tma::Path &path); + }; + static_assert(tma::IsIFileManager); + static_assert(tma::IsIDeprecatedFileManager); + +} diff --git a/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp b/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp new file mode 100644 index 000000000..643c98653 --- /dev/null +++ b/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htcfs { + + class HeaderFactory { + private: + s16 m_version; + public: + HeaderFactory() : m_version() { /* ... */ } + public: + /* ... */ + }; + +} diff --git a/libraries/libstratosphere/source/htcfs/htcfs_hipc_server.cpp b/libraries/libstratosphere/source/htcfs/htcfs_hipc_server.cpp new file mode 100644 index 000000000..f550aa9b4 --- /dev/null +++ b/libraries/libstratosphere/source/htcfs/htcfs_hipc_server.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcfs_client.hpp" +#include "htcfs_file_system_service_object.hpp" + +namespace ams::htcfs { + + namespace { + + static constexpr inline size_t NumServers = 1; + static constexpr inline size_t MaxSessions = 30; + static constexpr inline sm::ServiceName ServiceName = sm::ServiceName::Encode("htcfs"); + + struct ServerOptions { + static constexpr size_t PointerBufferSize = 0x1000; + static constexpr size_t MaxDomains = 0x10; + static constexpr size_t MaxDomainObjects = 0x100; + }; + + using ServerManager = sf::hipc::ServerManager; + + /* Service object. */ + ServerManager g_server_manager; + + /* Service object. */ + constinit sf::UnmanagedServiceObject g_htcfs_service_object; + constinit sf::UnmanagedServiceObject g_htcfs_deprecated_service_object; + + } + + void Initialize(htclow::HtclowManager *htclow_manager) { + /* Initialize the htcfs client library. */ + htcfs::InitializeClient(htclow_manager); + } + + void RegisterHipcServer() { + /* Register the service. */ + if (hos::GetVersion() >= hos::Version_6_0_0) { + R_ABORT_UNLESS(g_server_manager.RegisterObjectForServer(g_htcfs_service_object.GetShared(), ServiceName, MaxSessions)); + } else { + R_ABORT_UNLESS(g_server_manager.RegisterObjectForServer(g_htcfs_deprecated_service_object.GetShared(), ServiceName, MaxSessions)); + } + } + + void LoopHipcServer() { + /* Loop, servicing services. */ + g_server_manager.LoopProcess(); + } + +} diff --git a/libraries/libstratosphere/source/htcs/server/htcs_manager_service_object.cpp b/libraries/libstratosphere/source/htcs/server/htcs_manager_service_object.cpp index a01a6df4f..e3a328f0f 100644 --- a/libraries/libstratosphere/source/htcs/server/htcs_manager_service_object.cpp +++ b/libraries/libstratosphere/source/htcs/server/htcs_manager_service_object.cpp @@ -51,12 +51,12 @@ namespace ams::htcs::server { } Result ManagerServiceObject::RegisterProcessId(const sf::ClientProcessId &client_pid) { - /* NOTE: Nintend does nothing here. */ + /* NOTE: Nintendo does nothing here. */ return ResultSuccess(); } Result ManagerServiceObject::MonitorManager(const sf::ClientProcessId &client_pid) { - /* NOTE: Nintend does nothing here. */ + /* NOTE: Nintendo does nothing here. */ return ResultSuccess(); } diff --git a/stratosphere/htc/source/htc_main.cpp b/stratosphere/htc/source/htc_main.cpp index 4f4bd1c2a..893a98e5a 100644 --- a/stratosphere/htc/source/htc_main.cpp +++ b/stratosphere/htc/source/htc_main.cpp @@ -210,7 +210,7 @@ namespace ams::htc { } void HtcfsIpcThreadFunction(void *arg) { - //htcfs::LoopHipcServer(); + htcfs::LoopHipcServer(); } void HtcsIpcThreadFunction(void *arg) { @@ -254,8 +254,8 @@ int main(int argc, char **argv) os::SetThreadNamePointer(std::addressof(htc_ipc_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, HtcIpc)); /* Initialize the htcfs server. */ - //htcfs::Initialize(); - //htcfs::RegisterHipcServer(); + htcfs::Initialize(htclow_manager); + htcfs::RegisterHipcServer(); /* Create the htcfs ipc thread. */ os::ThreadType htcfs_ipc_thread; From 1bd0094bee8e06cccc14f0fab1225f12c89c9bc0 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 11 Feb 2021 05:51:03 -0800 Subject: [PATCH 057/280] htc: finish last code for Main() --- .../source/htc/server/htc_power_state_control.cpp | 4 ++++ stratosphere/htc/source/htc_main.cpp | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/libraries/libstratosphere/source/htc/server/htc_power_state_control.cpp b/libraries/libstratosphere/source/htc/server/htc_power_state_control.cpp index cb2e2bfee..68297f208 100644 --- a/libraries/libstratosphere/source/htc/server/htc_power_state_control.cpp +++ b/libraries/libstratosphere/source/htc/server/htc_power_state_control.cpp @@ -42,6 +42,10 @@ namespace ams::htc::server { g_is_suspended = false; } + void FinalizePowerStateMonitor() { + R_ABORT_UNLESS(g_pm_module.Finalize()); + } + void LoopMonitorPowerState() { /* Get the psc module's event pointer. */ auto *event = g_pm_module.GetEventPointer(); diff --git a/stratosphere/htc/source/htc_main.cpp b/stratosphere/htc/source/htc_main.cpp index 893a98e5a..a3e651a12 100644 --- a/stratosphere/htc/source/htc_main.cpp +++ b/stratosphere/htc/source/htc_main.cpp @@ -222,6 +222,8 @@ namespace ams::htc { namespace server { void InitializePowerStateMonitor(htclow::impl::DriverType driver_type, htclow::HtclowManager *htclow_manager); + void FinalizePowerStateMonitor(); + void LoopMonitorPowerState(); } @@ -297,7 +299,7 @@ int main(int argc, char **argv) os::DestroyThread(std::addressof(htc_ipc_thread)); /* Finalize psc monitor. */ - //htc::server::FinalizePowerStateMonitor(); + htc::server::FinalizePowerStateMonitor(); return 0; } From 5fc1981061495cfa8816a444f334e45d730d4572 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 12 Feb 2021 02:30:52 -0800 Subject: [PATCH 058/280] htc: fix htcfs sf definition --- .../include/stratosphere/tma/tma_i_file_manager.hpp | 12 ++++++------ .../libstratosphere/source/htcfs/htcfs_client.hpp | 2 +- .../htcfs/htcfs_file_system_service_object.cpp | 6 +++--- .../htcfs/htcfs_file_system_service_object.hpp | 6 +++--- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/libraries/libstratosphere/include/stratosphere/tma/tma_i_file_manager.hpp b/libraries/libstratosphere/include/stratosphere/tma/tma_i_file_manager.hpp index 6bd0190cf..2d5ed669c 100644 --- a/libraries/libstratosphere/include/stratosphere/tma/tma_i_file_manager.hpp +++ b/libraries/libstratosphere/include/stratosphere/tma/tma_i_file_manager.hpp @@ -21,15 +21,15 @@ /* NOTE: Minimum firmware version not enforced for any commands. */ #define AMS_TMA_I_FILE_MANAGER_INTERFACE_INFO(C, H) \ - AMS_SF_METHOD_INFO(C, H, 0, Result, OpenFile, (sf::Out> out, const tma::Path &path, bool case_sensitive), (out, path, case_sensitive)) \ + AMS_SF_METHOD_INFO(C, H, 0, Result, OpenFile, (sf::Out> out, const tma::Path &path, u32 open_mode, bool case_sensitive), (out, path, open_mode, case_sensitive)) \ AMS_SF_METHOD_INFO(C, H, 1, Result, FileExists, (sf::Out out, const tma::Path &path, bool case_sensitive), (out, path, case_sensitive)) \ AMS_SF_METHOD_INFO(C, H, 2, Result, DeleteFile, (const tma::Path &path, bool case_sensitive), (path, case_sensitive)) \ AMS_SF_METHOD_INFO(C, H, 3, Result, RenameFile, (const tma::Path &old_path, const tma::Path &new_path, bool case_sensitive), (old_path, new_path, case_sensitive)) \ AMS_SF_METHOD_INFO(C, H, 4, Result, GetIOType, (sf::Out out, const tma::Path &path, bool case_sensitive), (out, path, case_sensitive)) \ - AMS_SF_METHOD_INFO(C, H, 5, Result, OpenDirectory, (sf::Out> out, const tma::Path &path, bool case_sensitive), (out, path, case_sensitive)) \ + AMS_SF_METHOD_INFO(C, H, 5, Result, OpenDirectory, (sf::Out> out, const tma::Path &path, s32 open_mode, bool case_sensitive), (out, path, open_mode, case_sensitive)) \ AMS_SF_METHOD_INFO(C, H, 6, Result, DirectoryExists, (sf::Out out, const tma::Path &path, bool case_sensitive), (out, path, case_sensitive)) \ AMS_SF_METHOD_INFO(C, H, 7, Result, CreateDirectory, (const tma::Path &path, bool case_sensitive), (path, case_sensitive)) \ - AMS_SF_METHOD_INFO(C, H, 8, Result, DeleteDirectory, (const tma::Path &path, bool case_sensitive), (path, case_sensitive)) \ + AMS_SF_METHOD_INFO(C, H, 8, Result, DeleteDirectory, (const tma::Path &path, bool recursively, bool case_sensitive), (path, recursively, case_sensitive)) \ AMS_SF_METHOD_INFO(C, H, 9, Result, RenameDirectory, (const tma::Path &old_path, const tma::Path &new_path, bool case_sensitive), (old_path, new_path, case_sensitive)) \ AMS_SF_METHOD_INFO(C, H, 10, Result, CreateFile, (const tma::Path &path, s64 size, bool case_sensitive), (path, size, case_sensitive)) \ AMS_SF_METHOD_INFO(C, H, 11, Result, GetFileTimeStamp, (sf::Out out_create, sf::Out out_access, sf::Out out_modify, const tma::Path &path, bool case_sensitive), (out_create, out_access, out_modify, path, case_sensitive)) \ @@ -40,15 +40,15 @@ AMS_SF_DEFINE_INTERFACE(ams::tma, IFileManager, AMS_TMA_I_FILE_MANAGER_INTERFACE /* Prior to system version 6.0.0, case sensitivity was not parameterized. */ #define AMS_TMA_I_DEPRECATED_FILE_MANAGER_INTERFACE_INFO(C, H) \ - AMS_SF_METHOD_INFO(C, H, 0, Result, OpenFile, (sf::Out> out, const tma::Path &path), (out, path)) \ + AMS_SF_METHOD_INFO(C, H, 0, Result, OpenFile, (sf::Out> out, const tma::Path &path, u32 open_mode), (out, path, open_mode)) \ AMS_SF_METHOD_INFO(C, H, 1, Result, FileExists, (sf::Out out, const tma::Path &path), (out, path)) \ AMS_SF_METHOD_INFO(C, H, 2, Result, DeleteFile, (const tma::Path &path), (path)) \ AMS_SF_METHOD_INFO(C, H, 3, Result, RenameFile, (const tma::Path &old_path, const tma::Path &new_path), (old_path, new_path)) \ AMS_SF_METHOD_INFO(C, H, 4, Result, GetIOType, (sf::Out out, const tma::Path &path), (out, path)) \ - AMS_SF_METHOD_INFO(C, H, 5, Result, OpenDirectory, (sf::Out> out, const tma::Path &path), (out, path)) \ + AMS_SF_METHOD_INFO(C, H, 5, Result, OpenDirectory, (sf::Out> out, const tma::Path &path, s32 open_mode), (out, path, open_mode)) \ AMS_SF_METHOD_INFO(C, H, 6, Result, DirectoryExists, (sf::Out out, const tma::Path &path), (out, path)) \ AMS_SF_METHOD_INFO(C, H, 7, Result, CreateDirectory, (const tma::Path &path), (path)) \ - AMS_SF_METHOD_INFO(C, H, 8, Result, DeleteDirectory, (const tma::Path &path), (path)) \ + AMS_SF_METHOD_INFO(C, H, 8, Result, DeleteDirectory, (const tma::Path &path, bool recursively), (path, recursively)) \ AMS_SF_METHOD_INFO(C, H, 9, Result, RenameDirectory, (const tma::Path &old_path, const tma::Path &new_path), (old_path, new_path)) \ AMS_SF_METHOD_INFO(C, H, 10, Result, CreateFile, (const tma::Path &path, s64 size), (path, size)) \ AMS_SF_METHOD_INFO(C, H, 11, Result, GetFileTimeStamp, (sf::Out out_create, sf::Out out_access, sf::Out out_modify, const tma::Path &path), (out_create, out_access, out_modify, path)) \ diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client.hpp b/libraries/libstratosphere/source/htcfs/htcfs_client.hpp index 974342703..0b3da3889 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client.hpp @@ -23,7 +23,7 @@ namespace ams::htcfs { private: ClientImpl m_impl; public: - Client(htclow::HtclowManager *manager); + Client(htclow::HtclowManager *manager) : m_impl(manager) { /* ... */ } }; void InitializeClient(htclow::HtclowManager *manager); diff --git a/libraries/libstratosphere/source/htcfs/htcfs_file_system_service_object.cpp b/libraries/libstratosphere/source/htcfs/htcfs_file_system_service_object.cpp index e1e75af0d..353845c5b 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_file_system_service_object.cpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_file_system_service_object.cpp @@ -18,7 +18,7 @@ namespace ams::htcfs { - Result FileSystemServiceObject::OpenFile(sf::Out> out, const tma::Path &path, bool case_sensitive) { + Result FileSystemServiceObject::OpenFile(sf::Out> out, const tma::Path &path, u32 open_mode, bool case_sensitive) { AMS_ABORT("FileSystemServiceObject::OpenFile"); } @@ -38,7 +38,7 @@ namespace ams::htcfs { AMS_ABORT("FileSystemServiceObject::GetIOType"); } - Result FileSystemServiceObject::OpenDirectory(sf::Out> out, const tma::Path &path, bool case_sensitive) { + Result FileSystemServiceObject::OpenDirectory(sf::Out> out, const tma::Path &path, s32 open_mode, bool case_sensitive) { AMS_ABORT("FileSystemServiceObject::OpenDirectory"); } @@ -50,7 +50,7 @@ namespace ams::htcfs { AMS_ABORT("FileSystemServiceObject::CreateDirectory"); } - Result FileSystemServiceObject::DeleteDirectory(const tma::Path &path, bool case_sensitive) { + Result FileSystemServiceObject::DeleteDirectory(const tma::Path &path, bool recursively, bool case_sensitive) { AMS_ABORT("FileSystemServiceObject::DeleteDirectory"); } diff --git a/libraries/libstratosphere/source/htcfs/htcfs_file_system_service_object.hpp b/libraries/libstratosphere/source/htcfs/htcfs_file_system_service_object.hpp index 8c1546aa2..3f7bb366f 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_file_system_service_object.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_file_system_service_object.hpp @@ -22,15 +22,15 @@ namespace ams::htcfs { class FileSystemServiceObject { public: - Result OpenFile(sf::Out> out, const tma::Path &path, bool case_sensitive = DefaultCaseSensitivityForDeprecatedFileSystemServiceObject); + Result OpenFile(sf::Out> out, const tma::Path &path, u32 open_mode, bool case_sensitive = DefaultCaseSensitivityForDeprecatedFileSystemServiceObject); Result FileExists(sf::Out out, const tma::Path &path, bool case_sensitive = DefaultCaseSensitivityForDeprecatedFileSystemServiceObject); Result DeleteFile(const tma::Path &path, bool case_sensitive = DefaultCaseSensitivityForDeprecatedFileSystemServiceObject); Result RenameFile(const tma::Path &old_path, const tma::Path &new_path, bool case_sensitive = DefaultCaseSensitivityForDeprecatedFileSystemServiceObject); Result GetIOType(sf::Out out, const tma::Path &path, bool case_sensitive = DefaultCaseSensitivityForDeprecatedFileSystemServiceObject); - Result OpenDirectory(sf::Out> out, const tma::Path &path, bool case_sensitive = DefaultCaseSensitivityForDeprecatedFileSystemServiceObject); + Result OpenDirectory(sf::Out> out, const tma::Path &path, s32 open_mode, bool case_sensitive = DefaultCaseSensitivityForDeprecatedFileSystemServiceObject); Result DirectoryExists(sf::Out out, const tma::Path &path, bool case_sensitive = DefaultCaseSensitivityForDeprecatedFileSystemServiceObject); Result CreateDirectory(const tma::Path &path, bool case_sensitive = DefaultCaseSensitivityForDeprecatedFileSystemServiceObject); - Result DeleteDirectory(const tma::Path &path, bool case_sensitive = DefaultCaseSensitivityForDeprecatedFileSystemServiceObject); + Result DeleteDirectory(const tma::Path &path, bool recursively, bool case_sensitive = DefaultCaseSensitivityForDeprecatedFileSystemServiceObject); Result RenameDirectory(const tma::Path &old_path, const tma::Path &new_path, bool case_sensitive = DefaultCaseSensitivityForDeprecatedFileSystemServiceObject); Result CreateFile(const tma::Path &path, s64 size, bool case_sensitive = DefaultCaseSensitivityForDeprecatedFileSystemServiceObject); Result GetFileTimeStamp(sf::Out out_create, sf::Out out_access, sf::Out out_modify, const tma::Path &path, bool case_sensitive = DefaultCaseSensitivityForDeprecatedFileSystemServiceObject); From f28a410ba043ef74c9f1c7d16d65962024ac49fe Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 12 Feb 2021 06:08:16 -0800 Subject: [PATCH 059/280] htc: fixes, can now enter ReadyState with wip starlink code --- .../source/htc/server/htc_htcmisc_impl.cpp | 2 - .../source/htcfs/htcfs_client_impl.cpp | 166 ++++++++++++++++++ .../source/htcfs/htcfs_client_impl.hpp | 11 +- .../source/htcfs/htcfs_header_factory.hpp | 3 +- .../source/htcfs/htcfs_hipc_server.cpp | 2 +- .../ctrl/htclow_service_channel_parser.cpp | 4 +- stratosphere/htc/source/htc_main.cpp | 1 - 7 files changed, 182 insertions(+), 7 deletions(-) create mode 100644 libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp diff --git a/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp b/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp index 632c0e446..3ed5d71e7 100644 --- a/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp +++ b/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp @@ -111,7 +111,6 @@ namespace ams::htc::server { if (m_rpc_client.WaitAny(htclow::ChannelState_Disconnected, m_cancel_event.GetBase()) != 0) { break; } - } } @@ -150,7 +149,6 @@ namespace ams::htc::server { if (m_rpc_server.WaitAny(htclow::ChannelState_Disconnected, m_cancel_event.GetBase()) != 0) { break; } - } } diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp new file mode 100644 index 000000000..b7ff385be --- /dev/null +++ b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcfs_client_impl.hpp" +#include "../htclow/htclow_default_channel_config.hpp" + +namespace ams::htcfs { + + namespace { + + /* TODO: Move to a header? */ + constexpr u16 RpcChannelId = 0; + + alignas(os::ThreadStackAlignment) constinit u8 g_monitor_thread_stack[os::MemoryPageSize]; + + constinit u8 g_cache[32_KB]; + + } + + ClientImpl::ClientImpl(htclow::HtclowManager *manager) + : m_htclow_manager(manager), + m_cache_manager(g_cache, sizeof(g_cache)), + m_header_factory(), + m_mutex(), + m_module(htclow::ModuleId::Htcfs), + m_rpc_channel(manager), + m_data_channel(manager), + m_connected(false), + m_event(os::EventClearMode_ManualClear) + { + /* Start our thread. */ + this->Start(); + } + + void ClientImpl::Start() { + /* Create our thread. */ + os::CreateThread(std::addressof(m_monitor_thread), ThreadEntry, this, g_monitor_thread_stack, sizeof(g_monitor_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(htc, HtcfsMonitor)); + + /* Set thread name pointer. */ + os::SetThreadNamePointer(std::addressof(m_monitor_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, HtcfsMonitor)); + + /* Start our thread. */ + os::StartThread(std::addressof(m_monitor_thread)); + } + + void ClientImpl::Cancel() { + /* Signal our event. */ + m_event.Signal(); + } + + void ClientImpl::Wait() { + /* Wait for and destroy our thread. */ + os::WaitThread(std::addressof(m_monitor_thread)); + os::DestroyThread(std::addressof(m_monitor_thread)); + } + + void ClientImpl::ThreadBody() { + /* Loop forever, until we're cancelled. */ + while (!m_event.TryWait()) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Open our channel. */ + R_ABORT_UNLESS(m_rpc_channel.Open(std::addressof(m_module), RpcChannelId)); + + /* Ensure that we clean up our channel when we're done. */ + ON_SCOPE_EXIT { + m_rpc_channel.Close(); + m_cache_manager.Invalidate(); + }; + + /* Set our channel config and buffers. */ + m_rpc_channel.SetConfig(htclow::DefaultChannelConfig); + m_rpc_channel.SetReceiveBuffer(m_receive_buffer, sizeof(m_receive_buffer)); + m_rpc_channel.SetSendBuffer(m_send_buffer, sizeof(m_send_buffer)); + + /* Wait for our channel state to be connectable. */ + if (this->WaitAny(htclow::ChannelState_Connectable, m_event.GetBase()) != 0) { + return; + } + + /* Ensure that when we're done, we reset our connected status. */ + ON_SCOPE_EXIT { m_connected = false; }; + + /* Try to connect. */ + const Result conn_result = m_rpc_channel.Connect(); + if (R_FAILED(conn_result)) { + /* DEBUG */ + R_ABORT_UNLESS(conn_result); + continue; + } + + /* Ensure that we manage our connection correctly. */ + auto conn_guard = SCOPE_GUARD { m_rpc_channel.Shutdown(); }; + + /* Try to set up the protocol. */ + const Result setup_result = this->SetUpProtocol(); + if (R_FAILED(setup_result)) { + R_ABORT_UNLESS(setup_result); + continue; + } + + /* We're properly connected now. */ + m_connected = true; + conn_guard.Cancel(); + + /* Tear down the protocol when we're done processing. */ + ON_SCOPE_EXIT { this->TearDownProtocol(); }; + + /* Wait to become disconnected. */ + if (this->WaitAny(htclow::ChannelState_Disconnected, m_event.GetBase()) != 0) { + break; + } + } + } + + int ClientImpl::WaitAny(htclow::ChannelState state, os::EventType *event) { + /* Wait. */ + int idx = 0; + while (m_rpc_channel.GetChannelState() != state) { + /* Get the channel state event. */ + os::EventType *channel_state_event = m_rpc_channel.GetChannelStateEvent(); + + /* Perform wait with lock temporarily not held. */ + { + m_mutex.Unlock(); + idx = os::WaitAny(channel_state_event, event); + m_mutex.Lock(); + } + + /* If we're cancel-signalled, we're done. */ + if (idx != 0) { + break; + } + + /* Clear the channel state event. */ + os::ClearEvent(channel_state_event);; + } + return idx; + } + + Result ClientImpl::SetUpProtocol() { + /* TODO: Actual client <-> host RPC here. */ + m_header_factory.SetVersion(1); + return ResultSuccess(); + } + + void ClientImpl::TearDownProtocol() { + /* Set the header factory version to zero. */ + m_header_factory.SetVersion(0); + } + +} diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp index fd5508c62..8403186d1 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp @@ -32,11 +32,15 @@ namespace ams::htcfs { HeaderFactory m_header_factory; os::SdkMutex m_mutex; htclow::Module m_module; - htclow::Channel m_channel; + htclow::Channel m_rpc_channel; htclow::Channel m_data_channel; bool m_connected; os::ThreadType m_monitor_thread; os::Event m_event; + private: + static void ThreadEntry(void *arg) { static_cast(arg)->ThreadBody(); } + + void ThreadBody(); public: ClientImpl(htclow::HtclowManager *manager); @@ -48,6 +52,11 @@ namespace ams::htcfs { void Start(); void Cancel(); void Wait(); + private: + int WaitAny(htclow::ChannelState state, os::EventType *event); + + Result SetUpProtocol(); + void TearDownProtocol(); }; } diff --git a/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp b/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp index 643c98653..8fe691f5b 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp @@ -24,7 +24,8 @@ namespace ams::htcfs { public: HeaderFactory() : m_version() { /* ... */ } public: - /* ... */ + s16 GetVersion() const { return m_version; } + void SetVersion(s16 version) { m_version = version; } }; } diff --git a/libraries/libstratosphere/source/htcfs/htcfs_hipc_server.cpp b/libraries/libstratosphere/source/htcfs/htcfs_hipc_server.cpp index f550aa9b4..c23db56f2 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_hipc_server.cpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_hipc_server.cpp @@ -23,7 +23,7 @@ namespace ams::htcfs { static constexpr inline size_t NumServers = 1; static constexpr inline size_t MaxSessions = 30; - static constexpr inline sm::ServiceName ServiceName = sm::ServiceName::Encode("htcfs"); + static constexpr inline sm::ServiceName ServiceName = sm::ServiceName::Encode("file_io"); struct ServerOptions { static constexpr size_t PointerBufferSize = 0x1000; diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_service_channel_parser.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_service_channel_parser.cpp index 2a64382ce..48d27b132 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_service_channel_parser.cpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_service_channel_parser.cpp @@ -21,6 +21,8 @@ namespace ams::htclow::ctrl { namespace { + constexpr auto JsonParseFlags = rapidjson::kParseTrailingCommasFlag | rapidjson::kParseInsituFlag; + void ParseBody(s16 *out_version, const char **out_channels, int *out_num_channels, int max_channels, void *str, size_t str_size) { /* Create JSON handler. */ JsonHandler json_handler(out_version, out_channels, out_num_channels, max_channels); @@ -32,7 +34,7 @@ namespace ams::htclow::ctrl { rapidjson::InsituStringStream json_stream(static_cast(str)); /* Parse the json. */ - json_reader.Parse(json_stream, json_handler); + json_reader.Parse(json_stream, json_handler); } constexpr bool IsNumericCharacter(char c) { diff --git a/stratosphere/htc/source/htc_main.cpp b/stratosphere/htc/source/htc_main.cpp index a3e651a12..5f2bc88fd 100644 --- a/stratosphere/htc/source/htc_main.cpp +++ b/stratosphere/htc/source/htc_main.cpp @@ -303,4 +303,3 @@ int main(int argc, char **argv) return 0; } - From 99a38dce32c009f141e60d8aaca7a74dfddf05d8 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 12 Feb 2021 07:03:55 -0800 Subject: [PATCH 060/280] htc: fix event wait loops for rpc clients --- .../htc/server/rpc/htc_htcmisc_rpc_server.cpp | 16 +++++++--------- .../source/htc/server/rpc/htc_rpc_client.cpp | 16 +++++++--------- .../source/htcfs/htcfs_client_impl.cpp | 2 +- .../source/htcs/impl/htcs_monitor.cpp | 5 ++++- 4 files changed, 19 insertions(+), 20 deletions(-) diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.cpp b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.cpp index d0bd22eff..05948a064 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.cpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.cpp @@ -84,21 +84,19 @@ namespace ams::htc::server::rpc { if (os::TryWaitEvent(event)) { return 1; } - if (m_driver->GetChannelState(m_channel_id) == state) { - return 0; - } /* Wait. */ - while (true) { + while (m_driver->GetChannelState(m_channel_id) != state) { const auto idx = os::WaitAny(m_driver->GetChannelStateEvent(m_channel_id), event); - if (idx == 0) { - if (m_driver->GetChannelState(m_channel_id) == state) { - return 0; - } - } else { + if (idx != 0) { return idx; } + + /* Clear the channel state event. */ + os::ClearEvent(m_driver->GetChannelStateEvent(m_channel_id)); } + + return 0; } Result HtcmiscRpcServer::ReceiveThread() { diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp index 00cb9b352..b0250db4a 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp @@ -184,21 +184,19 @@ namespace ams::htc::server::rpc { if (os::TryWaitEvent(event)) { return 1; } - if (m_driver->GetChannelState(m_channel_id) == state) { - return 0; - } /* Wait. */ - while (true) { + while (m_driver->GetChannelState(m_channel_id) != state) { const auto idx = os::WaitAny(m_driver->GetChannelStateEvent(m_channel_id), event); - if (idx == 0) { - if (m_driver->GetChannelState(m_channel_id) == state) { - return 0; - } - } else { + if (idx != 0) { return idx; } + + /* Clear the channel state event. */ + os::ClearEvent(m_driver->GetChannelStateEvent(m_channel_id)); } + + return 0; } Result RpcClient::ReceiveThread() { diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp index b7ff385be..7d75464fb 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp @@ -147,7 +147,7 @@ namespace ams::htcfs { } /* Clear the channel state event. */ - os::ClearEvent(channel_state_event);; + os::ClearEvent(channel_state_event); } return idx; } diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_monitor.cpp b/libraries/libstratosphere/source/htcs/impl/htcs_monitor.cpp index cd949983b..7c9423c3c 100644 --- a/libraries/libstratosphere/source/htcs/impl/htcs_monitor.cpp +++ b/libraries/libstratosphere/source/htcs/impl/htcs_monitor.cpp @@ -77,7 +77,10 @@ namespace ams::htcs::impl { } /* Start the rpc client. */ - if (R_FAILED(m_rpc_client->Start())) { + const Result start_result = m_rpc_client->Start(); + if (R_FAILED(start_result)) { + /* DEBUG */ + R_ABORT_UNLESS(start_result); continue; } From 5c97469348a7437919982c6af815f12b7a7313d4 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 12 Feb 2021 22:07:34 -0800 Subject: [PATCH 061/280] htc: implement htcfs protocol bringup --- .../source/htcfs/htcfs_client_impl.cpp | 124 +++++++++++++++++- .../source/htcfs/htcfs_client_impl.hpp | 11 ++ .../source/htcfs/htcfs_header_factory.hpp | 85 ++++++++++++ .../source/htcfs/htcfs_result.hpp | 54 ++++++++ .../source/htclow/htclow_channel.cpp | 2 +- .../source/htclow/htclow_channel.hpp | 2 +- .../htclow/mux/htclow_mux_ring_buffer.cpp | 2 +- .../htclow/mux/htclow_mux_send_buffer.cpp | 4 +- .../libvapours/include/vapours/results.hpp | 1 + .../include/vapours/results/htcfs_results.hpp | 38 ++++++ 10 files changed, 316 insertions(+), 7 deletions(-) create mode 100644 libraries/libstratosphere/source/htcfs/htcfs_result.hpp create mode 100644 libraries/libvapours/include/vapours/results/htcfs_results.hpp diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp index 7d75464fb..ca218350e 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp @@ -15,6 +15,7 @@ */ #include #include "htcfs_client_impl.hpp" +#include "htcfs_result.hpp" #include "../htclow/htclow_default_channel_config.hpp" namespace ams::htcfs { @@ -153,8 +154,19 @@ namespace ams::htcfs { } Result ClientImpl::SetUpProtocol() { - /* TODO: Actual client <-> host RPC here. */ - m_header_factory.SetVersion(1); + /* Get the maximum supported protocol on the host side. */ + s16 max_host_protocol; + R_TRY(this->GetMaxProtocolVersion(std::addressof(max_host_protocol))); + + /* Verify that the host protocol is >= 0. */ + R_UNLESS(max_host_protocol >= 0, htcfs::ResultUnsupportedProtocolVersion()); + + /* Inform the host what protocol we're using. */ + const auto use_version = std::min(MaxProtocolVersion, max_host_protocol); + R_TRY(this->SetProtocolVersion(use_version)); + + /* Set the version in our header factory. */ + m_header_factory.SetVersion(use_version); return ResultSuccess(); } @@ -163,4 +175,112 @@ namespace ams::htcfs { m_header_factory.SetVersion(0); } + Result ClientImpl::CheckResponseHeaderWithoutVersion(const Header &response, PacketType packet_type) { + /* Check the protocol. */ + R_UNLESS(response.protocol == HtcfsProtocol, htcfs::ResultUnexpectedResponseProtocolId()); + + /* Check the packet category. */ + R_UNLESS(response.packet_category == PacketCategory::Response, htcfs::ResultUnexpectedResponsePacketCategory()); + + /* Check the type. */ + R_UNLESS(response.packet_type == packet_type, htcfs::ResultUnexpectedResponsePacketType()); + + return ResultSuccess(); + } + + Result ClientImpl::GetMaxProtocolVersion(s16 *out) { + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + m_header_factory.MakeGetMaxProtocolVersionHeader(std::addressof(request)); + + /* Send the request to the host. */ + R_TRY(this->SendToRpcChannel(std::addressof(request), sizeof(request))); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeaderWithoutVersion(response, request.packet_type)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(response.params[0])); + + /* Set the maximum protocol version. */ + *out = response.params[1]; + + return ResultSuccess(); + } + + Result ClientImpl::SetProtocolVersion(s16 version) { + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + m_header_factory.MakeSetProtocolVersionHeader(std::addressof(request), version); + + /* Send the request to the host. */ + R_TRY(this->SendToRpcChannel(std::addressof(request), sizeof(request))); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeaderWithoutVersion(response, request.packet_type)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(response.params[0])); + + return ResultSuccess(); + } + + Result ClientImpl::SendToRpcChannel(const void *src, s64 size) { + return this->SendToHtclow(src, size, std::addressof(m_rpc_channel)); + } + + Result ClientImpl::ReceiveFromRpcChannel(void *dst, s64 size) { + return this->ReceiveFromHtclow(dst, size, std::addressof(m_rpc_channel)); + } + + Result ClientImpl::SendToHtclow(const void *src, s64 size, htclow::Channel *channel) { + /* Check size. */ + R_UNLESS(size >= 0, htcfs::ResultInvalidArgument()); + + /* Iteratively send. */ + s64 sent; + for (s64 total = 0; total < size; total += sent) { + /* Send the current batch of data. */ + R_TRY(channel->Send(std::addressof(sent), static_cast(src) + total, size - total)); + + /* Check that we sent the right amount. */ + R_UNLESS(sent == size - total, htcfs::ResultHtclowChannelClosed()); + } + + /* Flush. */ + R_TRY(channel->Flush()); + + return ResultSuccess(); + } + + Result ClientImpl::ReceiveFromHtclow(void *dst, s64 size, htclow::Channel *channel) { + /* Check size. */ + R_UNLESS(size >= 0, htcfs::ResultInvalidArgument()); + + /* Iteratively receive. */ + s64 received; + for (s64 total = 0; total < size; total += received) { + /* Receive the current batch of data. */ + R_TRY(channel->Receive(std::addressof(received), static_cast(dst) + total, size - total, htclow::ReceiveOption_ReceiveAllData)); + + /* Check that we received the right amount. */ + R_UNLESS(received == size - total, htcfs::ResultHtclowChannelClosed()); + } + + /* Flush. */ + R_TRY(channel->Flush()); + + return ResultSuccess(); + } + } diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp index 8403186d1..4e84b404e 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp @@ -57,6 +57,17 @@ namespace ams::htcfs { Result SetUpProtocol(); void TearDownProtocol(); + + Result CheckResponseHeaderWithoutVersion(const Header &response, PacketType packet_type); + + Result GetMaxProtocolVersion(s16 *out); + Result SetProtocolVersion(s16 version); + + Result SendToRpcChannel(const void *src, s64 size); + Result ReceiveFromRpcChannel(void *dst, s64 size); + + Result SendToHtclow(const void *src, s64 size, htclow::Channel *channel); + Result ReceiveFromHtclow(void *dst, s64 size, htclow::Channel *channel); }; } diff --git a/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp b/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp index 8fe691f5b..2556abdfb 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp @@ -18,6 +18,63 @@ namespace ams::htcfs { + constexpr inline s16 HtcfsProtocol = 1; + constexpr inline s16 MaxProtocolVersion = 1; + + enum class PacketCategory : u16 { + Request = 0, + Response = 1, + }; + + enum class PacketType : u16 { + GetMaxProtocolVersion = 0, + SetProtocolVersion = 1, + GetEntryType = 16, + OpenFile = 32, + CloseFile = 33, + GetPriorityForFile = 34, + SetPriorityForFile = 35, + CreateFile = 36, + DeleteFile = 37, + RenameFile = 38, + FileExists = 39, + ReadFile = 40, + WriteFile = 41, + FlushFile = 42, + GetFileTimeStamp = 43, + GetFileSize = 44, + SetFileSize = 45, + ReadFileLarge = 46, + WriteFileLarge = 47, + OpenDirectory = 48, + CloseDirectory = 49, + GetPriorityForDirectory = 50, + SetPriorityForDirectory = 51, + CreateDirectory = 52, + DeleteDirectory = 53, + RenameDirectory = 54, + DirectoryExists = 55, + ReadDirectory = 56, + GetEntryCount = 57, + GetWorkingDirectory = 58, + GetWorkingDirectorySize = 59, + GetCaseSensitivePath = 60, + GetDiskFreeSpace = 61, + ReadDirectoryLarge = 62, + }; + + struct Header { + s16 protocol; + s16 version; + PacketCategory packet_category; + PacketType packet_type; + s64 body_size; + s64 params[5]; + s64 reserved; + }; + static_assert(util::is_pod
::value); + static_assert(sizeof(Header) == 0x40); + class HeaderFactory { private: s16 m_version; @@ -26,6 +83,34 @@ namespace ams::htcfs { public: s16 GetVersion() const { return m_version; } void SetVersion(s16 version) { m_version = version; } + public: + ALWAYS_INLINE void MakeRequestHeader(Header *out, PacketType packet_type, s64 body_size = 0, s64 param0 = 0, s64 param1 = 0, s64 param2 = 0, s64 param3 = 0, s64 param4 = 0) { + /* Set protocol and version. */ + out->protocol = HtcfsProtocol; + out->version = m_version; + + /* Set type and category. */ + out->packet_category = PacketCategory::Request; + out->packet_type = packet_type; + + /* Set body size. */ + out->body_size = body_size; + + /* Set params. */ + out->params[0] = param0; + out->params[1] = param1; + out->params[2] = param2; + out->params[3] = param3; + out->params[4] = param4; + } + + void MakeGetMaxProtocolVersionHeader(Header *out) { + return this->MakeRequestHeader(out, PacketType::GetMaxProtocolVersion, 0); + } + + void MakeSetProtocolVersionHeader(Header *out, s16 version) { + return this->MakeRequestHeader(out, PacketType::SetProtocolVersion, 0, version); + } }; } diff --git a/libraries/libstratosphere/source/htcfs/htcfs_result.hpp b/libraries/libstratosphere/source/htcfs/htcfs_result.hpp new file mode 100644 index 000000000..8346e66f6 --- /dev/null +++ b/libraries/libstratosphere/source/htcfs/htcfs_result.hpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "htcfs_client_impl.hpp" + +namespace ams::htcfs { + + enum class HtcfsResult { + Success = 0, + UnknownError = 1, + UnsupportedProtocolVersion = 2, + InvalidRequest = 3, + InvalidHandle = 4, + OutOfHandle = 5, + }; + + inline Result ConvertHtcfsResult(HtcfsResult result) { + switch (result) { + case HtcfsResult::Success: + return ResultSuccess(); + case HtcfsResult::UnknownError: + return htcfs::ResultUnknownError(); + case HtcfsResult::UnsupportedProtocolVersion: + return htcfs::ResultUnsupportedProtocolVersion(); + case HtcfsResult::InvalidRequest: + return htcfs::ResultInvalidRequest(); + case HtcfsResult::InvalidHandle: + return htcfs::ResultInvalidHandle(); + case HtcfsResult::OutOfHandle: + return htcfs::ResultOutOfHandle(); + default: + return htcfs::ResultUnknownError(); + } + } + + inline Result ConvertHtcfsResult(s64 param) { + return ConvertHtcfsResult(static_cast(param)); + } + +} diff --git a/libraries/libstratosphere/source/htclow/htclow_channel.cpp b/libraries/libstratosphere/source/htclow/htclow_channel.cpp index 830369e25..3c9046694 100644 --- a/libraries/libstratosphere/source/htclow/htclow_channel.cpp +++ b/libraries/libstratosphere/source/htclow/htclow_channel.cpp @@ -114,7 +114,7 @@ namespace ams::htclow { return ResultSuccess(); } - Result Channel::Send(s64 *out, const void *src, s64 size, ReceiveOption option) { + Result Channel::Send(s64 *out, const void *src, s64 size) { /* Check pre-conditions. */ AMS_ASSERT(util::IsIntValueRepresentable(size)); diff --git a/libraries/libstratosphere/source/htclow/htclow_channel.hpp b/libraries/libstratosphere/source/htclow/htclow_channel.hpp index 2d99cc7c4..908384809 100644 --- a/libraries/libstratosphere/source/htclow/htclow_channel.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_channel.hpp @@ -40,7 +40,7 @@ namespace ams::htclow { void Shutdown(); Result Receive(s64 *out, void *dst, s64 size, ReceiveOption option); - Result Send(s64 *out, const void *src, s64 size, ReceiveOption option); + Result Send(s64 *out, const void *src, s64 size); void SetConfig(const ChannelConfig &config); void SetReceiveBuffer(void *buf, size_t size); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.cpp index a772ce93c..40b780de8 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.cpp @@ -67,7 +67,7 @@ namespace ams::htclow::mux { /* Determine position and copy sizes. */ const size_t pos = (m_data_size + m_offset) % m_buffer_size; - const size_t left = m_buffer_size - pos; + const size_t left = std::min(m_buffer_size - pos, size); const size_t over = size - left; /* Copy. */ diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp index 214bc3032..a7add6b21 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp @@ -82,7 +82,7 @@ namespace ams::htclow::mux { /* Check that we have data. */ const auto ring_buffer_data_size = m_ring_buffer.GetDataSize(); - if (ring_buffer_data_size > 0) { + if (ring_buffer_data_size == 0) { return false; } @@ -102,7 +102,7 @@ namespace ams::htclow::mux { const auto data_size = std::min(sendable_size, m_max_packet_size); /* Make data packet header. */ - this->MakeDataPacketHeader(header, data_size, m_version, max_data, share); + this->MakeDataPacketHeader(header, data_size, m_version, max_data, offset); /* Copy the data. */ R_ABORT_UNLESS(m_ring_buffer.Copy(body, data_size)); diff --git a/libraries/libvapours/include/vapours/results.hpp b/libraries/libvapours/include/vapours/results.hpp index 23b7e2e9a..003bacf25 100644 --- a/libraries/libvapours/include/vapours/results.hpp +++ b/libraries/libvapours/include/vapours/results.hpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libvapours/include/vapours/results/htcfs_results.hpp b/libraries/libvapours/include/vapours/results/htcfs_results.hpp new file mode 100644 index 000000000..cfad9663b --- /dev/null +++ b/libraries/libvapours/include/vapours/results/htcfs_results.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htcfs { + + R_DEFINE_NAMESPACE_RESULT_MODULE(31); + + R_DEFINE_ERROR_RESULT(InvalidArgument, 3); + + R_DEFINE_ERROR_RESULT(HtclowChannelClosed, 101); + + R_DEFINE_ERROR_RESULT(UnexpectedResponseProtocolId, 111); + R_DEFINE_ERROR_RESULT(UnexpectedResponseProtocolVersion, 112); + R_DEFINE_ERROR_RESULT(UnexpectedResponsePacketCategory, 113); + R_DEFINE_ERROR_RESULT(UnexpectedResponsePacketType, 114); + + R_DEFINE_ERROR_RESULT(UnknownError, 211); + R_DEFINE_ERROR_RESULT(UnsupportedProtocolVersion, 212); + R_DEFINE_ERROR_RESULT(InvalidRequest, 213); + R_DEFINE_ERROR_RESULT(InvalidHandle, 214); + R_DEFINE_ERROR_RESULT(OutOfHandle, 215); + +} From b37148752520cab312d2816e088d65736dfc52dc Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 13 Feb 2021 00:15:05 -0800 Subject: [PATCH 062/280] sf: optimize argument parsing for const LargeData & --- .../sf/impl/sf_impl_command_serialization.hpp | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp b/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp index d403aa23d..6e576b092 100644 --- a/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp @@ -412,9 +412,13 @@ namespace ams::sf::impl { size_t out_object_index; }; + template + using DecayForCommandMetaArguments = typename std::conditional::type> && !std::is_base_of::type>::value, T, typename std::decay::type>::type; + template struct CommandMetaInfo { public: + using ArgsTypeForInvoke = std::tuple...>; using ArgsType = std::tuple::type...>; using InDatas = TupleFilter::FilteredType; @@ -814,7 +818,7 @@ namespace ams::sf::impl { } /* Useful defines. */ - using ArgsType = typename CommandMeta::ArgsType; + using ArgsTypeForInvoke = typename CommandMeta::ArgsTypeForInvoke; using BufferArrayType = std::array; using OutRawHolderType = OutRawHolder; using OutHandleHolderType = OutHandleHolder; @@ -1000,7 +1004,7 @@ namespace ams::sf::impl { /* Argument deserialization. */ private: - template::type> + template::type> NX_CONSTEXPR T DeserializeArgumentImpl(const cmif::ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data, const OutRawHolderType &out_raw_holder, const BufferArrayType &buffers, OutHandleHolderType &out_handles_holder, InOutObjectHolderType &in_out_objects_holder) { constexpr auto Info = CommandMeta::ArgumentSerializationInfos[Index]; if constexpr (Info.arg_type == ArgumentType::InData) { @@ -1047,6 +1051,8 @@ namespace ams::sf::impl { constexpr auto Attributes = CommandMeta::BufferAttributes[Info.buffer_index]; if constexpr (Attributes & SfBufferAttr_In) { /* TODO: AMS_ABORT_UNLESS()? N does not bother. */ + static_assert(std::is_reference::value); + static_assert(std::is_const::type>::value); return *reinterpret_cast(buffers[Info.buffer_index].GetAddress()); } else if constexpr (Attributes & SfBufferAttr_Out) { return T(buffers[Info.buffer_index]); @@ -1063,12 +1069,12 @@ namespace ams::sf::impl { } template - NX_CONSTEXPR ArgsType DeserializeArgumentsImpl(const cmif::ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data, const OutRawHolderType &out_raw_holder, const BufferArrayType &buffers, OutHandleHolderType &out_handles_holder, InOutObjectHolderType &in_out_objects_holder, std::index_sequence) { - return ArgsType { DeserializeArgumentImpl(ctx, in_raw_data, out_raw_holder, buffers, out_handles_holder, in_out_objects_holder)..., }; + NX_CONSTEXPR ArgsTypeForInvoke DeserializeArgumentsImpl(const cmif::ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data, const OutRawHolderType &out_raw_holder, const BufferArrayType &buffers, OutHandleHolderType &out_handles_holder, InOutObjectHolderType &in_out_objects_holder, std::index_sequence) { + return ArgsTypeForInvoke { DeserializeArgumentImpl(ctx, in_raw_data, out_raw_holder, buffers, out_handles_holder, in_out_objects_holder)..., }; } public: - NX_CONSTEXPR ArgsType DeserializeArguments(const cmif::ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data, const OutRawHolderType &out_raw_holder, const BufferArrayType &buffers, OutHandleHolderType &out_handles_holder, InOutObjectHolderType &in_out_objects_holder) { - return DeserializeArgumentsImpl(ctx, in_raw_data, out_raw_holder, buffers, out_handles_holder, in_out_objects_holder, std::make_index_sequence::value>{}); + NX_CONSTEXPR ArgsTypeForInvoke DeserializeArguments(const cmif::ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data, const OutRawHolderType &out_raw_holder, const BufferArrayType &buffers, OutHandleHolderType &out_handles_holder, InOutObjectHolderType &in_out_objects_holder) { + return DeserializeArgumentsImpl(ctx, in_raw_data, out_raw_holder, buffers, out_handles_holder, in_out_objects_holder, std::make_index_sequence::value>{}); } }; @@ -1118,16 +1124,16 @@ namespace ams::sf::impl { /* Decoding/Invocation. */ { - typename CommandMeta::ArgsType args_tuple = ImplProcessorType::DeserializeArguments(ctx, in_raw_data, out_raw_holder, buffers, out_handles_holder, in_out_objects_holder); + typename CommandMeta::ArgsTypeForInvoke args_tuple = ImplProcessorType::DeserializeArguments(ctx, in_raw_data, out_raw_holder, buffers, out_handles_holder, in_out_objects_holder); /* Handle in process ID holder if relevant. */ if constexpr (CommandMeta::HasInProcessIdHolder) { /* TODO: More precise value than 32? */ - static_assert(std::tuple_size::value <= 32, "Commands must have <= 32 arguments"); + static_assert(std::tuple_size::value <= 32, "Commands must have <= 32 arguments"); os::ProcessId process_id{ctx.request.pid}; #define _SF_IMPL_PROCESSOR_MARSHAL_PROCESS_ID(n) do { \ - using ArgsType = typename CommandMeta::ArgsType; \ - if constexpr (n < std::tuple_size::value) { \ + using ArgsTypeForInvoke = typename CommandMeta::ArgsTypeForInvoke; \ + if constexpr (n < std::tuple_size::value) { \ if constexpr (CommandMeta::template IsInProcessIdHolderIndex) { \ R_TRY(MarshalProcessId(std::get(args_tuple), process_id)); \ } \ @@ -1149,7 +1155,7 @@ namespace ams::sf::impl { sf::IServiceObject * const this_ptr = ctx.srv_obj; const auto command_result = [this_ptr, invoke_impl, &args_tuple](std::index_sequence) ALWAYS_INLINE_LAMBDA { return invoke_impl(this_ptr, std::forward::type>(std::get(args_tuple))...); - }(std::make_index_sequence::value>()); + }(std::make_index_sequence::value>()); if (R_FAILED(command_result)) { cmif::PointerAndSize out_raw_data; From e79417c37c1a7f97c2dc198f29857a05649166ea Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 13 Feb 2021 01:57:24 -0800 Subject: [PATCH 063/280] htcfs: implement OpenDirectory/CloseDirectory --- .../sf/impl/sf_impl_command_serialization.hpp | 9 +- .../source/htcfs/htcfs_client.cpp | 1 - .../source/htcfs/htcfs_client.hpp | 3 + .../source/htcfs/htcfs_client_impl.cpp | 130 ++++++++++++++++++ .../source/htcfs/htcfs_client_impl.hpp | 11 ++ .../htcfs/htcfs_directory_service_object.cpp | 4 +- .../htcfs_file_system_service_object.cpp | 39 +++++- .../source/htcfs/htcfs_header_factory.hpp | 11 ++ .../include/vapours/results/htcfs_results.hpp | 14 +- 9 files changed, 209 insertions(+), 13 deletions(-) diff --git a/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp b/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp index 6e576b092..16c2ab463 100644 --- a/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp @@ -819,6 +819,7 @@ namespace ams::sf::impl { /* Useful defines. */ using ArgsTypeForInvoke = typename CommandMeta::ArgsTypeForInvoke; + using ArgsType = typename CommandMeta::ArgsType; using BufferArrayType = std::array; using OutRawHolderType = OutRawHolder; using OutHandleHolderType = OutHandleHolder; @@ -1004,8 +1005,8 @@ namespace ams::sf::impl { /* Argument deserialization. */ private: - template::type> - NX_CONSTEXPR T DeserializeArgumentImpl(const cmif::ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data, const OutRawHolderType &out_raw_holder, const BufferArrayType &buffers, OutHandleHolderType &out_handles_holder, InOutObjectHolderType &in_out_objects_holder) { + template::type> + NX_CONSTEXPR typename std::tuple_element::type DeserializeArgumentImpl(const cmif::ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data, const OutRawHolderType &out_raw_holder, const BufferArrayType &buffers, OutHandleHolderType &out_handles_holder, InOutObjectHolderType &in_out_objects_holder) { constexpr auto Info = CommandMeta::ArgumentSerializationInfos[Index]; if constexpr (Info.arg_type == ArgumentType::InData) { /* New in rawdata. */ @@ -1051,8 +1052,8 @@ namespace ams::sf::impl { constexpr auto Attributes = CommandMeta::BufferAttributes[Info.buffer_index]; if constexpr (Attributes & SfBufferAttr_In) { /* TODO: AMS_ABORT_UNLESS()? N does not bother. */ - static_assert(std::is_reference::value); - static_assert(std::is_const::type>::value); + using InvokeType = typename std::tuple_element::type; + static_assert(std::same_as); return *reinterpret_cast(buffers[Info.buffer_index].GetAddress()); } else if constexpr (Attributes & SfBufferAttr_Out) { return T(buffers[Info.buffer_index]); diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client.cpp b/libraries/libstratosphere/source/htcfs/htcfs_client.cpp index 0d871278d..27972f265 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client.cpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client.cpp @@ -43,5 +43,4 @@ namespace ams::htcfs { return GetReference(g_client_storage); } - } diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client.hpp b/libraries/libstratosphere/source/htcfs/htcfs_client.hpp index 0b3da3889..977917da4 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client.hpp @@ -24,6 +24,9 @@ namespace ams::htcfs { ClientImpl m_impl; public: Client(htclow::HtclowManager *manager) : m_impl(manager) { /* ... */ } + public: + Result OpenDirectory(s32 *out_handle, const char *path, fs::OpenDirectoryMode mode, bool case_sensitive) { return m_impl.OpenDirectory(out_handle, path, mode, case_sensitive); } + Result CloseDirectory(s32 handle) { return m_impl.CloseDirectory(handle); } }; void InitializeClient(htclow::HtclowManager *manager); diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp index ca218350e..d1baf3e24 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp @@ -29,6 +29,10 @@ namespace ams::htcfs { constinit u8 g_cache[32_KB]; + ALWAYS_INLINE Result ConvertNativeResult(s64 value) { + return result::impl::MakeResult(value); + } + } ClientImpl::ClientImpl(htclow::HtclowManager *manager) @@ -188,6 +192,26 @@ namespace ams::htcfs { return ResultSuccess(); } + Result ClientImpl::CheckResponseHeader(const Header &response, PacketType packet_type) { + /* Perform base checks. */ + R_TRY(this->CheckResponseHeaderWithoutVersion(response, packet_type)); + + /* Check the version. */ + R_UNLESS(response.version == m_header_factory.GetVersion(), htcfs::ResultUnexpectedResponseProtocolVersion()); + + return ResultSuccess(); + } + + Result ClientImpl::CheckResponseHeader(const Header &response, PacketType packet_type, s64 body_size) { + /* Perform base checks. */ + R_TRY(this->CheckResponseHeader(response, packet_type)); + + /* Check the body size. */ + R_UNLESS(response.body_size == body_size, htcfs::ResultUnexpectedResponseBodySize()); + + return ResultSuccess(); + } + Result ClientImpl::GetMaxProtocolVersion(s16 *out) { /* Create space for request and response. */ Header request, response; @@ -283,4 +307,110 @@ namespace ams::htcfs { return ResultSuccess(); } + Result ClientImpl::InitializeRpcChannel() { + /* Check that we're not cancelled. */ + R_UNLESS(!m_event.TryWait(), htcfs::ResultConnectionFailure()); + + /* Check that we're connected. */ + R_UNLESS(m_connected, htcfs::ResultConnectionFailure()); + + return ResultSuccess(); + } + + Result ClientImpl::SendRequest(const Header &request, const void *arg1, size_t arg1_size, const void *arg2, size_t arg2_size) { + /* Try to perform an optimized send. */ + if (sizeof(request) + arg1_size + arg2_size < sizeof(m_packet_buffer)) { + /* Setup our packet buffer. */ + std::memcpy(m_packet_buffer, std::addressof(request), sizeof(request)); + if (arg1_size > 0) { + std::memcpy(m_packet_buffer + sizeof(request), arg1, arg1_size); + } + if (arg2_size > 0) { + std::memcpy(m_packet_buffer + sizeof(request) + arg1_size, arg2, arg2_size); + } + + /* Send the request. */ + R_TRY(this->SendToRpcChannel(m_packet_buffer, sizeof(request) + arg1_size + arg2_size)); + } else { + /* We can't perform a single optimized send, so perform three separate sends. */ + R_TRY(this->SendToRpcChannel(std::addressof(request), sizeof(request))); + + if (arg1_size > 0) { + R_TRY(this->SendToRpcChannel(arg1, arg1_size)); + } + + if (arg2_size > 0) { + R_TRY(this->SendToRpcChannel(arg2, arg2_size)); + } + } + + return ResultSuccess(); + } + + Result ClientImpl::OpenDirectory(s32 *out_handle, const char *path, fs::OpenDirectoryMode mode, bool case_sensitive) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + const auto path_len = std::strlen(path); + m_header_factory.MakeOpenDirectoryHeader(std::addressof(request), path_len, mode, case_sensitive); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request, path, path_len)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type, 0)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(response.params[0])); + + /* Check our operation's result. */ + R_TRY(ConvertNativeResult(response.params[1])); + + /* Set the output handle. */ + *out_handle = static_cast(response.params[2]); + + return ResultSuccess(); + } + + Result ClientImpl::CloseDirectory(s32 handle) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + m_header_factory.MakeCloseDirectoryHeader(std::addressof(request), handle); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type, 0)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(response.params[0])); + + /* Check our operation's result. */ + R_TRY(ConvertNativeResult(response.params[1])); + + return ResultSuccess(); + } + } diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp index 4e84b404e..b6166d336 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp @@ -52,6 +52,9 @@ namespace ams::htcfs { void Start(); void Cancel(); void Wait(); + public: + Result OpenDirectory(s32 *out_handle, const char *path, fs::OpenDirectoryMode mode, bool case_sensitive); + Result CloseDirectory(s32 handle); private: int WaitAny(htclow::ChannelState state, os::EventType *event); @@ -59,15 +62,23 @@ namespace ams::htcfs { void TearDownProtocol(); Result CheckResponseHeaderWithoutVersion(const Header &response, PacketType packet_type); + Result CheckResponseHeader(const Header &response, PacketType packet_type); + Result CheckResponseHeader(const Header &response, PacketType packet_type, s64 body_size); Result GetMaxProtocolVersion(s16 *out); Result SetProtocolVersion(s16 version); + Result InitializeRpcChannel(); + Result SendToRpcChannel(const void *src, s64 size); Result ReceiveFromRpcChannel(void *dst, s64 size); Result SendToHtclow(const void *src, s64 size, htclow::Channel *channel); Result ReceiveFromHtclow(void *dst, s64 size, htclow::Channel *channel); + + Result SendRequest(const Header &request) { return this->SendRequest(request, nullptr, 0, nullptr, 0); } + Result SendRequest(const Header &request, const void *arg1, size_t arg1_size) { return this->SendRequest(request, arg1, arg1_size, nullptr, 0); } + Result SendRequest(const Header &request, const void *arg1, size_t arg1_size, const void *arg2, size_t arg2_size); }; } diff --git a/libraries/libstratosphere/source/htcfs/htcfs_directory_service_object.cpp b/libraries/libstratosphere/source/htcfs/htcfs_directory_service_object.cpp index 1d0e53f80..9bd9397ff 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_directory_service_object.cpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_directory_service_object.cpp @@ -15,14 +15,14 @@ */ #include #include "htcfs_directory_service_object.hpp" +#include "htcfs_client.hpp" namespace ams::htcfs { DirectoryServiceObject::DirectoryServiceObject(s32 handle) : m_handle(handle) { /* ... */ } DirectoryServiceObject::~DirectoryServiceObject() { - /* TODO */ - AMS_ABORT("htcfs::GetClient().CloseDirectory(m_handle);"); + htcfs::GetClient().CloseDirectory(m_handle); } Result DirectoryServiceObject::GetEntryCount(ams::sf::Out out) { diff --git a/libraries/libstratosphere/source/htcfs/htcfs_file_system_service_object.cpp b/libraries/libstratosphere/source/htcfs/htcfs_file_system_service_object.cpp index 353845c5b..e93d3d6f4 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_file_system_service_object.cpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_file_system_service_object.cpp @@ -15,9 +15,37 @@ */ #include #include "htcfs_file_system_service_object.hpp" +#include "htcfs_file_service_object.hpp" +#include "htcfs_directory_service_object.hpp" +#include "htcfs_client.hpp" namespace ams::htcfs { + namespace { + + struct DirectoryServiceObjectAllocatorTag; + struct FileServiceObjectAllocatorTag; + + using DirectoryServiceObjectAllocator = ams::sf::ExpHeapStaticAllocator<4_KB, DirectoryServiceObjectAllocatorTag>; + using FileServiceObjectAllocator = ams::sf::ExpHeapStaticAllocator<4_KB, FileServiceObjectAllocatorTag>; + using DirectoryServiceObjectFactory = ams::sf::ObjectFactory; + using FileServiceObjectFactory = ams::sf::ObjectFactory; + + class StaticAllocatorInitializer { + public: + StaticAllocatorInitializer() { + DirectoryServiceObjectAllocator::Initialize(lmem::CreateOption_ThreadSafe); + FileServiceObjectAllocator::Initialize(lmem::CreateOption_ThreadSafe); + } + } g_static_allocator_initializer; + + constexpr bool IsValidPath(const tma::Path &path) { + const auto len = util::Strnlen(path.str, fs::EntryNameLengthMax + 1); + return 0 < len && len < static_cast(fs::EntryNameLengthMax + 1); + } + + } + Result FileSystemServiceObject::OpenFile(sf::Out> out, const tma::Path &path, u32 open_mode, bool case_sensitive) { AMS_ABORT("FileSystemServiceObject::OpenFile"); } @@ -39,7 +67,16 @@ namespace ams::htcfs { } Result FileSystemServiceObject::OpenDirectory(sf::Out> out, const tma::Path &path, s32 open_mode, bool case_sensitive) { - AMS_ABORT("FileSystemServiceObject::OpenDirectory"); + /* Check that the path is valid. */ + R_UNLESS(IsValidPath(path), htcfs::ResultInvalidArgument()); + + /* Open the directory. */ + s32 handle; + R_TRY(htcfs::GetClient().OpenDirectory(std::addressof(handle), path.str, static_cast(open_mode), case_sensitive)); + + /* Set the output directory. */ + *out = DirectoryServiceObjectFactory::CreateSharedEmplaced(handle); + return ResultSuccess(); } Result FileSystemServiceObject::DirectoryExists(sf::Out out, const tma::Path &path, bool case_sensitive) { diff --git a/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp b/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp index 2556abdfb..e19349f73 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp @@ -102,6 +102,9 @@ namespace ams::htcfs { out->params[2] = param2; out->params[3] = param3; out->params[4] = param4; + + /* Clear reserved. */ + out->reserved = 0; } void MakeGetMaxProtocolVersionHeader(Header *out) { @@ -111,6 +114,14 @@ namespace ams::htcfs { void MakeSetProtocolVersionHeader(Header *out, s16 version) { return this->MakeRequestHeader(out, PacketType::SetProtocolVersion, 0, version); } + + void MakeOpenDirectoryHeader(Header *out, int path_len, fs::OpenDirectoryMode mode, bool case_sensitive) { + return this->MakeRequestHeader(out, PacketType::OpenDirectory, path_len, static_cast(mode), case_sensitive ? 1 : 0); + } + + void MakeCloseDirectoryHeader(Header *out, s32 handle) { + return this->MakeRequestHeader(out, PacketType::CloseDirectory, 0, handle); + } }; } diff --git a/libraries/libvapours/include/vapours/results/htcfs_results.hpp b/libraries/libvapours/include/vapours/results/htcfs_results.hpp index cfad9663b..7fd689338 100644 --- a/libraries/libvapours/include/vapours/results/htcfs_results.hpp +++ b/libraries/libvapours/include/vapours/results/htcfs_results.hpp @@ -22,12 +22,16 @@ namespace ams::htcfs { R_DEFINE_ERROR_RESULT(InvalidArgument, 3); - R_DEFINE_ERROR_RESULT(HtclowChannelClosed, 101); - R_DEFINE_ERROR_RESULT(UnexpectedResponseProtocolId, 111); - R_DEFINE_ERROR_RESULT(UnexpectedResponseProtocolVersion, 112); - R_DEFINE_ERROR_RESULT(UnexpectedResponsePacketCategory, 113); - R_DEFINE_ERROR_RESULT(UnexpectedResponsePacketType, 114); + R_DEFINE_ERROR_RANGE(ConnectionFailure, 100, 199); + R_DEFINE_ERROR_RESULT(HtclowChannelClosed, 101); + + R_DEFINE_ERROR_RANGE(UnexpectedResponse, 110, 119); + R_DEFINE_ERROR_RESULT(UnexpectedResponseProtocolId, 111); + R_DEFINE_ERROR_RESULT(UnexpectedResponseProtocolVersion, 112); + R_DEFINE_ERROR_RESULT(UnexpectedResponsePacketCategory, 113); + R_DEFINE_ERROR_RESULT(UnexpectedResponsePacketType, 114); + R_DEFINE_ERROR_RESULT(UnexpectedResponseBodySize, 115); R_DEFINE_ERROR_RESULT(UnknownError, 211); R_DEFINE_ERROR_RESULT(UnsupportedProtocolVersion, 212); From 9daec3a66a3a40452881d96c34bb83f1cf73a1cf Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 13 Feb 2021 05:39:02 -0800 Subject: [PATCH 064/280] htc: Implement (almost) all host-directory commands --- .../source/htcfs/htcfs_client.hpp | 11 +- .../source/htcfs/htcfs_client_impl.cpp | 149 +++++++++++++++++- .../source/htcfs/htcfs_client_impl.hpp | 11 +- .../htcfs/htcfs_directory_service_object.cpp | 13 +- .../source/htcfs/htcfs_header_factory.hpp | 20 +++ .../source/htcfs/htcfs_result_utils.hpp | 32 ++++ .../include/vapours/results/htcfs_results.hpp | 12 +- 7 files changed, 235 insertions(+), 13 deletions(-) create mode 100644 libraries/libstratosphere/source/htcfs/htcfs_result_utils.hpp diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client.hpp b/libraries/libstratosphere/source/htcfs/htcfs_client.hpp index 977917da4..6c2396fc5 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client.hpp @@ -16,6 +16,7 @@ #pragma once #include #include "htcfs_client_impl.hpp" +#include "htcfs_result_utils.hpp" namespace ams::htcfs { @@ -25,8 +26,14 @@ namespace ams::htcfs { public: Client(htclow::HtclowManager *manager) : m_impl(manager) { /* ... */ } public: - Result OpenDirectory(s32 *out_handle, const char *path, fs::OpenDirectoryMode mode, bool case_sensitive) { return m_impl.OpenDirectory(out_handle, path, mode, case_sensitive); } - Result CloseDirectory(s32 handle) { return m_impl.CloseDirectory(handle); } + Result OpenDirectory(s32 *out_handle, const char *path, fs::OpenDirectoryMode mode, bool case_sensitive) { return ConvertToFsResult(m_impl.OpenDirectory(out_handle, path, mode, case_sensitive)); } + Result CloseDirectory(s32 handle) { return ConvertToFsResult(m_impl.CloseDirectory(handle)); } + + Result GetEntryCount(s64 *out, s32 handle) { return ConvertToFsResult(m_impl.GetEntryCount(out, handle)); } + Result ReadDirectory(s64 *out, fs::DirectoryEntry *out_entries, size_t max_out_entries, s32 handle) { return ConvertToFsResult(m_impl.ReadDirectory(out, out_entries, max_out_entries, handle)); } + Result ReadDirectoryLarge(s64 *out, fs::DirectoryEntry *out_entries, size_t max_out_entries, s32 handle) { return ConvertToFsResult( m_impl.ReadDirectoryLarge(out, out_entries, max_out_entries, handle)); } + Result GetPriorityForDirectory(s32 *out, s32 handle) { return ConvertToFsResult(m_impl.GetPriorityForDirectory(out, handle)); } + Result SetPriorityForDirectory(s32 priority, s32 handle) { return ConvertToFsResult(m_impl.SetPriorityForDirectory(priority, handle)); } }; void InitializeClient(htclow::HtclowManager *manager); diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp index d1baf3e24..320ac254b 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp @@ -16,7 +16,6 @@ #include #include "htcfs_client_impl.hpp" #include "htcfs_result.hpp" -#include "../htclow/htclow_default_channel_config.hpp" namespace ams::htcfs { @@ -413,4 +412,152 @@ namespace ams::htcfs { return ResultSuccess(); } + Result ClientImpl::GetEntryCount(s64 *out, s32 handle) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + m_header_factory.MakeGetEntryCountHeader(std::addressof(request), handle); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type, 0)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(response.params[0])); + + /* Check our operation's result. */ + R_TRY(ConvertNativeResult(response.params[1])); + + /* Set the output count. */ + *out = response.params[2]; + + return ResultSuccess(); + } + + Result ClientImpl::ReadDirectory(s64 *out, fs::DirectoryEntry *out_entries, size_t max_out_entries, s32 handle) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + m_header_factory.MakeReadDirectoryHeader(std::addressof(request), handle, max_out_entries); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(response.params[0])); + + /* Check our operation's result. */ + R_TRY(ConvertNativeResult(response.params[1])); + + /* Check that the response body size is expected. */ + R_UNLESS(static_cast(response.body_size) == max_out_entries * sizeof(*out_entries), htcfs::ResultUnexpectedResponseBody()); + + /* Check that the number of entries read is allowable. */ + R_UNLESS(static_cast(response.params[2]) <= max_out_entries, htcfs::ResultUnexpectedResponseBody()); + + /* Receive the entries. */ + *out = response.params[2]; + return this->ReceiveFromRpcChannel(out_entries, response.body_size); + } + + Result ClientImpl::ReadDirectoryLarge(s64 *out, fs::DirectoryEntry *out_entries, size_t max_out_entries, s32 handle) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + m_header_factory.MakeReadDirectoryLargeHeader(std::addressof(request), handle, max_out_entries); + + AMS_ABORT("TODO: Data channel setup/teardown"); + } + + Result ClientImpl::GetPriorityForDirectory(s32 *out, s32 handle) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + m_header_factory.MakeGetPriorityForDirectoryHeader(std::addressof(request), handle); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type, 0)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(response.params[0])); + + /* Set the output. */ + *out = static_cast(response.params[1]); + + return ResultSuccess(); + } + + Result ClientImpl::SetPriorityForDirectory(s32 priority, s32 handle) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + m_header_factory.MakeSetPriorityForDirectoryHeader(std::addressof(request), handle, priority); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type, 0)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(response.params[0])); + + return ResultSuccess(); + } + } diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp index b6166d336..b241a461d 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp @@ -19,14 +19,17 @@ #include "../htclow/htclow_channel.hpp" #include "htcfs_cache_manager.hpp" #include "htcfs_header_factory.hpp" +#include "../htclow/htclow_default_channel_config.hpp" namespace ams::htcfs { class ClientImpl { + public: + static constexpr size_t MaxPacketBodySize = htclow::DefaultChannelConfig.max_packet_size - sizeof(htclow::PacketHeader); private: u8 m_receive_buffer[0x1C040]; u8 m_send_buffer[0x1C040]; - u8 m_packet_buffer[0xE020]; + u8 m_packet_buffer[MaxPacketBodySize + sizeof(htclow::PacketHeader)]; htclow::HtclowManager *m_htclow_manager; CacheManager m_cache_manager; HeaderFactory m_header_factory; @@ -55,6 +58,12 @@ namespace ams::htcfs { public: Result OpenDirectory(s32 *out_handle, const char *path, fs::OpenDirectoryMode mode, bool case_sensitive); Result CloseDirectory(s32 handle); + + Result GetEntryCount(s64 *out, s32 handle); + Result ReadDirectory(s64 *out, fs::DirectoryEntry *out_entries, size_t max_out_entries, s32 handle); + Result ReadDirectoryLarge(s64 *out, fs::DirectoryEntry *out_entries, size_t max_out_entries, s32 handle); + Result GetPriorityForDirectory(s32 *out, s32 handle); + Result SetPriorityForDirectory(s32 priority, s32 handle); private: int WaitAny(htclow::ChannelState state, os::EventType *event); diff --git a/libraries/libstratosphere/source/htcfs/htcfs_directory_service_object.cpp b/libraries/libstratosphere/source/htcfs/htcfs_directory_service_object.cpp index 9bd9397ff..f0db33f07 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_directory_service_object.cpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_directory_service_object.cpp @@ -16,6 +16,7 @@ #include #include "htcfs_directory_service_object.hpp" #include "htcfs_client.hpp" +#include "../htclow/htclow_default_channel_config.hpp" namespace ams::htcfs { @@ -26,19 +27,23 @@ namespace ams::htcfs { } Result DirectoryServiceObject::GetEntryCount(ams::sf::Out out) { - AMS_ABORT("DirectoryServiceObject::GetEntryCount"); + return htcfs::GetClient().GetEntryCount(out.GetPointer(), m_handle); } Result DirectoryServiceObject::Read(ams::sf::Out out, const ams::sf::OutMapAliasArray &out_entries) { - AMS_ABORT("DirectoryServiceObject::Read"); + if (out_entries.GetSize() * sizeof(fs::DirectoryEntry) >= ClientImpl::MaxPacketBodySize) { + return htcfs::GetClient().ReadDirectoryLarge(out.GetPointer(), out_entries.GetPointer(), out_entries.GetSize(), m_handle); + } else { + return htcfs::GetClient().ReadDirectory(out.GetPointer(), out_entries.GetPointer(), out_entries.GetSize(), m_handle); + } } Result DirectoryServiceObject::SetPriorityForDirectory(s32 priority) { - AMS_ABORT("DirectoryServiceObject::SetPriorityForDirectory"); + return htcfs::GetClient().SetPriorityForDirectory(priority, m_handle); } Result DirectoryServiceObject::GetPriorityForDirectory(ams::sf::Out out) { - AMS_ABORT("DirectoryServiceObject::GetPriorityForDirectory"); + return htcfs::GetClient().GetPriorityForDirectory(out.GetPointer(), m_handle); } diff --git a/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp b/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp index e19349f73..d7511e81f 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp @@ -122,6 +122,26 @@ namespace ams::htcfs { void MakeCloseDirectoryHeader(Header *out, s32 handle) { return this->MakeRequestHeader(out, PacketType::CloseDirectory, 0, handle); } + + void MakeGetEntryCountHeader(Header *out, s32 handle) { + return this->MakeRequestHeader(out, PacketType::GetEntryCount, 0, handle); + } + + void MakeReadDirectoryHeader(Header *out, s32 handle, size_t max_out_entries) { + return this->MakeRequestHeader(out, PacketType::ReadDirectory, 0, handle, max_out_entries); + } + + void MakeReadDirectoryLargeHeader(Header *out, s32 handle, size_t max_out_entries) { + return this->MakeRequestHeader(out, PacketType::ReadDirectoryLarge, 0, handle, max_out_entries); + } + + void MakeGetPriorityForDirectoryHeader(Header *out, s32 handle) { + return this->MakeRequestHeader(out, PacketType::GetPriorityForDirectory, 0, handle); + } + + void MakeSetPriorityForDirectoryHeader(Header *out, s32 handle, s32 priority) { + return this->MakeRequestHeader(out, PacketType::SetPriorityForDirectory, 0, handle, priority); + } }; } diff --git a/libraries/libstratosphere/source/htcfs/htcfs_result_utils.hpp b/libraries/libstratosphere/source/htcfs/htcfs_result_utils.hpp new file mode 100644 index 000000000..48c2dbb99 --- /dev/null +++ b/libraries/libstratosphere/source/htcfs/htcfs_result_utils.hpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htcfs { + + inline Result ConvertToFsResult(Result result) { + R_TRY_CATCH(result) { + R_CONVERT(htcfs::ResultInvalidArgument, fs::ResultInvalidArgument()) + R_CONVERT(htcfs::ResultConnectionFailure, fs::ResultTargetNotFound()) + R_CONVERT(htcfs::ResultOutOfHandle, fs::ResultOpenCountLimit()) + R_CONVERT(htcfs::ResultInternalError, fs::ResultInternal()) + } R_END_TRY_CATCH; + + return result; + } + +} diff --git a/libraries/libvapours/include/vapours/results/htcfs_results.hpp b/libraries/libvapours/include/vapours/results/htcfs_results.hpp index 7fd689338..fc35e68f9 100644 --- a/libraries/libvapours/include/vapours/results/htcfs_results.hpp +++ b/libraries/libvapours/include/vapours/results/htcfs_results.hpp @@ -32,11 +32,13 @@ namespace ams::htcfs { R_DEFINE_ERROR_RESULT(UnexpectedResponsePacketCategory, 113); R_DEFINE_ERROR_RESULT(UnexpectedResponsePacketType, 114); R_DEFINE_ERROR_RESULT(UnexpectedResponseBodySize, 115); + R_DEFINE_ERROR_RESULT(UnexpectedResponseBody, 116); - R_DEFINE_ERROR_RESULT(UnknownError, 211); - R_DEFINE_ERROR_RESULT(UnsupportedProtocolVersion, 212); - R_DEFINE_ERROR_RESULT(InvalidRequest, 213); - R_DEFINE_ERROR_RESULT(InvalidHandle, 214); - R_DEFINE_ERROR_RESULT(OutOfHandle, 215); + R_DEFINE_ERROR_RANGE(InternalError, 200, 299); + R_DEFINE_ERROR_RESULT(UnknownError, 211); + R_DEFINE_ERROR_RESULT(UnsupportedProtocolVersion, 212); + R_DEFINE_ERROR_RESULT(InvalidRequest, 213); + R_DEFINE_ERROR_RESULT(InvalidHandle, 214); + R_DEFINE_ERROR_RESULT(OutOfHandle, 215); } From 1961cb1034c4f64860eab50abb8fb5d0494a7bb6 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 13 Feb 2021 10:01:19 -0800 Subject: [PATCH 065/280] htc: ReadDirectoryLarge/data channel support --- .../source/htcfs/htcfs_client_impl.cpp | 92 ++++++++++++++++++- .../source/htcfs/htcfs_client_impl.hpp | 7 ++ .../source/htcfs/htcfs_header_factory.hpp | 4 +- .../source/htclow/mux/htclow_mux.cpp | 2 +- 4 files changed, 100 insertions(+), 5 deletions(-) diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp index 320ac254b..ad5a73420 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp @@ -23,6 +23,7 @@ namespace ams::htcfs { /* TODO: Move to a header? */ constexpr u16 RpcChannelId = 0; + constexpr u16 DataChannelId = 1; alignas(os::ThreadStackAlignment) constinit u8 g_monitor_thread_stack[os::MemoryPageSize]; @@ -266,6 +267,14 @@ namespace ams::htcfs { return this->ReceiveFromHtclow(dst, size, std::addressof(m_rpc_channel)); } + Result ClientImpl::ReceiveFromDataChannel(s64 size) { + return m_data_channel.WaitReceive(size); + } + + Result ClientImpl::SendToDataChannel() { + return m_data_channel.Flush(); + } + Result ClientImpl::SendToHtclow(const void *src, s64 size, htclow::Channel *channel) { /* Check size. */ R_UNLESS(size >= 0, htcfs::ResultInvalidArgument()); @@ -316,6 +325,52 @@ namespace ams::htcfs { return ResultSuccess(); } + void ClientImpl::InitializeDataChannelForReceive(void *dst, size_t size) { + /* Open the data channel. */ + R_ABORT_UNLESS(m_data_channel.Open(std::addressof(m_module), DataChannelId)); + + /* Set our config. */ + constexpr htclow::ChannelConfig BulkReceiveConfig = { + .flow_control_enabled = false, + .handshake_enabled = false, + .max_packet_size = 0x3E000, + }; + m_data_channel.SetConfig(BulkReceiveConfig); + + /* Set receive buffer. */ + m_data_channel.SetReceiveBuffer(dst, size); + + /* Connect. */ + R_ABORT_UNLESS(m_data_channel.Connect()); + } + + void ClientImpl::InitializeDataChannelForSend(const void *src, size_t size) { + /* Open the data channel. */ + R_ABORT_UNLESS(m_data_channel.Open(std::addressof(m_module), DataChannelId)); + + /* Check that the size is valid. */ + AMS_ASSERT(util::IsIntValueRepresentable(size)); + + /* Set our config. */ + constexpr htclow::ChannelConfig BulkSendConfig = { + .flow_control_enabled = false, + .handshake_enabled = false, + .max_packet_size = 0xE020, + }; + m_data_channel.SetConfig(BulkSendConfig); + + /* Set our send buffer. */ + m_data_channel.SetSendBufferWithData(src, size); + + /* Connect. */ + R_ABORT_UNLESS(m_data_channel.Connect()); + } + + void ClientImpl::FinalizeDataChannel() { + /* Close our data channel. */ + m_data_channel.Close(); + } + Result ClientImpl::SendRequest(const Header &request, const void *arg1, size_t arg1_size, const void *arg2, size_t arg2_size) { /* Try to perform an optimized send. */ if (sizeof(request) + arg1_size + arg2_size < sizeof(m_packet_buffer)) { @@ -492,13 +547,46 @@ namespace ams::htcfs { /* Initialize our rpc channel. */ R_TRY(this->InitializeRpcChannel()); + /* Setup data channel. */ + const bool use_data_channel = max_out_entries > 0; + if (use_data_channel) { + this->InitializeDataChannelForReceive(out_entries, max_out_entries * sizeof(*out_entries)); + } + ON_SCOPE_EXIT { if (use_data_channel) { this->FinalizeDataChannel(); } }; + /* Create space for request and response. */ Header request, response; /* Create header for the request. */ - m_header_factory.MakeReadDirectoryLargeHeader(std::addressof(request), handle, max_out_entries); + m_header_factory.MakeReadDirectoryLargeHeader(std::addressof(request), handle, max_out_entries, DataChannelId); - AMS_ABORT("TODO: Data channel setup/teardown"); + /* Send the request to the host. */ + R_TRY(this->SendRequest(request)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type, 0)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(response.params[0])); + + /* Check our operation's result. */ + R_TRY(ConvertNativeResult(response.params[1])); + + /* Check that the number of entries read is allowable. */ + R_UNLESS(static_cast(response.params[2]) <= max_out_entries, htcfs::ResultUnexpectedResponseBody()); + + /* Read the entries, if there are any. */ + if (response.params[2] > 0) { + R_TRY(this->ReceiveFromDataChannel(response.params[2] * sizeof(*out_entries))); + } + + /* Set the number of output entries. */ + *out = response.params[2]; + + return ResultSuccess(); } Result ClientImpl::GetPriorityForDirectory(s32 *out, s32 handle) { diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp index b241a461d..e66fc7642 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp @@ -82,12 +82,19 @@ namespace ams::htcfs { Result SendToRpcChannel(const void *src, s64 size); Result ReceiveFromRpcChannel(void *dst, s64 size); + Result ReceiveFromDataChannel(s64 size); + Result SendToDataChannel(); + Result SendToHtclow(const void *src, s64 size, htclow::Channel *channel); Result ReceiveFromHtclow(void *dst, s64 size, htclow::Channel *channel); Result SendRequest(const Header &request) { return this->SendRequest(request, nullptr, 0, nullptr, 0); } Result SendRequest(const Header &request, const void *arg1, size_t arg1_size) { return this->SendRequest(request, arg1, arg1_size, nullptr, 0); } Result SendRequest(const Header &request, const void *arg1, size_t arg1_size, const void *arg2, size_t arg2_size); + + void InitializeDataChannelForReceive(void *dst, size_t size); + void InitializeDataChannelForSend(const void *src, size_t size); + void FinalizeDataChannel(); }; } diff --git a/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp b/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp index d7511e81f..8417d34a9 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp @@ -131,8 +131,8 @@ namespace ams::htcfs { return this->MakeRequestHeader(out, PacketType::ReadDirectory, 0, handle, max_out_entries); } - void MakeReadDirectoryLargeHeader(Header *out, s32 handle, size_t max_out_entries) { - return this->MakeRequestHeader(out, PacketType::ReadDirectoryLarge, 0, handle, max_out_entries); + void MakeReadDirectoryLargeHeader(Header *out, s32 handle, size_t max_out_entries, u16 data_channel_id) { + return this->MakeRequestHeader(out, PacketType::ReadDirectoryLarge, 0, handle, max_out_entries, data_channel_id); } void MakeGetPriorityForDirectoryHeader(Header *out, s32 handle) { diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp index 220d28c62..416f8ec3b 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp @@ -305,7 +305,7 @@ namespace ams::htclow::mux { auto it = m_channel_impl_map.GetMap().find(channel); R_UNLESS(it != m_channel_impl_map.GetMap().end(), htclow::ResultChannelNotExist()); - /* Perform the RECEIVE. */ + /* Perform the receive. */ return m_channel_impl_map[it->second].DoReceiveEnd(out, dst, dst_size); } else { *out = 0; From d20bceff75baa30ec2e73f11525b71c690f0c60a Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 15 Feb 2021 18:56:22 -0800 Subject: [PATCH 066/280] htc: implement the remaining commands for htcfs --- .../source/htcfs/htcfs_cache_manager.hpp | 10 + .../source/htcfs/htcfs_client.hpp | 28 +- .../source/htcfs/htcfs_client_impl.cpp | 895 +++++++++++++++++- .../source/htcfs/htcfs_client_impl.hpp | 26 + .../htcfs/htcfs_directory_service_object.cpp | 1 - .../htcfs/htcfs_file_service_object.cpp | 35 +- .../htcfs_file_system_service_object.cpp | 108 ++- .../source/htcfs/htcfs_header_factory.hpp | 92 ++ .../source/htcfs/htcfs_result.hpp | 1 + 9 files changed, 1169 insertions(+), 27 deletions(-) diff --git a/libraries/libstratosphere/source/htcfs/htcfs_cache_manager.hpp b/libraries/libstratosphere/source/htcfs/htcfs_cache_manager.hpp index deb65fc35..f4b6ad395 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_cache_manager.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_cache_manager.hpp @@ -51,6 +51,16 @@ namespace ams::htcfs { m_has_cached_handle = false; } + void Invalidate(s32 handle) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + if (m_has_cached_handle && m_cached_handle == handle) { + /* Note that we have no handle. */ + m_has_cached_handle = false; + } + } + void Record(s64 file_size, const void *data, s32 handle, size_t data_size) { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client.hpp b/libraries/libstratosphere/source/htcfs/htcfs_client.hpp index 6c2396fc5..716d025c9 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client.hpp @@ -26,14 +26,40 @@ namespace ams::htcfs { public: Client(htclow::HtclowManager *manager) : m_impl(manager) { /* ... */ } public: + Result OpenFile(s32 *out_handle, const char *path, fs::OpenMode mode, bool case_sensitive) { return ConvertToFsResult(m_impl.OpenFile(out_handle, path, mode, case_sensitive)); } + Result FileExists(bool *out, const char *path, bool case_sensitive) { return ConvertToFsResult(m_impl.FileExists(out, path, case_sensitive)); } + Result DeleteFile(const char *path, bool case_sensitive) { return ConvertToFsResult(m_impl.DeleteFile(path, case_sensitive)); } + Result RenameFile(const char *old_path, const char *new_path, bool case_sensitive) { return ConvertToFsResult(m_impl.RenameFile(old_path, new_path, case_sensitive)); } + Result GetEntryType(fs::DirectoryEntryType *out, const char *path, bool case_sensitive) { return ConvertToFsResult(m_impl.GetEntryType(out, path, case_sensitive)); } Result OpenDirectory(s32 *out_handle, const char *path, fs::OpenDirectoryMode mode, bool case_sensitive) { return ConvertToFsResult(m_impl.OpenDirectory(out_handle, path, mode, case_sensitive)); } + Result DirectoryExists(bool *out, const char *path, bool case_sensitive) { return ConvertToFsResult(m_impl.DirectoryExists(out, path, case_sensitive)); } + Result CreateDirectory(const char *path, bool case_sensitive) { return ConvertToFsResult(m_impl.CreateDirectory(path, case_sensitive)); } + Result DeleteDirectory(const char *path, bool recursively, bool case_sensitive) { return ConvertToFsResult(m_impl.DeleteDirectory(path, recursively, case_sensitive)); } + Result RenameDirectory(const char *old_path, const char *new_path, bool case_sensitive) { return ConvertToFsResult(m_impl.RenameDirectory(old_path, new_path, case_sensitive)); } + Result CreateFile(const char *path, s64 size, bool case_sensitive) { return ConvertToFsResult(m_impl.CreateFile(path, size, case_sensitive)); } + Result GetFileTimeStamp(u64 *out_create, u64 *out_access, u64 *out_modify, const char *path, bool case_sensitive) { return ConvertToFsResult(m_impl.GetFileTimeStamp(out_create, out_access, out_modify, path, case_sensitive)); } + Result GetCaseSensitivePath(char *dst, size_t dst_size, const char *path) { return ConvertToFsResult(m_impl.GetCaseSensitivePath(dst, dst_size, path)); } + Result GetDiskFreeSpace(s64 *out_free, s64 *out_total, s64 *out_total_free, const char *path) { return ConvertToFsResult(m_impl.GetDiskFreeSpace(out_free, out_total, out_total_free, path)); } + Result CloseDirectory(s32 handle) { return ConvertToFsResult(m_impl.CloseDirectory(handle)); } Result GetEntryCount(s64 *out, s32 handle) { return ConvertToFsResult(m_impl.GetEntryCount(out, handle)); } Result ReadDirectory(s64 *out, fs::DirectoryEntry *out_entries, size_t max_out_entries, s32 handle) { return ConvertToFsResult(m_impl.ReadDirectory(out, out_entries, max_out_entries, handle)); } - Result ReadDirectoryLarge(s64 *out, fs::DirectoryEntry *out_entries, size_t max_out_entries, s32 handle) { return ConvertToFsResult( m_impl.ReadDirectoryLarge(out, out_entries, max_out_entries, handle)); } + Result ReadDirectoryLarge(s64 *out, fs::DirectoryEntry *out_entries, size_t max_out_entries, s32 handle) { return ConvertToFsResult(m_impl.ReadDirectoryLarge(out, out_entries, max_out_entries, handle)); } Result GetPriorityForDirectory(s32 *out, s32 handle) { return ConvertToFsResult(m_impl.GetPriorityForDirectory(out, handle)); } Result SetPriorityForDirectory(s32 priority, s32 handle) { return ConvertToFsResult(m_impl.SetPriorityForDirectory(priority, handle)); } + + Result CloseFile(s32 handle) { return ConvertToFsResult(m_impl.CloseFile(handle)); } + + Result ReadFile(s64 *out, void *buffer, s32 handle, s64 offset, s64 buffer_size, fs::ReadOption option) { return ConvertToFsResult(m_impl.ReadFile(out, buffer, handle, offset, buffer_size, option)); } + Result ReadFileLarge(s64 *out, void *buffer, s32 handle, s64 offset, s64 buffer_size, fs::ReadOption option) { return ConvertToFsResult(m_impl.ReadFileLarge(out, buffer, handle, offset, buffer_size, option)); } + Result WriteFile(const void *buffer, s32 handle, s64 offset, s64 buffer_size, fs::WriteOption option) { return ConvertToFsResult(m_impl.WriteFile(buffer, handle, offset, buffer_size, option)); } + Result WriteFileLarge(const void *buffer, s32 handle, s64 offset, s64 buffer_size, fs::WriteOption option) { return ConvertToFsResult(m_impl.WriteFileLarge(buffer, handle, offset, buffer_size, option)); } + Result GetFileSize(s64 *out, s32 handle) { return ConvertToFsResult(m_impl.GetFileSize(out, handle)); } + Result SetFileSize(s64 size, s32 handle) { return ConvertToFsResult(m_impl.SetFileSize(size, handle)); } + Result FlushFile(s32 handle) { return ConvertToFsResult(m_impl.FlushFile(handle)); } + Result GetPriorityForFile(s32 *out, s32 handle) { return ConvertToFsResult(m_impl.GetPriorityForFile(out, handle)); } + Result SetPriorityForFile(s32 priority, s32 handle) { return ConvertToFsResult(m_impl.SetPriorityForFile(priority, handle)); } }; void InitializeClient(htclow::HtclowManager *manager); diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp index ad5a73420..67404632d 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp @@ -27,7 +27,8 @@ namespace ams::htcfs { alignas(os::ThreadStackAlignment) constinit u8 g_monitor_thread_stack[os::MemoryPageSize]; - constinit u8 g_cache[32_KB]; + constexpr size_t FileDataCacheSize = 32_KB; + constinit u8 g_cache[FileDataCacheSize]; ALWAYS_INLINE Result ConvertNativeResult(s64 value) { return result::impl::MakeResult(value); @@ -401,6 +402,191 @@ namespace ams::htcfs { return ResultSuccess(); } + Result ClientImpl::OpenFile(s32 *out_handle, const char *path, fs::OpenMode mode, bool case_sensitive) { + /* Invalidate the cache manager. */ + m_cache_manager.Invalidate(); + + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + const auto path_len = std::strlen(path); + m_header_factory.MakeOpenFileHeader(std::addressof(request), path_len, mode, case_sensitive, FileDataCacheSize); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request, path, path_len)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type)); + + /* Check the response body size. */ + R_UNLESS(response.body_size > 0, htcfs::ResultUnexpectedResponseBodySize()); + R_UNLESS(static_cast(response.body_size) <= MaxPacketBodySize, htcfs::ResultUnexpectedResponseBodySize()); + + /* Receive the response body. */ + R_TRY(this->ReceiveFromRpcChannel(m_packet_buffer, response.body_size)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(response.params[0])); + + /* Check our operation's result. */ + R_TRY(ConvertNativeResult(response.params[1])); + + /* Set our output handle. */ + *out_handle = response.params[2]; + + /* If we have data to cache, cache it. */ + if (response.params[3]) { + m_cache_manager.Record(response.params[4], m_packet_buffer, response.params[2], response.body_size); + } + + return ResultSuccess(); + } + + Result ClientImpl::FileExists(bool *out, const char *path, bool case_sensitive) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + const auto path_len = std::strlen(path); + m_header_factory.MakeFileExistsHeader(std::addressof(request), path_len, case_sensitive); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request, path, path_len)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type, 0)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(response.params[0])); + + /* Check our operation's result. */ + R_TRY(ConvertNativeResult(response.params[1])); + + /* Set the output. */ + *out = response.params[2] != 0; + + return ResultSuccess(); + } + + Result ClientImpl::DeleteFile(const char *path, bool case_sensitive) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + const auto path_len = std::strlen(path); + m_header_factory.MakeDeleteFileHeader(std::addressof(request), path_len, case_sensitive); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request, path, path_len)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type, 0)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(response.params[0])); + + /* Check our operation's result. */ + R_TRY(ConvertNativeResult(response.params[1])); + + return ResultSuccess(); + } + + Result ClientImpl::RenameFile(const char *old_path, const char *new_path, bool case_sensitive) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + const auto old_path_len = std::strlen(new_path); + const auto new_path_len = std::strlen(old_path); + m_header_factory.MakeRenameFileHeader(std::addressof(request), old_path_len, new_path_len, case_sensitive); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request, old_path, old_path_len, new_path, new_path_len)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type, 0)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(response.params[0])); + + /* Check our operation's result. */ + R_TRY(ConvertNativeResult(response.params[1])); + + return ResultSuccess(); + } + + Result ClientImpl::GetEntryType(fs::DirectoryEntryType *out, const char *path, bool case_sensitive) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + const auto path_len = std::strlen(path); + m_header_factory.MakeGetEntryTypeHeader(std::addressof(request), path_len, case_sensitive); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request, path, path_len)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type, 0)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(response.params[0])); + + /* Check our operation's result. */ + R_TRY(ConvertNativeResult(response.params[1])); + + /* Set the output. */ + *out = static_cast(response.params[2]); + + return ResultSuccess(); + } + Result ClientImpl::OpenDirectory(s32 *out_handle, const char *path, fs::OpenDirectoryMode mode, bool case_sensitive) { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); @@ -436,6 +622,297 @@ namespace ams::htcfs { return ResultSuccess(); } + Result ClientImpl::DirectoryExists(bool *out, const char *path, bool case_sensitive) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + const auto path_len = std::strlen(path); + m_header_factory.MakeDirectoryExistsHeader(std::addressof(request), path_len, case_sensitive); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request, path, path_len)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type, 0)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(response.params[0])); + + /* Check our operation's result. */ + R_TRY(ConvertNativeResult(response.params[1])); + + /* Set the output. */ + *out = response.params[2] != 0; + + return ResultSuccess(); + } + + Result ClientImpl::CreateDirectory(const char *path, bool case_sensitive) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + const auto path_len = std::strlen(path); + m_header_factory.MakeCreateDirectoryHeader(std::addressof(request), path_len, case_sensitive); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request, path, path_len)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type, 0)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(response.params[0])); + + /* Check our operation's result. */ + R_TRY(ConvertNativeResult(response.params[1])); + + return ResultSuccess(); + } + + Result ClientImpl::DeleteDirectory(const char *path, bool recursively, bool case_sensitive) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + const auto path_len = std::strlen(path); + m_header_factory.MakeDeleteDirectoryHeader(std::addressof(request), path_len, recursively, case_sensitive); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request, path, path_len)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type, 0)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(response.params[0])); + + /* Check our operation's result. */ + R_TRY(ConvertNativeResult(response.params[1])); + + return ResultSuccess(); + } + + Result ClientImpl::RenameDirectory(const char *old_path, const char *new_path, bool case_sensitive) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + const auto old_path_len = std::strlen(new_path); + const auto new_path_len = std::strlen(old_path); + m_header_factory.MakeRenameDirectoryHeader(std::addressof(request), old_path_len, new_path_len, case_sensitive); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request, old_path, old_path_len, new_path, new_path_len)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type, 0)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(response.params[0])); + + /* Check our operation's result. */ + R_TRY(ConvertNativeResult(response.params[1])); + + return ResultSuccess(); + } + + Result ClientImpl::CreateFile(const char *path, s64 size, bool case_sensitive) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + const auto path_len = std::strlen(path); + m_header_factory.MakeCreateFileHeader(std::addressof(request), path_len, size, case_sensitive); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request, path, path_len)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type, 0)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(response.params[0])); + + /* Check our operation's result. */ + R_TRY(ConvertNativeResult(response.params[1])); + + return ResultSuccess(); + } + + Result ClientImpl::GetFileTimeStamp(u64 *out_create, u64 *out_access, u64 *out_modify, const char *path, bool case_sensitive) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + const auto path_len = std::strlen(path); + m_header_factory.MakeGetFileTimeStampHeader(std::addressof(request), path_len, case_sensitive); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request, path, path_len)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type, 0)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(response.params[0])); + + /* Check our operation's result. */ + R_TRY(ConvertNativeResult(response.params[1])); + + /* Set output. */ + *out_create = static_cast(response.params[2]); + *out_access = static_cast(response.params[3]); + *out_modify = static_cast(response.params[4]); + + return ResultSuccess(); + } + + Result ClientImpl::GetCaseSensitivePath(char *dst, size_t dst_size, const char *path) { + /* Sanity check the output buffer. */ + R_UNLESS(util::IsIntValueRepresentable(dst_size), htcfs::ResultInvalidArgument()); + R_UNLESS(dst_size > 0, htcfs::ResultInvalidArgument()); + + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + const auto path_len = std::strlen(path); + m_header_factory.MakeGetCaseSensitivePathHeader(std::addressof(request), path_len); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request, path, path_len)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type)); + + /* Check that we succeeded. */ + const auto htcfs_result = ConvertHtcfsResult(response.params[0]); + if (R_FAILED(htcfs_result)) { + R_UNLESS(response.body_size == 0, htcfs::ResultUnexpectedResponseBodySize()); + return htcfs_result; + } + + /* Check our operation's result. */ + const auto native_result = ConvertNativeResult(response.params[1]); + if (R_FAILED(native_result)) { + R_UNLESS(response.body_size == 0, htcfs::ResultUnexpectedResponseBodySize()); + return native_result; + } + + /* Check the body size. */ + R_UNLESS(response.body_size < static_cast(dst_size), htcfs::ResultUnexpectedResponseBodySize()); + + /* Receive the response body. */ + R_TRY(this->ReceiveFromRpcChannel(dst, response.body_size)); + + /* Null-terminate the output path. */ + dst[response.body_size] = '\x00'; + + return ResultSuccess(); + } + + Result ClientImpl::GetDiskFreeSpace(s64 *out_free, s64 *out_total, s64 *out_total_free, const char *path) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + const auto path_len = std::strlen(path); + m_header_factory.MakeGetDiskFreeSpaceHeader(std::addressof(request), path_len); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request, path, path_len)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type, 0)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(response.params[0])); + + /* Check our operation's result. */ + R_TRY(ConvertNativeResult(response.params[1])); + + /* Set output. */ + *out_free = response.params[2]; + *out_total = response.params[3]; + *out_total_free = response.params[4]; + + return ResultSuccess(); + } + Result ClientImpl::CloseDirectory(s32 handle) { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); @@ -570,10 +1047,18 @@ namespace ams::htcfs { R_TRY(this->CheckResponseHeader(response, request.packet_type, 0)); /* Check that we succeeded. */ - R_TRY(ConvertHtcfsResult(response.params[0])); + const auto htcfs_result = ConvertHtcfsResult(response.params[0]); + if (R_FAILED(htcfs_result)) { + R_UNLESS(response.body_size == 0, htcfs::ResultUnexpectedResponseBodySize()); + return htcfs_result; + } /* Check our operation's result. */ - R_TRY(ConvertNativeResult(response.params[1])); + const auto native_result = ConvertNativeResult(response.params[1]); + if (R_FAILED(native_result)) { + R_UNLESS(response.body_size == 0, htcfs::ResultUnexpectedResponseBodySize()); + return native_result; + } /* Check that the number of entries read is allowable. */ R_UNLESS(static_cast(response.params[2]) <= max_out_entries, htcfs::ResultUnexpectedResponseBody()); @@ -648,4 +1133,408 @@ namespace ams::htcfs { return ResultSuccess(); } + Result ClientImpl::CloseFile(s32 handle) { + /* Invalidate the cache. */ + m_cache_manager.Invalidate(handle); + + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + m_header_factory.MakeCloseFileHeader(std::addressof(request), handle); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type, 0)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(response.params[0])); + + /* Check our operation's result. */ + R_TRY(ConvertNativeResult(response.params[1])); + + return ResultSuccess(); + } + + Result ClientImpl::ReadFile(s64 *out, void *buffer, s32 handle, s64 offset, s64 buffer_size, fs::ReadOption option) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Try to read from our cache. */ + if (util::IsIntValueRepresentable(offset) && util::IsIntValueRepresentable(buffer_size)) { + size_t read_size; + if (m_cache_manager.ReadFile(std::addressof(read_size), buffer, handle, static_cast(offset), static_cast(buffer_size))) { + AMS_ASSERT(util::IsIntValueRepresentable(read_size)); + + *out = static_cast(read_size); + return ResultSuccess(); + } + } + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + m_header_factory.MakeReadFileHeader(std::addressof(request), handle, offset, buffer_size); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type)); + + /* Check that we succeeded. */ + const auto htcfs_result = ConvertHtcfsResult(response.params[0]); + if (R_FAILED(htcfs_result)) { + R_UNLESS(response.body_size == 0, htcfs::ResultUnexpectedResponseBodySize()); + return htcfs_result; + } + + /* Check our operation's result. */ + const auto native_result = ConvertNativeResult(response.params[1]); + if (R_FAILED(native_result)) { + R_UNLESS(response.body_size == 0, htcfs::ResultUnexpectedResponseBodySize()); + return native_result; + } + + /* Check the body size. */ + R_UNLESS(response.body_size <= buffer_size, htcfs::ResultUnexpectedResponseBodySize()); + + /* Receive the file data. */ + R_TRY(this->ReceiveFromRpcChannel(buffer, response.body_size)); + + /* Set the output size. */ + *out = response.body_size; + + return ResultSuccess(); + } + + Result ClientImpl::ReadFileLarge(s64 *out, void *buffer, s32 handle, s64 offset, s64 buffer_size, fs::ReadOption option) { + /* Check our buffer size. */ + R_UNLESS(util::IsIntValueRepresentable(buffer_size), htcfs::ResultInvalidArgument()); + + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Setup data channel. */ + this->InitializeDataChannelForReceive(buffer, buffer_size); + ON_SCOPE_EXIT { this->FinalizeDataChannel(); }; + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + m_header_factory.MakeReadFileLargeHeader(std::addressof(request), handle, offset, buffer_size, DataChannelId); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type, 0)); + + /* Check that we succeeded. */ + const auto htcfs_result = ConvertHtcfsResult(response.params[0]); + if (R_FAILED(htcfs_result)) { + R_UNLESS(response.body_size == 0, htcfs::ResultUnexpectedResponseBodySize()); + return htcfs_result; + } + + /* Check our operation's result. */ + const auto native_result = ConvertNativeResult(response.params[1]); + if (R_FAILED(native_result)) { + R_UNLESS(response.body_size == 0, htcfs::ResultUnexpectedResponseBodySize()); + return native_result; + } + + /* Check that the size read is allowable. */ + R_UNLESS(response.params[2] <= buffer_size, htcfs::ResultUnexpectedResponseBodySize()); + + /* Read the entries, if there are any. */ + R_TRY(this->ReceiveFromDataChannel(response.params[2])); + + /* Set the number of output entries. */ + *out = response.params[2]; + + return ResultSuccess(); + } + + Result ClientImpl::WriteFile(const void *buffer, s32 handle, s64 offset, s64 buffer_size, fs::WriteOption option) { + /* Invalidate the cache. */ + m_cache_manager.Invalidate(handle); + + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + m_header_factory.MakeWriteFileHeader(std::addressof(request), buffer_size, handle, option.value, offset); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request, buffer, buffer_size)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type, 0)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(response.params[0])); + + /* Check our operation's result. */ + R_TRY(ConvertNativeResult(response.params[1])); + + return ResultSuccess(); + } + + Result ClientImpl::WriteFileLarge(const void *buffer, s32 handle, s64 offset, s64 buffer_size, fs::WriteOption option) { + /* Invalidate the cache. */ + m_cache_manager.Invalidate(handle); + + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + m_header_factory.MakeWriteFileLargeHeader(std::addressof(request), handle, option.value, offset, buffer_size, DataChannelId); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request, buffer, buffer_size)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type, 0)); + + /* Verify that the host reports ready to receive our data. */ + if (static_cast(response.params[0]) != HtcfsResult::Ready) { + return ConvertHtcfsResult(response.params[0]); + } + + /* Verify that our send will be valid. */ + AMS_ASSERT(util::IsIntValueRepresentable(buffer_size)); + + /* Perform the send. */ + { + /* Initialize data channel for our write. */ + this->InitializeDataChannelForSend(buffer, buffer_size); + + /* Ensure that we clean up our data channel. */ + ON_SCOPE_EXIT { this->FinalizeDataChannel(); }; + + /* Send to our data channel. */ + R_TRY(this->SendToDataChannel()); + } + + /* Receive the large-write response. */ + Header write_resp; + R_TRY(this->ReceiveFromRpcChannel(std::addressof(write_resp), sizeof(write_resp))); + + /* Check the write-response header. */ + R_TRY(this->CheckResponseHeader(write_resp, request.packet_type, 0)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(write_resp.params[0])); + + /* Check our operation's result. */ + R_TRY(ConvertNativeResult(write_resp.params[1])); + + return ResultSuccess(); + } + + Result ClientImpl::GetFileSize(s64 *out, s32 handle) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Check if we have the file size cached. */ + R_SUCCEED_IF(m_cache_manager.GetFileSize(out, handle)); + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + m_header_factory.MakeGetFileSizeHeader(std::addressof(request), handle); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type, 0)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(response.params[0])); + + /* Check our operation's result. */ + R_TRY(ConvertNativeResult(response.params[1])); + + /* Set the output. */ + *out = response.params[2]; + + return ResultSuccess(); + } + + Result ClientImpl::SetFileSize(s64 size, s32 handle) { + /* Invalidate the cache. */ + m_cache_manager.Invalidate(handle); + + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + m_header_factory.MakeSetFileSizeHeader(std::addressof(request), handle, size); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type, 0)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(response.params[0])); + + /* Check our operation's result. */ + R_TRY(ConvertNativeResult(response.params[1])); + + return ResultSuccess(); + } + + Result ClientImpl::FlushFile(s32 handle) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + m_header_factory.MakeFlushFileHeader(std::addressof(request), handle); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type, 0)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(response.params[0])); + + /* Check our operation's result. */ + R_TRY(ConvertNativeResult(response.params[1])); + + return ResultSuccess(); + } + + Result ClientImpl::GetPriorityForFile(s32 *out, s32 handle) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + m_header_factory.MakeGetPriorityForFileHeader(std::addressof(request), handle); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type, 0)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(response.params[0])); + + /* Set the output. */ + *out = static_cast(response.params[1]); + + return ResultSuccess(); + } + + Result ClientImpl::SetPriorityForFile(s32 priority, s32 handle) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + m_header_factory.MakeSetPriorityForFileHeader(std::addressof(request), handle, priority); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type, 0)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(response.params[0])); + + return ResultSuccess(); + } + } diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp index e66fc7642..219f289a1 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp @@ -56,7 +56,21 @@ namespace ams::htcfs { void Cancel(); void Wait(); public: + Result OpenFile(s32 *out_handle, const char *path, fs::OpenMode mode, bool case_sensitive); + Result FileExists(bool *out, const char *path, bool case_sensitive); + Result DeleteFile(const char *path, bool case_sensitive); + Result RenameFile(const char *old_path, const char *new_path, bool case_sensitive); + Result GetEntryType(fs::DirectoryEntryType *out, const char *path, bool case_sensitive); Result OpenDirectory(s32 *out_handle, const char *path, fs::OpenDirectoryMode mode, bool case_sensitive); + Result DirectoryExists(bool *out, const char *path, bool case_sensitive); + Result CreateDirectory(const char *path, bool case_sensitive); + Result DeleteDirectory(const char *path, bool recursively, bool case_sensitive); + Result RenameDirectory(const char *old_path, const char *new_path, bool case_sensitive); + Result CreateFile(const char *path, s64 size, bool case_sensitive); + Result GetFileTimeStamp(u64 *out_create, u64 *out_access, u64 *out_modify, const char *path, bool case_sensitive); + Result GetCaseSensitivePath(char *dst, size_t dst_size, const char *path); + Result GetDiskFreeSpace(s64 *out_free, s64 *out_total, s64 *out_total_free, const char *path); + Result CloseDirectory(s32 handle); Result GetEntryCount(s64 *out, s32 handle); @@ -64,6 +78,18 @@ namespace ams::htcfs { Result ReadDirectoryLarge(s64 *out, fs::DirectoryEntry *out_entries, size_t max_out_entries, s32 handle); Result GetPriorityForDirectory(s32 *out, s32 handle); Result SetPriorityForDirectory(s32 priority, s32 handle); + + Result CloseFile(s32 handle); + + Result ReadFile(s64 *out, void *buffer, s32 handle, s64 offset, s64 buffer_size, fs::ReadOption option); + Result ReadFileLarge(s64 *out, void *buffer, s32 handle, s64 offset, s64 buffer_size, fs::ReadOption option); + Result WriteFile(const void *buffer, s32 handle, s64 offset, s64 buffer_size, fs::WriteOption option); + Result WriteFileLarge(const void *buffer, s32 handle, s64 offset, s64 buffer_size, fs::WriteOption option); + Result GetFileSize(s64 *out, s32 handle); + Result SetFileSize(s64 size, s32 handle); + Result FlushFile(s32 handle); + Result GetPriorityForFile(s32 *out, s32 handle); + Result SetPriorityForFile(s32 priority, s32 handle); private: int WaitAny(htclow::ChannelState state, os::EventType *event); diff --git a/libraries/libstratosphere/source/htcfs/htcfs_directory_service_object.cpp b/libraries/libstratosphere/source/htcfs/htcfs_directory_service_object.cpp index f0db33f07..e37a3f89b 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_directory_service_object.cpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_directory_service_object.cpp @@ -16,7 +16,6 @@ #include #include "htcfs_directory_service_object.hpp" #include "htcfs_client.hpp" -#include "../htclow/htclow_default_channel_config.hpp" namespace ams::htcfs { diff --git a/libraries/libstratosphere/source/htcfs/htcfs_file_service_object.cpp b/libraries/libstratosphere/source/htcfs/htcfs_file_service_object.cpp index edae19ad0..ff1025784 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_file_service_object.cpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_file_service_object.cpp @@ -15,42 +15,59 @@ */ #include #include "htcfs_file_service_object.hpp" +#include "htcfs_client.hpp" namespace ams::htcfs { FileServiceObject::FileServiceObject(s32 handle) : m_handle(handle) { /* ... */ } FileServiceObject::~FileServiceObject() { - /* TODO */ - AMS_ABORT("htcfs::GetClient().CloseFile(m_handle);"); + htcfs::GetClient().CloseFile(m_handle); } Result FileServiceObject::ReadFile(ams::sf::Out out, s64 offset, const ams::sf::OutNonSecureBuffer &buffer, ams::fs::ReadOption option) { - AMS_ABORT("FileServiceObject::ReadFile"); + /* Validate offset. */ + R_UNLESS(offset >= 0, htcfs::ResultInvalidArgument()); + + if (buffer.GetSize() >= ClientImpl::MaxPacketBodySize) { + return htcfs::GetClient().ReadFileLarge(out.GetPointer(), buffer.GetPointer(), m_handle, offset, buffer.GetSize(), option); + } else { + return htcfs::GetClient().ReadFile(out.GetPointer(), buffer.GetPointer(), m_handle, offset, buffer.GetSize(), option); + } } Result FileServiceObject::WriteFile(s64 offset, const ams::sf::InNonSecureBuffer &buffer, ams::fs::WriteOption option) { - AMS_ABORT("FileServiceObject::WriteFile"); + /* Validate offset. */ + R_UNLESS(offset >= 0, htcfs::ResultInvalidArgument()); + + if (buffer.GetSize() >= ClientImpl::MaxPacketBodySize) { + return htcfs::GetClient().WriteFileLarge(buffer.GetPointer(), m_handle, offset, buffer.GetSize(), option); + } else { + return htcfs::GetClient().WriteFile(buffer.GetPointer(), m_handle, offset, buffer.GetSize(), option); + } } Result FileServiceObject::GetFileSize(ams::sf::Out out) { - AMS_ABORT("FileServiceObject::GetFileSize"); + return htcfs::GetClient().GetFileSize(out.GetPointer(), m_handle); } Result FileServiceObject::SetFileSize(s64 size) { - AMS_ABORT("FileServiceObject::SetFileSize"); + /* Validate size. */ + R_UNLESS(size >= 0, htcfs::ResultInvalidArgument()); + + return htcfs::GetClient().SetFileSize(size, m_handle); } Result FileServiceObject::FlushFile() { - AMS_ABORT("FileServiceObject::FlushFile"); + return htcfs::GetClient().FlushFile(m_handle); } Result FileServiceObject::SetPriorityForFile(s32 priority) { - AMS_ABORT("FileServiceObject::SetPriorityForFile"); + return htcfs::GetClient().SetPriorityForFile(priority, m_handle); } Result FileServiceObject::GetPriorityForFile(ams::sf::Out out) { - AMS_ABORT("FileServiceObject::GetPriorityForFile"); + return htcfs::GetClient().GetPriorityForFile(out.GetPointer(), m_handle); } } diff --git a/libraries/libstratosphere/source/htcfs/htcfs_file_system_service_object.cpp b/libraries/libstratosphere/source/htcfs/htcfs_file_system_service_object.cpp index e93d3d6f4..468574bde 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_file_system_service_object.cpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_file_system_service_object.cpp @@ -44,26 +44,75 @@ namespace ams::htcfs { return 0 < len && len < static_cast(fs::EntryNameLengthMax + 1); } + Result ConvertOpenMode(fs::OpenMode *out, u32 open_mode) { + switch (open_mode) { + case 1: + *out = fs::OpenMode_Read; + break; + case 2: + *out = static_cast(fs::OpenMode_Write | fs::OpenMode_AllowAppend); + break; + case 3: + *out = static_cast(fs::OpenMode_ReadWrite | fs::OpenMode_AllowAppend); + break; + default: + return htcfs::ResultInvalidArgument(); + } + + return ResultSuccess(); + } + } Result FileSystemServiceObject::OpenFile(sf::Out> out, const tma::Path &path, u32 open_mode, bool case_sensitive) { - AMS_ABORT("FileSystemServiceObject::OpenFile"); + /* Check that the path is valid. */ + R_UNLESS(IsValidPath(path), htcfs::ResultInvalidArgument()); + + /* Convert the open mode. */ + fs::OpenMode fs_open_mode; + R_TRY(ConvertOpenMode(std::addressof(fs_open_mode), open_mode)); + + /* Open the file. */ + s32 handle; + R_TRY(htcfs::GetClient().OpenFile(std::addressof(handle), path.str, fs_open_mode, case_sensitive)); + + /* Set the output file. */ + *out = FileServiceObjectFactory::CreateSharedEmplaced(handle); + return ResultSuccess(); } Result FileSystemServiceObject::FileExists(sf::Out out, const tma::Path &path, bool case_sensitive) { - AMS_ABORT("FileSystemServiceObject::FileExists"); + /* Check that the path is valid. */ + R_UNLESS(IsValidPath(path), htcfs::ResultInvalidArgument()); + + /* Get whether the file exists. */ + return htcfs::GetClient().FileExists(out.GetPointer(), path.str, case_sensitive); } Result FileSystemServiceObject::DeleteFile(const tma::Path &path, bool case_sensitive) { - AMS_ABORT("FileSystemServiceObject::DeleteFile"); + /* Check that the path is valid. */ + R_UNLESS(IsValidPath(path), htcfs::ResultInvalidArgument()); + + /* Delete the file. */ + return htcfs::GetClient().DeleteFile(path.str, case_sensitive); } Result FileSystemServiceObject::RenameFile(const tma::Path &old_path, const tma::Path &new_path, bool case_sensitive) { - AMS_ABORT("FileSystemServiceObject::RenameFile"); + /* Check that the paths are valid. */ + R_UNLESS(IsValidPath(old_path), htcfs::ResultInvalidArgument()); + R_UNLESS(IsValidPath(new_path), htcfs::ResultInvalidArgument()); + + /* Rename the file. */ + return htcfs::GetClient().RenameFile(old_path.str, new_path.str, case_sensitive); } Result FileSystemServiceObject::GetIOType(sf::Out out, const tma::Path &path, bool case_sensitive) { - AMS_ABORT("FileSystemServiceObject::GetIOType"); + /* Check that the path is valid. */ + R_UNLESS(IsValidPath(path), htcfs::ResultInvalidArgument()); + + /* Get the entry type. */ + static_assert(sizeof(s32) == sizeof(fs::DirectoryEntryType)); + return htcfs::GetClient().GetEntryType(reinterpret_cast(out.GetPointer()), path.str, case_sensitive); } Result FileSystemServiceObject::OpenDirectory(sf::Out> out, const tma::Path &path, s32 open_mode, bool case_sensitive) { @@ -80,35 +129,68 @@ namespace ams::htcfs { } Result FileSystemServiceObject::DirectoryExists(sf::Out out, const tma::Path &path, bool case_sensitive) { - AMS_ABORT("FileSystemServiceObject::DirectoryExists"); + /* Check that the path is valid. */ + R_UNLESS(IsValidPath(path), htcfs::ResultInvalidArgument()); + + /* Get whether the file exists. */ + return htcfs::GetClient().DirectoryExists(out.GetPointer(), path.str, case_sensitive); } Result FileSystemServiceObject::CreateDirectory(const tma::Path &path, bool case_sensitive) { - AMS_ABORT("FileSystemServiceObject::CreateDirectory"); + /* Check that the path is valid. */ + R_UNLESS(IsValidPath(path), htcfs::ResultInvalidArgument()); + + /* Create the directory. */ + return htcfs::GetClient().CreateDirectory(path.str, case_sensitive); } Result FileSystemServiceObject::DeleteDirectory(const tma::Path &path, bool recursively, bool case_sensitive) { - AMS_ABORT("FileSystemServiceObject::DeleteDirectory"); + /* Check that the path is valid. */ + R_UNLESS(IsValidPath(path), htcfs::ResultInvalidArgument()); + + /* Delete the directory. */ + return htcfs::GetClient().DeleteDirectory(path.str, recursively, case_sensitive); } Result FileSystemServiceObject::RenameDirectory(const tma::Path &old_path, const tma::Path &new_path, bool case_sensitive) { - AMS_ABORT("FileSystemServiceObject::RenameDirectory"); + /* Check that the paths are valid. */ + R_UNLESS(IsValidPath(old_path), htcfs::ResultInvalidArgument()); + R_UNLESS(IsValidPath(new_path), htcfs::ResultInvalidArgument()); + + /* Rename the file. */ + return htcfs::GetClient().RenameDirectory(old_path.str, new_path.str, case_sensitive); } Result FileSystemServiceObject::CreateFile(const tma::Path &path, s64 size, bool case_sensitive) { - AMS_ABORT("FileSystemServiceObject::CreateFile"); + /* Check that the path is valid. */ + R_UNLESS(IsValidPath(path), htcfs::ResultInvalidArgument()); + + /* Create the file. */ + return htcfs::GetClient().CreateFile(path.str, size, case_sensitive); } Result FileSystemServiceObject::GetFileTimeStamp(sf::Out out_create, sf::Out out_access, sf::Out out_modify, const tma::Path &path, bool case_sensitive) { - AMS_ABORT("FileSystemServiceObject::GetFileTimeStamp"); + /* Check that the path is valid. */ + R_UNLESS(IsValidPath(path), htcfs::ResultInvalidArgument()); + + /* Get the timestamp. */ + return htcfs::GetClient().GetFileTimeStamp(out_create.GetPointer(), out_access.GetPointer(), out_modify.GetPointer(), path.str, case_sensitive); } Result FileSystemServiceObject::GetCaseSensitivePath(const tma::Path &path, const sf::OutBuffer &out) { - AMS_ABORT("FileSystemServiceObject::GetCaseSensitivePath"); + /* Check that the path is valid. */ + R_UNLESS(IsValidPath(path), htcfs::ResultInvalidArgument()); + + /* Get the case sensitive path. */ + return htcfs::GetClient().GetCaseSensitivePath(reinterpret_cast(out.GetPointer()), out.GetSize(), path.str); } Result FileSystemServiceObject::GetDiskFreeSpaceExW(sf::Out out_free, sf::Out out_total, sf::Out out_total_free, const tma::Path &path) { - AMS_ABORT("FileSystemServiceObject::GetDiskFreeSpaceExW"); + /* Check that the path is valid. */ + R_UNLESS(IsValidPath(path), htcfs::ResultInvalidArgument()); + + /* Get the timestamp. */ + return htcfs::GetClient().GetDiskFreeSpace(out_free.GetPointer(), out_total.GetPointer(), out_total_free.GetPointer(), path.str); } } diff --git a/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp b/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp index 8417d34a9..c2fceba23 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp @@ -115,10 +115,62 @@ namespace ams::htcfs { return this->MakeRequestHeader(out, PacketType::SetProtocolVersion, 0, version); } + void MakeOpenFileHeader(Header *out, int path_len, fs::OpenMode mode, bool case_sensitive, s64 cache_size) { + return this->MakeRequestHeader(out, PacketType::OpenFile, path_len, static_cast(mode), case_sensitive ? 1 : 0, cache_size); + } + + void MakeFileExistsHeader(Header *out, int path_len, bool case_sensitive) { + return this->MakeRequestHeader(out, PacketType::FileExists, path_len, case_sensitive ? 1 : 0); + } + + void MakeDeleteFileHeader(Header *out, int path_len, bool case_sensitive) { + return this->MakeRequestHeader(out, PacketType::DeleteFile, path_len, case_sensitive ? 1 : 0); + } + + void MakeRenameFileHeader(Header *out, int old_path_len, int new_path_len, bool case_sensitive) { + return this->MakeRequestHeader(out, PacketType::RenameFile, old_path_len + new_path_len, old_path_len, new_path_len, case_sensitive ? 1 : 0); + } + + void MakeGetEntryTypeHeader(Header *out, int path_len, bool case_sensitive) { + return this->MakeRequestHeader(out, PacketType::GetEntryType, path_len, case_sensitive ? 1 : 0); + } + void MakeOpenDirectoryHeader(Header *out, int path_len, fs::OpenDirectoryMode mode, bool case_sensitive) { return this->MakeRequestHeader(out, PacketType::OpenDirectory, path_len, static_cast(mode), case_sensitive ? 1 : 0); } + void MakeDirectoryExistsHeader(Header *out, int path_len, bool case_sensitive) { + return this->MakeRequestHeader(out, PacketType::DirectoryExists, path_len, case_sensitive ? 1 : 0); + } + + void MakeCreateDirectoryHeader(Header *out, int path_len, bool case_sensitive) { + return this->MakeRequestHeader(out, PacketType::CreateDirectory, path_len, case_sensitive ? 1 : 0); + } + + void MakeDeleteDirectoryHeader(Header *out, int path_len, bool recursively, bool case_sensitive) { + return this->MakeRequestHeader(out, PacketType::DeleteDirectory, path_len, recursively ? 1 : 0, case_sensitive ? 1 : 0); + } + + void MakeRenameDirectoryHeader(Header *out, int old_path_len, int new_path_len, bool case_sensitive) { + return this->MakeRequestHeader(out, PacketType::RenameDirectory, old_path_len + new_path_len, old_path_len, new_path_len, case_sensitive ? 1 : 0); + } + + void MakeCreateFileHeader(Header *out, int path_len, s64 size, bool case_sensitive) { + return this->MakeRequestHeader(out, PacketType::CreateDirectory, path_len, size, case_sensitive ? 1 : 0); + } + + void MakeGetFileTimeStampHeader(Header *out, int path_len, bool case_sensitive) { + return this->MakeRequestHeader(out, PacketType::GetFileTimeStamp, path_len, case_sensitive ? 1 : 0); + } + + void MakeGetCaseSensitivePathHeader(Header *out, int path_len) { + return this->MakeRequestHeader(out, PacketType::GetCaseSensitivePath, path_len); + } + + void MakeGetDiskFreeSpaceHeader(Header *out, int path_len) { + return this->MakeRequestHeader(out, PacketType::GetDiskFreeSpace, path_len); + } + void MakeCloseDirectoryHeader(Header *out, s32 handle) { return this->MakeRequestHeader(out, PacketType::CloseDirectory, 0, handle); } @@ -142,6 +194,46 @@ namespace ams::htcfs { void MakeSetPriorityForDirectoryHeader(Header *out, s32 handle, s32 priority) { return this->MakeRequestHeader(out, PacketType::SetPriorityForDirectory, 0, handle, priority); } + + void MakeCloseFileHeader(Header *out, s32 handle) { + return this->MakeRequestHeader(out, PacketType::CloseFile, 0, handle); + } + + void MakeReadFileHeader(Header *out, s32 handle, s64 offset, s64 buffer_size) { + return this->MakeRequestHeader(out, PacketType::ReadFile, 0, handle, offset, buffer_size); + } + + void MakeReadFileLargeHeader(Header *out, s32 handle, s64 offset, s64 buffer_size, u16 data_channel_id) { + return this->MakeRequestHeader(out, PacketType::ReadFileLarge, 0, handle, offset, buffer_size, data_channel_id); + } + + void MakeWriteFileHeader(Header *out, s64 buffer_size, s32 handle, u32 option, s64 offset) { + return this->MakeRequestHeader(out, PacketType::WriteFile, buffer_size, handle, option, offset); + } + + void MakeWriteFileLargeHeader(Header *out, s32 handle, u32 option, s64 offset, s64 buffer_size, u16 data_channel_id) { + return this->MakeRequestHeader(out, PacketType::WriteFileLarge, 0, handle, option, offset, buffer_size, data_channel_id); + } + + void MakeGetFileSizeHeader(Header *out, s32 handle) { + return this->MakeRequestHeader(out, PacketType::GetFileSize, 0, handle); + } + + void MakeSetFileSizeHeader(Header *out, s32 handle, s64 size) { + return this->MakeRequestHeader(out, PacketType::SetFileSize, 0, handle, size); + } + + void MakeFlushFileHeader(Header *out, s32 handle) { + return this->MakeRequestHeader(out, PacketType::FlushFile, 0, handle); + } + + void MakeGetPriorityForFileHeader(Header *out, s32 handle) { + return this->MakeRequestHeader(out, PacketType::GetPriorityForFile, 0, handle); + } + + void MakeSetPriorityForFileHeader(Header *out, s32 handle, s32 priority) { + return this->MakeRequestHeader(out, PacketType::SetPriorityForFile, 0, handle, priority); + } }; } diff --git a/libraries/libstratosphere/source/htcfs/htcfs_result.hpp b/libraries/libstratosphere/source/htcfs/htcfs_result.hpp index 8346e66f6..4568fefd6 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_result.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_result.hpp @@ -26,6 +26,7 @@ namespace ams::htcfs { InvalidRequest = 3, InvalidHandle = 4, OutOfHandle = 5, + Ready = 6, }; inline Result ConvertHtcfsResult(HtcfsResult result) { From 7621bd4e133a5468543753906bbe8bf79b41c987 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 16 Feb 2021 02:15:59 -0800 Subject: [PATCH 067/280] htcfs: fix CreateFile packet header --- libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp b/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp index c2fceba23..b998c47eb 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp @@ -156,7 +156,7 @@ namespace ams::htcfs { } void MakeCreateFileHeader(Header *out, int path_len, s64 size, bool case_sensitive) { - return this->MakeRequestHeader(out, PacketType::CreateDirectory, path_len, size, case_sensitive ? 1 : 0); + return this->MakeRequestHeader(out, PacketType::CreateFile, path_len, size, case_sensitive ? 1 : 0); } void MakeGetFileTimeStampHeader(Header *out, int path_len, bool case_sensitive) { From f0ef9fb9183f04941cbc42074d1c407536bd9c63 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 16 Feb 2021 17:15:57 -0800 Subject: [PATCH 068/280] htc: fixes for WriteFileLarge/sending over data channel --- .../libstratosphere/source/htcfs/htcfs_client_impl.cpp | 9 ++++++--- .../source/htclow/mux/htclow_mux_channel_impl.cpp | 3 +++ .../source/htclow/mux/htclow_mux_send_buffer.cpp | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp index 67404632d..fbeacf55c 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp @@ -429,11 +429,13 @@ namespace ams::htcfs { R_TRY(this->CheckResponseHeader(response, request.packet_type)); /* Check the response body size. */ - R_UNLESS(response.body_size > 0, htcfs::ResultUnexpectedResponseBodySize()); + R_UNLESS(response.body_size >= 0, htcfs::ResultUnexpectedResponseBodySize()); R_UNLESS(static_cast(response.body_size) <= MaxPacketBodySize, htcfs::ResultUnexpectedResponseBodySize()); /* Receive the response body. */ - R_TRY(this->ReceiveFromRpcChannel(m_packet_buffer, response.body_size)); + if (response.body_size > 0) { + R_TRY(this->ReceiveFromRpcChannel(m_packet_buffer, response.body_size)); + } /* Check that we succeeded. */ R_TRY(ConvertHtcfsResult(response.params[0])); @@ -1332,7 +1334,7 @@ namespace ams::htcfs { m_header_factory.MakeWriteFileLargeHeader(std::addressof(request), handle, option.value, offset, buffer_size, DataChannelId); /* Send the request to the host. */ - R_TRY(this->SendRequest(request, buffer, buffer_size)); + R_TRY(this->SendRequest(request)); /* Receive response from the host. */ R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); @@ -1537,4 +1539,5 @@ namespace ams::htcfs { return ResultSuccess(); } + } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp index b465eeadf..cbe7fed79 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp @@ -465,6 +465,9 @@ namespace ams::htclow::mux { /* Set max packet size. */ m_send_buffer.SetMaxPacketSize(max_packet_size); + + /* Set our total send size. */ + m_total_send_size = buf_size; } } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp index a7add6b21..4336d8d17 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp @@ -93,7 +93,7 @@ namespace ams::htclow::mux { /* Determine the sendable size. */ const auto offset = total_send_size - ring_buffer_data_size; - const auto sendable_size = std::min(share - offset, ring_buffer_data_size); + const auto sendable_size = m_flow_control_enabled ? std::min(share - offset, ring_buffer_data_size) : ring_buffer_data_size; if (sendable_size == 0) { return false; } From 61929d6e21e3297c681e122d2228a61c5184a236 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 17 Feb 2021 04:08:58 -0800 Subject: [PATCH 069/280] htcs: hook service objects up to (unimplemented) manager apis --- .../include/stratosphere/sf/sf_buffers.hpp | 8 +- .../source/htcs/impl/htcs_impl.cpp | 31 +++ .../source/htcs/impl/htcs_impl.hpp | 24 ++ .../source/htcs/impl/htcs_manager.hpp | 30 +++ .../server/htcs_manager_service_object.cpp | 40 +++- ...tcs_manager_service_object_deprecated.cpp} | 0 .../server/htcs_service_object_allocator.hpp | 25 ++ .../server/htcs_socket_service_object.cpp | 224 +++++++++++++++--- .../htcs_socket_service_object_deprecated.cpp | 29 +++ .../libvapours/include/vapours/results.hpp | 1 + .../include/vapours/results/htcs_results.hpp | 25 ++ 11 files changed, 396 insertions(+), 41 deletions(-) create mode 100644 libraries/libstratosphere/source/htcs/impl/htcs_impl.cpp create mode 100644 libraries/libstratosphere/source/htcs/impl/htcs_impl.hpp rename libraries/libstratosphere/source/htcs/server/{htcs_manager_service_object_reprecated.cpp => htcs_manager_service_object_deprecated.cpp} (100%) create mode 100644 libraries/libstratosphere/source/htcs/server/htcs_service_object_allocator.hpp create mode 100644 libraries/libstratosphere/source/htcs/server/htcs_socket_service_object_deprecated.cpp create mode 100644 libraries/libvapours/include/vapours/results/htcs_results.hpp diff --git a/libraries/libstratosphere/include/stratosphere/sf/sf_buffers.hpp b/libraries/libstratosphere/include/stratosphere/sf/sf_buffers.hpp index 1ae3ec12c..438e88f67 100644 --- a/libraries/libstratosphere/include/stratosphere/sf/sf_buffers.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf/sf_buffers.hpp @@ -206,11 +206,11 @@ namespace ams::sf { } constexpr explicit operator Span() const { - return {this->GetPointer(), static_cast(this->GetSize())}; + return {this->GetPointer(), this->GetSize()}; } constexpr Span ToSpan() const { - return {this->GetPointer(), static_cast(this->GetSize())}; + return {this->GetPointer(), this->GetSize()}; } }; @@ -238,11 +238,11 @@ namespace ams::sf { } constexpr explicit operator Span() const { - return {this->GetPointer(), static_cast(this->GetSize())}; + return {this->GetPointer(), this->GetSize()}; } constexpr Span ToSpan() const { - return {this->GetPointer(), static_cast(this->GetSize())}; + return {this->GetPointer(), this->GetSize()}; } }; diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_impl.cpp b/libraries/libstratosphere/source/htcs/impl/htcs_impl.cpp new file mode 100644 index 000000000..8226fdf8d --- /dev/null +++ b/libraries/libstratosphere/source/htcs/impl/htcs_impl.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcs_impl.hpp" + +namespace ams::htcs::impl { + + namespace { + + constexpr const htcs::HtcsPeerName PeerNameAny = {""}; + constexpr const htcs::HtcsPeerName DefaultHostName = {""}; + + } + + const htcs::HtcsPeerName GetPeerNameAny() { return PeerNameAny; } + const htcs::HtcsPeerName GetDefaultHostName() { return DefaultHostName; } + +} diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_impl.hpp b/libraries/libstratosphere/source/htcs/impl/htcs_impl.hpp new file mode 100644 index 000000000..4ba4e3e25 --- /dev/null +++ b/libraries/libstratosphere/source/htcs/impl/htcs_impl.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htcs::impl { + + const htcs::HtcsPeerName GetPeerNameAny(); + const htcs::HtcsPeerName GetDefaultHostName(); + +} diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_manager.hpp b/libraries/libstratosphere/source/htcs/impl/htcs_manager.hpp index 433fc8b7f..4fcb48984 100644 --- a/libraries/libstratosphere/source/htcs/impl/htcs_manager.hpp +++ b/libraries/libstratosphere/source/htcs/impl/htcs_manager.hpp @@ -32,6 +32,36 @@ namespace ams::htcs::impl { os::EventType *GetServiceAvailabilityEvent(); bool IsServiceAvailable(); + public: + void Socket(s32 *out_err, s32 *out_desc, bool enable_disconnection_emulation); + void Close(s32 *out_err, s32 *out_res, s32 desc); + void Connect(s32 *out_err, s32 *out_res, const SockAddrHtcs &address, s32 desc); + void Bind(s32 *out_err, s32 *out_res, const SockAddrHtcs &address, s32 desc); + void Listen(s32 *out_err, s32 *out_res, s32 backlog_count, s32 desc); + void Recv(s32 *out_err, s64 *out_size, char *buffer, size_t size, s32 flags, s32 desc); + void Send(s32 *out_err, s64 *out_size, const char *buffer, size_t size, s32 flags, s32 desc); + void Shutdown(s32 *out_err, s32 *out_res, s32 how, s32 desc); + void Fcntl(s32 *out_err, s32 *out_res, s32 command, s32 value, s32 desc); + + Result AcceptStart(u32 *out_task_id, Handle *out_handle, s32 desc); + void AcceptResults(s32 *out_err, s32 *out_desc, SockAddrHtcs *out_address, u32 task_id, s32 desc); + + Result RecvStart(u32 *out_task_id, Handle *out_handle, s64 size, s32 desc, s32 flags); + void RecvResults(s32 *out_err, s64 *out_size, char *buffer, s64 buffer_size, u32 task_id, s32 desc); + + Result SendStart(u32 *out_task_id, Handle *out_handle, const char *buffer, s64 size, s32 desc, s32 flags); + Result SendLargeStart(u32 *out_task_id, Handle *out_handle, const char **buffers, const s64 *sizes, s32 count, s32 desc, s32 flags); + void SendResults(s32 *out_err, s64 *out_size, u32 task_id, s32 desc); + + Result StartSend(u32 *out_task_id, Handle *out_handle, s32 desc, s64 size, s32 flags); + Result ContinueSend(s64 *out_size, const char *buffer, s64 buffer_size, u32 task_id, s32 desc); + void EndSend(s32 *out_err, s64 *out_size, u32 task_id, s32 desc); + + Result StartRecv(u32 *out_task_id, Handle *out_handle, s64 size, s32 desc, s32 flags); + void EndRecv(s32 *out_err, s64 *out_size, char *buffer, s64 buffer_size, u32 task_id, s32 desc); + + Result StartSelect(u32 *out_task_id, Handle *out_handle, Span read_handles, Span write_handles, Span exception_handles, s64 tv_sec, s64 tv_usec); + Result EndSelect(s32 *out_err, s32 *out_res, Span read_handles, Span write_handles, Span exception_handles, u32 task_id); }; } diff --git a/libraries/libstratosphere/source/htcs/server/htcs_manager_service_object.cpp b/libraries/libstratosphere/source/htcs/server/htcs_manager_service_object.cpp index e3a328f0f..7eb2ef215 100644 --- a/libraries/libstratosphere/source/htcs/server/htcs_manager_service_object.cpp +++ b/libraries/libstratosphere/source/htcs/server/htcs_manager_service_object.cpp @@ -16,15 +16,14 @@ #include #include "htcs_manager_service_object.hpp" #include "htcs_socket_service_object.hpp" +#include "htcs_service_object_allocator.hpp" +#include "../impl/htcs_manager.hpp" +#include "../impl/htcs_impl.hpp" namespace ams::htcs::server { namespace { - struct ServiceObjectAllocatorTag; - using ServiceObjectAllocator = ams::sf::ExpHeapStaticAllocator<32_KB, ServiceObjectAllocatorTag>; - using ServiceObjectFactory = ams::sf::ObjectFactory; - class StaticAllocatorInitializer { public: StaticAllocatorInitializer() { @@ -35,11 +34,13 @@ namespace ams::htcs::server { } Result ManagerServiceObject::GetPeerNameAny(sf::Out out) { - AMS_ABORT("ManagerServiceObject::GetPeerNameAny"); + *out = impl::GetPeerNameAny(); + return ResultSuccess(); } Result ManagerServiceObject::GetDefaultHostName(sf::Out out) { - AMS_ABORT("ManagerServiceObject::GetDefaultHostName"); + *out = impl::GetDefaultHostName(); + return ResultSuccess(); } Result ManagerServiceObject::CreateSocketOld(sf::Out out_err, sf::Out> out) { @@ -47,7 +48,20 @@ namespace ams::htcs::server { } Result ManagerServiceObject::CreateSocket(sf::Out out_err, sf::Out> out, bool enable_disconnection_emulation) { - AMS_ABORT("ManagerServiceObject::CreateSocket"); + /* Get the htcs manager. */ + auto *manager = impl::HtcsManagerHolder::GetHtcsManager(); + + /* Create a new socket. */ + s32 desc; + manager->Socket(out_err.GetPointer(), std::addressof(desc), enable_disconnection_emulation); + + /* If an error occurred, we're done. */ + R_SUCCEED_IF(*out_err != 0); + + /* Create a new socket object. */ + *out = ServiceObjectFactory::CreateSharedEmplaced(this, desc); + + return ResultSuccess(); } Result ManagerServiceObject::RegisterProcessId(const sf::ClientProcessId &client_pid) { @@ -61,11 +75,19 @@ namespace ams::htcs::server { } Result ManagerServiceObject::StartSelect(sf::Out out_task_id, sf::OutCopyHandle out_event, const sf::InMapAliasArray &read_handles, const sf::InMapAliasArray &write_handles, const sf::InMapAliasArray &exception_handles, s64 tv_sec, s64 tv_usec) { - AMS_ABORT("ManagerServiceObject::StartSelect"); + /* Get the htcs manager. */ + auto *manager = impl::HtcsManagerHolder::GetHtcsManager(); + + /* Start the select. */ + return manager->StartSelect(out_task_id.GetPointer(), out_event.GetHandlePointer(), read_handles.ToSpan(), write_handles.ToSpan(), exception_handles.ToSpan(), tv_sec, tv_usec); } Result ManagerServiceObject::EndSelect(sf::Out out_err, sf::Out out_res, const sf::OutMapAliasArray &read_handles, const sf::OutMapAliasArray &write_handles, const sf::OutMapAliasArray &exception_handles, u32 task_id) { - AMS_ABORT("ManagerServiceObject::EndSelect"); + /* Get the htcs manager. */ + auto *manager = impl::HtcsManagerHolder::GetHtcsManager(); + + /* End the select. */ + return manager->EndSelect(out_err.GetPointer(), out_res.GetPointer(), read_handles.ToSpan(), write_handles.ToSpan(), exception_handles.ToSpan(), task_id); } } diff --git a/libraries/libstratosphere/source/htcs/server/htcs_manager_service_object_reprecated.cpp b/libraries/libstratosphere/source/htcs/server/htcs_manager_service_object_deprecated.cpp similarity index 100% rename from libraries/libstratosphere/source/htcs/server/htcs_manager_service_object_reprecated.cpp rename to libraries/libstratosphere/source/htcs/server/htcs_manager_service_object_deprecated.cpp diff --git a/libraries/libstratosphere/source/htcs/server/htcs_service_object_allocator.hpp b/libraries/libstratosphere/source/htcs/server/htcs_service_object_allocator.hpp new file mode 100644 index 000000000..66cf20870 --- /dev/null +++ b/libraries/libstratosphere/source/htcs/server/htcs_service_object_allocator.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htcs::server { + + struct ServiceObjectAllocatorTag; + using ServiceObjectAllocator = ams::sf::ExpHeapStaticAllocator<32_KB, ServiceObjectAllocatorTag>; + using ServiceObjectFactory = ams::sf::ObjectFactory; + +} diff --git a/libraries/libstratosphere/source/htcs/server/htcs_socket_service_object.cpp b/libraries/libstratosphere/source/htcs/server/htcs_socket_service_object.cpp index ebf277520..9e7e31692 100644 --- a/libraries/libstratosphere/source/htcs/server/htcs_socket_service_object.cpp +++ b/libraries/libstratosphere/source/htcs/server/htcs_socket_service_object.cpp @@ -15,6 +15,8 @@ */ #include #include "htcs_socket_service_object.hpp" +#include "htcs_service_object_allocator.hpp" +#include "../impl/htcs_manager.hpp" namespace ams::htcs::server { @@ -23,107 +25,273 @@ namespace ams::htcs::server { } SocketServiceObject::~SocketServiceObject() { - AMS_ABORT("SocketServiceObject::~SocketServiceObject"); + /* Get the htcs manager. */ + auto *manager = impl::HtcsManagerHolder::GetHtcsManager(); + + /* Close the underlying socket. */ + s32 dummy_err, dummy_res; + manager->Close(std::addressof(dummy_err), std::addressof(dummy_res), m_desc); } Result SocketServiceObject::Close(sf::Out out_err, sf::Out out_res) { - AMS_ABORT("SocketServiceObject::Close"); + /* Get the htcs manager. */ + auto *manager = impl::HtcsManagerHolder::GetHtcsManager(); + + /* Close the underlying socket. */ + manager->Close(out_err.GetPointer(), out_res.GetPointer(), m_desc); + + return ResultSuccess(); } Result SocketServiceObject::Connect(sf::Out out_err, sf::Out out_res, const htcs::SockAddrHtcs &address) { - AMS_ABORT("SocketServiceObject::Connect"); + /* Get the htcs manager. */ + auto *manager = impl::HtcsManagerHolder::GetHtcsManager(); + + /* Perform the connect. */ + manager->Connect(out_err.GetPointer(), out_res.GetPointer(), address, m_desc); + + return ResultSuccess(); } Result SocketServiceObject::Bind(sf::Out out_err, sf::Out out_res, const htcs::SockAddrHtcs &address) { - AMS_ABORT("SocketServiceObject::Bind"); + /* Get the htcs manager. */ + auto *manager = impl::HtcsManagerHolder::GetHtcsManager(); + + /* Perform the bind. */ + manager->Bind(out_err.GetPointer(), out_res.GetPointer(), address, m_desc); + + return ResultSuccess(); } Result SocketServiceObject::Listen(sf::Out out_err, sf::Out out_res, s32 backlog_count) { - AMS_ABORT("SocketServiceObject::Listen"); - } + /* Get the htcs manager. */ + auto *manager = impl::HtcsManagerHolder::GetHtcsManager(); - Result SocketServiceObject::Accept(sf::Out out_err, sf::Out> out, sf::Out out_address) { - AMS_ABORT("SocketServiceObject::Accept"); + /* Perform the listen. */ + manager->Listen(out_err.GetPointer(), out_res.GetPointer(), backlog_count, m_desc); + + return ResultSuccess(); } Result SocketServiceObject::Recv(sf::Out out_err, sf::Out out_size, const sf::OutAutoSelectBuffer &buffer, s32 flags) { - AMS_ABORT("SocketServiceObject::Recv"); + /* Get the htcs manager. */ + auto *manager = impl::HtcsManagerHolder::GetHtcsManager(); + + /* Perform the recv. */ + manager->Recv(out_err.GetPointer(), out_size.GetPointer(), reinterpret_cast(buffer.GetPointer()), buffer.GetSize(), flags, m_desc); + + return ResultSuccess(); } Result SocketServiceObject::Send(sf::Out out_err, sf::Out out_size, const sf::InAutoSelectBuffer &buffer, s32 flags) { - AMS_ABORT("SocketServiceObject::Send"); + /* Get the htcs manager. */ + auto *manager = impl::HtcsManagerHolder::GetHtcsManager(); + + /* Perform the send. */ + manager->Send(out_err.GetPointer(), out_size.GetPointer(), reinterpret_cast(buffer.GetPointer()), buffer.GetSize(), flags, m_desc); + + return ResultSuccess(); } Result SocketServiceObject::Shutdown(sf::Out out_err, sf::Out out_res, s32 how) { - AMS_ABORT("SocketServiceObject::Shutdown"); + /* Get the htcs manager. */ + auto *manager = impl::HtcsManagerHolder::GetHtcsManager(); + + /* Perform the shutdown. */ + manager->Shutdown(out_err.GetPointer(), out_res.GetPointer(), how, m_desc); + + return ResultSuccess(); } Result SocketServiceObject::Fcntl(sf::Out out_err, sf::Out out_res, s32 command, s32 value) { - AMS_ABORT("SocketServiceObject::Fcntl"); + /* Get the htcs manager. */ + auto *manager = impl::HtcsManagerHolder::GetHtcsManager(); + + /* Perform the fcntl. */ + manager->Fcntl(out_err.GetPointer(), out_res.GetPointer(), command, value, m_desc); + + return ResultSuccess(); } Result SocketServiceObject::AcceptStart(sf::Out out_task_id, sf::OutCopyHandle out_event) { - AMS_ABORT("SocketServiceObject::AcceptStart"); + /* Get the htcs manager. */ + auto *manager = impl::HtcsManagerHolder::GetHtcsManager(); + + /* Start the accept. */ + return manager->AcceptStart(out_task_id.GetPointer(), out_event.GetHandlePointer(), m_desc); } Result SocketServiceObject::AcceptResults(sf::Out out_err, sf::Out> out, sf::Out out_address, u32 task_id) { - AMS_ABORT("SocketServiceObject::AcceptResults"); + /* Get the htcs manager. */ + auto *manager = impl::HtcsManagerHolder::GetHtcsManager(); + + /* Get the accept results. */ + s32 desc; + manager->AcceptResults(out_err.GetPointer(), std::addressof(desc), out_address.GetPointer(), task_id, m_desc); + + /* If an error occurred, we're done. */ + R_SUCCEED_IF(*out_err != 0); + + /* Create a new socket object. */ + *out = ServiceObjectFactory::CreateSharedEmplaced(m_manager.Get(), desc); + + return ResultSuccess(); } Result SocketServiceObject::RecvStart(sf::Out out_task_id, sf::OutCopyHandle out_event, s32 mem_size, s32 flags) { - AMS_ABORT("SocketServiceObject::RecvStart"); + /* Get the htcs manager. */ + auto *manager = impl::HtcsManagerHolder::GetHtcsManager(); + + /* Start the recv. */ + return manager->RecvStart(out_task_id.GetPointer(), out_event.GetHandlePointer(), mem_size, m_desc, flags); } Result SocketServiceObject::RecvResults(sf::Out out_err, sf::Out out_size, const sf::OutAutoSelectBuffer &buffer, u32 task_id) { - AMS_ABORT("SocketServiceObject::RecvResults"); + /* Get the htcs manager. */ + auto *manager = impl::HtcsManagerHolder::GetHtcsManager(); + + /* Get the recv results. */ + manager->RecvResults(out_err.GetPointer(), out_size.GetPointer(), reinterpret_cast(buffer.GetPointer()), buffer.GetSize(), task_id, m_desc); + + return ResultSuccess(); } Result SocketServiceObject::RecvLargeStart(sf::Out out_task_id, sf::OutCopyHandle out_event, s32 unaligned_size_start, s32 unaligned_size_end, s64 aligned_size, sf::CopyHandle mem_handle, s32 flags) { - AMS_ABORT("SocketServiceObject::RecvLargeStart"); + /* Check that the transfer memory size is okay. */ + R_UNLESS(util::IsIntValueRepresentable(aligned_size), htcs::ResultInvalidSize()); + + /* Attach the transfer memory. */ + os::TransferMemoryType tmem; + R_ABORT_UNLESS(os::AttachTransferMemory(std::addressof(tmem), static_cast(aligned_size), mem_handle.GetValue(), true)); + ON_SCOPE_EXIT { os::DestroyTransferMemory(std::addressof(tmem)); }; + + /* Map the transfer memory. */ + void *address; + R_TRY(os::MapTransferMemory(std::addressof(address), std::addressof(tmem), os::MemoryPermission_None)); + ON_SCOPE_EXIT { os::UnmapTransferMemory(std::addressof(tmem)); }; + + /* Get the htcs manager. */ + auto *manager = impl::HtcsManagerHolder::GetHtcsManager(); + + /* Start the large receive. */ + return manager->RecvStart(out_task_id.GetPointer(), out_event.GetHandlePointer(), unaligned_size_start + aligned_size + unaligned_size_end, m_desc, flags); } Result SocketServiceObject::SendStartOld(sf::Out out_task_id, sf::OutCopyHandle out_event, const sf::InAutoSelectBuffer &buffer, s32 flags) { - AMS_ABORT("SocketServiceObject::SendStartOld"); + return this->SendStart(out_task_id, out_event, sf::InNonSecureAutoSelectBuffer(buffer.GetPointer(), buffer.GetSize()), flags); } Result SocketServiceObject::SendLargeStart(sf::Out out_task_id, sf::OutCopyHandle out_event, const sf::InAutoSelectBuffer &start_buffer, const sf::InAutoSelectBuffer &end_buffer, sf::CopyHandle mem_handle, s64 aligned_size, s32 flags) { - AMS_ABORT("SocketServiceObject::SendLargeStart"); + /* Check that the sizes are okay. */ + R_UNLESS(util::IsIntValueRepresentable(start_buffer.GetSize()), htcs::ResultInvalidSize()); + R_UNLESS(util::IsIntValueRepresentable(end_buffer.GetSize()), htcs::ResultInvalidSize()); + R_UNLESS(util::IsIntValueRepresentable(aligned_size), htcs::ResultInvalidSize()); + + /* Attach the transfer memory. */ + os::TransferMemoryType tmem; + R_ABORT_UNLESS(os::AttachTransferMemory(std::addressof(tmem), static_cast(aligned_size), mem_handle.GetValue(), true)); + ON_SCOPE_EXIT { os::DestroyTransferMemory(std::addressof(tmem)); }; + + /* Map the transfer memory. */ + void *address; + R_TRY(os::MapTransferMemory(std::addressof(address), std::addressof(tmem), os::MemoryPermission_None)); + ON_SCOPE_EXIT { os::UnmapTransferMemory(std::addressof(tmem)); }; + + /* Get the htcs manager. */ + auto *manager = impl::HtcsManagerHolder::GetHtcsManager(); + + /* Start the large send. */ + constexpr auto NumBuffers = 3; + const char *pointers[NumBuffers] = { reinterpret_cast(start_buffer.GetPointer()), static_cast(address), reinterpret_cast(end_buffer.GetPointer()) }; + s64 sizes[NumBuffers] = { static_cast(start_buffer.GetSize()), aligned_size, static_cast(end_buffer.GetSize()) }; + + return manager->SendLargeStart(out_task_id.GetPointer(), out_event.GetHandlePointer(), pointers, sizes, NumBuffers, m_desc, flags); } Result SocketServiceObject::SendResults(sf::Out out_err, sf::Out out_size, u32 task_id) { - AMS_ABORT("SocketServiceObject::SendResults"); + /* Get the htcs manager. */ + auto *manager = impl::HtcsManagerHolder::GetHtcsManager(); + + /* Get the send results. */ + manager->SendResults(out_err.GetPointer(), out_size.GetPointer(), task_id, m_desc); + + return ResultSuccess(); } Result SocketServiceObject::StartSend(sf::Out out_task_id, sf::OutCopyHandle out_event, sf::Out out_max_size, s64 size, s32 flags) { - AMS_ABORT("SocketServiceObject::StartSend"); + /* Get the htcs manager. */ + auto *manager = impl::HtcsManagerHolder::GetHtcsManager(); + + /* Start the send. */ + R_TRY(manager->StartSend(out_task_id.GetPointer(), out_event.GetHandlePointer(), m_desc, size, flags)); + + /* Set the output max size to the size. */ + *out_max_size = size; + return ResultSuccess(); } Result SocketServiceObject::ContinueSendOld(sf::Out out_size, sf::Out out_wait, const sf::InAutoSelectBuffer &buffer, u32 task_id) { - AMS_ABORT("SocketServiceObject::ContinueSendOld"); + return this->ContinueSend(out_size, out_wait, sf::InNonSecureAutoSelectBuffer(buffer.GetPointer(), buffer.GetSize()), task_id); } Result SocketServiceObject::EndSend(sf::Out out_err, sf::Out out_size, u32 task_id) { - AMS_ABORT("SocketServiceObject::EndSend"); + /* Get the htcs manager. */ + auto *manager = impl::HtcsManagerHolder::GetHtcsManager(); + + /* End the send. */ + manager->EndSend(out_err.GetPointer(), out_size.GetPointer(), task_id, m_desc); + + return ResultSuccess(); } Result SocketServiceObject::StartRecv(sf::Out out_task_id, sf::OutCopyHandle out_event, s64 size, s32 flags) { - AMS_ABORT("SocketServiceObject::StartRecv"); + /* Get the htcs manager. */ + auto *manager = impl::HtcsManagerHolder::GetHtcsManager(); + + /* Start the recv. */ + return manager->StartRecv(out_task_id.GetPointer(), out_event.GetHandlePointer(), size, m_desc, flags); } Result SocketServiceObject::EndRecv(sf::Out out_err, sf::Out out_size, const sf::OutAutoSelectBuffer &buffer, u32 task_id) { - AMS_ABORT("SocketServiceObject::EndRecv"); + /* Get the htcs manager. */ + auto *manager = impl::HtcsManagerHolder::GetHtcsManager(); + + /* End the recv. */ + manager->EndRecv(out_err.GetPointer(), out_size.GetPointer(), reinterpret_cast(buffer.GetPointer()), buffer.GetSize(), task_id, m_desc); + + return ResultSuccess(); } Result SocketServiceObject::SendStart(sf::Out out_task_id, sf::OutCopyHandle out_event, const sf::InNonSecureAutoSelectBuffer &buffer, s32 flags) { - AMS_ABORT("SocketServiceObject::SendStart"); + /* Check that the sizes are okay. */ + R_UNLESS(util::IsIntValueRepresentable(buffer.GetSize()), htcs::ResultInvalidSize()); + + /* Get the htcs manager. */ + auto *manager = impl::HtcsManagerHolder::GetHtcsManager(); + + /* Start the send. */ + return manager->SendStart(out_task_id.GetPointer(), out_event.GetHandlePointer(), reinterpret_cast(buffer.GetPointer()), buffer.GetSize(), m_desc, flags); } Result SocketServiceObject::ContinueSend(sf::Out out_size, sf::Out out_wait, const sf::InNonSecureAutoSelectBuffer &buffer, u32 task_id) { - AMS_ABORT("SocketServiceObject::ContinueSend"); + /* Check that the sizes are okay. */ + R_UNLESS(util::IsIntValueRepresentable(buffer.GetSize()), htcs::ResultInvalidSize()); + + /* Get the htcs manager. */ + auto *manager = impl::HtcsManagerHolder::GetHtcsManager(); + + /* Continue the send. */ + R_TRY(manager->ContinueSend(out_size.GetPointer(), reinterpret_cast(buffer.GetPointer()), buffer.GetSize(), task_id, m_desc)); + + /* We aren't doing a waiting send. */ + *out_wait = false; + return ResultSuccess(); } Result SocketServiceObject::GetPrimitive(sf::Out out) { - AMS_ABORT("SocketServiceObject::GetPrimitive"); + /* Get our descriptor. */ + *out = m_desc; + return ResultSuccess(); } } diff --git a/libraries/libstratosphere/source/htcs/server/htcs_socket_service_object_deprecated.cpp b/libraries/libstratosphere/source/htcs/server/htcs_socket_service_object_deprecated.cpp new file mode 100644 index 000000000..f4b2dd438 --- /dev/null +++ b/libraries/libstratosphere/source/htcs/server/htcs_socket_service_object_deprecated.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcs_manager_service_object.hpp" +#include "htcs_socket_service_object.hpp" + +namespace ams::htcs::server { + + #define AMS_HTCS_MANAGER_DEPRECATED_API() AMS_ABORT("Deprecated IHtcsManager API %s was called.\n", AMS_CURRENT_FUNCTION_NAME) + + Result SocketServiceObject::Accept(sf::Out out_err, sf::Out> out, sf::Out out_address) { + /* NOTE: This is a deprecated API, and Nintendo aborts when it is called. */ + AMS_HTCS_MANAGER_DEPRECATED_API(); + } + +} diff --git a/libraries/libvapours/include/vapours/results.hpp b/libraries/libvapours/include/vapours/results.hpp index 003bacf25..29bfbc250 100644 --- a/libraries/libvapours/include/vapours/results.hpp +++ b/libraries/libvapours/include/vapours/results.hpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libvapours/include/vapours/results/htcs_results.hpp b/libraries/libvapours/include/vapours/results/htcs_results.hpp new file mode 100644 index 000000000..68c9e7f60 --- /dev/null +++ b/libraries/libvapours/include/vapours/results/htcs_results.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htcs { + + R_DEFINE_NAMESPACE_RESULT_MODULE(4); + + R_DEFINE_ERROR_RESULT(InvalidSize, 2014); + +} From 1541985222ec81595ae0882f7c86f5015f877d6f Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 17 Feb 2021 19:14:59 -0800 Subject: [PATCH 070/280] htcs: hook manager up to (unimplemented) manager impl --- .../stratosphere/tma/tma_i_htcs_manager.hpp | 2 +- .../source/htcs/impl/htcs_manager.cpp | 359 ++++++++++++++++++ .../source/htcs/impl/htcs_manager.hpp | 2 +- .../source/htcs/impl/htcs_manager_impl.hpp | 30 ++ .../source/htcs/impl/htcs_util.cpp | 44 +++ .../source/htcs/impl/htcs_util.hpp | 23 ++ .../server/htcs_manager_service_object.cpp | 4 +- .../server/htcs_manager_service_object.hpp | 2 +- .../libvapours/include/vapours/results.hpp | 1 + .../include/vapours/results/htc_results.hpp | 4 + .../include/vapours/results/htcs_results.hpp | 2 + .../include/vapours/results/tma_results.hpp | 25 ++ 12 files changed, 493 insertions(+), 5 deletions(-) create mode 100644 libraries/libstratosphere/source/htcs/impl/htcs_util.cpp create mode 100644 libraries/libstratosphere/source/htcs/impl/htcs_util.hpp create mode 100644 libraries/libvapours/include/vapours/results/tma_results.hpp diff --git a/libraries/libstratosphere/include/stratosphere/tma/tma_i_htcs_manager.hpp b/libraries/libstratosphere/include/stratosphere/tma/tma_i_htcs_manager.hpp index 066a1c597..18201335e 100644 --- a/libraries/libstratosphere/include/stratosphere/tma/tma_i_htcs_manager.hpp +++ b/libraries/libstratosphere/include/stratosphere/tma/tma_i_htcs_manager.hpp @@ -38,6 +38,6 @@ AMS_SF_METHOD_INFO(C, H, 100, Result, RegisterProcessId, (const sf::ClientProcessId &client_pid), (client_pid)) \ AMS_SF_METHOD_INFO(C, H, 101, Result, MonitorManager, (const sf::ClientProcessId &client_pid), (client_pid)) \ AMS_SF_METHOD_INFO(C, H, 130, Result, StartSelect, (sf::Out out_task_id, sf::OutCopyHandle out_event, const sf::InMapAliasArray &read_handles, const sf::InMapAliasArray &write_handles, const sf::InMapAliasArray &exception_handles, s64 tv_sec, s64 tv_usec), (out_task_id, out_event, read_handles, write_handles, exception_handles, tv_sec, tv_usec)) \ - AMS_SF_METHOD_INFO(C, H, 131, Result, EndSelect, (sf::Out out_err, sf::Out out_res, const sf::OutMapAliasArray &read_handles, const sf::OutMapAliasArray &write_handles, const sf::OutMapAliasArray &exception_handles, u32 task_id), (out_err, out_res, read_handles, write_handles, exception_handles, task_id)) + AMS_SF_METHOD_INFO(C, H, 131, Result, EndSelect, (sf::Out out_err, sf::Out out_count, const sf::OutMapAliasArray &read_handles, const sf::OutMapAliasArray &write_handles, const sf::OutMapAliasArray &exception_handles, u32 task_id), (out_err, out_count, read_handles, write_handles, exception_handles, task_id)) AMS_SF_DEFINE_INTERFACE(ams::tma, IHtcsManager, AMS_TMA_I_HTCS_MANAGER_INTERFACE_INFO) diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_manager.cpp b/libraries/libstratosphere/source/htcs/impl/htcs_manager.cpp index d6c6360ea..42dadd090 100644 --- a/libraries/libstratosphere/source/htcs/impl/htcs_manager.cpp +++ b/libraries/libstratosphere/source/htcs/impl/htcs_manager.cpp @@ -16,6 +16,7 @@ #include #include "htcs_manager.hpp" #include "htcs_manager_impl.hpp" +#include "htcs_util.hpp" namespace ams::htcs::impl { @@ -36,4 +37,362 @@ namespace ams::htcs::impl { return m_impl->IsServiceAvailable(); } + void HtcsManager::Socket(s32 *out_err, s32 *out_desc, bool enable_disconnection_emulation) { + /* Invoke our implementation. */ + s32 err, desc; + const Result result = m_impl->CreateSocket(std::addressof(err), std::addressof(desc), enable_disconnection_emulation); + + /* Set output. */ + if (R_SUCCEEDED(result)) { + *out_err = err; + if (out_err == 0) { + *out_desc = desc; + } else { + *out_desc = -1; + } + } else { + *out_err = ConvertResultToErrorCode(result); + *out_desc = -1; + } + } + + void HtcsManager::Close(s32 *out_err, s32 *out_res, s32 desc) { + /* Invoke our implementation. */ + s32 err; + const Result result = m_impl->DestroySocket(std::addressof(err), desc); + + /* Set output. */ + *out_err = ConvertResultToErrorCode(result); + if (R_SUCCEEDED(result)) { + *out_res = 0; + } else { + *out_res = -1; + } + } + + void HtcsManager::Connect(s32 *out_err, s32 *out_res, const SockAddrHtcs &address, s32 desc) { + /* Invoke our implementation. */ + s32 err; + const Result result = m_impl->Connect(std::addressof(err), desc, address); + + /* Set output. */ + if (R_SUCCEEDED(result)) { + *out_err = err; + if (out_err == 0) { + *out_res = 0; + } else { + *out_res = -1; + } + } else { + *out_err = ConvertResultToErrorCode(result); + *out_res = -1; + } + } + + void HtcsManager::Bind(s32 *out_err, s32 *out_res, const SockAddrHtcs &address, s32 desc) { + /* Invoke our implementation. */ + s32 err; + const Result result = m_impl->Bind(std::addressof(err), desc, address); + + /* Set output. */ + if (R_SUCCEEDED(result)) { + *out_err = err; + if (out_err == 0) { + *out_res = 0; + } else { + *out_res = -1; + } + } else { + *out_err = ConvertResultToErrorCode(result); + *out_res = -1; + } + } + + void HtcsManager::Listen(s32 *out_err, s32 *out_res, s32 backlog_count, s32 desc) { + /* Invoke our implementation. */ + s32 err; + const Result result = m_impl->Listen(std::addressof(err), desc, backlog_count); + + /* Set output. */ + if (R_SUCCEEDED(result)) { + *out_err = err; + if (out_err == 0) { + *out_res = 0; + } else { + *out_res = -1; + } + } else { + *out_err = ConvertResultToErrorCode(result); + *out_res = -1; + } + } + + void HtcsManager::Recv(s32 *out_err, s64 *out_size, char *buffer, size_t size, s32 flags, s32 desc) { + /* Invoke our implementation. */ + s32 err; + s64 recv_size; + const Result result = m_impl->Recv(std::addressof(err), std::addressof(recv_size), buffer, size, desc, flags); + + /* Set output. */ + if (R_SUCCEEDED(result)) { + *out_err = err; + if (out_err == 0) { + *out_size = recv_size; + } else { + *out_size = -1; + } + } else { + *out_err = ConvertResultToErrorCode(result); + *out_size = -1; + } + } + + void HtcsManager::Send(s32 *out_err, s64 *out_size, const char *buffer, size_t size, s32 flags, s32 desc) { + /* Invoke our implementation. */ + s32 err; + s64 send_size; + const Result result = m_impl->Send(std::addressof(err), std::addressof(send_size), buffer, size, desc, flags); + + /* Set output. */ + if (R_SUCCEEDED(result)) { + *out_err = err; + if (out_err == 0) { + *out_size = send_size; + } else { + *out_size = -1; + } + } else { + *out_err = ConvertResultToErrorCode(result); + *out_size = -1; + } + } + + void HtcsManager::Shutdown(s32 *out_err, s32 *out_res, s32 how, s32 desc) { + /* Invoke our implementation. */ + s32 err; + const Result result = m_impl->Shutdown(std::addressof(err), desc, how); + + /* Set output. */ + if (R_SUCCEEDED(result)) { + *out_err = err; + if (out_err == 0) { + *out_res = 0; + } else { + *out_res = -1; + } + } else { + if (htcs::ResultInvalidHandle::Includes(result)) { + *out_err = HTCS_ENOTCONN; + } else { + *out_err = ConvertResultToErrorCode(result); + } + *out_res = -1; + } + } + + void HtcsManager::Fcntl(s32 *out_err, s32 *out_res, s32 command, s32 value, s32 desc) { + /* Invoke our implementation. */ + s32 err, res; + const Result result = m_impl->Fcntl(std::addressof(err), std::addressof(res), desc, command, value); + + /* Set output. */ + if (R_SUCCEEDED(result)) { + *out_err = err; + *out_res = res; + } else { + *out_err = ConvertResultToErrorCode(result); + *out_res = -1; + } + } + + Result HtcsManager::AcceptStart(u32 *out_task_id, Handle *out_handle, s32 desc) { + return m_impl->AcceptStart(out_task_id, out_handle, desc); + } + + void HtcsManager::AcceptResults(s32 *out_err, s32 *out_desc, SockAddrHtcs *out_address, u32 task_id, s32 desc) { + /* Invoke our implementation. */ + s32 err; + const Result result = m_impl->AcceptResults(std::addressof(err), out_desc, out_address, task_id, desc); + + /* Set output. */ + if (R_SUCCEEDED(result)) { + *out_err = err; + } else { + if (htc::ResultCancelled::Includes(result)) { + *out_err = HTCS_ENETDOWN; + } else if (htc::ResultUnknown2033::Includes(result)) { + *out_err = HTCS_EINTR; + } else { + *out_err = ConvertResultToErrorCode(result); + } + } + } + + Result HtcsManager::RecvStart(u32 *out_task_id, Handle *out_handle, s64 size, s32 desc, s32 flags) { + return m_impl->RecvStart(out_task_id, out_handle, size, desc, flags); + } + + void HtcsManager::RecvResults(s32 *out_err, s64 *out_size, char *buffer, s64 buffer_size, u32 task_id, s32 desc) { + /* Invoke our implementation. */ + s32 err; + s64 size; + const Result result = m_impl->RecvResults(std::addressof(err), std::addressof(size), buffer, buffer_size, task_id, desc); + + /* Set output. */ + if (R_SUCCEEDED(result)) { + *out_err = err; + if (err == 0) { + *out_size = size; + } else { + *out_size = -1; + } + } else { + if (htc::ResultUnknown2033::Includes(result)) { + *out_err = HTCS_EINTR; + } else { + *out_err = ConvertResultToErrorCode(result); + } + *out_size = -1; + } + } + + Result HtcsManager::SendStart(u32 *out_task_id, Handle *out_handle, const char *buffer, s64 size, s32 desc, s32 flags) { + return m_impl->SendStart(out_task_id, out_handle, buffer, size, desc, flags); + } + + Result HtcsManager::SendLargeStart(u32 *out_task_id, Handle *out_handle, const char **buffers, const s64 *sizes, s32 count, s32 desc, s32 flags) { + return m_impl->SendLargeStart(out_task_id, out_handle, buffers, sizes, count, desc, flags); + } + + void HtcsManager::SendResults(s32 *out_err, s64 *out_size, u32 task_id, s32 desc) { + /* Invoke our implementation. */ + s32 err; + s64 size; + const Result result = m_impl->SendResults(std::addressof(err), std::addressof(size), task_id, desc); + + /* Set output. */ + if (R_SUCCEEDED(result)) { + *out_err = err; + if (err == 0) { + *out_size = size; + } else { + *out_size = -1; + } + } else { + if (htc::ResultUnknown2033::Includes(result)) { + *out_err = HTCS_EINTR; + } else { + *out_err = ConvertResultToErrorCode(result); + } + *out_size = -1; + } + } + + Result HtcsManager::StartSend(u32 *out_task_id, Handle *out_handle, s32 desc, s64 size, s32 flags) { + return m_impl->StartSend(out_task_id, out_handle, desc, size, flags); + } + + Result HtcsManager::ContinueSend(s64 *out_size, const char *buffer, s64 buffer_size, u32 task_id, s32 desc) { + /* Invoke our implementation. */ + s64 size; + R_TRY_CATCH(m_impl->ContinueSend(std::addressof(size), buffer, buffer_size, task_id, desc)) { + R_CONVERT(htclow::ResultInvalidChannelState, tma::ResultUnknown()) + R_CONVERT(htc::ResultUnknown2021, tma::ResultUnknown()) + } R_END_TRY_CATCH; + + /* Set output. */ + *out_size = size; + return ResultSuccess(); + } + + void HtcsManager::EndSend(s32 *out_err, s64 *out_size, u32 task_id, s32 desc) { + /* Invoke our implementation. */ + s32 err; + s64 size; + const Result result = m_impl->EndSend(std::addressof(err), std::addressof(size), task_id, desc); + + /* Set output. */ + if (R_SUCCEEDED(result)) { + *out_err = err; + if (err == 0) { + *out_size = size; + } else { + *out_size = -1; + } + } else { + if (htc::ResultUnknown2033::Includes(result)) { + *out_err = HTCS_EINTR; + } else { + *out_err = ConvertResultToErrorCode(result); + } + *out_size = -1; + } + } + + Result HtcsManager::StartRecv(u32 *out_task_id, Handle *out_handle, s64 size, s32 desc, s32 flags) { + return m_impl->StartRecv(out_task_id, out_handle, size, desc, flags); + } + + void HtcsManager::EndRecv(s32 *out_err, s64 *out_size, char *buffer, s64 buffer_size, u32 task_id, s32 desc) { + /* Invoke our implementation. */ + s32 err; + s64 size; + const Result result = m_impl->EndRecv(std::addressof(err), std::addressof(size), buffer, buffer_size, task_id, desc); + + /* Set output. */ + if (R_SUCCEEDED(result)) { + *out_err = err; + if (err == 0) { + *out_size = size; + } else { + *out_size = -1; + } + } else { + if (htc::ResultCancelled::Includes(result) || htc::ResultUnknown2033::Includes(result)) { + *out_err = 0; + } else { + *out_err = ConvertResultToErrorCode(result); + } + *out_size = -1; + } + } + + Result HtcsManager::StartSelect(u32 *out_task_id, Handle *out_handle, Span read_handles, Span write_handles, Span exception_handles, s64 tv_sec, s64 tv_usec) { + /* Invoke our implementation. */ + R_TRY_CATCH(m_impl->StartSelect(out_task_id, out_handle, read_handles, write_handles, exception_handles, tv_sec, tv_usec)) { + R_CONVERT(htc::ResultUnknown2021, tma::ResultUnknown()) + } R_END_TRY_CATCH; + + return ResultSuccess(); + } + + Result HtcsManager::EndSelect(s32 *out_err, s32 *out_count, Span read_handles, Span write_handles, Span exception_handles, u32 task_id) { + /* Invoke our implementation. */ + s32 err, res; + const Result result = m_impl->EndSelect(std::addressof(err), std::addressof(res), read_handles, write_handles, exception_handles, task_id); + + /* Set output. */ + if (R_SUCCEEDED(result) && res == 0) { + *out_err = err; + if (err == 0) { + const auto num_read = std::count_if(read_handles.begin(), read_handles.end(), [](int handle) { return handle != 0; }); + const auto num_write = std::count_if(write_handles.begin(), write_handles.end(), [](int handle) { return handle != 0; }); + const auto num_exception = std::count_if(exception_handles.begin(), exception_handles.end(), [](int handle) { return handle != 0; }); + *out_count = num_read + num_write + num_exception; + } else { + *out_count = -1; + } + } else { + if (R_SUCCEEDED(result)) { + *out_err = 0; + *out_count = 0; + } else { + *out_err = ConvertResultToErrorCode(err); + *out_count = -1; + } + } + + return ResultSuccess(); + } + } diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_manager.hpp b/libraries/libstratosphere/source/htcs/impl/htcs_manager.hpp index 4fcb48984..de2f44ca2 100644 --- a/libraries/libstratosphere/source/htcs/impl/htcs_manager.hpp +++ b/libraries/libstratosphere/source/htcs/impl/htcs_manager.hpp @@ -61,7 +61,7 @@ namespace ams::htcs::impl { void EndRecv(s32 *out_err, s64 *out_size, char *buffer, s64 buffer_size, u32 task_id, s32 desc); Result StartSelect(u32 *out_task_id, Handle *out_handle, Span read_handles, Span write_handles, Span exception_handles, s64 tv_sec, s64 tv_usec); - Result EndSelect(s32 *out_err, s32 *out_res, Span read_handles, Span write_handles, Span exception_handles, u32 task_id); + Result EndSelect(s32 *out_err, s32 *out_count, Span read_handles, Span write_handles, Span exception_handles, u32 task_id); }; } diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.hpp b/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.hpp index 38c0916ec..a2875fbbc 100644 --- a/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.hpp +++ b/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.hpp @@ -41,6 +41,36 @@ namespace ams::htcs::impl { os::EventType *GetServiceAvailabilityEvent(); bool IsServiceAvailable(); + public: + Result CreateSocket(s32 *out_err, s32 *out_desc, bool enable_disconnection_emulation); + Result DestroySocket(s32 *out_err, s32 desc); + Result Connect(s32 *out_err, s32 desc, const SockAddrHtcs &address); + Result Bind(s32 *out_err, s32 desc, const SockAddrHtcs &address); + Result Listen(s32 *out_err, s32 desc, s32 backlog_count); + Result Recv(s32 *out_err, s64 *out_size, char *buffer, size_t size, s32 desc, s32 flags); + Result Send(s32 *out_err, s64 *out_size, const char *buffer, size_t size, s32 desc, s32 flags); + Result Shutdown(s32 *out_err, s32 desc, s32 how); + Result Fcntl(s32 *out_err, s32 *out_res, s32 desc, s32 command, s32 value); + + Result AcceptStart(u32 *out_task_id, Handle *out_handle, s32 desc); + Result AcceptResults(s32 *out_err, s32 *out_desc, SockAddrHtcs *out_address, u32 task_id, s32 desc); + + Result RecvStart(u32 *out_task_id, Handle *out_handle, s64 size, s32 desc, s32 flags); + Result RecvResults(s32 *out_err, s64 *out_size, char *buffer, s64 buffer_size, u32 task_id, s32 desc); + + Result SendStart(u32 *out_task_id, Handle *out_handle, const char *buffer, s64 size, s32 desc, s32 flags); + Result SendLargeStart(u32 *out_task_id, Handle *out_handle, const char **buffers, const s64 *sizes, s32 count, s32 desc, s32 flags); + Result SendResults(s32 *out_err, s64 *out_size, u32 task_id, s32 desc); + + Result StartSend(u32 *out_task_id, Handle *out_handle, s32 desc, s64 size, s32 flags); + Result ContinueSend(s64 *out_size, const char *buffer, s64 buffer_size, u32 task_id, s32 desc); + Result EndSend(s32 *out_err, s64 *out_size, u32 task_id, s32 desc); + + Result StartRecv(u32 *out_task_id, Handle *out_handle, s64 size, s32 desc, s32 flags); + Result EndRecv(s32 *out_err, s64 *out_size, char *buffer, s64 buffer_size, u32 task_id, s32 desc); + + Result StartSelect(u32 *out_task_id, Handle *out_handle, Span read_handles, Span write_handles, Span exception_handles, s64 tv_sec, s64 tv_usec); + Result EndSelect(s32 *out_err, s32 *out_res, Span read_handles, Span write_handles, Span exception_handles, u32 task_id); }; } diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_util.cpp b/libraries/libstratosphere/source/htcs/impl/htcs_util.cpp new file mode 100644 index 000000000..448648c8d --- /dev/null +++ b/libraries/libstratosphere/source/htcs/impl/htcs_util.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcs_util.hpp" + +namespace ams::htcs::impl { + + s32 ConvertResultToErrorCode(const Result result) { + /* Convert success. */ + if (R_SUCCEEDED(result)) { + return 0; + } + + R_TRY_CATCH(result) { + R_CATCH(htclow::ResultNonBlockingReceiveFailed) { return HTCS_EWOULDBLOCK; } + R_CATCH(htcs::ResultInvalidHandle) { return HTCS_EBADF; } + R_CATCH(htc::ResultUnknown2001) { return HTCS_EINVAL; } + R_CATCH(htc::ResultUnknown2101) { return HTCS_EMFILE; } + R_CATCH(htc::ResultUnknown2021) { return HTCS_EINTR; } + R_CATCH(htc::ResultInvalidTaskId) { return HTCS_EINTR; } + R_CATCH(htc::ResultCancelled) { return HTCS_EINTR; } + R_CATCH(htc::ResultUnknown2033) { return HTCS_ENETDOWN; } + R_CATCH(htclow::ResultConnectionFailure) { return HTCS_ENETDOWN; } + R_CATCH(htclow::ResultChannelNotExist) { return HTCS_ENOTCONN; } + R_CATCH_ALL() { return HTCS_EUNKNOWN; } + } R_END_TRY_CATCH_WITH_ABORT_UNLESS; + + __builtin_unreachable(); + } + +} diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_util.hpp b/libraries/libstratosphere/source/htcs/impl/htcs_util.hpp new file mode 100644 index 000000000..9f7684cfe --- /dev/null +++ b/libraries/libstratosphere/source/htcs/impl/htcs_util.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htcs::impl { + + s32 ConvertResultToErrorCode(const Result result); + +} diff --git a/libraries/libstratosphere/source/htcs/server/htcs_manager_service_object.cpp b/libraries/libstratosphere/source/htcs/server/htcs_manager_service_object.cpp index 7eb2ef215..d0c6a6bb0 100644 --- a/libraries/libstratosphere/source/htcs/server/htcs_manager_service_object.cpp +++ b/libraries/libstratosphere/source/htcs/server/htcs_manager_service_object.cpp @@ -82,12 +82,12 @@ namespace ams::htcs::server { return manager->StartSelect(out_task_id.GetPointer(), out_event.GetHandlePointer(), read_handles.ToSpan(), write_handles.ToSpan(), exception_handles.ToSpan(), tv_sec, tv_usec); } - Result ManagerServiceObject::EndSelect(sf::Out out_err, sf::Out out_res, const sf::OutMapAliasArray &read_handles, const sf::OutMapAliasArray &write_handles, const sf::OutMapAliasArray &exception_handles, u32 task_id) { + Result ManagerServiceObject::EndSelect(sf::Out out_err, sf::Out out_count, const sf::OutMapAliasArray &read_handles, const sf::OutMapAliasArray &write_handles, const sf::OutMapAliasArray &exception_handles, u32 task_id) { /* Get the htcs manager. */ auto *manager = impl::HtcsManagerHolder::GetHtcsManager(); /* End the select. */ - return manager->EndSelect(out_err.GetPointer(), out_res.GetPointer(), read_handles.ToSpan(), write_handles.ToSpan(), exception_handles.ToSpan(), task_id); + return manager->EndSelect(out_err.GetPointer(), out_count.GetPointer(), read_handles.ToSpan(), write_handles.ToSpan(), exception_handles.ToSpan(), task_id); } } diff --git a/libraries/libstratosphere/source/htcs/server/htcs_manager_service_object.hpp b/libraries/libstratosphere/source/htcs/server/htcs_manager_service_object.hpp index 43e62d5b1..44e9bf2ef 100644 --- a/libraries/libstratosphere/source/htcs/server/htcs_manager_service_object.hpp +++ b/libraries/libstratosphere/source/htcs/server/htcs_manager_service_object.hpp @@ -37,7 +37,7 @@ namespace ams::htcs::server { Result RegisterProcessId(const sf::ClientProcessId &client_pid); Result MonitorManager(const sf::ClientProcessId &client_pid); Result StartSelect(sf::Out out_task_id, sf::OutCopyHandle out_event, const sf::InMapAliasArray &read_handles, const sf::InMapAliasArray &write_handles, const sf::InMapAliasArray &exception_handles, s64 tv_sec, s64 tv_usec); - Result EndSelect(sf::Out out_err, sf::Out out_res, const sf::OutMapAliasArray &read_handles, const sf::OutMapAliasArray &write_handles, const sf::OutMapAliasArray &exception_handles, u32 task_id); + Result EndSelect(sf::Out out_err, sf::Out out_count, const sf::OutMapAliasArray &read_handles, const sf::OutMapAliasArray &write_handles, const sf::OutMapAliasArray &exception_handles, u32 task_id); }; static_assert(tma::IsIHtcsManager); diff --git a/libraries/libvapours/include/vapours/results.hpp b/libraries/libvapours/include/vapours/results.hpp index 29bfbc250..406e1ec24 100644 --- a/libraries/libvapours/include/vapours/results.hpp +++ b/libraries/libvapours/include/vapours/results.hpp @@ -62,6 +62,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libvapours/include/vapours/results/htc_results.hpp b/libraries/libvapours/include/vapours/results/htc_results.hpp index 3025aacbc..8ff7ee0f9 100644 --- a/libraries/libvapours/include/vapours/results/htc_results.hpp +++ b/libraries/libvapours/include/vapours/results/htc_results.hpp @@ -28,9 +28,13 @@ namespace ams::htc { R_DEFINE_ERROR_RESULT(Unknown, 1023); + R_DEFINE_ERROR_RESULT(Unknown2001, 2001); R_DEFINE_ERROR_RESULT(InvalidTaskId, 2003); R_DEFINE_ERROR_RESULT(InvalidSize, 2011); + R_DEFINE_ERROR_RESULT(Unknown2021, 2021); + R_DEFINE_ERROR_RESULT(Unknown2033, 2033); + R_DEFINE_ERROR_RESULT(Unknown2101, 2101); R_DEFINE_ERROR_RESULT(OutOfRpcTask, 2102); R_DEFINE_ERROR_RESULT(InvalidCategory, 2123); diff --git a/libraries/libvapours/include/vapours/results/htcs_results.hpp b/libraries/libvapours/include/vapours/results/htcs_results.hpp index 68c9e7f60..de4c6206e 100644 --- a/libraries/libvapours/include/vapours/results/htcs_results.hpp +++ b/libraries/libvapours/include/vapours/results/htcs_results.hpp @@ -20,6 +20,8 @@ namespace ams::htcs { R_DEFINE_NAMESPACE_RESULT_MODULE(4); + R_DEFINE_ERROR_RESULT(InvalidHandle, 9); + R_DEFINE_ERROR_RESULT(InvalidSize, 2014); } diff --git a/libraries/libvapours/include/vapours/results/tma_results.hpp b/libraries/libvapours/include/vapours/results/tma_results.hpp new file mode 100644 index 000000000..649051940 --- /dev/null +++ b/libraries/libvapours/include/vapours/results/tma_results.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::tma { + + R_DEFINE_NAMESPACE_RESULT_MODULE(12); + + R_DEFINE_ERROR_RESULT(Unknown, 1); + +} From abff428212014d2c2f3070f89a55f775d09fcf99 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 17 Feb 2021 22:34:05 -0800 Subject: [PATCH 071/280] htcs: hook up manager impl to (unimplemented) service --- .../source/htcs/impl/htcs_manager.cpp | 2 +- .../source/htcs/impl/htcs_manager_impl.cpp | 136 ++++++++++++++++++ .../source/htcs/impl/htcs_manager_impl.hpp | 2 +- .../source/htcs/impl/htcs_service.hpp | 30 ++++ .../include/vapours/results/htcs_results.hpp | 2 + 5 files changed, 170 insertions(+), 2 deletions(-) diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_manager.cpp b/libraries/libstratosphere/source/htcs/impl/htcs_manager.cpp index 42dadd090..51e7a7296 100644 --- a/libraries/libstratosphere/source/htcs/impl/htcs_manager.cpp +++ b/libraries/libstratosphere/source/htcs/impl/htcs_manager.cpp @@ -131,7 +131,7 @@ namespace ams::htcs::impl { /* Invoke our implementation. */ s32 err; s64 recv_size; - const Result result = m_impl->Recv(std::addressof(err), std::addressof(recv_size), buffer, size, desc, flags); + const Result result = m_impl->Receive(std::addressof(err), std::addressof(recv_size), buffer, size, desc, flags); /* Set output. */ if (R_SUCCEEDED(result)) { diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.cpp b/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.cpp index d6c71fb20..0c0b30daa 100644 --- a/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.cpp +++ b/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.cpp @@ -46,4 +46,140 @@ namespace ams::htcs::impl { return m_monitor.IsServiceAvailable(); } + Result HtcsManagerImpl::CreateSocket(s32 *out_err, s32 *out_desc, bool enable_disconnection_emulation) { + return m_service.CreateSocket(out_err, out_desc, enable_disconnection_emulation); + } + + Result HtcsManagerImpl::DestroySocket(s32 *out_err, s32 desc) { + return m_service.DestroySocket(out_err, desc); + } + + Result HtcsManagerImpl::Connect(s32 *out_err, s32 desc, const SockAddrHtcs &address) { + return m_service.Connect(out_err, desc, address); + } + + Result HtcsManagerImpl::Bind(s32 *out_err, s32 desc, const SockAddrHtcs &address) { + return m_service.Bind(out_err, desc, address); + } + + Result HtcsManagerImpl::Listen(s32 *out_err, s32 desc, s32 backlog_count) { + return m_service.Listen(out_err, desc, backlog_count); + } + + Result HtcsManagerImpl::Receive(s32 *out_err, s64 *out_size, char *buffer, size_t size, s32 desc, s32 flags) { + return m_service.Receive(out_err, out_size, buffer, size, desc, flags); + } + + Result HtcsManagerImpl::Send(s32 *out_err, s64 *out_size, const char *buffer, size_t size, s32 desc, s32 flags) { + return m_service.Send(out_err, out_size, buffer, size, desc, flags); + } + + Result HtcsManagerImpl::Shutdown(s32 *out_err, s32 desc, s32 how) { + return m_service.Shutdown(out_err, desc, how); + } + + Result HtcsManagerImpl::Fcntl(s32 *out_err, s32 *out_res, s32 desc, s32 command, s32 value) { + return m_service.Fcntl(out_err, out_res, desc, command, value); + } + + Result HtcsManagerImpl::AcceptStart(u32 *out_task_id, Handle *out_handle, s32 desc) { + return m_service.AcceptStart(out_task_id, out_handle, desc); + } + + Result HtcsManagerImpl::AcceptResults(s32 *out_err, s32 *out_desc, SockAddrHtcs *out_address, u32 task_id, s32 desc) { + return m_service.AcceptResults(out_err, out_desc, out_address, task_id, desc); + } + + Result HtcsManagerImpl::RecvStart(u32 *out_task_id, Handle *out_handle, s64 size, s32 desc, s32 flags) { + return m_service.ReceiveSmallStart(out_task_id, out_handle, size, desc, flags); + } + + Result HtcsManagerImpl::RecvResults(s32 *out_err, s64 *out_size, char *buffer, s64 buffer_size, u32 task_id, s32 desc) { + return m_service.ReceiveSmallResults(out_err, out_size, buffer, buffer_size, task_id, desc); + } + + Result HtcsManagerImpl::SendStart(u32 *out_task_id, Handle *out_handle, const char *buffer, s64 size, s32 desc, s32 flags) { + /* Start the send. */ + u32 task_id; + Handle handle; + R_TRY(m_service.SendSmallStart(std::addressof(task_id), std::addressof(handle), desc, size, flags)); + + /* Continue the send. */ + s64 continue_size; + const Result result = m_service.SendSmallContinue(std::addressof(continue_size), buffer, size, task_id, desc); + if (R_SUCCEEDED(result) || htcs::ResultUnknown2023::Includes(result) || htc::ResultUnknown2033::Includes(result)) { + *out_task_id = task_id; + *out_handle = handle; + } else { + os::SystemEventType event; + os::AttachReadableHandleToSystemEvent(std::addressof(event), handle, true, os::EventClearMode_ManualClear); + + s32 err; + s64 rsize; + m_service.SendSmallResults(std::addressof(err), std::addressof(rsize), task_id, desc); + + os::DestroySystemEvent(std::addressof(event)); + + return result; + } + + return ResultSuccess(); + } + + Result HtcsManagerImpl::SendLargeStart(u32 *out_task_id, Handle *out_handle, const char **buffers, const s64 *sizes, s32 count, s32 desc, s32 flags) { + /* NOTE: Nintendo aborts here, too. */ + AMS_ABORT("HtcsManagerImpl::SendLargeStart is not implemented"); + } + + Result HtcsManagerImpl::SendResults(s32 *out_err, s64 *out_size, u32 task_id, s32 desc) { + return m_service.SendSmallResults(out_err, out_size, task_id, desc); + } + + Result HtcsManagerImpl::StartSend(u32 *out_task_id, Handle *out_handle, s32 desc, s64 size, s32 flags) { + return m_service.SendStart(out_task_id, out_handle, desc, size, flags); + } + + Result HtcsManagerImpl::ContinueSend(s64 *out_size, const char *buffer, s64 buffer_size, u32 task_id, s32 desc) { + return m_service.SendContinue(out_size, buffer, buffer_size, task_id, desc); + } + + Result HtcsManagerImpl::EndSend(s32 *out_err, s64 *out_size, u32 task_id, s32 desc) { + return m_service.SendResults(out_err, out_size, task_id, desc); + } + + Result HtcsManagerImpl::StartRecv(u32 *out_task_id, Handle *out_handle, s64 size, s32 desc, s32 flags) { + return m_service.ReceiveStart(out_task_id, out_handle, size, desc, flags); + } + + Result HtcsManagerImpl::EndRecv(s32 *out_err, s64 *out_size, char *buffer, s64 buffer_size, u32 task_id, s32 desc) { + return m_service.ReceiveResults(out_err, out_size, buffer, buffer_size, task_id, desc); + } + + Result HtcsManagerImpl::StartSelect(u32 *out_task_id, Handle *out_handle, Span read_handles, Span write_handles, Span exception_handles, s64 tv_sec, s64 tv_usec) { + /* Start the select. */ + u32 task_id; + Handle handle; + const Result result = m_service.SelectStart(std::addressof(task_id), std::addressof(handle), read_handles, write_handles, exception_handles, tv_sec, tv_usec); + + /* Ensure our state ends up clean. */ + if (htcs::ResultUnknown2021::Includes(result)) { + os::SystemEventType event; + os::AttachReadableHandleToSystemEvent(std::addressof(event), handle, true, os::EventClearMode_ManualClear); + + s32 err, res; + m_service.SelectEnd(std::addressof(err), std::addressof(res), Span{}, Span{}, Span{}, task_id); + + os::DestroySystemEvent(std::addressof(event)); + } else { + *out_task_id = task_id; + *out_handle = handle; + } + + return result; + } + + Result HtcsManagerImpl::EndSelect(s32 *out_err, s32 *out_res, Span read_handles, Span write_handles, Span exception_handles, u32 task_id) { + return m_service.SelectEnd(out_err, out_res, read_handles, write_handles, exception_handles, task_id); + } + } diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.hpp b/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.hpp index a2875fbbc..68047eb94 100644 --- a/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.hpp +++ b/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.hpp @@ -47,7 +47,7 @@ namespace ams::htcs::impl { Result Connect(s32 *out_err, s32 desc, const SockAddrHtcs &address); Result Bind(s32 *out_err, s32 desc, const SockAddrHtcs &address); Result Listen(s32 *out_err, s32 desc, s32 backlog_count); - Result Recv(s32 *out_err, s64 *out_size, char *buffer, size_t size, s32 desc, s32 flags); + Result Receive(s32 *out_err, s64 *out_size, char *buffer, size_t size, s32 desc, s32 flags); Result Send(s32 *out_err, s64 *out_size, const char *buffer, size_t size, s32 desc, s32 flags); Result Shutdown(s32 *out_err, s32 desc, s32 how); Result Fcntl(s32 *out_err, s32 *out_res, s32 desc, s32 command, s32 value); diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_service.hpp b/libraries/libstratosphere/source/htcs/impl/htcs_service.hpp index 9968b81ec..e00b887c2 100644 --- a/libraries/libstratosphere/source/htcs/impl/htcs_service.hpp +++ b/libraries/libstratosphere/source/htcs/impl/htcs_service.hpp @@ -32,6 +32,36 @@ namespace ams::htcs::impl { : m_allocator(allocator), m_driver(drv), m_rpc_client(rc), m_data_channel_manager(dcm) { /* ... */ } public: /* TODO */ + public: + Result CreateSocket(s32 *out_err, s32 *out_desc, bool enable_disconnection_emulation); + Result DestroySocket(s32 *out_err, s32 desc); + Result Connect(s32 *out_err, s32 desc, const SockAddrHtcs &address); + Result Bind(s32 *out_err, s32 desc, const SockAddrHtcs &address); + Result Listen(s32 *out_err, s32 desc, s32 backlog_count); + Result Receive(s32 *out_err, s64 *out_size, char *buffer, size_t size, s32 desc, s32 flags); + Result Send(s32 *out_err, s64 *out_size, const char *buffer, size_t size, s32 desc, s32 flags); + Result Shutdown(s32 *out_err, s32 desc, s32 how); + Result Fcntl(s32 *out_err, s32 *out_res, s32 desc, s32 command, s32 value); + + Result AcceptStart(u32 *out_task_id, Handle *out_handle, s32 desc); + Result AcceptResults(s32 *out_err, s32 *out_desc, SockAddrHtcs *out_address, u32 task_id, s32 desc); + + Result ReceiveSmallStart(u32 *out_task_id, Handle *out_handle, s64 size, s32 desc, s32 flags); + Result ReceiveSmallResults(s32 *out_err, s64 *out_size, char *buffer, s64 buffer_size, u32 task_id, s32 desc); + + Result SendSmallStart(u32 *out_task_id, Handle *out_handle, s32 desc, s64 size, s32 flags); + Result SendSmallContinue(s64 *out_size, const char *buffer, s64 buffer_size, u32 task_id, s32 desc); + Result SendSmallResults(s32 *out_err, s64 *out_size, u32 task_id, s32 desc); + + Result SendStart(u32 *out_task_id, Handle *out_handle, s32 desc, s64 size, s32 flags); + Result SendContinue(s64 *out_size, const char *buffer, s64 buffer_size, u32 task_id, s32 desc); + Result SendResults(s32 *out_err, s64 *out_size, u32 task_id, s32 desc); + + Result ReceiveStart(u32 *out_task_id, Handle *out_handle, s64 size, s32 desc, s32 flags); + Result ReceiveResults(s32 *out_err, s64 *out_size, char *buffer, s64 buffer_size, u32 task_id, s32 desc); + + Result SelectStart(u32 *out_task_id, Handle *out_handle, Span read_handles, Span write_handles, Span exception_handles, s64 tv_sec, s64 tv_usec); + Result SelectEnd(s32 *out_err, s32 *out_res, Span read_handles, Span write_handles, Span exception_handles, u32 task_id); }; } diff --git a/libraries/libvapours/include/vapours/results/htcs_results.hpp b/libraries/libvapours/include/vapours/results/htcs_results.hpp index de4c6206e..14683cdcc 100644 --- a/libraries/libvapours/include/vapours/results/htcs_results.hpp +++ b/libraries/libvapours/include/vapours/results/htcs_results.hpp @@ -23,5 +23,7 @@ namespace ams::htcs { R_DEFINE_ERROR_RESULT(InvalidHandle, 9); R_DEFINE_ERROR_RESULT(InvalidSize, 2014); + R_DEFINE_ERROR_RESULT(Unknown2021, 2021); + R_DEFINE_ERROR_RESULT(Unknown2023, 2023); } From 536e3e99a89825a34833047c7fcb60ba4da1fb6e Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 17 Feb 2021 23:28:05 -0800 Subject: [PATCH 072/280] htcs: hook up CreateSocket/RpcClient Begin<>/End<> --- .../source/htc/server/rpc/htc_rpc_client.hpp | 103 ++++++++++++++++++ .../source/htc/server/rpc/htc_rpc_tasks.hpp | 6 +- .../source/htcs/impl/htcs_manager.cpp | 10 +- .../source/htcs/impl/htcs_manager_impl.cpp | 2 +- .../source/htcs/impl/htcs_service.cpp | 48 ++++++++ .../source/htcs/impl/htcs_service.hpp | 2 + .../source/htcs/impl/htcs_util.cpp | 2 +- .../source/htcs/impl/rpc/htcs_rpc_tasks.hpp | 66 +++++++++++ .../include/vapours/results/htc_results.hpp | 10 +- 9 files changed, 235 insertions(+), 14 deletions(-) create mode 100644 libraries/libstratosphere/source/htcs/impl/htcs_service.cpp create mode 100644 libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_tasks.hpp diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp index 1d8bf9fa3..ef13c7f7b 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp @@ -22,6 +22,27 @@ namespace ams::htc::server::rpc { + template + concept IsRpcTask = std::derived_from; + + struct RpcTaskFunctionTraits { + public: + template + static std::tuple GetArgsImpl(R(C::*)(A...)); + }; + + template requires IsRpcTask + using RpcTaskArgumentsType = decltype(RpcTaskFunctionTraits::GetArgsImpl(&T::SetArguments)); + + template requires IsRpcTask + using RpcTaskResultType = decltype(RpcTaskFunctionTraits::GetArgsImpl(&T::GetResult)); + + template + concept IsRpcTaskArgumentsType = IsRpcTask && std::same_as, RpcTaskArgumentsType>; + + template + concept IsRpcTaskResultType = IsRpcTask && std::same_as, RpcTaskResultType>; + class RpcClient { private: /* TODO: where is this value coming from, again? */ @@ -68,6 +89,88 @@ namespace ams::htc::server::rpc { Result ReceiveHeader(RpcPacket *header); Result ReceiveBody(char *dst, size_t size); Result SendRequest(const char *src, size_t size); + public: + void Wait(u32 task_id) { + os::WaitEvent(m_task_table.Get(task_id)->GetEvent()); + } + + template requires IsRpcTaskArgumentsType + Result Begin(u32 *out_task_id, Args... args) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Allocate a free task id. */ + u32 task_id; + R_TRY(m_task_id_free_list.Allocate(std::addressof(task_id))); + + /* Create the new task. */ + T *task = m_task_table.New(task_id); + m_task_active[task_id] = true; + + /* Ensure we clean up the task, if we fail after this. */ + auto task_guard = SCOPE_GUARD { + m_task_active[task_id] = false; + m_task_table.Delete(task_id); + m_task_id_free_list.Free(task_id); + }; + + /* Set the task arguments. */ + R_TRY(task->SetArguments(args...)); + + /* Clear the task's events. */ + os::ClearEvent(std::addressof(m_receive_buffer_available_events[task_id])); + os::ClearEvent(std::addressof(m_send_buffer_available_events[task_id])); + + /* Add the task to our queue if we can, or cancel it. */ + if (m_thread_running) { + m_task_queue.Add(task_id, PacketCategory::Request); + } else { + task->Cancel(RpcTaskCancelReason::QueueNotAvailable); + } + + /* Set the output task id. */ + *out_task_id = task_id; + + /* We succeeded. */ + task_guard.Cancel(); + return ResultSuccess(); + } + + template requires IsRpcTaskResultType + Result End(u32 task_id, Args... args) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Get the task. */ + T *task = m_task_table.Get(task_id); + R_UNLESS(task != nullptr, htc::ResultInvalidTaskId()); + + /* Ensure the task is freed if it needs to be, when we're done. */ + auto task_guard = SCOPE_GUARD { + m_task_active[task_id] = false; + m_task_table.Delete(task_id); + m_task_id_free_list.Free(task_id); + }; + + /* If the task was cancelled, handle that. */ + if (task->GetTaskState() == RpcTaskState::Cancelled) { + switch (task->GetTaskCancelReason()) { + case RpcTaskCancelReason::One: + task_guard.Cancel(); + return htc::ResultUnknown2021(); + case RpcTaskCancelReason::Two: + return htc::ResultCancelled(); + case RpcTaskCancelReason::QueueNotAvailable: + return htc::ResultTaskQueueNotAvailable(); + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + /* Get the task's result. */ + R_TRY(task->GetResult(args...)); + + return ResultSuccess(); + } }; } diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_tasks.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_tasks.hpp index 848c1ee3c..18de1cf54 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_tasks.hpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_tasks.hpp @@ -39,8 +39,10 @@ namespace ams::htc::server::rpc { static_assert(sizeof(RpcPacket) == 0x40); enum class RpcTaskCancelReason { - None = 0, - /* ... */ + None = 0, + One = 1, + Two = 2, + QueueNotAvailable = 3, }; enum class RpcTaskState { diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_manager.cpp b/libraries/libstratosphere/source/htcs/impl/htcs_manager.cpp index 51e7a7296..2d20fd85a 100644 --- a/libraries/libstratosphere/source/htcs/impl/htcs_manager.cpp +++ b/libraries/libstratosphere/source/htcs/impl/htcs_manager.cpp @@ -220,7 +220,7 @@ namespace ams::htcs::impl { } else { if (htc::ResultCancelled::Includes(result)) { *out_err = HTCS_ENETDOWN; - } else if (htc::ResultUnknown2033::Includes(result)) { + } else if (htc::ResultTaskQueueNotAvailable::Includes(result)) { *out_err = HTCS_EINTR; } else { *out_err = ConvertResultToErrorCode(result); @@ -247,7 +247,7 @@ namespace ams::htcs::impl { *out_size = -1; } } else { - if (htc::ResultUnknown2033::Includes(result)) { + if (htc::ResultTaskQueueNotAvailable::Includes(result)) { *out_err = HTCS_EINTR; } else { *out_err = ConvertResultToErrorCode(result); @@ -279,7 +279,7 @@ namespace ams::htcs::impl { *out_size = -1; } } else { - if (htc::ResultUnknown2033::Includes(result)) { + if (htc::ResultTaskQueueNotAvailable::Includes(result)) { *out_err = HTCS_EINTR; } else { *out_err = ConvertResultToErrorCode(result); @@ -320,7 +320,7 @@ namespace ams::htcs::impl { *out_size = -1; } } else { - if (htc::ResultUnknown2033::Includes(result)) { + if (htc::ResultTaskQueueNotAvailable::Includes(result)) { *out_err = HTCS_EINTR; } else { *out_err = ConvertResultToErrorCode(result); @@ -348,7 +348,7 @@ namespace ams::htcs::impl { *out_size = -1; } } else { - if (htc::ResultCancelled::Includes(result) || htc::ResultUnknown2033::Includes(result)) { + if (htc::ResultCancelled::Includes(result) || htc::ResultTaskQueueNotAvailable::Includes(result)) { *out_err = 0; } else { *out_err = ConvertResultToErrorCode(result); diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.cpp b/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.cpp index 0c0b30daa..75312e134 100644 --- a/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.cpp +++ b/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.cpp @@ -107,7 +107,7 @@ namespace ams::htcs::impl { /* Continue the send. */ s64 continue_size; const Result result = m_service.SendSmallContinue(std::addressof(continue_size), buffer, size, task_id, desc); - if (R_SUCCEEDED(result) || htcs::ResultUnknown2023::Includes(result) || htc::ResultUnknown2033::Includes(result)) { + if (R_SUCCEEDED(result) || htcs::ResultUnknown2023::Includes(result) || htc::ResultTaskQueueNotAvailable::Includes(result)) { *out_task_id = task_id; *out_handle = handle; } else { diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_service.cpp b/libraries/libstratosphere/source/htcs/impl/htcs_service.cpp new file mode 100644 index 000000000..893ce65ed --- /dev/null +++ b/libraries/libstratosphere/source/htcs/impl/htcs_service.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcs_service.hpp" +#include "rpc/htcs_rpc_tasks.hpp" + +namespace ams::htcs::impl { + + void HtcsService::WaitTask(u32 task_id) { + return m_rpc_client->Wait(task_id); + } + + Result HtcsService::CreateSocket(s32 *out_err, s32 *out_desc, bool enable_disconnection_emulation) { + /* Set disconnection emulation enabled. */ + m_driver->SetDisconnectionEmulationEnabled(enable_disconnection_emulation); + + /* Begin the task. */ + u32 task_id; + R_TRY(m_rpc_client->Begin(std::addressof(task_id))); + + /* Wait for the task to complete. */ + this->WaitTask(task_id); + + /* Finish the task. */ + htcs::SocketError err; + s32 desc; + R_TRY(m_rpc_client->End(task_id, std::addressof(err), std::addressof(desc))); + + /* Set output. */ + *out_err = err; + *out_desc = desc; + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_service.hpp b/libraries/libstratosphere/source/htcs/impl/htcs_service.hpp index e00b887c2..2ae89cf55 100644 --- a/libraries/libstratosphere/source/htcs/impl/htcs_service.hpp +++ b/libraries/libstratosphere/source/htcs/impl/htcs_service.hpp @@ -62,6 +62,8 @@ namespace ams::htcs::impl { Result SelectStart(u32 *out_task_id, Handle *out_handle, Span read_handles, Span write_handles, Span exception_handles, s64 tv_sec, s64 tv_usec); Result SelectEnd(s32 *out_err, s32 *out_res, Span read_handles, Span write_handles, Span exception_handles, u32 task_id); + private: + void WaitTask(u32 task_id); }; } diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_util.cpp b/libraries/libstratosphere/source/htcs/impl/htcs_util.cpp index 448648c8d..b30fdb749 100644 --- a/libraries/libstratosphere/source/htcs/impl/htcs_util.cpp +++ b/libraries/libstratosphere/source/htcs/impl/htcs_util.cpp @@ -32,7 +32,7 @@ namespace ams::htcs::impl { R_CATCH(htc::ResultUnknown2021) { return HTCS_EINTR; } R_CATCH(htc::ResultInvalidTaskId) { return HTCS_EINTR; } R_CATCH(htc::ResultCancelled) { return HTCS_EINTR; } - R_CATCH(htc::ResultUnknown2033) { return HTCS_ENETDOWN; } + R_CATCH(htc::ResultTaskQueueNotAvailable) { return HTCS_ENETDOWN; } R_CATCH(htclow::ResultConnectionFailure) { return HTCS_ENETDOWN; } R_CATCH(htclow::ResultChannelNotExist) { return HTCS_ENOTCONN; } R_CATCH_ALL() { return HTCS_EUNKNOWN; } diff --git a/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_tasks.hpp b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_tasks.hpp new file mode 100644 index 000000000..efc87f224 --- /dev/null +++ b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_tasks.hpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "../../../htc/server/rpc/htc_rpc_tasks.hpp" + +namespace ams::htcs::impl::rpc { + + enum class HtcsTaskType { + Receive = 0, + Send = 1, + Shutdown = 2, + Close = 3, + Connect = 4, + Listen = 5, + Accept = 6, + Socket = 7, + Bind = 8, + Fcntl = 9, + ReceiveSmall = 10, + SendSmall = 11, + Select = 12, + }; + + constexpr inline const s16 ProtocolVersion = 4; + + class HtcsTask : public htc::server::rpc::Task { + private: + HtcsTaskType m_task_type; + s16 m_version; + public: + HtcsTask(HtcsTaskType type) : m_task_type(type), m_version(ProtocolVersion) { /* ... */ } + + HtcsTaskType GetTaskType() const { return m_task_type; } + s16 GetVersion() const { return m_version; } + }; + + class SocketTask : public HtcsTask { + private: + htcs::SocketError m_err; + s32 m_desc; + public: + SocketTask() : HtcsTask(HtcsTaskType::Socket) { /* ... */ } + + Result SetArguments(); + void Complete(htcs::SocketError err, s32 desc); + Result GetResult(htcs::SocketError *out_err, s32 *out_desc); + public: + virtual Result ProcessResponse(const char *data, size_t size) override; + virtual Result CreateRequest(size_t *out, char *data, size_t size, u32 task_id) override; + }; + +} diff --git a/libraries/libvapours/include/vapours/results/htc_results.hpp b/libraries/libvapours/include/vapours/results/htc_results.hpp index 8ff7ee0f9..3f1a5752b 100644 --- a/libraries/libvapours/include/vapours/results/htc_results.hpp +++ b/libraries/libvapours/include/vapours/results/htc_results.hpp @@ -28,11 +28,11 @@ namespace ams::htc { R_DEFINE_ERROR_RESULT(Unknown, 1023); - R_DEFINE_ERROR_RESULT(Unknown2001, 2001); - R_DEFINE_ERROR_RESULT(InvalidTaskId, 2003); - R_DEFINE_ERROR_RESULT(InvalidSize, 2011); - R_DEFINE_ERROR_RESULT(Unknown2021, 2021); - R_DEFINE_ERROR_RESULT(Unknown2033, 2033); + R_DEFINE_ERROR_RESULT(Unknown2001, 2001); + R_DEFINE_ERROR_RESULT(InvalidTaskId, 2003); + R_DEFINE_ERROR_RESULT(InvalidSize, 2011); + R_DEFINE_ERROR_RESULT(Unknown2021, 2021); + R_DEFINE_ERROR_RESULT(TaskQueueNotAvailable, 2033); R_DEFINE_ERROR_RESULT(Unknown2101, 2101); R_DEFINE_ERROR_RESULT(OutOfRpcTask, 2102); From f71943c03a5bccc03823c356d9e2e1442883e449 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 18 Feb 2021 02:49:37 -0800 Subject: [PATCH 073/280] htcs: declare all rpc tasks --- .../source/htc/server/rpc/htc_rpc_client.hpp | 8 +- .../source/htcs/impl/rpc/htcs_rpc_tasks.hpp | 325 +++++++++++++++++- 2 files changed, 328 insertions(+), 5 deletions(-) diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp index ef13c7f7b..11a712b51 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp @@ -28,14 +28,16 @@ namespace ams::htc::server::rpc { struct RpcTaskFunctionTraits { public: template - static std::tuple GetArgsImpl(R(C::*)(A...)); + static std::tuple GetSetArgumentsImpl(R(C::*)(A...)); + template + static std::tuple GetGetResultImpl(R(C::*)(A...) const); }; template requires IsRpcTask - using RpcTaskArgumentsType = decltype(RpcTaskFunctionTraits::GetArgsImpl(&T::SetArguments)); + using RpcTaskArgumentsType = decltype(RpcTaskFunctionTraits::GetSetArgumentsImpl(&T::SetArguments)); template requires IsRpcTask - using RpcTaskResultType = decltype(RpcTaskFunctionTraits::GetArgsImpl(&T::GetResult)); + using RpcTaskResultType = decltype(RpcTaskFunctionTraits::GetGetResultImpl(&T::GetResult)); template concept IsRpcTaskArgumentsType = IsRpcTask && std::same_as, RpcTaskArgumentsType>; diff --git a/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_tasks.hpp b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_tasks.hpp index efc87f224..cf872e877 100644 --- a/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_tasks.hpp +++ b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_tasks.hpp @@ -48,16 +48,337 @@ namespace ams::htcs::impl::rpc { s16 GetVersion() const { return m_version; } }; + class HtcsSignalingTask : public HtcsTask { + private: + os::SystemEventType m_system_event; + bool m_is_valid; + public: + HtcsSignalingTask(HtcsTaskType type); + virtual ~HtcsSignalingTask(); + + bool IsValid() const { return m_is_valid; } + + void Complete() { + os::SignalSystemEvent(std::addressof(m_system_event)); + HtcsTask::Complete(); + } + public: + virtual void Cancel(htc::server::rpc::RpcTaskCancelReason reason) override { + HtcsTask::Cancel(reason); + os::SignalSystemEvent(std::addressof(m_system_event)); + } + + virtual os::SystemEventType *GetSystemEvent() override { return std::addressof(m_system_event); } + }; + + class ReceiveTask : public HtcsSignalingTask { + private: + s32 m_handle; + s64 m_size; + htcs::MessageFlag m_flags; + void *m_buffer; + s64 m_buffer_size; + htcs::SocketError m_err; + s64 m_result_size; + public: + ReceiveTask() : HtcsSignalingTask(HtcsTaskType::Receive) { /* ... */ } + + s32 GetHandle() const { return m_handle; } + s64 GetSize() const { return m_size; } + htcs::MessageFlag GetFlags() const { return m_flags; } + void *GetBuffer() const { return m_buffer; } + s64 GetBufferSize() const { return m_buffer_size; } + + s64 GetResultSize() const { + AMS_ASSERT(this->GetTaskState() == htc::server::rpc::RpcTaskState::Completed); + return m_result_size; + } + public: + Result SetArguments(s32 handle, s64 size, htcs::MessageFlag flags); + void Complete(htcs::SocketError err, s64 size); + Result GetResult(htcs::SocketError *out_err, s64 *out_size) const; + public: + virtual Result ProcessResponse(const char *data, size_t size) override; + virtual Result CreateRequest(size_t *out, char *data, size_t size, u32 task_id) override; + virtual Result CreateNotification(size_t *out, char *data, size_t size, u32 task_id) override; + }; + + class SendTask : public HtcsSignalingTask { + private: + os::Event m_ready_event; + s32 m_handle; + s64 m_size; + htcs::MessageFlag m_flags; + void *m_buffer; + s64 m_buffer_size; + htcs::SocketError m_err; + s64 m_result_size; + public: + SendTask() : HtcsSignalingTask(HtcsTaskType::Send), m_ready_event(os::EventClearMode_ManualClear) { /* ... */ } + + s32 GetHandle() const { return m_handle; } + s64 GetSize() const { return m_size; } + htcs::MessageFlag GetFlags() const { return m_flags; } + void *GetBuffer() const { return m_buffer; } + s64 GetBufferSize() const { return m_buffer_size; } + + void SetBuffer(const void *buffer, s64 buffer_size); + void NotifyDataChannelReady(); + void WaitNotification(); + public: + Result SetArguments(s32 handle, s64 size, htcs::MessageFlag flags); + void Complete(htcs::SocketError err, s64 size); + Result GetResult(htcs::SocketError *out_err, s64 *out_size) const; + public: + virtual void Cancel(htc::server::rpc::RpcTaskCancelReason reason) override; + virtual Result ProcessResponse(const char *data, size_t size) override; + virtual Result CreateRequest(size_t *out, char *data, size_t size, u32 task_id) override; + virtual Result ProcessNotification(const char *data, size_t size) override; + virtual Result CreateNotification(size_t *out, char *data, size_t size, u32 task_id) override; + }; + + class ShutdownTask : public HtcsTask { + private: + s32 m_handle; + ShutdownType m_how; + htcs::SocketError m_err; + public: + ShutdownTask() : HtcsTask(HtcsTaskType::Shutdown) { /* ... */ } + + s32 GetHandle() const { return m_handle; } + ShutdownType GetHow() const { return m_how; } + public: + Result SetArguments(s32 handle, ShutdownType how); + void Complete(htcs::SocketError err); + Result GetResult(htcs::SocketError *out_err) const; + public: + virtual Result ProcessResponse(const char *data, size_t size) override; + virtual Result CreateRequest(size_t *out, char *data, size_t size, u32 task_id) override; + }; + + class CloseTask : public HtcsTask { + private: + s32 m_handle; + htcs::SocketError m_err; + public: + CloseTask() : HtcsTask(HtcsTaskType::Close) { /* ... */ } + + s32 GetHandle() const { return m_handle; } + public: + Result SetArguments(s32 handle); + void Complete(htcs::SocketError err); + Result GetResult(htcs::SocketError *out_err) const; + public: + virtual Result ProcessResponse(const char *data, size_t size) override; + virtual Result CreateRequest(size_t *out, char *data, size_t size, u32 task_id) override; + }; + + class ConnectTask : public HtcsTask { + private: + s32 m_handle; + HtcsPeerName m_peer_name; + HtcsPortName m_port_name; + htcs::SocketError m_err; + public: + ConnectTask() : HtcsTask(HtcsTaskType::Connect) { /* ... */ } + + s32 GetHandle() const { return m_handle; } + const HtcsPeerName &GetPeerName() const { return m_peer_name; } + const HtcsPortName &GetPortName() const { return m_port_name; } + public: + Result SetArguments(s32 handle, const HtcsPeerName &peer_name, const HtcsPortName &port_name); + void Complete(htcs::SocketError err); + Result GetResult(htcs::SocketError *out_err) const; + public: + virtual Result ProcessResponse(const char *data, size_t size) override; + virtual Result CreateRequest(size_t *out, char *data, size_t size, u32 task_id) override; + }; + + class ListenTask : public HtcsTask { + private: + s32 m_handle; + s32 m_backlog; + htcs::SocketError m_err; + public: + ListenTask() : HtcsTask(HtcsTaskType::Listen) { /* ... */ } + + s32 GetHandle() const { return m_handle; } + s32 GetBacklog() const { return m_backlog; } + public: + Result SetArguments(s32 handle, s32 backlog); + void Complete(htcs::SocketError err); + Result GetResult(htcs::SocketError *out_err) const; + public: + virtual Result ProcessResponse(const char *data, size_t size) override; + virtual Result CreateRequest(size_t *out, char *data, size_t size, u32 task_id) override; + }; + + class AcceptTask : public HtcsSignalingTask { + private: + s32 m_server_handle; + htcs::SocketError m_err; + s32 m_desc; + public: + AcceptTask() : HtcsSignalingTask(HtcsTaskType::Accept) { /* ... */ } + + s32 GetServerHandle() const { return m_server_handle; } + public: + Result SetArguments(s32 server_handle); + void Complete(htcs::SocketError err, s32 desc); + Result GetResult(htcs::SocketError *out_err, s32 *out_desc, s32 server_handle) const; + public: + virtual Result ProcessResponse(const char *data, size_t size) override; + virtual Result CreateRequest(size_t *out, char *data, size_t size, u32 task_id) override; + }; + class SocketTask : public HtcsTask { private: htcs::SocketError m_err; s32 m_desc; public: SocketTask() : HtcsTask(HtcsTaskType::Socket) { /* ... */ } - + public: Result SetArguments(); void Complete(htcs::SocketError err, s32 desc); - Result GetResult(htcs::SocketError *out_err, s32 *out_desc); + Result GetResult(htcs::SocketError *out_err, s32 *out_desc) const; + public: + virtual Result ProcessResponse(const char *data, size_t size) override; + virtual Result CreateRequest(size_t *out, char *data, size_t size, u32 task_id) override; + }; + + class BindTask : public HtcsTask { + private: + s32 m_handle; + HtcsPeerName m_peer_name; + HtcsPortName m_port_name; + htcs::SocketError m_err; + public: + BindTask() : HtcsTask(HtcsTaskType::Bind) { /* ... */ } + + s32 GetHandle() const { return m_handle; } + const HtcsPeerName &GetPeerName() const { return m_peer_name; } + const HtcsPortName &GetPortName() const { return m_port_name; } + public: + Result SetArguments(s32 handle, const HtcsPeerName &peer_name, const HtcsPortName &port_name); + void Complete(htcs::SocketError err); + Result GetResult(htcs::SocketError *out_err) const; + public: + virtual Result ProcessResponse(const char *data, size_t size) override; + virtual Result CreateRequest(size_t *out, char *data, size_t size, u32 task_id) override; + }; + + class FcntlTask : public HtcsTask { + private: + s32 m_handle; + s32 m_command; + s32 m_value; + htcs::SocketError m_err; + s32 m_res; + public: + FcntlTask() : HtcsTask(HtcsTaskType::Fcntl) { /* ... */ } + + s32 GetHandle() const { return m_handle; } + s32 GetCommand() const { return m_command; } + s32 GetValue() const { return m_value; } + public: + Result SetArguments(s32 handle, s32 command, s32 value); + void Complete(htcs::SocketError err); + Result GetResult(htcs::SocketError *out_err, s32 *out_res) const; + public: + virtual Result ProcessResponse(const char *data, size_t size) override; + virtual Result CreateRequest(size_t *out, char *data, size_t size, u32 task_id) override; + }; + + class ReceiveSmallTask : public HtcsSignalingTask { + private: + s32 m_handle; + s64 m_size; + htcs::MessageFlag m_flags; + char m_buffer[0xE000]; + htcs::SocketError m_err; + s64 m_result_size; + public: + ReceiveSmallTask() : HtcsSignalingTask(HtcsTaskType::ReceiveSmall) { /* ... */ } + + s32 GetHandle() const { return m_handle; } + s64 GetSize() const { return m_size; } + htcs::MessageFlag GetFlags() const { return m_flags; } + void *GetBuffer() { return m_buffer; } + s64 GetBufferSize() const { return static_cast(sizeof(m_buffer)); } + + s64 GetResultSize() const { + AMS_ASSERT(this->GetTaskState() == htc::server::rpc::RpcTaskState::Completed); + return m_result_size; + } + public: + Result SetArguments(s32 handle, s64 size, htcs::MessageFlag flags); + void Complete(htcs::SocketError err, s64 size); + Result GetResult(htcs::SocketError *out_err, s64 *out_size) const; + public: + virtual Result ProcessResponse(const char *data, size_t size) override; + virtual Result CreateRequest(size_t *out, char *data, size_t size, u32 task_id) override; + virtual bool IsReceiveBufferRequired() override; + }; + + class SendSmallTask : public HtcsSignalingTask { + private: + os::Event m_ready_event; + s32 m_handle; + s64 m_size; + htcs::MessageFlag m_flags; + char m_buffer[0xE000]; + s64 m_buffer_size; + htcs::SocketError m_err; + s64 m_result_size; + public: + SendSmallTask() : HtcsSignalingTask(HtcsTaskType::SendSmall), m_ready_event(os::EventClearMode_ManualClear) { /* ... */ } + + s32 GetHandle() const { return m_handle; } + s64 GetSize() const { return m_size; } + htcs::MessageFlag GetFlags() const { return m_flags; } + void *GetBuffer() { return m_buffer; } + s64 GetBufferSize() const { return m_buffer_size; } + + void SetBuffer(const void *buffer, s64 buffer_size); + void NotifyDataChannelReady(); + void WaitNotification(); + public: + Result SetArguments(s32 handle, s64 size, htcs::MessageFlag flags); + void Complete(htcs::SocketError err, s64 size); + Result GetResult(htcs::SocketError *out_err, s64 *out_size) const; + public: + virtual void Cancel(htc::server::rpc::RpcTaskCancelReason reason) override; + virtual Result ProcessResponse(const char *data, size_t size) override; + virtual Result CreateRequest(size_t *out, char *data, size_t size, u32 task_id) override; + virtual bool IsSendBufferRequired() override; + }; + + class SelectTask : public HtcsSignalingTask { + private: + s32 m_handles[SocketCountMax * 3]; + s32 m_read_handle_count; + s32 m_write_handle_count; + s32 m_exception_handle_count; + s64 m_tv_sec; + s64 m_tv_usec; + htcs::SocketError m_err; + s32 m_out_handles[SocketCountMax * 3]; + s32 m_out_read_handle_count; + s32 m_out_write_handle_count; + s32 m_out_exception_handle_count; + public: + SelectTask() : HtcsSignalingTask(HtcsTaskType::Select) { /* ... */ } + + const s32 *GetHandles() const { return m_handles; } + s32 GetReadHandleCount() const { return m_read_handle_count; } + s32 GetWriteHandleCount() const { return m_write_handle_count; } + s32 GetExceptionHandleCount() const { return m_exception_handle_count; } + s64 GetTimeoutSeconds() const { return m_tv_sec; } + s64 GetTimeoutMicroSeconds() const { return m_tv_usec; } + public: + Result SetArguments(Span read_handles, Span write_handles, Span exception_handles, s64 tv_sec, s64 tv_usec); + void Complete(htcs::SocketError err, s32 read_handle_count, s32 write_handle_count, s32 exception_handle_count, const void *body, s64 body_size); + Result GetResult(htcs::SocketError *out_err, bool *out_empty, Span read_handles, Span write_handles, Span exception_handles) const; public: virtual Result ProcessResponse(const char *data, size_t size) override; virtual Result CreateRequest(size_t *out, char *data, size_t size, u32 task_id) override; From 0c791f22794adf7d6b8516e108aa6f32b0655868 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 18 Feb 2021 04:15:23 -0800 Subject: [PATCH 074/280] htcs: fix magic template argument deduction, do Close/Connect/Bind --- .../source/htc/server/rpc/htc_rpc_client.hpp | 38 +++++--- .../source/htcs/impl/htcs_manager.cpp | 3 +- .../source/htcs/impl/htcs_manager_impl.cpp | 4 +- .../source/htcs/impl/htcs_manager_impl.hpp | 2 +- .../source/htcs/impl/htcs_service.cpp | 89 +++++++++++++++++++ .../source/htcs/impl/htcs_service.hpp | 2 +- .../source/htcs/impl/htcs_util.hpp | 13 +++ .../include/vapours/results/htcs_results.hpp | 7 +- 8 files changed, 135 insertions(+), 23 deletions(-) diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp index 11a712b51..9b44488c4 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp @@ -37,13 +37,13 @@ namespace ams::htc::server::rpc { using RpcTaskArgumentsType = decltype(RpcTaskFunctionTraits::GetSetArgumentsImpl(&T::SetArguments)); template requires IsRpcTask - using RpcTaskResultType = decltype(RpcTaskFunctionTraits::GetGetResultImpl(&T::GetResult)); + using RpcTaskResultsType = decltype(RpcTaskFunctionTraits::GetGetResultImpl(&T::GetResult)); - template - concept IsRpcTaskArgumentsType = IsRpcTask && std::same_as, RpcTaskArgumentsType>; + template requires IsRpcTask + using RpcTaskArgumentType = typename std::tuple_element>::type; - template - concept IsRpcTaskResultType = IsRpcTask && std::same_as, RpcTaskResultType>; + template requires IsRpcTask + using RpcTaskResultType = typename std::tuple_element>::type; class RpcClient { private: @@ -91,13 +91,9 @@ namespace ams::htc::server::rpc { Result ReceiveHeader(RpcPacket *header); Result ReceiveBody(char *dst, size_t size); Result SendRequest(const char *src, size_t size); - public: - void Wait(u32 task_id) { - os::WaitEvent(m_task_table.Get(task_id)->GetEvent()); - } - - template requires IsRpcTaskArgumentsType - Result Begin(u32 *out_task_id, Args... args) { + private: + template requires IsRpcTask + ALWAYS_INLINE Result BeginImpl(std::index_sequence, u32 *out_task_id, RpcTaskArgumentType... args) { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); @@ -138,8 +134,8 @@ namespace ams::htc::server::rpc { return ResultSuccess(); } - template requires IsRpcTaskResultType - Result End(u32 task_id, Args... args) { + template requires IsRpcTask + ALWAYS_INLINE Result EndImpl(std::index_sequence, u32 task_id, RpcTaskResultType... args) { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); @@ -173,6 +169,20 @@ namespace ams::htc::server::rpc { return ResultSuccess(); } + public: + void Wait(u32 task_id) { + os::WaitEvent(m_task_table.Get(task_id)->GetEvent()); + } + + template requires (IsRpcTask && sizeof...(Args) == std::tuple_size>::value) + Result Begin(u32 *out_task_id, Args &&... args) { + return this->BeginImpl(std::make_index_sequence>::value>(), out_task_id, std::forward(args)...); + } + + template requires (IsRpcTask && sizeof...(Args) == std::tuple_size>::value) + Result End(u32 task_id, Args &&... args) { + return this->EndImpl(std::make_index_sequence>::value>(), task_id, std::forward(args)...); + } }; } diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_manager.cpp b/libraries/libstratosphere/source/htcs/impl/htcs_manager.cpp index 2d20fd85a..6b5aff2e3 100644 --- a/libraries/libstratosphere/source/htcs/impl/htcs_manager.cpp +++ b/libraries/libstratosphere/source/htcs/impl/htcs_manager.cpp @@ -58,8 +58,7 @@ namespace ams::htcs::impl { void HtcsManager::Close(s32 *out_err, s32 *out_res, s32 desc) { /* Invoke our implementation. */ - s32 err; - const Result result = m_impl->DestroySocket(std::addressof(err), desc); + const Result result = m_impl->DestroySocket(desc); /* Set output. */ *out_err = ConvertResultToErrorCode(result); diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.cpp b/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.cpp index 75312e134..37644a727 100644 --- a/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.cpp +++ b/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.cpp @@ -50,8 +50,8 @@ namespace ams::htcs::impl { return m_service.CreateSocket(out_err, out_desc, enable_disconnection_emulation); } - Result HtcsManagerImpl::DestroySocket(s32 *out_err, s32 desc) { - return m_service.DestroySocket(out_err, desc); + Result HtcsManagerImpl::DestroySocket(s32 desc) { + return m_service.DestroySocket(desc); } Result HtcsManagerImpl::Connect(s32 *out_err, s32 desc, const SockAddrHtcs &address) { diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.hpp b/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.hpp index 68047eb94..5b2926864 100644 --- a/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.hpp +++ b/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.hpp @@ -43,7 +43,7 @@ namespace ams::htcs::impl { bool IsServiceAvailable(); public: Result CreateSocket(s32 *out_err, s32 *out_desc, bool enable_disconnection_emulation); - Result DestroySocket(s32 *out_err, s32 desc); + Result DestroySocket(s32 desc); Result Connect(s32 *out_err, s32 desc, const SockAddrHtcs &address); Result Bind(s32 *out_err, s32 desc, const SockAddrHtcs &address); Result Listen(s32 *out_err, s32 desc, s32 backlog_count); diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_service.cpp b/libraries/libstratosphere/source/htcs/impl/htcs_service.cpp index 893ce65ed..5f4f3697a 100644 --- a/libraries/libstratosphere/source/htcs/impl/htcs_service.cpp +++ b/libraries/libstratosphere/source/htcs/impl/htcs_service.cpp @@ -16,6 +16,7 @@ #include #include "htcs_service.hpp" #include "rpc/htcs_rpc_tasks.hpp" +#include "htcs_util.hpp" namespace ams::htcs::impl { @@ -45,4 +46,92 @@ namespace ams::htcs::impl { return ResultSuccess(); } + Result HtcsService::DestroySocket(s32 desc) { + /* Begin the task. */ + u32 task_id; + R_TRY(m_rpc_client->Begin(std::addressof(task_id), desc)); + + /* Wait for the task to complete. */ + this->WaitTask(task_id); + + /* Cancel the socket. */ + m_rpc_client->CancelBySocket(desc); + + /* Finish the task. */ + htcs::SocketError err; + R_TRY(m_rpc_client->End(task_id, std::addressof(err))); + + return ResultSuccess(); + } + + Result HtcsService::Connect(s32 *out_err, s32 desc, const SockAddrHtcs &address) { + /* Validate the address. */ + R_UNLESS(address.family == 0, htcs::ResultInvalidArgument()); + R_UNLESS(IsValidName(address.peer_name), htcs::ResultInvalidArgument()); + R_UNLESS(IsValidName(address.port_name), htcs::ResultInvalidArgument()); + + /* Begin the task. */ + u32 task_id; + R_TRY(m_rpc_client->Begin(std::addressof(task_id), desc, address.peer_name, address.port_name)); + + /* Wait for the task to complete. */ + this->WaitTask(task_id); + + /* Finish the task. */ + htcs::SocketError err; + R_TRY(m_rpc_client->End(task_id, std::addressof(err))); + + /* Set output. */ + *out_err = err; + return ResultSuccess(); + } + + Result HtcsService::Bind(s32 *out_err, s32 desc, const SockAddrHtcs &address) { + /* Validate the address. */ + R_UNLESS(address.family == 0, htcs::ResultInvalidArgument()); + R_UNLESS(IsValidName(address.peer_name), htcs::ResultInvalidArgument()); + R_UNLESS(IsValidName(address.port_name), htcs::ResultInvalidArgument()); + + /* Begin the task. */ + u32 task_id; + R_TRY(m_rpc_client->Begin(std::addressof(task_id), desc, address.peer_name, address.port_name)); + + /* Wait for the task to complete. */ + this->WaitTask(task_id); + + /* Finish the task. */ + htcs::SocketError err; + R_TRY(m_rpc_client->End(task_id, std::addressof(err))); + + /* Set output. */ + *out_err = err; + return ResultSuccess(); + } + + Result HtcsService::Listen(s32 *out_err, s32 desc, s32 backlog_count); + Result HtcsService::Receive(s32 *out_err, s64 *out_size, char *buffer, size_t size, s32 desc, s32 flags); + Result HtcsService::Send(s32 *out_err, s64 *out_size, const char *buffer, size_t size, s32 desc, s32 flags); + Result HtcsService::Shutdown(s32 *out_err, s32 desc, s32 how); + Result HtcsService::Fcntl(s32 *out_err, s32 *out_res, s32 desc, s32 command, s32 value); + + Result HtcsService::AcceptStart(u32 *out_task_id, Handle *out_handle, s32 desc); + Result HtcsService::AcceptResults(s32 *out_err, s32 *out_desc, SockAddrHtcs *out_address, u32 task_id, s32 desc); + + Result HtcsService::ReceiveSmallStart(u32 *out_task_id, Handle *out_handle, s64 size, s32 desc, s32 flags); + Result HtcsService::ReceiveSmallResults(s32 *out_err, s64 *out_size, char *buffer, s64 buffer_size, u32 task_id, s32 desc); + + Result HtcsService::SendSmallStart(u32 *out_task_id, Handle *out_handle, s32 desc, s64 size, s32 flags); + Result HtcsService::SendSmallContinue(s64 *out_size, const char *buffer, s64 buffer_size, u32 task_id, s32 desc); + Result HtcsService::SendSmallResults(s32 *out_err, s64 *out_size, u32 task_id, s32 desc); + + Result HtcsService::SendStart(u32 *out_task_id, Handle *out_handle, s32 desc, s64 size, s32 flags); + Result HtcsService::SendContinue(s64 *out_size, const char *buffer, s64 buffer_size, u32 task_id, s32 desc); + Result HtcsService::SendResults(s32 *out_err, s64 *out_size, u32 task_id, s32 desc); + + Result HtcsService::ReceiveStart(u32 *out_task_id, Handle *out_handle, s64 size, s32 desc, s32 flags); + Result HtcsService::ReceiveResults(s32 *out_err, s64 *out_size, char *buffer, s64 buffer_size, u32 task_id, s32 desc); + + Result HtcsService::SelectStart(u32 *out_task_id, Handle *out_handle, Span read_handles, Span write_handles, Span exception_handles, s64 tv_sec, s64 tv_usec); + Result HtcsService::SelectEnd(s32 *out_err, s32 *out_res, Span read_handles, Span write_handles, Span exception_handles, u32 task_id); + } diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_service.hpp b/libraries/libstratosphere/source/htcs/impl/htcs_service.hpp index 2ae89cf55..5546ca128 100644 --- a/libraries/libstratosphere/source/htcs/impl/htcs_service.hpp +++ b/libraries/libstratosphere/source/htcs/impl/htcs_service.hpp @@ -34,7 +34,7 @@ namespace ams::htcs::impl { /* TODO */ public: Result CreateSocket(s32 *out_err, s32 *out_desc, bool enable_disconnection_emulation); - Result DestroySocket(s32 *out_err, s32 desc); + Result DestroySocket(s32 desc); Result Connect(s32 *out_err, s32 desc, const SockAddrHtcs &address); Result Bind(s32 *out_err, s32 desc, const SockAddrHtcs &address); Result Listen(s32 *out_err, s32 desc, s32 backlog_count); diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_util.hpp b/libraries/libstratosphere/source/htcs/impl/htcs_util.hpp index 9f7684cfe..8189c4a20 100644 --- a/libraries/libstratosphere/source/htcs/impl/htcs_util.hpp +++ b/libraries/libstratosphere/source/htcs/impl/htcs_util.hpp @@ -20,4 +20,17 @@ namespace ams::htcs::impl { s32 ConvertResultToErrorCode(const Result result); + constexpr bool IsValidName(const char *name) { + static_assert(PeerNameBufferLength == PortNameBufferLength); + return util::Strnlen(name, PeerNameBufferLength) < PeerNameBufferLength; + } + + constexpr bool IsValidName(const HtcsPeerName &name) { + return IsValidName(name.name); + } + + constexpr bool IsValidName(const HtcsPortName &name) { + return IsValidName(name.name); + } + } diff --git a/libraries/libvapours/include/vapours/results/htcs_results.hpp b/libraries/libvapours/include/vapours/results/htcs_results.hpp index 14683cdcc..7903cb4bd 100644 --- a/libraries/libvapours/include/vapours/results/htcs_results.hpp +++ b/libraries/libvapours/include/vapours/results/htcs_results.hpp @@ -22,8 +22,9 @@ namespace ams::htcs { R_DEFINE_ERROR_RESULT(InvalidHandle, 9); - R_DEFINE_ERROR_RESULT(InvalidSize, 2014); - R_DEFINE_ERROR_RESULT(Unknown2021, 2021); - R_DEFINE_ERROR_RESULT(Unknown2023, 2023); + R_DEFINE_ERROR_RESULT(InvalidArgument, 2001); + R_DEFINE_ERROR_RESULT(InvalidSize, 2014); + R_DEFINE_ERROR_RESULT(Unknown2021, 2021); + R_DEFINE_ERROR_RESULT(Unknown2023, 2023); } From 76671049619b97bb80dd70601476f45ef8243c85 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 18 Feb 2021 21:24:30 -0800 Subject: [PATCH 075/280] htcs: hook up HtcsService to rpc client --- .../sf/impl/sf_impl_command_serialization.hpp | 18 +- .../include/stratosphere/sf/sf_handles.hpp | 10 +- .../source/htc/server/rpc/htc_rpc_client.cpp | 130 +++++++- .../source/htc/server/rpc/htc_rpc_client.hpp | 187 ++++++++++- .../htc/server/rpc/htc_rpc_task_table.hpp | 7 +- .../source/htc/server/rpc/htc_rpc_tasks.hpp | 4 +- .../source/htcs/impl/htcs_manager.cpp | 11 +- .../source/htcs/impl/htcs_manager_impl.cpp | 13 +- .../source/htcs/impl/htcs_manager_impl.hpp | 2 +- .../source/htcs/impl/htcs_service.cpp | 310 ++++++++++++++++-- .../source/htcs/impl/htcs_service.hpp | 6 +- .../source/htcs/impl/htcs_util.cpp | 2 +- .../impl/rpc/htcs_data_channel_manager.hpp | 3 +- .../source/htcs/impl/rpc/htcs_rpc_tasks.hpp | 55 +++- .../server/htcs_manager_service_object.cpp | 6 +- .../server/htcs_socket_service_object.cpp | 39 ++- .../include/vapours/results/htc_results.hpp | 3 +- .../include/vapours/results/htcs_results.hpp | 4 +- 18 files changed, 733 insertions(+), 77 deletions(-) diff --git a/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp b/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp index 16c2ab463..9a0a83e41 100644 --- a/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp @@ -632,8 +632,9 @@ namespace ams::sf::impl { private: MoveHandle move_handles[NumMove]; CopyHandle copy_handles[NumCopy]; + bool copy_managed[NumCopy]; public: - constexpr OutHandleHolder() : move_handles(), copy_handles() { /* ... */ } + constexpr OutHandleHolder() : move_handles(), copy_handles(), copy_managed() { /* ... */ } template constexpr inline MoveHandle *GetMoveHandlePointer() { @@ -647,8 +648,15 @@ namespace ams::sf::impl { return ©_handles[Index]; } - constexpr inline void CopyTo(const HipcRequest &response, const size_t num_out_object_handles) { - #define _SF_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(n) do { if constexpr (NumCopy > n) { response.copy_handles[n] = copy_handles[n].GetValue(); } } while (0) + template + constexpr inline bool *GetCopyHandleManagedPointer() { + static_assert(Index < NumCopy, "Index < NumCopy"); + return ©_managed[Index]; + } + + constexpr inline void CopyTo(const cmif::ServiceDispatchContext &ctx, const HipcRequest &response, const size_t num_out_object_handles) { + ctx.handles_to_close->num_handles = 0; + #define _SF_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(n) do { if constexpr (NumCopy > n) { const auto handle = copy_handles[n].GetValue(); response.copy_handles[n] = handle; if (copy_managed[n]) { ctx.handles_to_close->handles[ctx.handles_to_close->num_handles++] = handle; } } } while (0) _SF_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(0); _SF_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(1); _SF_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(2); @@ -1035,7 +1043,7 @@ namespace ams::sf::impl { if constexpr (std::is_same>::value) { return T(out_handles_holder.template GetMoveHandlePointer()); } else if constexpr (std::is_same>::value) { - return T(out_handles_holder.template GetCopyHandlePointer()); + return T(out_handles_holder.template GetCopyHandlePointer(), out_handles_holder.template GetCopyHandleManagedPointer()); } else { static_assert(!std::is_same::value, "Invalid OutHandle kind"); } @@ -1179,7 +1187,7 @@ namespace ams::sf::impl { ImplProcessorType::SetOutBuffers(response, buffers, is_buffer_map_alias); /* Set out handles. */ - out_handles_holder.CopyTo(response, runtime_metadata.GetOutObjectCount()); + out_handles_holder.CopyTo(ctx, response, runtime_metadata.GetOutObjectCount()); /* Set output objects. */ #define _SF_IMPL_PROCESSOR_MARSHAL_OUT_OBJECT(n) do { \ diff --git a/libraries/libstratosphere/include/stratosphere/sf/sf_handles.hpp b/libraries/libstratosphere/include/stratosphere/sf/sf_handles.hpp index 52999127e..047a18737 100644 --- a/libraries/libstratosphere/include/stratosphere/sf/sf_handles.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf/sf_handles.hpp @@ -133,8 +133,11 @@ namespace ams::sf { private: using T = CopyHandle; using Base = impl::OutHandleImpl; + private: + bool *m_managed; public: - constexpr Out(T *p) : Base(p) { /* ... */ } + constexpr Out(T *p) : Base(p), m_managed(nullptr) { /* ... */ } + constexpr Out(T *p, bool *m) : Base(p), m_managed(m) { /* ... */ } constexpr void SetValue(const Handle &value) { Base::SetValue(value); @@ -144,6 +147,11 @@ namespace ams::sf { Base::SetValue(value); } + constexpr void SetManaged(bool m) { + AMS_ASSERT(m_managed != nullptr); + *m_managed = m; + } + constexpr const T &GetValue() const { return Base::GetValue(); } diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp index b0250db4a..68d1175be 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp @@ -42,6 +42,7 @@ namespace ams::htc::server::rpc { m_task_id_free_list(g_task_id_free_list), m_task_table(g_task_table), m_task_active(), + m_is_htcs_task(), m_task_queue(), m_cancelled(false), m_thread_running(false) @@ -63,6 +64,7 @@ namespace ams::htc::server::rpc { m_task_id_free_list(g_task_id_free_list), m_task_table(g_task_table), m_task_active(), + m_is_htcs_task(), m_task_queue(), m_cancelled(false), m_thread_running(false) @@ -173,8 +175,7 @@ namespace ams::htc::server::rpc { /* Cancel all tasks. */ for (size_t i = 0; i < MaxRpcCount; ++i) { if (m_task_active[i]) { - /* TODO: enum member */ - m_task_table.Get(i)->Cancel(static_cast(2)); + m_task_table.Get(i)->Cancel(RpcTaskCancelReason::ClientFinalized); } } } @@ -235,6 +236,7 @@ namespace ams::htc::server::rpc { /* If the task is canceled, free it. */ if (task->GetTaskState() == RpcTaskState::Cancelled) { m_task_active[header->task_id] = false; + m_is_htcs_task[header->task_id] = false; m_task_table.Delete(header->task_id); m_task_id_free_list.Free(header->task_id); continue; @@ -340,4 +342,128 @@ namespace ams::htc::server::rpc { return ResultSuccess(); } + void RpcClient::CancelBySocket(s32 handle) { + /* Check if we need to cancel each task. */ + for (size_t i = 0; i < MaxRpcCount; ++i) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Check that the task is active and is an htcs task. */ + if (!m_task_active[i] || !m_is_htcs_task[i]) { + continue; + } + + /* Get the htcs task. */ + auto *htcs_task = m_task_table.Get(i); + + /* Handle the case where the task handle is the one we're cancelling. */ + if (this->GetTaskHandle(i) == handle) { + /* If the task is complete, free it. */ + if (htcs_task->GetTaskState() == RpcTaskState::Completed) { + m_task_active[i] = false; + m_is_htcs_task[i] = false; + m_task_table.Delete(i); + m_task_id_free_list.Free(i); + } else { + /* If the task is a send task, notify. */ + if (htcs_task->GetTaskType() == htcs::impl::rpc::HtcsTaskType::Send) { + m_task_queue.Add(i, PacketCategory::Notification); + } + + /* Cancel the task. */ + htcs_task->Cancel(RpcTaskCancelReason::BySocket); + } + + /* The task has been cancelled, so we can move on. */ + continue; + } + + /* Handle the case where the task is a select task. */ + if (htcs_task->GetTaskType() == htcs::impl::rpc::HtcsTaskType::Select) { + /* Get the select task. */ + auto *select_task = m_task_table.Get(i); + + /* Get the handle counts. */ + const auto num_read = select_task->GetReadHandleCount(); + const auto num_write = select_task->GetWriteHandleCount(); + const auto num_exception = select_task->GetExceptionHandleCount(); + const auto total = num_read + num_write + num_exception; + + /* Get the handle array. */ + const auto *handles = select_task->GetHandles(); + + /* Check each handle. */ + for (auto handle_idx = 0; handle_idx < total; ++handle_idx) { + if (handles[handle_idx] == handle) { + /* If the select is complete, free it. */ + if (select_task->GetTaskState() == RpcTaskState::Completed) { + m_task_active[i] = false; + m_is_htcs_task[i] = false; + m_task_table.Delete(i); + m_task_id_free_list.Free(i); + } else { + /* Cancel the task. */ + select_task->Cancel(RpcTaskCancelReason::BySocket); + } + } + } + } + } + } + + s32 RpcClient::GetTaskHandle(u32 task_id) { + /* Check pre-conditions. */ + AMS_ASSERT(m_task_active[task_id]); + AMS_ASSERT(m_is_htcs_task[task_id]); + + /* Get the htcs task. */ + auto *task = m_task_table.Get(task_id); + + /* Check that the task has a handle. */ + if (!m_task_active[task_id] || !m_is_htcs_task[task_id] || task == nullptr) { + return -1; + } + + /* Get the task's type. */ + const auto type = task->GetTaskType(); + + /* Check that the task is new enough. */ + if (task->GetVersion() == 3) { + if (type == htcs::impl::rpc::HtcsTaskType::Receive || type == htcs::impl::rpc::HtcsTaskType::Send) { + return -1; + } + } + + /* Get the handle from the task. */ + switch (type) { + case htcs::impl::rpc::HtcsTaskType::Receive: + return static_cast(task)->GetHandle(); + case htcs::impl::rpc::HtcsTaskType::Send: + return static_cast(task)->GetHandle(); + case htcs::impl::rpc::HtcsTaskType::Shutdown: + return static_cast(task)->GetHandle(); + case htcs::impl::rpc::HtcsTaskType::Close: + return -1; + case htcs::impl::rpc::HtcsTaskType::Connect: + return static_cast(task)->GetHandle(); + case htcs::impl::rpc::HtcsTaskType::Listen: + return static_cast(task)->GetHandle(); + case htcs::impl::rpc::HtcsTaskType::Accept: + return static_cast(task)->GetServerHandle(); + case htcs::impl::rpc::HtcsTaskType::Socket: + return -1; + case htcs::impl::rpc::HtcsTaskType::Bind: + return static_cast(task)->GetHandle(); + case htcs::impl::rpc::HtcsTaskType::Fcntl: + return static_cast(task)->GetHandle(); + case htcs::impl::rpc::HtcsTaskType::ReceiveSmall: + return static_cast(task)->GetHandle(); + case htcs::impl::rpc::HtcsTaskType::SendSmall: + return static_cast(task)->GetHandle(); + case htcs::impl::rpc::HtcsTaskType::Select: + return -1; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + } diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp index 9b44488c4..c4715652a 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp @@ -19,6 +19,7 @@ #include "htc_rpc_task_table.hpp" #include "htc_rpc_task_queue.hpp" #include "htc_rpc_task_id_free_list.hpp" +#include "../../../htcs/impl/rpc/htcs_rpc_tasks.hpp" namespace ams::htc::server::rpc { @@ -61,6 +62,7 @@ namespace ams::htc::server::rpc { RpcTaskIdFreeList &m_task_id_free_list; RpcTaskTable &m_task_table; bool m_task_active[MaxRpcCount]; + bool m_is_htcs_task[MaxRpcCount]; RpcTaskQueue m_task_queue; bool m_cancelled; bool m_thread_running; @@ -104,10 +106,12 @@ namespace ams::htc::server::rpc { /* Create the new task. */ T *task = m_task_table.New(task_id); m_task_active[task_id] = true; + m_is_htcs_task[task_id] = htcs::impl::rpc::IsHtcsTask; /* Ensure we clean up the task, if we fail after this. */ auto task_guard = SCOPE_GUARD { m_task_active[task_id] = false; + m_is_htcs_task[task_id] = false; m_task_table.Delete(task_id); m_task_id_free_list.Free(task_id); }; @@ -134,6 +138,24 @@ namespace ams::htc::server::rpc { return ResultSuccess(); } + template requires IsRpcTask + ALWAYS_INLINE Result GetResultImpl(std::index_sequence, u32 task_id, RpcTaskResultType... args) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Get the task. */ + T *task = m_task_table.Get(task_id); + R_UNLESS(task != nullptr, htc::ResultInvalidTaskId()); + + /* Check that the task is completed. */ + R_UNLESS(task->GetTaskState() == RpcTaskState::Completed, htc::ResultTaskNotCompleted()); + + /* Get the task's result. */ + R_TRY(task->GetResult(args...)); + + return ResultSuccess(); + } + template requires IsRpcTask ALWAYS_INLINE Result EndImpl(std::index_sequence, u32 task_id, RpcTaskResultType... args) { /* Lock ourselves. */ @@ -146,6 +168,7 @@ namespace ams::htc::server::rpc { /* Ensure the task is freed if it needs to be, when we're done. */ auto task_guard = SCOPE_GUARD { m_task_active[task_id] = false; + m_is_htcs_task[task_id] = false; m_task_table.Delete(task_id); m_task_id_free_list.Free(task_id); }; @@ -153,10 +176,10 @@ namespace ams::htc::server::rpc { /* If the task was cancelled, handle that. */ if (task->GetTaskState() == RpcTaskState::Cancelled) { switch (task->GetTaskCancelReason()) { - case RpcTaskCancelReason::One: + case RpcTaskCancelReason::BySocket: task_guard.Cancel(); - return htc::ResultUnknown2021(); - case RpcTaskCancelReason::Two: + return htc::ResultTaskCancelled(); + case RpcTaskCancelReason::ClientFinalized: return htc::ResultCancelled(); case RpcTaskCancelReason::QueueNotAvailable: return htc::ResultTaskQueueNotAvailable(); @@ -169,20 +192,178 @@ namespace ams::htc::server::rpc { return ResultSuccess(); } + + s32 GetTaskHandle(u32 task_id); public: void Wait(u32 task_id) { os::WaitEvent(m_task_table.Get(task_id)->GetEvent()); } + Handle DetachReadableHandle(u32 task_id) { + return os::DetachReadableHandleOfSystemEvent(m_task_table.Get(task_id)->GetSystemEvent()); + } + + void CancelBySocket(s32 handle); + template requires (IsRpcTask && sizeof...(Args) == std::tuple_size>::value) Result Begin(u32 *out_task_id, Args &&... args) { return this->BeginImpl(std::make_index_sequence>::value>(), out_task_id, std::forward(args)...); } + template requires (IsRpcTask && sizeof...(Args) == std::tuple_size>::value) + Result GetResult(u32 task_id, Args &&... args) { + return this->GetResultImpl(std::make_index_sequence>::value>(), task_id, std::forward(args)...); + } + template requires (IsRpcTask && sizeof...(Args) == std::tuple_size>::value) Result End(u32 task_id, Args &&... args) { return this->EndImpl(std::make_index_sequence>::value>(), task_id, std::forward(args)...); } + + template requires IsRpcTask + Result VerifyTaskIdWitHandle(u32 task_id, s32 handle) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Get the task. */ + T *task = m_task_table.Get(task_id); + R_UNLESS(task != nullptr, htc::ResultInvalidTaskId()); + + /* Check the task handle. */ + R_UNLESS(task->GetHandle() == handle, htc::ResultInvalidTaskId()); + + return ResultSuccess(); + } + + template requires IsRpcTask + Result Notify(u32 task_id) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Check that our queue is available. */ + R_UNLESS(m_thread_running, htc::ResultTaskQueueNotAvailable()); + + /* Get the task. */ + T *task = m_task_table.Get(task_id); + R_UNLESS(task != nullptr, htc::ResultInvalidTaskId()); + + /* Add notification to our queue. */ + m_task_queue.Add(task_id, PacketCategory::Notification); + + return ResultSuccess(); + } + + template requires IsRpcTask + void WaitNotification(u32 task_id) { + /* Get the task from the table, releasing our lock afterwards. */ + T *task; + { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Get the task. */ + task = m_task_table.Get(task_id); + } + + /* Wait for a notification. */ + task->WaitNotification(); + } + + template requires IsRpcTask + bool IsCancelled(u32 task_id) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Get the task. */ + T *task = m_task_table.Get(task_id); + + /* Check the task state. */ + return task != nullptr && task->GetTaskState() == RpcTaskState::Cancelled; + } + + template requires IsRpcTask + bool IsCompleted(u32 task_id) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Get the task. */ + T *task = m_task_table.Get(task_id); + + /* Check the task state. */ + return task != nullptr && task->GetTaskState() == RpcTaskState::Completed; + } + + template requires IsRpcTask + Result SendContinue(u32 task_id, const void *buffer, s64 buffer_size) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Get the task. */ + T *task = m_task_table.Get(task_id); + R_UNLESS(task != nullptr, htc::ResultInvalidTaskId()); + + /* If the task was cancelled, handle that. */ + if (task->GetTaskState() == RpcTaskState::Cancelled) { + switch (task->GetTaskCancelReason()) { + case RpcTaskCancelReason::QueueNotAvailable: + return htc::ResultTaskQueueNotAvailable(); + default: + return htc::ResultTaskCancelled(); + } + } + + /* Set the task's buffer. */ + if (buffer_size > 0) { + task->SetBuffer(buffer, buffer_size); + os::SignalEvent(std::addressof(m_send_buffer_available_events[task_id])); + } + + return ResultSuccess(); + } + + template requires IsRpcTask + Result ReceiveContinue(u32 task_id, void *buffer, s64 buffer_size) { + /* Get the task's buffer, and prepare to receive. */ + const void *result_buffer; + s64 result_size; + { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Get the task. */ + T *task = m_task_table.Get(task_id); + R_UNLESS(task != nullptr, htc::ResultInvalidTaskId()); + + /* If the task was cancelled, handle that. */ + if (task->GetTaskState() == RpcTaskState::Cancelled) { + switch (task->GetTaskCancelReason()) { + case RpcTaskCancelReason::QueueNotAvailable: + return htc::ResultTaskQueueNotAvailable(); + default: + return htc::ResultTaskCancelled(); + } + } + + /* Get the result size. */ + result_size = task->GetResultSize(); + R_SUCCEED_IF(result_size == 0); + + /* Get the result buffer. */ + result_buffer = task->GetBuffer(); + } + + /* Wait for the receive buffer to become available. */ + os::WaitEvent(std::addressof(m_receive_buffer_available_events[task_id])); + + /* Check that we weren't cancelled. */ + R_UNLESS(!m_cancelled, htc::ResultCancelled()); + + /* Copy the received data. */ + AMS_ASSERT(0 <= result_size && result_size <= buffer_size); + std::memcpy(buffer, result_buffer, result_size); + + return ResultSuccess(); + } }; } diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_table.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_table.hpp index f283f9232..91f472e33 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_table.hpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_table.hpp @@ -31,9 +31,10 @@ namespace ams::htc::server::rpc { class RpcTaskTable { private: - /* TODO: How is this variable derived...? */ - /* Nintendo has a value of 0xE1D8, which is deeply magic. */ - static constexpr size_t MaxTaskSize = 0xA000; + /* htcs::ReceiveSmallTask/htcs::ReceiveSendTask are the largest tasks, containing an inline 0xE000 buffer. */ + /* We allow for ~0x100 task overhead from the additional events those contain. */ + /* NOTE: Nintnedo hardcodes a maximum size of 0xE1D8, despite SendSmallTask being 0xE098 as of latest check. */ + static constexpr size_t MaxTaskSize = 0xE100; using TaskStorage = typename std::aligned_storage::type; private: bool m_valid[MaxRpcCount]; diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_tasks.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_tasks.hpp index 18de1cf54..0b6019dc8 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_tasks.hpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_tasks.hpp @@ -40,8 +40,8 @@ namespace ams::htc::server::rpc { enum class RpcTaskCancelReason { None = 0, - One = 1, - Two = 2, + BySocket = 1, + ClientFinalized = 2, QueueNotAvailable = 3, }; diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_manager.cpp b/libraries/libstratosphere/source/htcs/impl/htcs_manager.cpp index 6b5aff2e3..0999b284d 100644 --- a/libraries/libstratosphere/source/htcs/impl/htcs_manager.cpp +++ b/libraries/libstratosphere/source/htcs/impl/htcs_manager.cpp @@ -296,7 +296,7 @@ namespace ams::htcs::impl { s64 size; R_TRY_CATCH(m_impl->ContinueSend(std::addressof(size), buffer, buffer_size, task_id, desc)) { R_CONVERT(htclow::ResultInvalidChannelState, tma::ResultUnknown()) - R_CONVERT(htc::ResultUnknown2021, tma::ResultUnknown()) + R_CONVERT(htc::ResultTaskCancelled, tma::ResultUnknown()) } R_END_TRY_CATCH; /* Set output. */ @@ -359,7 +359,7 @@ namespace ams::htcs::impl { Result HtcsManager::StartSelect(u32 *out_task_id, Handle *out_handle, Span read_handles, Span write_handles, Span exception_handles, s64 tv_sec, s64 tv_usec) { /* Invoke our implementation. */ R_TRY_CATCH(m_impl->StartSelect(out_task_id, out_handle, read_handles, write_handles, exception_handles, tv_sec, tv_usec)) { - R_CONVERT(htc::ResultUnknown2021, tma::ResultUnknown()) + R_CONVERT(htc::ResultTaskCancelled, tma::ResultUnknown()) } R_END_TRY_CATCH; return ResultSuccess(); @@ -367,11 +367,12 @@ namespace ams::htcs::impl { Result HtcsManager::EndSelect(s32 *out_err, s32 *out_count, Span read_handles, Span write_handles, Span exception_handles, u32 task_id) { /* Invoke our implementation. */ - s32 err, res; - const Result result = m_impl->EndSelect(std::addressof(err), std::addressof(res), read_handles, write_handles, exception_handles, task_id); + s32 err; + bool empty; + const Result result = m_impl->EndSelect(std::addressof(err), std::addressof(empty), read_handles, write_handles, exception_handles, task_id); /* Set output. */ - if (R_SUCCEEDED(result) && res == 0) { + if (R_SUCCEEDED(result) && !empty) { *out_err = err; if (err == 0) { const auto num_read = std::count_if(read_handles.begin(), read_handles.end(), [](int handle) { return handle != 0; }); diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.cpp b/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.cpp index 37644a727..9b4fb5c11 100644 --- a/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.cpp +++ b/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.cpp @@ -107,7 +107,7 @@ namespace ams::htcs::impl { /* Continue the send. */ s64 continue_size; const Result result = m_service.SendSmallContinue(std::addressof(continue_size), buffer, size, task_id, desc); - if (R_SUCCEEDED(result) || htcs::ResultUnknown2023::Includes(result) || htc::ResultTaskQueueNotAvailable::Includes(result)) { + if (R_SUCCEEDED(result) || htcs::ResultCompleted::Includes(result) || htc::ResultTaskQueueNotAvailable::Includes(result)) { *out_task_id = task_id; *out_handle = handle; } else { @@ -162,12 +162,13 @@ namespace ams::htcs::impl { const Result result = m_service.SelectStart(std::addressof(task_id), std::addressof(handle), read_handles, write_handles, exception_handles, tv_sec, tv_usec); /* Ensure our state ends up clean. */ - if (htcs::ResultUnknown2021::Includes(result)) { + if (htcs::ResultCancelled::Includes(result)) { os::SystemEventType event; os::AttachReadableHandleToSystemEvent(std::addressof(event), handle, true, os::EventClearMode_ManualClear); - s32 err, res; - m_service.SelectEnd(std::addressof(err), std::addressof(res), Span{}, Span{}, Span{}, task_id); + s32 err; + bool empty; + m_service.SelectEnd(std::addressof(err), std::addressof(empty), Span{}, Span{}, Span{}, task_id); os::DestroySystemEvent(std::addressof(event)); } else { @@ -178,8 +179,8 @@ namespace ams::htcs::impl { return result; } - Result HtcsManagerImpl::EndSelect(s32 *out_err, s32 *out_res, Span read_handles, Span write_handles, Span exception_handles, u32 task_id) { - return m_service.SelectEnd(out_err, out_res, read_handles, write_handles, exception_handles, task_id); + Result HtcsManagerImpl::EndSelect(s32 *out_err, bool *out_empty, Span read_handles, Span write_handles, Span exception_handles, u32 task_id) { + return m_service.SelectEnd(out_err, out_empty, read_handles, write_handles, exception_handles, task_id); } } diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.hpp b/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.hpp index 5b2926864..921a00f76 100644 --- a/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.hpp +++ b/libraries/libstratosphere/source/htcs/impl/htcs_manager_impl.hpp @@ -70,7 +70,7 @@ namespace ams::htcs::impl { Result EndRecv(s32 *out_err, s64 *out_size, char *buffer, s64 buffer_size, u32 task_id, s32 desc); Result StartSelect(u32 *out_task_id, Handle *out_handle, Span read_handles, Span write_handles, Span exception_handles, s64 tv_sec, s64 tv_usec); - Result EndSelect(s32 *out_err, s32 *out_res, Span read_handles, Span write_handles, Span exception_handles, u32 task_id); + Result EndSelect(s32 *out_err, bool *out_empty, Span read_handles, Span write_handles, Span exception_handles, u32 task_id); }; } diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_service.cpp b/libraries/libstratosphere/source/htcs/impl/htcs_service.cpp index 5f4f3697a..6fd77f87a 100644 --- a/libraries/libstratosphere/source/htcs/impl/htcs_service.cpp +++ b/libraries/libstratosphere/source/htcs/impl/htcs_service.cpp @@ -82,7 +82,7 @@ namespace ams::htcs::impl { R_TRY(m_rpc_client->End(task_id, std::addressof(err))); /* Set output. */ - *out_err = err; + *out_err = err; return ResultSuccess(); } @@ -104,34 +104,302 @@ namespace ams::htcs::impl { R_TRY(m_rpc_client->End(task_id, std::addressof(err))); /* Set output. */ - *out_err = err; + *out_err = err; return ResultSuccess(); } - Result HtcsService::Listen(s32 *out_err, s32 desc, s32 backlog_count); - Result HtcsService::Receive(s32 *out_err, s64 *out_size, char *buffer, size_t size, s32 desc, s32 flags); - Result HtcsService::Send(s32 *out_err, s64 *out_size, const char *buffer, size_t size, s32 desc, s32 flags); - Result HtcsService::Shutdown(s32 *out_err, s32 desc, s32 how); - Result HtcsService::Fcntl(s32 *out_err, s32 *out_res, s32 desc, s32 command, s32 value); + Result HtcsService::Listen(s32 *out_err, s32 desc, s32 backlog_count) { + /* Begin the task. */ + u32 task_id; + R_TRY(m_rpc_client->Begin(std::addressof(task_id), desc, backlog_count)); - Result HtcsService::AcceptStart(u32 *out_task_id, Handle *out_handle, s32 desc); - Result HtcsService::AcceptResults(s32 *out_err, s32 *out_desc, SockAddrHtcs *out_address, u32 task_id, s32 desc); + /* Wait for the task to complete. */ + this->WaitTask(task_id); - Result HtcsService::ReceiveSmallStart(u32 *out_task_id, Handle *out_handle, s64 size, s32 desc, s32 flags); - Result HtcsService::ReceiveSmallResults(s32 *out_err, s64 *out_size, char *buffer, s64 buffer_size, u32 task_id, s32 desc); + /* Finish the task. */ + htcs::SocketError err; + R_TRY(m_rpc_client->End(task_id, std::addressof(err))); - Result HtcsService::SendSmallStart(u32 *out_task_id, Handle *out_handle, s32 desc, s64 size, s32 flags); - Result HtcsService::SendSmallContinue(s64 *out_size, const char *buffer, s64 buffer_size, u32 task_id, s32 desc); - Result HtcsService::SendSmallResults(s32 *out_err, s64 *out_size, u32 task_id, s32 desc); + /* Set output. */ + *out_err = err; + return ResultSuccess(); + } - Result HtcsService::SendStart(u32 *out_task_id, Handle *out_handle, s32 desc, s64 size, s32 flags); - Result HtcsService::SendContinue(s64 *out_size, const char *buffer, s64 buffer_size, u32 task_id, s32 desc); - Result HtcsService::SendResults(s32 *out_err, s64 *out_size, u32 task_id, s32 desc); + Result HtcsService::Receive(s32 *out_err, s64 *out_size, char *buffer, s64 size, s32 desc, s32 flags) { + /* Begin the task. */ + u32 task_id; + R_TRY(m_rpc_client->Begin(std::addressof(task_id), desc, size, static_cast(flags))); - Result HtcsService::ReceiveStart(u32 *out_task_id, Handle *out_handle, s64 size, s32 desc, s32 flags); - Result HtcsService::ReceiveResults(s32 *out_err, s64 *out_size, char *buffer, s64 buffer_size, u32 task_id, s32 desc); + /* Wait for the task to complete. */ + this->WaitTask(task_id); - Result HtcsService::SelectStart(u32 *out_task_id, Handle *out_handle, Span read_handles, Span write_handles, Span exception_handles, s64 tv_sec, s64 tv_usec); - Result HtcsService::SelectEnd(s32 *out_err, s32 *out_res, Span read_handles, Span write_handles, Span exception_handles, u32 task_id); + /* Finish the task. */ + R_TRY(this->ReceiveResults(out_err, out_size, buffer, size, task_id, desc)); + + return ResultSuccess(); + } + + Result HtcsService::Send(s32 *out_err, s64 *out_size, const char *buffer, s64 size, s32 desc, s32 flags) { + /* Begin the task. */ + u32 task_id; + R_TRY(m_rpc_client->Begin(std::addressof(task_id), desc, size, static_cast(flags))); + + /* Send the data. */ + s64 cont_size; + const Result result = this->SendContinue(std::addressof(cont_size), buffer, size, task_id, desc); + if (R_FAILED(result)) { + return this->SendResults(out_err, out_size, task_id, desc); + } + + /* Wait for the task to complete. */ + this->WaitTask(task_id); + + /* Finish the task. */ + R_TRY(this->SendResults(out_err, out_size, task_id, desc)); + + return ResultSuccess(); + } + + Result HtcsService::Shutdown(s32 *out_err, s32 desc, s32 how) { + /* Begin the task. */ + u32 task_id; + R_TRY(m_rpc_client->Begin(std::addressof(task_id), desc, static_cast(how))); + + /* Wait for the task to complete. */ + this->WaitTask(task_id); + + /* Finish the task. */ + htcs::SocketError err; + R_TRY(m_rpc_client->End(task_id, std::addressof(err))); + + /* Set output. */ + *out_err = err; + return ResultSuccess(); + } + + Result HtcsService::Fcntl(s32 *out_err, s32 *out_res, s32 desc, s32 command, s32 value) { + /* Begin the task. */ + u32 task_id; + R_TRY(m_rpc_client->Begin(std::addressof(task_id), desc, command, value)); + + /* Wait for the task to complete. */ + this->WaitTask(task_id); + + /* Finish the task. */ + htcs::SocketError err; + s32 res; + R_TRY(m_rpc_client->End(task_id, std::addressof(err), std::addressof(res))); + + /* Set output. */ + *out_err = err; + *out_res = res; + return ResultSuccess(); + } + + Result HtcsService::AcceptStart(u32 *out_task_id, Handle *out_handle, s32 desc) { + /* Begin the task. */ + u32 task_id; + R_TRY(m_rpc_client->Begin(std::addressof(task_id), desc)); + + /* Detach the task. */ + *out_task_id = task_id; + *out_handle = m_rpc_client->DetachReadableHandle(task_id); + + return ResultSuccess(); + } + + Result HtcsService::AcceptResults(s32 *out_err, s32 *out_desc, SockAddrHtcs *out_address, u32 task_id, s32 desc) { + /* Finish the task. */ + htcs::SocketError err; + s32 ret_desc; + R_TRY(m_rpc_client->End(task_id, std::addressof(err), std::addressof(ret_desc), desc)); + + /* Set output. */ + *out_err = err; + *out_desc = ret_desc; + return ResultSuccess(); + } + + Result HtcsService::ReceiveSmallStart(u32 *out_task_id, Handle *out_handle, s64 size, s32 desc, s32 flags) { + /* Begin the task. */ + u32 task_id; + R_TRY(m_rpc_client->Begin(std::addressof(task_id), desc, size, static_cast(flags))); + + /* Detach the task. */ + *out_task_id = task_id; + *out_handle = m_rpc_client->DetachReadableHandle(task_id); + + return ResultSuccess(); + } + + Result HtcsService::ReceiveSmallResults(s32 *out_err, s64 *out_size, char *buffer, s64 buffer_size, u32 task_id, s32 desc) { + /* Continue the task. */ + m_rpc_client->ReceiveContinue(task_id, buffer, buffer_size); + + /* Finish the task. */ + htcs::SocketError err; + R_TRY(m_rpc_client->End(task_id, std::addressof(err), out_size)); + + /* Set output. */ + *out_err = err; + return ResultSuccess(); + } + + Result HtcsService::SendSmallStart(u32 *out_task_id, Handle *out_handle, s32 desc, s64 size, s32 flags) { + /* Begin the task. */ + u32 task_id; + R_TRY(m_rpc_client->Begin(std::addressof(task_id), desc, size, static_cast(flags))); + + /* Detach the task. */ + *out_task_id = task_id; + *out_handle = m_rpc_client->DetachReadableHandle(task_id); + + return ResultSuccess(); + } + + Result HtcsService::SendSmallContinue(s64 *out_size, const char *buffer, s64 buffer_size, u32 task_id, s32 desc) { + /* Verify the task. */ + R_TRY(m_rpc_client->VerifyTaskIdWitHandle(task_id, desc)); + + /* Continue the task. */ + R_TRY(m_rpc_client->SendContinue(task_id, buffer, buffer_size)); + + /* Set output. */ + *out_size = buffer_size; + return ResultSuccess(); + } + + Result HtcsService::SendSmallResults(s32 *out_err, s64 *out_size, u32 task_id, s32 desc) { + /* Finish the task. */ + htcs::SocketError err; + R_TRY(m_rpc_client->End(task_id, std::addressof(err), out_size)); + + /* Set output. */ + *out_err = err; + return ResultSuccess(); + } + + Result HtcsService::SendStart(u32 *out_task_id, Handle *out_handle, s32 desc, s64 size, s32 flags) { + /* Begin the task. */ + u32 task_id; + R_TRY(m_rpc_client->Begin(std::addressof(task_id), desc, size, static_cast(flags))); + + /* Detach the task. */ + *out_task_id = task_id; + *out_handle = m_rpc_client->DetachReadableHandle(task_id); + + return ResultSuccess(); + } + + Result HtcsService::SendContinue(s64 *out_size, const char *buffer, s64 buffer_size, u32 task_id, s32 desc) { + /* Verify the task. */ + R_TRY(m_rpc_client->VerifyTaskIdWitHandle(task_id, desc)); + + /* Wait for the task to notify. */ + m_rpc_client->WaitNotification(task_id); + + /* Check the task status. */ + R_UNLESS(!m_rpc_client->IsCompleted(task_id), htcs::ResultCompleted()); + R_UNLESS(!m_rpc_client->IsCancelled(task_id), htcs::ResultCancelled()); + + /* Send the data. */ + if (buffer_size > 0) { + R_TRY(m_data_channel_manager->Send(buffer, buffer_size, task_id)); + } + + return ResultSuccess(); + } + + Result HtcsService::SendResults(s32 *out_err, s64 *out_size, u32 task_id, s32 desc) { + /* Verify the task. */ + R_TRY(m_rpc_client->VerifyTaskIdWitHandle(task_id, desc)); + + /* Finish the task. */ + htcs::SocketError err; + R_TRY(m_rpc_client->End(task_id, std::addressof(err), out_size)); + + /* Set output. */ + *out_err = err; + return ResultSuccess(); + } + + Result HtcsService::ReceiveStart(u32 *out_task_id, Handle *out_handle, s64 size, s32 desc, s32 flags) { + /* Begin the task. */ + u32 task_id; + R_TRY(m_rpc_client->Begin(std::addressof(task_id), desc, size, static_cast(flags))); + + /* Detach the task. */ + *out_task_id = task_id; + *out_handle = m_rpc_client->DetachReadableHandle(task_id); + + return ResultSuccess(); + } + + Result HtcsService::ReceiveResults(s32 *out_err, s64 *out_size, char *buffer, s64 buffer_size, u32 task_id, s32 desc) { + /* Verify the task. */ + R_TRY(m_rpc_client->VerifyTaskIdWitHandle(task_id, desc)); + + /* Get the result. */ + htcs::SocketError err; + s64 recv_size; + const Result result = m_rpc_client->GetResult(task_id, std::addressof(err), std::addressof(recv_size)); + if (R_FAILED(result) || err != HTCS_ENONE) { + /* Finish the task. */ + R_TRY(m_rpc_client->End(task_id, std::addressof(err), out_size)); + + /* Set output. */ + *out_err = err; + return ResultSuccess(); + } + + /* Check the size. */ + R_UNLESS(recv_size <= buffer_size, htcs::ResultInvalidArgument()); + + /* Perform remaining processing. */ + if (recv_size > 0) { + /* Receive data. */ + const Result recv_result = m_data_channel_manager->Receive(buffer, recv_size, task_id); + + /* Finish the task. */ + R_TRY(m_rpc_client->End(task_id, std::addressof(err), out_size)); + + /* Check that our receive succeeded. */ + R_TRY(recv_result); + } else { + /* Finish the task. */ + R_TRY(m_rpc_client->End(task_id, std::addressof(err), out_size)); + } + + /* Set output. */ + *out_err = err; + return ResultSuccess(); + } + + Result HtcsService::SelectStart(u32 *out_task_id, Handle *out_handle, Span read_handles, Span write_handles, Span exception_handles, s64 tv_sec, s64 tv_usec) { + /* Begin the task. */ + u32 task_id; + R_TRY(m_rpc_client->Begin(std::addressof(task_id), read_handles, write_handles, exception_handles, tv_sec, tv_usec)); + + /* Check that the task isn't cancelled. */ + R_UNLESS(!m_rpc_client->IsCancelled(task_id), htcs::ResultCancelled()); + + /* Detach the task. */ + *out_task_id = task_id; + *out_handle = m_rpc_client->DetachReadableHandle(task_id); + + return ResultSuccess(); + } + + Result HtcsService::SelectEnd(s32 *out_err, bool *out_empty, Span read_handles, Span write_handles, Span exception_handles, u32 task_id) { + /* Finish the task. */ + htcs::SocketError err; + bool empty; + R_TRY(m_rpc_client->End(task_id, std::addressof(err), std::addressof(empty), read_handles, write_handles, exception_handles)); + + /* Set output. */ + *out_err = err; + *out_empty = empty; + return ResultSuccess(); + } } diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_service.hpp b/libraries/libstratosphere/source/htcs/impl/htcs_service.hpp index 5546ca128..8d656c86b 100644 --- a/libraries/libstratosphere/source/htcs/impl/htcs_service.hpp +++ b/libraries/libstratosphere/source/htcs/impl/htcs_service.hpp @@ -38,8 +38,8 @@ namespace ams::htcs::impl { Result Connect(s32 *out_err, s32 desc, const SockAddrHtcs &address); Result Bind(s32 *out_err, s32 desc, const SockAddrHtcs &address); Result Listen(s32 *out_err, s32 desc, s32 backlog_count); - Result Receive(s32 *out_err, s64 *out_size, char *buffer, size_t size, s32 desc, s32 flags); - Result Send(s32 *out_err, s64 *out_size, const char *buffer, size_t size, s32 desc, s32 flags); + Result Receive(s32 *out_err, s64 *out_size, char *buffer, s64 size, s32 desc, s32 flags); + Result Send(s32 *out_err, s64 *out_size, const char *buffer, s64 size, s32 desc, s32 flags); Result Shutdown(s32 *out_err, s32 desc, s32 how); Result Fcntl(s32 *out_err, s32 *out_res, s32 desc, s32 command, s32 value); @@ -61,7 +61,7 @@ namespace ams::htcs::impl { Result ReceiveResults(s32 *out_err, s64 *out_size, char *buffer, s64 buffer_size, u32 task_id, s32 desc); Result SelectStart(u32 *out_task_id, Handle *out_handle, Span read_handles, Span write_handles, Span exception_handles, s64 tv_sec, s64 tv_usec); - Result SelectEnd(s32 *out_err, s32 *out_res, Span read_handles, Span write_handles, Span exception_handles, u32 task_id); + Result SelectEnd(s32 *out_err, bool *out_empty, Span read_handles, Span write_handles, Span exception_handles, u32 task_id); private: void WaitTask(u32 task_id); }; diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_util.cpp b/libraries/libstratosphere/source/htcs/impl/htcs_util.cpp index b30fdb749..993bea17e 100644 --- a/libraries/libstratosphere/source/htcs/impl/htcs_util.cpp +++ b/libraries/libstratosphere/source/htcs/impl/htcs_util.cpp @@ -29,7 +29,7 @@ namespace ams::htcs::impl { R_CATCH(htcs::ResultInvalidHandle) { return HTCS_EBADF; } R_CATCH(htc::ResultUnknown2001) { return HTCS_EINVAL; } R_CATCH(htc::ResultUnknown2101) { return HTCS_EMFILE; } - R_CATCH(htc::ResultUnknown2021) { return HTCS_EINTR; } + R_CATCH(htc::ResultTaskCancelled) { return HTCS_EINTR; } R_CATCH(htc::ResultInvalidTaskId) { return HTCS_EINTR; } R_CATCH(htc::ResultCancelled) { return HTCS_EINTR; } R_CATCH(htc::ResultTaskQueueNotAvailable) { return HTCS_ENETDOWN; } diff --git a/libraries/libstratosphere/source/htcs/impl/rpc/htcs_data_channel_manager.hpp b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_data_channel_manager.hpp index 7d683e523..aaac64d48 100644 --- a/libraries/libstratosphere/source/htcs/impl/rpc/htcs_data_channel_manager.hpp +++ b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_data_channel_manager.hpp @@ -28,7 +28,8 @@ namespace ams::htcs::impl::rpc { public: DataChannelManager(htc::server::rpc::RpcClient *client, htclow::HtclowManager *htclow_manager) : m_rpc_client(client), m_htclow_manager(htclow_manager), m_module(htclow::ModuleId::Htcs) { /* ... */ } public: - /* TODO */ + Result Send(const void *buffer, s64 buffer_size, u32 task_id); + Result Receive(void *buffer, s64 buffer_size, u32 task_id); }; } diff --git a/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_tasks.hpp b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_tasks.hpp index cf872e877..582b62858 100644 --- a/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_tasks.hpp +++ b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_tasks.hpp @@ -48,6 +48,9 @@ namespace ams::htcs::impl::rpc { s16 GetVersion() const { return m_version; } }; + template + concept IsHtcsTask = std::derived_from; + class HtcsSignalingTask : public HtcsTask { private: os::SystemEventType m_system_event; @@ -72,6 +75,8 @@ namespace ams::htcs::impl::rpc { }; class ReceiveTask : public HtcsSignalingTask { + public: + static constexpr inline HtcsTaskType TaskType = HtcsTaskType::Receive; private: s32 m_handle; s64 m_size; @@ -81,7 +86,7 @@ namespace ams::htcs::impl::rpc { htcs::SocketError m_err; s64 m_result_size; public: - ReceiveTask() : HtcsSignalingTask(HtcsTaskType::Receive) { /* ... */ } + ReceiveTask() : HtcsSignalingTask(TaskType) { /* ... */ } s32 GetHandle() const { return m_handle; } s64 GetSize() const { return m_size; } @@ -104,6 +109,8 @@ namespace ams::htcs::impl::rpc { }; class SendTask : public HtcsSignalingTask { + public: + static constexpr inline HtcsTaskType TaskType = HtcsTaskType::Send; private: os::Event m_ready_event; s32 m_handle; @@ -114,7 +121,7 @@ namespace ams::htcs::impl::rpc { htcs::SocketError m_err; s64 m_result_size; public: - SendTask() : HtcsSignalingTask(HtcsTaskType::Send), m_ready_event(os::EventClearMode_ManualClear) { /* ... */ } + SendTask() : HtcsSignalingTask(TaskType), m_ready_event(os::EventClearMode_ManualClear) { /* ... */ } s32 GetHandle() const { return m_handle; } s64 GetSize() const { return m_size; } @@ -138,12 +145,14 @@ namespace ams::htcs::impl::rpc { }; class ShutdownTask : public HtcsTask { + public: + static constexpr inline HtcsTaskType TaskType = HtcsTaskType::Shutdown; private: s32 m_handle; ShutdownType m_how; htcs::SocketError m_err; public: - ShutdownTask() : HtcsTask(HtcsTaskType::Shutdown) { /* ... */ } + ShutdownTask() : HtcsTask(TaskType) { /* ... */ } s32 GetHandle() const { return m_handle; } ShutdownType GetHow() const { return m_how; } @@ -157,11 +166,13 @@ namespace ams::htcs::impl::rpc { }; class CloseTask : public HtcsTask { + public: + static constexpr inline HtcsTaskType TaskType = HtcsTaskType::Close; private: s32 m_handle; htcs::SocketError m_err; public: - CloseTask() : HtcsTask(HtcsTaskType::Close) { /* ... */ } + CloseTask() : HtcsTask(TaskType) { /* ... */ } s32 GetHandle() const { return m_handle; } public: @@ -174,13 +185,15 @@ namespace ams::htcs::impl::rpc { }; class ConnectTask : public HtcsTask { + public: + static constexpr inline HtcsTaskType TaskType = HtcsTaskType::Connect; private: s32 m_handle; HtcsPeerName m_peer_name; HtcsPortName m_port_name; htcs::SocketError m_err; public: - ConnectTask() : HtcsTask(HtcsTaskType::Connect) { /* ... */ } + ConnectTask() : HtcsTask(TaskType) { /* ... */ } s32 GetHandle() const { return m_handle; } const HtcsPeerName &GetPeerName() const { return m_peer_name; } @@ -195,12 +208,14 @@ namespace ams::htcs::impl::rpc { }; class ListenTask : public HtcsTask { + public: + static constexpr inline HtcsTaskType TaskType = HtcsTaskType::Listen; private: s32 m_handle; s32 m_backlog; htcs::SocketError m_err; public: - ListenTask() : HtcsTask(HtcsTaskType::Listen) { /* ... */ } + ListenTask() : HtcsTask(TaskType) { /* ... */ } s32 GetHandle() const { return m_handle; } s32 GetBacklog() const { return m_backlog; } @@ -214,12 +229,14 @@ namespace ams::htcs::impl::rpc { }; class AcceptTask : public HtcsSignalingTask { + public: + static constexpr inline HtcsTaskType TaskType = HtcsTaskType::Accept; private: s32 m_server_handle; htcs::SocketError m_err; s32 m_desc; public: - AcceptTask() : HtcsSignalingTask(HtcsTaskType::Accept) { /* ... */ } + AcceptTask() : HtcsSignalingTask(TaskType) { /* ... */ } s32 GetServerHandle() const { return m_server_handle; } public: @@ -232,11 +249,13 @@ namespace ams::htcs::impl::rpc { }; class SocketTask : public HtcsTask { + public: + static constexpr inline HtcsTaskType TaskType = HtcsTaskType::Socket; private: htcs::SocketError m_err; s32 m_desc; public: - SocketTask() : HtcsTask(HtcsTaskType::Socket) { /* ... */ } + SocketTask() : HtcsTask(TaskType) { /* ... */ } public: Result SetArguments(); void Complete(htcs::SocketError err, s32 desc); @@ -247,13 +266,15 @@ namespace ams::htcs::impl::rpc { }; class BindTask : public HtcsTask { + public: + static constexpr inline HtcsTaskType TaskType = HtcsTaskType::Bind; private: s32 m_handle; HtcsPeerName m_peer_name; HtcsPortName m_port_name; htcs::SocketError m_err; public: - BindTask() : HtcsTask(HtcsTaskType::Bind) { /* ... */ } + BindTask() : HtcsTask(TaskType) { /* ... */ } s32 GetHandle() const { return m_handle; } const HtcsPeerName &GetPeerName() const { return m_peer_name; } @@ -268,6 +289,8 @@ namespace ams::htcs::impl::rpc { }; class FcntlTask : public HtcsTask { + public: + static constexpr inline HtcsTaskType TaskType = HtcsTaskType::Fcntl; private: s32 m_handle; s32 m_command; @@ -275,7 +298,7 @@ namespace ams::htcs::impl::rpc { htcs::SocketError m_err; s32 m_res; public: - FcntlTask() : HtcsTask(HtcsTaskType::Fcntl) { /* ... */ } + FcntlTask() : HtcsTask(TaskType) { /* ... */ } s32 GetHandle() const { return m_handle; } s32 GetCommand() const { return m_command; } @@ -290,6 +313,8 @@ namespace ams::htcs::impl::rpc { }; class ReceiveSmallTask : public HtcsSignalingTask { + public: + static constexpr inline HtcsTaskType TaskType = HtcsTaskType::ReceiveSmall; private: s32 m_handle; s64 m_size; @@ -298,7 +323,7 @@ namespace ams::htcs::impl::rpc { htcs::SocketError m_err; s64 m_result_size; public: - ReceiveSmallTask() : HtcsSignalingTask(HtcsTaskType::ReceiveSmall) { /* ... */ } + ReceiveSmallTask() : HtcsSignalingTask(TaskType) { /* ... */ } s32 GetHandle() const { return m_handle; } s64 GetSize() const { return m_size; } @@ -321,6 +346,8 @@ namespace ams::htcs::impl::rpc { }; class SendSmallTask : public HtcsSignalingTask { + public: + static constexpr inline HtcsTaskType TaskType = HtcsTaskType::SendSmall; private: os::Event m_ready_event; s32 m_handle; @@ -331,7 +358,7 @@ namespace ams::htcs::impl::rpc { htcs::SocketError m_err; s64 m_result_size; public: - SendSmallTask() : HtcsSignalingTask(HtcsTaskType::SendSmall), m_ready_event(os::EventClearMode_ManualClear) { /* ... */ } + SendSmallTask() : HtcsSignalingTask(TaskType), m_ready_event(os::EventClearMode_ManualClear) { /* ... */ } s32 GetHandle() const { return m_handle; } s64 GetSize() const { return m_size; } @@ -354,6 +381,8 @@ namespace ams::htcs::impl::rpc { }; class SelectTask : public HtcsSignalingTask { + public: + static constexpr inline HtcsTaskType TaskType = HtcsTaskType::Select; private: s32 m_handles[SocketCountMax * 3]; s32 m_read_handle_count; @@ -367,7 +396,7 @@ namespace ams::htcs::impl::rpc { s32 m_out_write_handle_count; s32 m_out_exception_handle_count; public: - SelectTask() : HtcsSignalingTask(HtcsTaskType::Select) { /* ... */ } + SelectTask() : HtcsSignalingTask(TaskType) { /* ... */ } const s32 *GetHandles() const { return m_handles; } s32 GetReadHandleCount() const { return m_read_handle_count; } diff --git a/libraries/libstratosphere/source/htcs/server/htcs_manager_service_object.cpp b/libraries/libstratosphere/source/htcs/server/htcs_manager_service_object.cpp index d0c6a6bb0..1f9b2ab8d 100644 --- a/libraries/libstratosphere/source/htcs/server/htcs_manager_service_object.cpp +++ b/libraries/libstratosphere/source/htcs/server/htcs_manager_service_object.cpp @@ -79,7 +79,11 @@ namespace ams::htcs::server { auto *manager = impl::HtcsManagerHolder::GetHtcsManager(); /* Start the select. */ - return manager->StartSelect(out_task_id.GetPointer(), out_event.GetHandlePointer(), read_handles.ToSpan(), write_handles.ToSpan(), exception_handles.ToSpan(), tv_sec, tv_usec); + R_TRY(manager->StartSelect(out_task_id.GetPointer(), out_event.GetHandlePointer(), read_handles.ToSpan(), write_handles.ToSpan(), exception_handles.ToSpan(), tv_sec, tv_usec)); + + /* Mark the output event as managed. */ + out_event.SetManaged(true); + return ResultSuccess(); } Result ManagerServiceObject::EndSelect(sf::Out out_err, sf::Out out_count, const sf::OutMapAliasArray &read_handles, const sf::OutMapAliasArray &write_handles, const sf::OutMapAliasArray &exception_handles, u32 task_id) { diff --git a/libraries/libstratosphere/source/htcs/server/htcs_socket_service_object.cpp b/libraries/libstratosphere/source/htcs/server/htcs_socket_service_object.cpp index 9e7e31692..bb0619cda 100644 --- a/libraries/libstratosphere/source/htcs/server/htcs_socket_service_object.cpp +++ b/libraries/libstratosphere/source/htcs/server/htcs_socket_service_object.cpp @@ -118,7 +118,11 @@ namespace ams::htcs::server { auto *manager = impl::HtcsManagerHolder::GetHtcsManager(); /* Start the accept. */ - return manager->AcceptStart(out_task_id.GetPointer(), out_event.GetHandlePointer(), m_desc); + R_TRY(manager->AcceptStart(out_task_id.GetPointer(), out_event.GetHandlePointer(), m_desc)); + + /* Mark the output event as managed. */ + out_event.SetManaged(true); + return ResultSuccess(); } Result SocketServiceObject::AcceptResults(sf::Out out_err, sf::Out> out, sf::Out out_address, u32 task_id) { @@ -143,7 +147,11 @@ namespace ams::htcs::server { auto *manager = impl::HtcsManagerHolder::GetHtcsManager(); /* Start the recv. */ - return manager->RecvStart(out_task_id.GetPointer(), out_event.GetHandlePointer(), mem_size, m_desc, flags); + R_TRY(manager->RecvStart(out_task_id.GetPointer(), out_event.GetHandlePointer(), mem_size, m_desc, flags)); + + /* Mark the output event as managed. */ + out_event.SetManaged(true); + return ResultSuccess(); } Result SocketServiceObject::RecvResults(sf::Out out_err, sf::Out out_size, const sf::OutAutoSelectBuffer &buffer, u32 task_id) { @@ -174,7 +182,11 @@ namespace ams::htcs::server { auto *manager = impl::HtcsManagerHolder::GetHtcsManager(); /* Start the large receive. */ - return manager->RecvStart(out_task_id.GetPointer(), out_event.GetHandlePointer(), unaligned_size_start + aligned_size + unaligned_size_end, m_desc, flags); + R_TRY(manager->RecvStart(out_task_id.GetPointer(), out_event.GetHandlePointer(), unaligned_size_start + aligned_size + unaligned_size_end, m_desc, flags)); + + /* Mark the output event as managed. */ + out_event.SetManaged(true); + return ResultSuccess(); } Result SocketServiceObject::SendStartOld(sf::Out out_task_id, sf::OutCopyHandle out_event, const sf::InAutoSelectBuffer &buffer, s32 flags) { @@ -205,7 +217,11 @@ namespace ams::htcs::server { const char *pointers[NumBuffers] = { reinterpret_cast(start_buffer.GetPointer()), static_cast(address), reinterpret_cast(end_buffer.GetPointer()) }; s64 sizes[NumBuffers] = { static_cast(start_buffer.GetSize()), aligned_size, static_cast(end_buffer.GetSize()) }; - return manager->SendLargeStart(out_task_id.GetPointer(), out_event.GetHandlePointer(), pointers, sizes, NumBuffers, m_desc, flags); + R_TRY(manager->SendLargeStart(out_task_id.GetPointer(), out_event.GetHandlePointer(), pointers, sizes, NumBuffers, m_desc, flags)); + + /* Mark the output event as managed. */ + out_event.SetManaged(true); + return ResultSuccess(); } Result SocketServiceObject::SendResults(sf::Out out_err, sf::Out out_size, u32 task_id) { @@ -227,6 +243,9 @@ namespace ams::htcs::server { /* Set the output max size to the size. */ *out_max_size = size; + + /* Mark the output event as managed. */ + out_event.SetManaged(true); return ResultSuccess(); } @@ -249,7 +268,11 @@ namespace ams::htcs::server { auto *manager = impl::HtcsManagerHolder::GetHtcsManager(); /* Start the recv. */ - return manager->StartRecv(out_task_id.GetPointer(), out_event.GetHandlePointer(), size, m_desc, flags); + R_TRY(manager->StartRecv(out_task_id.GetPointer(), out_event.GetHandlePointer(), size, m_desc, flags)); + + /* Mark the output event as managed. */ + out_event.SetManaged(true); + return ResultSuccess(); } Result SocketServiceObject::EndRecv(sf::Out out_err, sf::Out out_size, const sf::OutAutoSelectBuffer &buffer, u32 task_id) { @@ -270,7 +293,11 @@ namespace ams::htcs::server { auto *manager = impl::HtcsManagerHolder::GetHtcsManager(); /* Start the send. */ - return manager->SendStart(out_task_id.GetPointer(), out_event.GetHandlePointer(), reinterpret_cast(buffer.GetPointer()), buffer.GetSize(), m_desc, flags); + R_TRY(manager->SendStart(out_task_id.GetPointer(), out_event.GetHandlePointer(), reinterpret_cast(buffer.GetPointer()), buffer.GetSize(), m_desc, flags)); + + /* Mark the output event as managed. */ + out_event.SetManaged(true); + return ResultSuccess(); } Result SocketServiceObject::ContinueSend(sf::Out out_size, sf::Out out_wait, const sf::InNonSecureAutoSelectBuffer &buffer, u32 task_id) { diff --git a/libraries/libvapours/include/vapours/results/htc_results.hpp b/libraries/libvapours/include/vapours/results/htc_results.hpp index 3f1a5752b..1a402f988 100644 --- a/libraries/libvapours/include/vapours/results/htc_results.hpp +++ b/libraries/libvapours/include/vapours/results/htc_results.hpp @@ -31,7 +31,8 @@ namespace ams::htc { R_DEFINE_ERROR_RESULT(Unknown2001, 2001); R_DEFINE_ERROR_RESULT(InvalidTaskId, 2003); R_DEFINE_ERROR_RESULT(InvalidSize, 2011); - R_DEFINE_ERROR_RESULT(Unknown2021, 2021); + R_DEFINE_ERROR_RESULT(TaskCancelled, 2021); + R_DEFINE_ERROR_RESULT(TaskNotCompleted, 2022); R_DEFINE_ERROR_RESULT(TaskQueueNotAvailable, 2033); R_DEFINE_ERROR_RESULT(Unknown2101, 2101); diff --git a/libraries/libvapours/include/vapours/results/htcs_results.hpp b/libraries/libvapours/include/vapours/results/htcs_results.hpp index 7903cb4bd..3b3bc0a5d 100644 --- a/libraries/libvapours/include/vapours/results/htcs_results.hpp +++ b/libraries/libvapours/include/vapours/results/htcs_results.hpp @@ -24,7 +24,7 @@ namespace ams::htcs { R_DEFINE_ERROR_RESULT(InvalidArgument, 2001); R_DEFINE_ERROR_RESULT(InvalidSize, 2014); - R_DEFINE_ERROR_RESULT(Unknown2021, 2021); - R_DEFINE_ERROR_RESULT(Unknown2023, 2023); + R_DEFINE_ERROR_RESULT(Cancelled, 2021); + R_DEFINE_ERROR_RESULT(Completed, 2023); } From 70caadafd5032c2cb31ae9cb115c77b43df107a4 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 19 Feb 2021 01:33:01 -0800 Subject: [PATCH 076/280] htcs: implement rpc tasks --- .../htcs/impl/rpc/htcs_rpc_accept_task.cpp | 85 ++++++++++ .../htcs/impl/rpc/htcs_rpc_bind_task.cpp | 81 ++++++++++ .../htcs/impl/rpc/htcs_rpc_close_task.cpp | 77 +++++++++ .../htcs/impl/rpc/htcs_rpc_connect_task.cpp | 81 ++++++++++ .../htcs/impl/rpc/htcs_rpc_fcntl_task.cpp | 83 ++++++++++ .../htcs/impl/rpc/htcs_rpc_listen_task.cpp | 79 +++++++++ .../impl/rpc/htcs_rpc_receive_small_task.cpp | 93 +++++++++++ .../htcs/impl/rpc/htcs_rpc_receive_task.cpp | 108 +++++++++++++ .../htcs/impl/rpc/htcs_rpc_select_task.cpp | 150 ++++++++++++++++++ .../impl/rpc/htcs_rpc_send_small_task.cpp | 133 ++++++++++++++++ .../htcs/impl/rpc/htcs_rpc_send_task.cpp | 143 +++++++++++++++++ .../htcs/impl/rpc/htcs_rpc_shutdown_task.cpp | 79 +++++++++ .../htcs/impl/rpc/htcs_rpc_signaling_task.cpp | 63 ++++++++ .../htcs/impl/rpc/htcs_rpc_socket_task.cpp | 89 +++++++++++ .../source/htcs/impl/rpc/htcs_rpc_tasks.hpp | 67 +++++++- .../include/vapours/results/htcs_results.hpp | 10 +- 16 files changed, 1412 insertions(+), 9 deletions(-) create mode 100644 libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_accept_task.cpp create mode 100644 libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_bind_task.cpp create mode 100644 libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_close_task.cpp create mode 100644 libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_connect_task.cpp create mode 100644 libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_fcntl_task.cpp create mode 100644 libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_listen_task.cpp create mode 100644 libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_receive_small_task.cpp create mode 100644 libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_receive_task.cpp create mode 100644 libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_select_task.cpp create mode 100644 libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_send_small_task.cpp create mode 100644 libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_send_task.cpp create mode 100644 libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_shutdown_task.cpp create mode 100644 libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_signaling_task.cpp create mode 100644 libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_socket_task.cpp diff --git a/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_accept_task.cpp b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_accept_task.cpp new file mode 100644 index 000000000..3dfd71bcd --- /dev/null +++ b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_accept_task.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcs_rpc_tasks.hpp" + +namespace ams::htcs::impl::rpc { + + Result AcceptTask::SetArguments(s32 server_handle) { + /* Check that we're valid. */ + R_UNLESS(this->IsValid(), htcs::ResultInvalidTask()); + + /* Set our arguments. */ + m_server_handle = server_handle; + + return ResultSuccess(); + } + + void AcceptTask::Complete(htcs::SocketError err, s32 desc) { + /* Set our results. */ + m_err = err; + m_desc = desc; + + /* Complete. */ + HtcsSignalingTask::Complete(); + } + + Result AcceptTask::GetResult(htcs::SocketError *out_err, s32 *out_desc, s32 server_handle) const { + /* Check the server handle. */ + R_UNLESS(m_server_handle == server_handle, htcs::ResultInvalidServerHandle()); + + /* Sanity check our state. */ + AMS_ASSERT(this->GetTaskState() == htc::server::rpc::RpcTaskState::Completed); + + /* Set the output. */ + *out_err = m_err; + *out_desc = m_desc; + + return ResultSuccess(); + } + + Result AcceptTask::ProcessResponse(const char *data, size_t size) { + /* Convert the input to a packet. */ + auto *packet = reinterpret_cast(data); + + /* Complete the task. */ + this->Complete(static_cast(packet->params[0]), packet->params[1]); + + return ResultSuccess(); + } + + Result AcceptTask::CreateRequest(size_t *out, char *data, size_t size, u32 task_id) { + /* Create the packet. */ + auto *packet = reinterpret_cast(data); + *packet = { + .protocol = HtcsProtocol, + .version = this->GetVersion(), + .category = HtcsPacketCategory::Request, + .type = HtcsPacketType::Accept, + .body_size = 0, + .task_id = task_id, + .params = { + m_server_handle, + }, + }; + + /* Set the output size. */ + *out = sizeof(*packet); + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_bind_task.cpp b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_bind_task.cpp new file mode 100644 index 000000000..1149867bc --- /dev/null +++ b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_bind_task.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcs_rpc_tasks.hpp" + +namespace ams::htcs::impl::rpc { + + Result BindTask::SetArguments(s32 handle, const HtcsPeerName &peer_name, const HtcsPortName &port_name) { + /* Set our arguments. */ + m_handle = handle; + m_peer_name = peer_name; + m_port_name = port_name; + + return ResultSuccess(); + } + + void BindTask::Complete(htcs::SocketError err) { + /* Set our results. */ + m_err = err; + + /* Complete. */ + HtcsTask::Complete(); + } + + Result BindTask::GetResult(htcs::SocketError *out_err) const { + /* Sanity check our state. */ + AMS_ASSERT(this->GetTaskState() == htc::server::rpc::RpcTaskState::Completed); + + /* Set the output. */ + *out_err = m_err; + + return ResultSuccess(); + } + + Result BindTask::ProcessResponse(const char *data, size_t size) { + /* Convert the input to a packet. */ + auto *packet = reinterpret_cast(data); + + /* Complete the task. */ + this->Complete(static_cast(packet->params[0])); + + return ResultSuccess(); + } + + Result BindTask::CreateRequest(size_t *out, char *data, size_t size, u32 task_id) { + /* Create the packet. */ + auto *packet = reinterpret_cast(data); + *packet = { + .protocol = HtcsProtocol, + .version = this->GetVersion(), + .category = HtcsPacketCategory::Request, + .type = HtcsPacketType::Bind, + .body_size = sizeof(m_peer_name) + sizeof(m_port_name), + .task_id = task_id, + .params = { + m_handle, + }, + }; + std::memcpy(packet->data + 0, std::addressof(m_peer_name), sizeof(m_peer_name)); + std::memcpy(packet->data + sizeof(m_peer_name), std::addressof(m_port_name), sizeof(m_port_name)); + + /* Set the output size. */ + *out = sizeof(*packet) + sizeof(m_peer_name) + sizeof(m_port_name); + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_close_task.cpp b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_close_task.cpp new file mode 100644 index 000000000..f896b749e --- /dev/null +++ b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_close_task.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcs_rpc_tasks.hpp" + +namespace ams::htcs::impl::rpc { + + Result CloseTask::SetArguments(s32 handle) { + /* Set our arguments. */ + m_handle = handle; + + return ResultSuccess(); + } + + void CloseTask::Complete(htcs::SocketError err) { + /* Set our results. */ + m_err = err; + + /* Complete. */ + HtcsTask::Complete(); + } + + Result CloseTask::GetResult(htcs::SocketError *out_err) const { + /* Sanity check our state. */ + AMS_ASSERT(this->GetTaskState() == htc::server::rpc::RpcTaskState::Completed); + + /* Set the output. */ + *out_err = m_err; + + return ResultSuccess(); + } + + Result CloseTask::ProcessResponse(const char *data, size_t size) { + /* Convert the input to a packet. */ + auto *packet = reinterpret_cast(data); + + /* Complete the task. */ + this->Complete(static_cast(packet->params[0])); + + return ResultSuccess(); + } + + Result CloseTask::CreateRequest(size_t *out, char *data, size_t size, u32 task_id) { + /* Create the packet. */ + auto *packet = reinterpret_cast(data); + *packet = { + .protocol = HtcsProtocol, + .version = this->GetVersion(), + .category = HtcsPacketCategory::Request, + .type = HtcsPacketType::Close, + .body_size = 0, + .task_id = task_id, + .params = { + m_handle, + }, + }; + + /* Set the output size. */ + *out = sizeof(*packet); + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_connect_task.cpp b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_connect_task.cpp new file mode 100644 index 000000000..dbbb46d6e --- /dev/null +++ b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_connect_task.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcs_rpc_tasks.hpp" + +namespace ams::htcs::impl::rpc { + + Result ConnectTask::SetArguments(s32 handle, const HtcsPeerName &peer_name, const HtcsPortName &port_name) { + /* Set our arguments. */ + m_handle = handle; + m_peer_name = peer_name; + m_port_name = port_name; + + return ResultSuccess(); + } + + void ConnectTask::Complete(htcs::SocketError err) { + /* Set our results. */ + m_err = err; + + /* Complete. */ + HtcsTask::Complete(); + } + + Result ConnectTask::GetResult(htcs::SocketError *out_err) const { + /* Sanity check our state. */ + AMS_ASSERT(this->GetTaskState() == htc::server::rpc::RpcTaskState::Completed); + + /* Set the output. */ + *out_err = m_err; + + return ResultSuccess(); + } + + Result ConnectTask::ProcessResponse(const char *data, size_t size) { + /* Convert the input to a packet. */ + auto *packet = reinterpret_cast(data); + + /* Complete the task. */ + this->Complete(static_cast(packet->params[0])); + + return ResultSuccess(); + } + + Result ConnectTask::CreateRequest(size_t *out, char *data, size_t size, u32 task_id) { + /* Create the packet. */ + auto *packet = reinterpret_cast(data); + *packet = { + .protocol = HtcsProtocol, + .version = this->GetVersion(), + .category = HtcsPacketCategory::Request, + .type = HtcsPacketType::Connect, + .body_size = sizeof(m_peer_name) + sizeof(m_port_name), + .task_id = task_id, + .params = { + m_handle, + }, + }; + std::memcpy(packet->data + 0, std::addressof(m_peer_name), sizeof(m_peer_name)); + std::memcpy(packet->data + sizeof(m_peer_name), std::addressof(m_port_name), sizeof(m_port_name)); + + /* Set the output size. */ + *out = sizeof(*packet) + sizeof(m_peer_name) + sizeof(m_port_name); + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_fcntl_task.cpp b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_fcntl_task.cpp new file mode 100644 index 000000000..29d84ffae --- /dev/null +++ b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_fcntl_task.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcs_rpc_tasks.hpp" + +namespace ams::htcs::impl::rpc { + + Result FcntlTask::SetArguments(s32 handle, s32 command, s32 value) { + /* Set our arguments. */ + m_handle = handle; + m_command = command; + m_value = value; + + return ResultSuccess(); + } + + void FcntlTask::Complete(htcs::SocketError err, s32 res) { + /* Set our results. */ + m_err = err; + m_res = res; + + /* Complete. */ + HtcsTask::Complete(); + } + + Result FcntlTask::GetResult(htcs::SocketError *out_err, s32 *out_res) const { + /* Sanity check our state. */ + AMS_ASSERT(this->GetTaskState() == htc::server::rpc::RpcTaskState::Completed); + + /* Set the output. */ + *out_err = m_err; + *out_res = m_res; + + return ResultSuccess(); + } + + Result FcntlTask::ProcessResponse(const char *data, size_t size) { + /* Convert the input to a packet. */ + auto *packet = reinterpret_cast(data); + + /* Complete the task. */ + this->Complete(static_cast(packet->params[0]), packet->params[1]); + + return ResultSuccess(); + } + + Result FcntlTask::CreateRequest(size_t *out, char *data, size_t size, u32 task_id) { + /* Create the packet. */ + auto *packet = reinterpret_cast(data); + *packet = { + .protocol = HtcsProtocol, + .version = this->GetVersion(), + .category = HtcsPacketCategory::Request, + .type = HtcsPacketType::Fcntl, + .body_size = 0, + .task_id = task_id, + .params = { + m_handle, + m_command, + m_value, + }, + }; + + /* Set the output size. */ + *out = sizeof(*packet); + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_listen_task.cpp b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_listen_task.cpp new file mode 100644 index 000000000..101157e18 --- /dev/null +++ b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_listen_task.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcs_rpc_tasks.hpp" + +namespace ams::htcs::impl::rpc { + + Result ListenTask::SetArguments(s32 handle, s32 backlog) { + /* Set our arguments. */ + m_handle = handle; + m_backlog = backlog; + + return ResultSuccess(); + } + + void ListenTask::Complete(htcs::SocketError err) { + /* Set our results. */ + m_err = err; + + /* Complete. */ + HtcsTask::Complete(); + } + + Result ListenTask::GetResult(htcs::SocketError *out_err) const { + /* Sanity check our state. */ + AMS_ASSERT(this->GetTaskState() == htc::server::rpc::RpcTaskState::Completed); + + /* Set the output. */ + *out_err = m_err; + + return ResultSuccess(); + } + + Result ListenTask::ProcessResponse(const char *data, size_t size) { + /* Convert the input to a packet. */ + auto *packet = reinterpret_cast(data); + + /* Complete the task. */ + this->Complete(static_cast(packet->params[0])); + + return ResultSuccess(); + } + + Result ListenTask::CreateRequest(size_t *out, char *data, size_t size, u32 task_id) { + /* Create the packet. */ + auto *packet = reinterpret_cast(data); + *packet = { + .protocol = HtcsProtocol, + .version = this->GetVersion(), + .category = HtcsPacketCategory::Request, + .type = HtcsPacketType::Listen, + .body_size = 0, + .task_id = task_id, + .params = { + m_handle, + m_backlog, + }, + }; + + /* Set the output size. */ + *out = sizeof(*packet); + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_receive_small_task.cpp b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_receive_small_task.cpp new file mode 100644 index 000000000..312d8a02b --- /dev/null +++ b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_receive_small_task.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcs_rpc_tasks.hpp" + +namespace ams::htcs::impl::rpc { + + Result ReceiveSmallTask::SetArguments(s32 handle, s64 size, htcs::MessageFlag flags) { + /* Check that we're valid. */ + R_UNLESS(this->IsValid(), htcs::ResultInvalidTask()); + + /* Set our arguments. */ + m_handle = handle; + m_size = size; + m_flags = flags; + + return ResultSuccess(); + } + + void ReceiveSmallTask::Complete(htcs::SocketError err, s64 size) { + /* Set our results. */ + m_err = err; + m_result_size = size; + + /* Complete. */ + HtcsSignalingTask::Complete(); + } + + Result ReceiveSmallTask::GetResult(htcs::SocketError *out_err, s64 *out_size) const { + /* Sanity check our state. */ + AMS_ASSERT(this->GetTaskState() == htc::server::rpc::RpcTaskState::Completed); + + /* Set the output. */ + *out_err = m_err; + *out_size = m_result_size; + + return ResultSuccess(); + } + + Result ReceiveSmallTask::ProcessResponse(const char *data, size_t size) { + /* Convert the input to a packet. */ + auto *packet = reinterpret_cast(data); + + /* Copy the data to our buffer. */ + std::memcpy(m_buffer, packet->data, packet->body_size); + + /* Complete the task. */ + this->Complete(static_cast(packet->params[0]), packet->body_size); + + return ResultSuccess(); + } + + Result ReceiveSmallTask::CreateRequest(size_t *out, char *data, size_t size, u32 task_id) { + /* Create the packet. */ + auto *packet = reinterpret_cast(data); + *packet = { + .protocol = HtcsProtocol, + .version = this->GetVersion(), + .category = HtcsPacketCategory::Request, + .type = HtcsPacketType::Receive, + .body_size = 0, + .task_id = task_id, + .params = { + m_handle, + m_size, + static_cast(m_flags), + }, + }; + + /* Set the output size. */ + *out = sizeof(*packet); + + return ResultSuccess(); + } + + bool ReceiveSmallTask::IsReceiveBufferRequired() { + return true; + } + +} diff --git a/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_receive_task.cpp b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_receive_task.cpp new file mode 100644 index 000000000..d95420993 --- /dev/null +++ b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_receive_task.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcs_rpc_tasks.hpp" + +namespace ams::htcs::impl::rpc { + + Result ReceiveTask::SetArguments(s32 handle, s64 size, htcs::MessageFlag flags) { + /* Check that we're valid. */ + R_UNLESS(this->IsValid(), htcs::ResultInvalidTask()); + + /* Set our arguments. */ + m_handle = handle; + m_size = size; + m_flags = flags; + + return ResultSuccess(); + } + + void ReceiveTask::Complete(htcs::SocketError err, s64 size) { + /* Set our results. */ + m_err = err; + m_result_size = size; + + /* Complete. */ + HtcsSignalingTask::Complete(); + } + + Result ReceiveTask::GetResult(htcs::SocketError *out_err, s64 *out_size) const { + /* Sanity check our state. */ + AMS_ASSERT(this->GetTaskState() == htc::server::rpc::RpcTaskState::Completed); + + /* Set the output. */ + *out_err = m_err; + *out_size = m_result_size; + + return ResultSuccess(); + } + + Result ReceiveTask::ProcessResponse(const char *data, size_t size) { + /* Convert the input to a packet. */ + auto *packet = reinterpret_cast(data); + + /* Complete the task. */ + this->Complete(static_cast(packet->params[0]), packet->params[1]); + + return ResultSuccess(); + } + + Result ReceiveTask::CreateRequest(size_t *out, char *data, size_t size, u32 task_id) { + /* Create the packet. */ + auto *packet = reinterpret_cast(data); + *packet = { + .protocol = HtcsProtocol, + .version = this->GetVersion(), + .category = HtcsPacketCategory::Request, + .type = HtcsPacketType::ReceiveLarge, + .body_size = 0, + .task_id = task_id, + .params = { + m_handle, + m_size, + static_cast(m_flags), + GetReceiveDataChannelId(task_id), + }, + }; + + /* Set the output size. */ + *out = sizeof(*packet); + + return ResultSuccess(); + } + + Result ReceiveTask::CreateNotification(size_t *out, char *data, size_t size, u32 task_id) { + /* Create the packet. */ + auto *packet = reinterpret_cast(data); + *packet = { + .protocol = HtcsProtocol, + .version = this->GetVersion(), + .category = HtcsPacketCategory::Notification, + .type = HtcsPacketType::ReceiveLarge, + .body_size = 0, + .task_id = task_id, + .params = { + /* ... */ + }, + }; + + /* Set the output size. */ + *out = sizeof(*packet); + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_select_task.cpp b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_select_task.cpp new file mode 100644 index 000000000..09e1507a9 --- /dev/null +++ b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_select_task.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcs_rpc_tasks.hpp" + +namespace ams::htcs::impl::rpc { + + Result SelectTask::SetArguments(Span read_handles, Span write_handles, Span exception_handles, s64 tv_sec, s64 tv_usec) { + /* Check that we're valid. */ + R_UNLESS(this->IsValid(), htcs::ResultInvalidTask()); + + /* Sanity check the spans. */ + AMS_ASSERT(0 <= read_handles.size() && read_handles.size() < static_cast(SocketCountMax)); + AMS_ASSERT(0 <= write_handles.size() && write_handles.size() < static_cast(SocketCountMax)); + AMS_ASSERT(0 <= exception_handles.size() && exception_handles.size() < static_cast(SocketCountMax)); + + /* Set our arguments. */ + m_read_handle_count = static_cast(read_handles.size()); + m_write_handle_count = static_cast(write_handles.size()); + m_exception_handle_count = static_cast(exception_handles.size()); + m_tv_sec = tv_sec; + m_tv_usec = tv_usec; + + /* Copy the handles. */ + std::memcpy(m_handles, read_handles.data(), read_handles.size_bytes()); + std::memcpy(m_handles + m_read_handle_count, write_handles.data(), write_handles.size_bytes()); + std::memcpy(m_handles + m_read_handle_count + m_write_handle_count, exception_handles.data(), exception_handles.size_bytes()); + + return ResultSuccess(); + } + + void SelectTask::Complete(htcs::SocketError err, s32 read_handle_count, s32 write_handle_count, s32 exception_handle_count, const void *body, s64 body_size) { + /* Sanity check the handle counts. */ + const auto handle_count = read_handle_count + write_handle_count + exception_handle_count; + AMS_ASSERT(0 <= read_handle_count && read_handle_count < SocketCountMax); + AMS_ASSERT(0 <= write_handle_count && write_handle_count < SocketCountMax); + AMS_ASSERT(0 <= exception_handle_count && exception_handle_count < SocketCountMax); + AMS_ASSERT(handle_count * static_cast(sizeof(s32)) == body_size); + + /* Set our results. */ + m_err = err; + m_out_read_handle_count = read_handle_count; + m_out_write_handle_count = write_handle_count; + m_out_exception_handle_count = exception_handle_count; + + /* Copy the handles. */ + std::memcpy(m_out_handles, static_cast(body), sizeof(s32) * read_handle_count); + std::memcpy(m_out_handles + read_handle_count, static_cast(body) + read_handle_count, sizeof(s32) * write_handle_count); + std::memcpy(m_out_handles + read_handle_count + write_handle_count, static_cast(body) + read_handle_count + write_handle_count, sizeof(s32) * exception_handle_count); + + /* Complete. */ + HtcsSignalingTask::Complete(); + } + + Result SelectTask::GetResult(htcs::SocketError *out_err, bool *out_empty, Span read_handles, Span write_handles, Span exception_handles) const { + /* Set the output error. */ + *out_err = m_err; + + /* Set the output empty value. */ + const bool empty = m_err == HTCS_ENONE && m_out_read_handle_count == 0 && m_out_write_handle_count == 0 && m_out_exception_handle_count == 0; + *out_empty = empty; + + /* Clear the output spans. */ + std::fill(read_handles.begin(), read_handles.end(), 0); + std::fill(write_handles.begin(), write_handles.end(), 0); + std::fill(exception_handles.begin(), exception_handles.end(), 0); + + /* Copy the handles. */ + if (m_err == HTCS_ENONE && !empty) { + const s32 * const out_read_start = m_out_handles; + const s32 * const out_read_end = out_read_start + m_out_read_handle_count; + const s32 * const out_write_start = out_read_end; + const s32 * const out_write_end = out_write_start + m_out_write_handle_count; + const s32 * const out_exception_start = out_write_end; + const s32 * const out_exception_end = out_exception_start + m_out_exception_handle_count; + std::copy(out_read_start, out_read_end, read_handles.begin()); + std::copy(out_write_start, out_write_end, write_handles.begin()); + std::copy(out_exception_start, out_exception_end, exception_handles.begin()); + } else { + const s32 * const read_start = m_handles; + const s32 * const read_end = read_start + m_read_handle_count; + const s32 * const write_start = read_end; + const s32 * const write_end = write_start + m_write_handle_count; + const s32 * const exception_start = write_end; + const s32 * const exception_end = exception_start + m_exception_handle_count; + std::copy(read_start, read_end, read_handles.begin()); + std::copy(write_start, write_end, write_handles.begin()); + std::copy(exception_start, exception_end, exception_handles.begin()); + } + + return ResultSuccess(); + } + + Result SelectTask::ProcessResponse(const char *data, size_t size) { + /* Convert the input to a packet. */ + auto *packet = reinterpret_cast(data); + + /* Complete the task. */ + this->Complete(static_cast(packet->params[0]), packet->params[1], packet->params[2], packet->params[3], packet->data, size - sizeof(*packet)); + + return ResultSuccess(); + } + + Result SelectTask::CreateRequest(size_t *out, char *data, size_t size, u32 task_id) { + /* Determine the body size. */ + const auto handle_count = m_read_handle_count + m_write_handle_count + m_exception_handle_count; + const s64 body_size = static_cast(handle_count * sizeof(s32)); + AMS_ASSERT(sizeof(HtcsRpcPacket) + body_size <= size); + + /* Create the packet. */ + auto *packet = reinterpret_cast(data); + *packet = { + .protocol = HtcsProtocol, + .version = this->GetVersion(), + .category = HtcsPacketCategory::Request, + .type = HtcsPacketType::Select, + .body_size = body_size, + .task_id = task_id, + .params = { + m_read_handle_count, + m_write_handle_count, + m_exception_handle_count, + m_tv_sec, + m_tv_usec, + }, + }; + + /* Set the packet body. */ + std::memcpy(packet->data, m_handles, body_size); + + /* Set the output size. */ + *out = sizeof(*packet) + body_size; + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_send_small_task.cpp b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_send_small_task.cpp new file mode 100644 index 000000000..1765089ca --- /dev/null +++ b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_send_small_task.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcs_rpc_tasks.hpp" + +namespace ams::htcs::impl::rpc { + + void SendSmallTask::SetBuffer(const void *buffer, s64 buffer_size) { + /* Sanity check the buffer size. */ + AMS_ASSERT(0 <= buffer_size && buffer_size <= static_cast(sizeof(m_buffer))); + + /* Set our buffer. */ + if (buffer_size > 0) { + std::memcpy(m_buffer, buffer, buffer_size); + } + m_buffer_size = buffer_size; + } + + void SendSmallTask::NotifyDataChannelReady() { + /* Notify. */ + this->Notify(); + + /* Signal our ready event. */ + m_ready_event.Signal(); + } + + void SendSmallTask::WaitNotification() { + /* Wait on our ready event. */ + m_ready_event.Wait(); + } + + Result SendSmallTask::SetArguments(s32 handle, s64 size, htcs::MessageFlag flags) { + /* Check that we're valid. */ + R_UNLESS(this->IsValid(), htcs::ResultInvalidTask()); + + /* Set our arguments. */ + m_handle = handle; + m_size = size; + m_flags = flags; + + return ResultSuccess(); + } + + void SendSmallTask::Complete(htcs::SocketError err, s64 size) { + /* Set our results. */ + m_err = err; + m_result_size = size; + + /* Signal our ready event. */ + m_ready_event.Signal(); + + /* Complete. */ + HtcsSignalingTask::Complete(); + } + + Result SendSmallTask::GetResult(htcs::SocketError *out_err, s64 *out_size) const { + /* Sanity check our state. */ + AMS_ASSERT(this->GetTaskState() == htc::server::rpc::RpcTaskState::Completed); + + /* Set the output. */ + *out_err = m_err; + *out_size = m_result_size; + + return ResultSuccess(); + } + + void SendSmallTask::Cancel(htc::server::rpc::RpcTaskCancelReason reason) { + /* Cancel the task. */ + HtcsSignalingTask::Cancel(reason); + + /* Signal our ready event. */ + m_ready_event.Signal(); + } + + Result SendSmallTask::ProcessResponse(const char *data, size_t size) { + /* Convert the input to a packet. */ + auto *packet = reinterpret_cast(data); + + /* Complete the task. */ + this->Complete(static_cast(packet->params[0]), this->GetSize()); + + return ResultSuccess(); + } + + Result SendSmallTask::CreateRequest(size_t *out, char *data, size_t size, u32 task_id) { + /* Sanity check our size. */ + AMS_ASSERT(sizeof(HtcsRpcPacket) + this->GetBufferSize() <= size); + + /* Create the packet. */ + auto *packet = reinterpret_cast(data); + *packet = { + .protocol = HtcsProtocol, + .version = this->GetVersion(), + .category = HtcsPacketCategory::Request, + .type = HtcsPacketType::Send, + .body_size = this->GetSize(), + .task_id = task_id, + .params = { + m_handle, + m_size, + static_cast(m_flags), + }, + }; + + /* Set the body. */ + if (this->GetSize() > 0) { + std::memcpy(packet->data, this->GetBuffer(), this->GetSize()); + } + + /* Set the output size. */ + *out = sizeof(*packet) + this->GetSize(); + + return ResultSuccess(); + } + + bool SendSmallTask::IsSendBufferRequired() { + return this->GetSize() > 0; + } + +} diff --git a/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_send_task.cpp b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_send_task.cpp new file mode 100644 index 000000000..126c4af52 --- /dev/null +++ b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_send_task.cpp @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcs_rpc_tasks.hpp" + +namespace ams::htcs::impl::rpc { + + void SendTask::SetBuffer(const void *buffer, s64 buffer_size) { + /* Set our buffer. */ + m_buffer = buffer; + m_buffer_size = buffer_size; + } + + void SendTask::NotifyDataChannelReady() { + /* Notify. */ + this->Notify(); + + /* Signal our ready event. */ + m_ready_event.Signal(); + } + + void SendTask::WaitNotification() { + /* Wait on our ready event. */ + m_ready_event.Wait(); + } + + Result SendTask::SetArguments(s32 handle, s64 size, htcs::MessageFlag flags) { + /* Check that we're valid. */ + R_UNLESS(this->IsValid(), htcs::ResultInvalidTask()); + + /* Set our arguments. */ + m_handle = handle; + m_size = size; + m_flags = flags; + + return ResultSuccess(); + } + + void SendTask::Complete(htcs::SocketError err, s64 size) { + /* Set our results. */ + m_err = err; + m_result_size = size; + + /* Signal our ready event. */ + m_ready_event.Signal(); + + /* Complete. */ + HtcsSignalingTask::Complete(); + } + + Result SendTask::GetResult(htcs::SocketError *out_err, s64 *out_size) const { + /* Sanity check our state. */ + AMS_ASSERT(this->GetTaskState() == htc::server::rpc::RpcTaskState::Completed); + + /* Set the output. */ + *out_err = m_err; + *out_size = m_result_size; + + return ResultSuccess(); + } + + void SendTask::Cancel(htc::server::rpc::RpcTaskCancelReason reason) { + /* Cancel the task. */ + HtcsSignalingTask::Cancel(reason); + + /* Signal our ready event. */ + m_ready_event.Signal(); + } + + Result SendTask::ProcessResponse(const char *data, size_t size) { + /* Convert the input to a packet. */ + auto *packet = reinterpret_cast(data); + + /* Complete the task. */ + this->Complete(static_cast(packet->params[0]), packet->params[1]); + + return ResultSuccess(); + } + + Result SendTask::CreateRequest(size_t *out, char *data, size_t size, u32 task_id) { + /* Create the packet. */ + auto *packet = reinterpret_cast(data); + *packet = { + .protocol = HtcsProtocol, + .version = this->GetVersion(), + .category = HtcsPacketCategory::Request, + .type = HtcsPacketType::SendLarge, + .body_size = 0, + .task_id = task_id, + .params = { + m_handle, + m_size, + static_cast(m_flags), + GetSendDataChannelId(task_id), + }, + }; + + /* Set the output size. */ + *out = sizeof(*packet); + + return ResultSuccess(); + } + + Result SendTask::ProcessNotification(const char *data, size_t size) { + this->NotifyDataChannelReady(); + return ResultSuccess(); + } + + Result SendTask::CreateNotification(size_t *out, char *data, size_t size, u32 task_id) { + /* Create the packet. */ + auto *packet = reinterpret_cast(data); + *packet = { + .protocol = HtcsProtocol, + .version = this->GetVersion(), + .category = HtcsPacketCategory::Notification, + .type = HtcsPacketType::SendLarge, + .body_size = 0, + .task_id = task_id, + .params = { + /* ... */ + }, + }; + + /* Set the output size. */ + *out = sizeof(*packet); + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_shutdown_task.cpp b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_shutdown_task.cpp new file mode 100644 index 000000000..c7c5d9a40 --- /dev/null +++ b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_shutdown_task.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcs_rpc_tasks.hpp" + +namespace ams::htcs::impl::rpc { + + Result ShutdownTask::SetArguments(s32 handle, ShutdownType how) { + /* Set our arguments. */ + m_handle = handle; + m_how = how; + + return ResultSuccess(); + } + + void ShutdownTask::Complete(htcs::SocketError err) { + /* Set our results. */ + m_err = err; + + /* Complete. */ + HtcsTask::Complete(); + } + + Result ShutdownTask::GetResult(htcs::SocketError *out_err) const { + /* Sanity check our state. */ + AMS_ASSERT(this->GetTaskState() == htc::server::rpc::RpcTaskState::Completed); + + /* Set the output. */ + *out_err = m_err; + + return ResultSuccess(); + } + + Result ShutdownTask::ProcessResponse(const char *data, size_t size) { + /* Convert the input to a packet. */ + auto *packet = reinterpret_cast(data); + + /* Complete the task. */ + this->Complete(static_cast(packet->params[0])); + + return ResultSuccess(); + } + + Result ShutdownTask::CreateRequest(size_t *out, char *data, size_t size, u32 task_id) { + /* Create the packet. */ + auto *packet = reinterpret_cast(data); + *packet = { + .protocol = HtcsProtocol, + .version = this->GetVersion(), + .category = HtcsPacketCategory::Request, + .type = HtcsPacketType::Shutdown, + .body_size = 0, + .task_id = task_id, + .params = { + m_handle, + static_cast(m_how), + }, + }; + + /* Set the output size. */ + *out = sizeof(*packet); + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_signaling_task.cpp b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_signaling_task.cpp new file mode 100644 index 000000000..d1fde1067 --- /dev/null +++ b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_signaling_task.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcs_rpc_tasks.hpp" + +namespace ams::htcs::impl::rpc { + + namespace { + + constexpr int MaxEventCount = 0x22; + + constinit os::SdkMutex g_event_count_mutex; + constinit int g_event_count = 0; + + } + + HtcsSignalingTask::HtcsSignalingTask(HtcsTaskType type) : HtcsTask(type), m_is_valid(false) { + /* Acquire the exclusive right to create an event. */ + std::scoped_lock lk(g_event_count_mutex); + + /* Create an event. */ + if (AMS_LIKELY(g_event_count < MaxEventCount)) { + /* Make the event. */ + R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(m_system_event), os::EventClearMode_ManualClear, true)); + + /* Increment the event count. */ + ++g_event_count; + + /* Mark ourselves as valid. */ + m_is_valid = true; + } + } + + HtcsSignalingTask::~HtcsSignalingTask() { + /* If we have an event, we need to destroy it. */ + if (AMS_LIKELY(m_is_valid)) { + /* Acquire exclusive access to the event count. */ + std::scoped_lock lk(g_event_count_mutex); + + /* Destroy our event. */ + os::DestroySystemEvent(std::addressof(m_system_event)); + + /* Decrement the event count. */ + if ((--g_event_count) < 0) { + g_event_count = 0; + } + } + } + +} diff --git a/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_socket_task.cpp b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_socket_task.cpp new file mode 100644 index 000000000..828fe0ee4 --- /dev/null +++ b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_socket_task.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcs_rpc_tasks.hpp" + +namespace ams::htcs::impl::rpc { + + namespace { + + constinit s16 g_protocol_version = HtcsMaxVersion; + + } + + HtcsTask::HtcsTask(HtcsTaskType type) : m_task_type(type), m_version(g_protocol_version) { + /* ... */ + } + + Result SocketTask::SetArguments() { + return ResultSuccess(); + } + + void SocketTask::Complete(htcs::SocketError err, s32 desc) { + /* Set our results. */ + m_err = err; + m_desc = desc; + + /* Complete. */ + HtcsTask::Complete(); + } + + Result SocketTask::GetResult(htcs::SocketError *out_err, s32 *out_desc) const { + /* Sanity check our state. */ + AMS_ASSERT(this->GetTaskState() == htc::server::rpc::RpcTaskState::Completed); + + /* Set the output. */ + *out_err = m_err; + *out_desc = m_desc; + + return ResultSuccess(); + } + + Result SocketTask::ProcessResponse(const char *data, size_t size) { + /* Convert the input to a packet. */ + auto *packet = reinterpret_cast(data); + + /* Update the global protocol version. */ + g_protocol_version = std::min(g_protocol_version, packet->version); + + /* Complete the task. */ + this->Complete(static_cast(packet->params[0]), packet->params[1]); + + return ResultSuccess(); + } + + Result SocketTask::CreateRequest(size_t *out, char *data, size_t size, u32 task_id) { + /* Create the packet. */ + auto *packet = reinterpret_cast(data); + *packet = { + .protocol = HtcsProtocol, + .version = 3, + .category = HtcsPacketCategory::Request, + .type = HtcsPacketType::Socket, + .body_size = 0, + .task_id = task_id, + .params = { + /* ... */ + }, + }; + + /* Set the output size. */ + *out = sizeof(*packet); + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_tasks.hpp b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_tasks.hpp index 582b62858..e4c388189 100644 --- a/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_tasks.hpp +++ b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_rpc_tasks.hpp @@ -35,14 +35,71 @@ namespace ams::htcs::impl::rpc { Select = 12, }; - constexpr inline const s16 ProtocolVersion = 4; + constexpr inline s16 HtcsProtocol = 5; + constexpr inline const s16 HtcsMaxVersion = 4; + + enum class HtcsPacketCategory : s16 { + Request = 0, + Response = 1, + Notification = 2, + }; + + enum class HtcsPacketType : s16 { + Receive = 32, + Send = 33, + Shutdown = 34, + Close = 35, + Connect = 36, + Listen = 37, + Accept = 38, + Socket = 39, + Bind = 40, + Fcntl = 41, + ReceiveLarge = 42, + SendLarge = 43, + Select = 44, + }; + + struct HtcsRpcPacket { + s16 protocol; + s16 version; + HtcsPacketCategory category; + HtcsPacketType type; + s64 body_size; + u32 task_id; + s64 params[5]; + char data[]; + }; + static_assert(sizeof(HtcsRpcPacket) == 0x40); + + constexpr inline u16 ReceiveDataChannelIdBegin = htc::server::rpc::MaxRpcCount; + constexpr inline u16 ReceiveDataChannelIdEnd = ReceiveDataChannelIdBegin + htc::server::rpc::MaxRpcCount; + static_assert(ReceiveDataChannelIdEnd - ReceiveDataChannelIdBegin == htc::server::rpc::MaxRpcCount); + + constexpr inline u16 SendDataChannelIdBegin = ReceiveDataChannelIdEnd; + constexpr inline u16 SendDataChannelIdEnd = SendDataChannelIdBegin + htc::server::rpc::MaxRpcCount; + static_assert(SendDataChannelIdEnd - SendDataChannelIdBegin == htc::server::rpc::MaxRpcCount); + + constexpr inline u16 GetReceiveDataChannelId(u32 task_id) { + const u16 channel_id = task_id + ReceiveDataChannelIdBegin; + AMS_ASSERT(ReceiveDataChannelIdBegin <= channel_id && channel_id < ReceiveDataChannelIdEnd); + + return channel_id; + } + + constexpr inline u16 GetSendDataChannelId(u32 task_id) { + const u16 channel_id = task_id + SendDataChannelIdBegin; + AMS_ASSERT(SendDataChannelIdBegin <= channel_id && channel_id < SendDataChannelIdEnd); + + return channel_id; + } class HtcsTask : public htc::server::rpc::Task { private: HtcsTaskType m_task_type; s16 m_version; public: - HtcsTask(HtcsTaskType type) : m_task_type(type), m_version(ProtocolVersion) { /* ... */ } + HtcsTask(HtcsTaskType type); /* Defined in socket_task.cpp, for namespacing reasons. */ HtcsTaskType GetTaskType() const { return m_task_type; } s16 GetVersion() const { return m_version; } @@ -116,7 +173,7 @@ namespace ams::htcs::impl::rpc { s32 m_handle; s64 m_size; htcs::MessageFlag m_flags; - void *m_buffer; + const void *m_buffer; s64 m_buffer_size; htcs::SocketError m_err; s64 m_result_size; @@ -126,7 +183,7 @@ namespace ams::htcs::impl::rpc { s32 GetHandle() const { return m_handle; } s64 GetSize() const { return m_size; } htcs::MessageFlag GetFlags() const { return m_flags; } - void *GetBuffer() const { return m_buffer; } + const void *GetBuffer() const { return m_buffer; } s64 GetBufferSize() const { return m_buffer_size; } void SetBuffer(const void *buffer, s64 buffer_size); @@ -305,7 +362,7 @@ namespace ams::htcs::impl::rpc { s32 GetValue() const { return m_value; } public: Result SetArguments(s32 handle, s32 command, s32 value); - void Complete(htcs::SocketError err); + void Complete(htcs::SocketError err, s32 res); Result GetResult(htcs::SocketError *out_err, s32 *out_res) const; public: virtual Result ProcessResponse(const char *data, size_t size) override; diff --git a/libraries/libvapours/include/vapours/results/htcs_results.hpp b/libraries/libvapours/include/vapours/results/htcs_results.hpp index 3b3bc0a5d..216fe80ed 100644 --- a/libraries/libvapours/include/vapours/results/htcs_results.hpp +++ b/libraries/libvapours/include/vapours/results/htcs_results.hpp @@ -22,9 +22,11 @@ namespace ams::htcs { R_DEFINE_ERROR_RESULT(InvalidHandle, 9); - R_DEFINE_ERROR_RESULT(InvalidArgument, 2001); - R_DEFINE_ERROR_RESULT(InvalidSize, 2014); - R_DEFINE_ERROR_RESULT(Cancelled, 2021); - R_DEFINE_ERROR_RESULT(Completed, 2023); + R_DEFINE_ERROR_RESULT(InvalidArgument, 2001); + R_DEFINE_ERROR_RESULT(InvalidServerHandle, 2003); + R_DEFINE_ERROR_RESULT(InvalidSize, 2014); + R_DEFINE_ERROR_RESULT(Cancelled, 2021); + R_DEFINE_ERROR_RESULT(Completed, 2023); + R_DEFINE_ERROR_RESULT(InvalidTask, 2103); } From ec643789aba7386851cb26080d6f75ec8f3a8912 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 19 Feb 2021 01:56:04 -0800 Subject: [PATCH 077/280] htcs: implement data channel manager --- .../impl/rpc/htcs_data_channel_manager.cpp | 98 +++++++++++++++++++ .../impl/rpc/htcs_data_channel_manager.hpp | 2 +- 2 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 libraries/libstratosphere/source/htcs/impl/rpc/htcs_data_channel_manager.cpp diff --git a/libraries/libstratosphere/source/htcs/impl/rpc/htcs_data_channel_manager.cpp b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_data_channel_manager.cpp new file mode 100644 index 000000000..22d3f987f --- /dev/null +++ b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_data_channel_manager.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcs_rpc_tasks.hpp" +#include "htcs_data_channel_manager.hpp" +#include "../../../htclow/htclow_channel.hpp" + +namespace ams::htcs::impl::rpc { + + Result DataChannelManager::Receive(void *buffer, s64 buffer_size, u32 task_id) { + /* Check that the buffer size is allowable. */ + R_UNLESS(util::IsIntValueRepresentable(buffer_size), htcs::ResultInvalidSize()); + + /* Create an htclow channel. */ + htclow::Channel channel(m_htclow_manager); + + /* Open the channel. */ + R_ABORT_UNLESS(channel.Open(std::addressof(m_module), GetReceiveDataChannelId(task_id))); + + /* Ensure that we close the channel, when we're done. */ + ON_SCOPE_EXIT { channel.Close(); }; + + /* Set the channel config. */ + constexpr htclow::ChannelConfig BulkReceiveConfig = { + .flow_control_enabled = false, + .handshake_enabled = false, + .max_packet_size = 0x3E000, + }; + channel.SetConfig(BulkReceiveConfig); + + /* Set the receive buffer. */ + channel.SetReceiveBuffer(buffer, buffer_size); + + /* Connect the channel. */ + R_TRY(channel.Connect()); + + /* Ensure that we clean up when we're done. */ + ON_SCOPE_EXIT { channel.Shutdown(); }; + + /* Notify the receive task. */ + R_TRY(m_rpc_client->Notify(task_id)); + + /* Wait to receive the data. */ + R_TRY(channel.WaitReceive(buffer_size)); + + return ResultSuccess(); + } + + Result DataChannelManager::Send(const void *buffer, s64 buffer_size, u32 task_id) { + /* Check that the buffer size is allowable. */ + R_UNLESS(util::IsIntValueRepresentable(buffer_size), htcs::ResultInvalidSize()); + + /* Create an htclow channel. */ + htclow::Channel channel(m_htclow_manager); + + /* Open the channel. */ + R_ABORT_UNLESS(channel.Open(std::addressof(m_module), GetSendDataChannelId(task_id))); + + /* Ensure that we close the channel, when we're done. */ + ON_SCOPE_EXIT { channel.Close(); }; + + /* Set the channel config. */ + constexpr htclow::ChannelConfig BulkSendConfig = { + .flow_control_enabled = false, + .handshake_enabled = false, + .max_packet_size = 0x3E000, + }; + channel.SetConfig(BulkSendConfig); + + /* Set the send buffer. */ + channel.SetSendBufferWithData(buffer, buffer_size); + + /* Connect the channel. */ + R_TRY(channel.Connect()); + + /* Ensure that we clean up when we're done. */ + ON_SCOPE_EXIT { channel.Shutdown(); }; + + /* Wait to send the data. */ + R_TRY(channel.Flush()); + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/htcs/impl/rpc/htcs_data_channel_manager.hpp b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_data_channel_manager.hpp index aaac64d48..af4980efb 100644 --- a/libraries/libstratosphere/source/htcs/impl/rpc/htcs_data_channel_manager.hpp +++ b/libraries/libstratosphere/source/htcs/impl/rpc/htcs_data_channel_manager.hpp @@ -28,8 +28,8 @@ namespace ams::htcs::impl::rpc { public: DataChannelManager(htc::server::rpc::RpcClient *client, htclow::HtclowManager *htclow_manager) : m_rpc_client(client), m_htclow_manager(htclow_manager), m_module(htclow::ModuleId::Htcs) { /* ... */ } public: - Result Send(const void *buffer, s64 buffer_size, u32 task_id); Result Receive(void *buffer, s64 buffer_size, u32 task_id); + Result Send(const void *buffer, s64 buffer_size, u32 task_id); }; } From f7fcb54622923fcd3fd60f34d23c7a352ff67ca1 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 19 Feb 2021 13:03:48 -0800 Subject: [PATCH 078/280] htcs: implement virtual socket collection --- .../include/stratosphere/htcs.hpp | 2 + .../include/stratosphere/htcs/htcs_api.hpp | 36 + .../include/stratosphere/htcs/htcs_socket.hpp | 46 + .../include/stratosphere/htcs/htcs_types.hpp | 2 + .../htc/server/rpc/htc_rpc_task_table.hpp | 2 +- .../source/htcs/client/htcs_session.hpp | 24 + .../client/htcs_virtual_socket_collection.cpp | 834 ++++++++++++++++++ .../client/htcs_virtual_socket_collection.hpp | 81 ++ .../source/htcs/htcs_socket.cpp | 178 ++++ 9 files changed, 1204 insertions(+), 1 deletion(-) create mode 100644 libraries/libstratosphere/include/stratosphere/htcs/htcs_api.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/htcs/htcs_socket.hpp create mode 100644 libraries/libstratosphere/source/htcs/client/htcs_session.hpp create mode 100644 libraries/libstratosphere/source/htcs/client/htcs_virtual_socket_collection.cpp create mode 100644 libraries/libstratosphere/source/htcs/client/htcs_virtual_socket_collection.hpp create mode 100644 libraries/libstratosphere/source/htcs/htcs_socket.cpp diff --git a/libraries/libstratosphere/include/stratosphere/htcs.hpp b/libraries/libstratosphere/include/stratosphere/htcs.hpp index cda430983..0f88fdda6 100644 --- a/libraries/libstratosphere/include/stratosphere/htcs.hpp +++ b/libraries/libstratosphere/include/stratosphere/htcs.hpp @@ -16,6 +16,8 @@ #pragma once #include +#include +#include #include #include #include diff --git a/libraries/libstratosphere/include/stratosphere/htcs/htcs_api.hpp b/libraries/libstratosphere/include/stratosphere/htcs/htcs_api.hpp new file mode 100644 index 000000000..3673fd0f2 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htcs/htcs_api.hpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::htcs { + + bool IsInitialized(); + + size_t GetWorkingMemorySize(int max_socket_count); + + void Initialize(AllocateFunction allocate, DeallocateFunction deallocate, int num_sessions = SessionCountMax); + void Initialize(void *buffer, size_t buffer_size); + + void InitializeForDisableDisconnectionEmulation(AllocateFunction allocate, DeallocateFunction deallocate, int num_sessions = SessionCountMax); + void InitializeForDisableDisconnectionEmulation(void *buffer, size_t buffer_size); + + void InitializeForSystem(void *buffer, size_t buffer_size, int num_sessions); + + void Finalize(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/htcs/htcs_socket.hpp b/libraries/libstratosphere/include/stratosphere/htcs/htcs_socket.hpp new file mode 100644 index 000000000..3931d1b2b --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htcs/htcs_socket.hpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::htcs { + + const HtcsPeerName GetPeerNameAny(); + const HtcsPeerName GetDefaultHostName(); + + s32 GetLastError(); + + s32 Socket(); + s32 Close(s32 desc); + s32 Connect(s32 desc, const SockAddrHtcs *address); + s32 Bind(s32 desc, const SockAddrHtcs *address); + s32 Listen(s32 desc, s32 backlog_count); + s32 Accept(s32 desc, SockAddrHtcs *address); + s32 Shutdown(s32 desc, s32 how); + s32 Fcntl(s32 desc, s32 command, s32 value); + + s32 Select(s32 count, FdSet *read, FdSet *write, FdSet *exception, TimeVal *timeout); + + ssize_t Recv(s32 desc, void *buffer, size_t buffer_size, s32 flags); + ssize_t Send(s32 desc, const void *buffer, size_t buffer_size, s32 flags); + + void FdSetZero(FdSet *set); + void FdSetSet(s32 fd, FdSet *set); + void FdSetClr(s32 fd, FdSet *set); + bool FdSetIsSet(s32 fd, const FdSet *set); + +} diff --git a/libraries/libstratosphere/include/stratosphere/htcs/htcs_types.hpp b/libraries/libstratosphere/include/stratosphere/htcs/htcs_types.hpp index 03cb7b893..b5f3708b6 100644 --- a/libraries/libstratosphere/include/stratosphere/htcs/htcs_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/htcs/htcs_types.hpp @@ -24,6 +24,8 @@ namespace ams::htcs { constexpr inline int PeerNameBufferLength = 32; constexpr inline int PortNameBufferLength = 32; + constexpr inline int SessionCountMax = 0x10; + constexpr inline int SocketCountMax = 40; constexpr inline int FdSetSize = SocketCountMax; diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_table.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_table.hpp index 91f472e33..dbc006fcb 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_table.hpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_table.hpp @@ -33,7 +33,7 @@ namespace ams::htc::server::rpc { private: /* htcs::ReceiveSmallTask/htcs::ReceiveSendTask are the largest tasks, containing an inline 0xE000 buffer. */ /* We allow for ~0x100 task overhead from the additional events those contain. */ - /* NOTE: Nintnedo hardcodes a maximum size of 0xE1D8, despite SendSmallTask being 0xE098 as of latest check. */ + /* NOTE: Nintendo hardcodes a maximum size of 0xE1D8, despite SendSmallTask being 0xE098 as of latest check. */ static constexpr size_t MaxTaskSize = 0xE100; using TaskStorage = typename std::aligned_storage::type; private: diff --git a/libraries/libstratosphere/source/htcs/client/htcs_session.hpp b/libraries/libstratosphere/source/htcs/client/htcs_session.hpp new file mode 100644 index 000000000..bbd088e06 --- /dev/null +++ b/libraries/libstratosphere/source/htcs/client/htcs_session.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htcs::client { + + void InitializeSessionManager(tma::IHtcsManager **out_manager, tma::IHtcsManager **out_monitor); + void FinalizeSessionManager(); + +} diff --git a/libraries/libstratosphere/source/htcs/client/htcs_virtual_socket_collection.cpp b/libraries/libstratosphere/source/htcs/client/htcs_virtual_socket_collection.cpp new file mode 100644 index 000000000..0c6794dd7 --- /dev/null +++ b/libraries/libstratosphere/source/htcs/client/htcs_virtual_socket_collection.cpp @@ -0,0 +1,834 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcs_session.hpp" +#include "htcs_virtual_socket_collection.hpp" + +namespace ams::htcs::client { + + namespace { + + constexpr inline s32 InvalidSocket = -1; + constexpr inline s32 InvalidPrimitive = -1; + + } + + /* Declare client functions. */ + sf::SharedPointer socket(s32 &last_error); + s32 close(sf::SharedPointer socket, s32 &last_error); + s32 bind(sf::SharedPointer socket, const htcs::SockAddrHtcs *address, s32 &last_error); + s32 listen(sf::SharedPointer socket, s32 backlog_count, s32 &last_error); + sf::SharedPointer accept(sf::SharedPointer socket, htcs::SockAddrHtcs *address, s32 &last_error); + s32 fcntl(sf::SharedPointer socket, s32 command, s32 value, s32 &last_error); + + s32 shutdown(sf::SharedPointer socket, s32 how, s32 &last_error); + + ssize_t recv(sf::SharedPointer socket, void *buffer, size_t buffer_size, s32 flags, s32 &last_error); + ssize_t send(sf::SharedPointer socket, const void *buffer, size_t buffer_size, s32 flags, s32 &last_error); + + s32 connect(sf::SharedPointer socket, const htcs::SockAddrHtcs *address, s32 &last_error); + + s32 select(s32 * const read, s32 &num_read, s32 * const write, s32 &num_write, s32 * const except, s32 &num_except, htcs::TimeVal *timeout, s32 &last_error); + + struct VirtualSocket { + s32 m_id; + s32 m_primitive; + sf::SharedPointer m_socket; + bool m_do_bind; + htcs::SockAddrHtcs m_address; + s32 m_listen_backlog_count; + s32 m_fcntl_command; + s32 m_fcntl_value; + bool m_blocking; + + + VirtualSocket() { + /* Initialize. */ + this->Init(); + } + + ~VirtualSocket() { /* ... */ } + + void Init() { + /* Setup fields. */ + m_id = InvalidSocket; + m_primitive = InvalidPrimitive; + m_socket = nullptr; + m_blocking = true; + m_do_bind = false; + + std::memset(std::addressof(m_address), 0, sizeof(m_address)); + + m_listen_backlog_count = -1; + + m_fcntl_command = -1; + m_fcntl_value = 0; + } + + s32 Bind(const htcs::SockAddrHtcs *address, s32 &last_error) { + /* Mark the bind. */ + m_do_bind = true; + + /* Set our address. */ + std::memcpy(std::addressof(m_address), address, sizeof(m_address)); + + /* Clear the error. */ + last_error = 0; + return 0; + } + + s32 Listen(s32 backlog_count, s32 &last_error) { + s32 res = -1; + if (m_do_bind) { + /* Set backlog count. */ + m_listen_backlog_count = std::max(backlog_count, 1); + + /* Clear error. */ + last_error = 0; + res = 0; + } else { + last_error = HTCS_EINVAL; + } + return res; + } + + s32 Fcntl(s32 command, s32 value, s32 &last_error) { + /* Clear error. */ + s32 res = 0; + last_error = 0; + + if (command == HTCS_F_SETFL) { + m_fcntl_command = command; + m_fcntl_value = value; + + m_blocking = (value & HTCS_O_NONBLOCK) == 0; + } else if (command == HTCS_F_GETFL) { + res = m_fcntl_value; + } else { + last_error = HTCS_EINVAL; + res = -1; + } + + return res; + } + + s32 SetSocket(sf::SharedPointer socket, s32 &last_error) { + s32 res = 0; + + if (m_socket == nullptr && socket != nullptr) { + /* Set our socket. */ + m_socket = socket; + + /* Bind, fcntl, and listen, since those may have been deferred. */ + if (m_do_bind) { + res = bind(m_socket, std::addressof(m_address), last_error); + } + + if (res == 0 && m_fcntl_command != -1) { + res = fcntl(m_socket, m_fcntl_command, m_fcntl_value, last_error); + } + + if (res == 0 && m_listen_backlog_count > 0) { + res = listen(m_socket, m_listen_backlog_count, last_error); + } + } + + return res; + } + }; + + VirtualSocketCollection::VirtualSocketCollection() + : m_socket_list(nullptr), + m_list_count(0), + m_list_size(0), + m_next_id(1), + m_mutex() + { + /* ... */ + } + + VirtualSocketCollection::~VirtualSocketCollection() { + /* Clear ourselves. */ + this->Clear(); + + /* Destroy all sockets in our list. */ + for (auto i = 0; i < m_list_size; ++i) { + std::destroy_at(m_socket_list + i); + } + + /* Clear the backing memory for our socket list. */ + std::memset(m_buffer, 0, sizeof(VirtualSocket) * m_list_size); + } + + size_t VirtualSocketCollection::GetWorkingMemorySize(int num_sockets) { + AMS_ASSERT(num_sockets < htcs::SocketCountMax); + return num_sockets * sizeof(VirtualSocket); + } + + void VirtualSocketCollection::Init(void *buffer, size_t buffer_size) { + /* Set our buffer. */ + m_buffer = buffer; + m_buffer_size = buffer_size; + + /* Configure our list. */ + m_list_size = static_cast(m_buffer_size / sizeof(VirtualSocket)); + m_list_size = std::min(m_list_size, htcs::SocketCountMax); + + /* Initialize our list. */ + m_socket_list = static_cast(m_buffer); + for (auto i = 0; i < m_list_size; ++i) { + std::construct_at(m_socket_list + i); + } + } + + void VirtualSocketCollection::Clear() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Clear our list. */ + m_list_count = 0; + } + + s32 VirtualSocketCollection::Socket(s32 &error_code) { + /* Clear error code. */ + error_code = 0; + + /* Create the socket. */ + sf::SharedPointer socket(nullptr); + return this->CreateSocket(sf::SharedPointer{nullptr}, error_code); + } + + s32 VirtualSocketCollection::Close(s32 id, s32 &error_code) { + /* Clear error code. */ + error_code = 0; + + /* Prepare to find the socket. */ + s32 res = 0; + sf::SharedPointer socket = nullptr; + + /* Find the socket. */ + { + std::scoped_lock lk(m_mutex); + + if (auto index = this->Find(id, std::addressof(error_code)); index >= 0) { + /* Get the socket's object. */ + VirtualSocket *virt_socket = m_socket_list + index; + socket = virt_socket->m_socket; + + /* Move the list. */ + for (/* ... */; index < m_list_count - 1; ++index) { + m_socket_list[index] = m_socket_list[index + 1]; + } + + /* Clear the now unused last list entry. */ + m_socket_list[index].Init(); + + /* Decrement our list count. */ + --m_list_count; + } + } + + /* If we found the socket, close it. */ + if (socket != nullptr) { + close(socket, error_code); + + /* Clear the error code. */ + res = 0; + error_code = 0; + } + + return index >= 0 ? res : -1; + } + + s32 VirtualSocketCollection::Bind(s32 id, const htcs::SockAddrHtcs *address, s32 &error_code) { + /* Setup result/error code. */ + s32 res = -1; + error_code = 0; + + /* Get the socket. */ + sf::SharedPointer socket = this->GetSocket(id, std::addressof(error_code)); + + /* If we found the socket, bind. */ + if (socket != nullptr) { + res = bind(socket, address, error_code); + } else if (error_code != HTCS_EBADF) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Check if the socket is already bound. */ + if (const auto index = this->Find(id); index >= 0) { + const auto exists = this->HasAddr(address); + + if (m_socket_list[index].m_do_bind) { + error_code = HTCS_EINVAL; + res = -1; + } else if (exists) { + error_code = HTCS_EADDRINUSE; + res = -1; + } else { + res = m_socket_list[index].Bind(address, error_code); + } + } else { + error_code = HTCS_EBADF; + } + } + + return res; + } + s32 VirtualSocketCollection::Listen(s32 id, s32 backlog_count, s32 &error_code) { + /* Setup result/error code. */ + s32 res = -1; + error_code = 0; + + /* Get the socket. */ + sf::SharedPointer socket = this->GetSocket(id, std::addressof(error_code)); + + /* If we found the socket, bind. */ + if (socket != nullptr) { + res = listen(socket, backlog_count, error_code); + } else if (error_code != HTCS_EBADF) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Try to listen on the virtual socket. */ + if (const auto index = this->Find(id); index >= 0) { + res = m_socket_list[index].Listen(backlog_count, error_code); + } + } + + return res; + } + + s32 VirtualSocketCollection::Accept(s32 id, htcs::SockAddrHtcs *address, s32 &error_code) { + /* Setup result/error code. */ + s32 res = -1; + error_code = 0; + + /* Declare socket that we're creating. */ + sf::SharedPointer new_socket = nullptr; + + /* Get the socket. */ + sf::SharedPointer socket = this->GetSocket(id, std::addressof(error_code)); + + /* If we found the socket, bind. */ + if (socket != nullptr) { + if (error_code != HTCS_ENONE) { + return -1; + } + + new_socket = this->DoAccept(socket, id, address, error_code); + } else if (error_code != HTCS_EBADF) { + /* Fetch the socket. */ + socket = this->FetchSocket(id, error_code); + + /* Wait for the socket. */ + while (socket == nullptr) { + /* Determine whether we should block/listen. */ + bool block_until_done = true; + bool listened = false; + + s32 index; + { + std::scoped_lock lk(m_mutex); + if (index = this->Find(id, std::addressof(error_code)); index >= 0) { + block_until_done = m_socket_list[index].m_blocking; + listened = m_socket_list[index].m_listen_backlog_count > 0; + } + } + + /* Check that the socket exists. */ + if (index < 0) { + error_code = HTCS_EINTR; + return -1; + } + + /* Check that the socket has been listened. */ + if (!listened) { + error_code = HTCS_EINVAL; + return -1; + } + + /* Check that we should block. */ + if (!block_until_done) { + error_code = HTCS_EWOULDBLOCK; + return -1; + } + + /* Wait before trying again. */ + os::SleepThread(TimeSpan::FromMilliSeconds(500)); + + /* Fetch the potentially updated socket. */ + socket = this->FetchSocket(id, error_code); + } + + /* Check that we haven't errored. */ + if (error_code != HTCS_ENONE) { + return -1; + } + + /* Do the accept. */ + new_socket = this->DoAccept(socket, id, address, error_code); + } + + /* If we have a new socket, register it. */ + if (new_socket != 0) { + res = this->CreateSocket(new_socket, error_code); + if (res < 0) { + s32 tmp_error_code; + close(new_socket, tmp_error_code); + } + } + + return res; + } + + s32 VirtualSocketCollection::Fcntl(s32 id, s32 command, s32 value, s32 &error_code) { + /* Setup result/error code. */ + s32 res = -1; + error_code = 0; + + /* Get the socket. */ + sf::SharedPointer socket = this->GetSocket(id, std::addressof(error_code)); + + /* If we found the socket, bind. */ + if (socket != nullptr) { + res = fcntl(socket, command, value, error_code); + } else if (error_code != HTCS_EBADF) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Try to listen on the virtual socket. */ + if (const auto index = this->Find(id); index >= 0) { + res = m_socket_list[index].Fcntl(command, value, error_code); + } + } + + return res; + } + + s32 VirtualSocketCollection::Shutdown(s32 id, s32 how, s32 &error_code) { + /* Setup result/error code. */ + s32 res = -1; + error_code = 0; + + /* Get the socket. */ + sf::SharedPointer socket = this->GetSocket(id, std::addressof(error_code)); + + /* If we found the socket, bind. */ + if (socket != nullptr) { + res = shutdown(socket, how, error_code); + } else if (error_code != HTCS_EBADF) { + error_code = HTCS_ENOTCONN; + } + + return res; + } + + ssize_t VirtualSocketCollection::Recv(s32 id, void *buffer, size_t buffer_size, s32 flags, s32 &error_code) { + /* Setup result/error code. */ + ssize_t res = -1; + error_code = 0; + + /* Fetch the socket. */ + sf::SharedPointer socket = this->FetchSocket(id, error_code); + + /* If we found the socket, bind. */ + if (socket != nullptr) { + if (error_code != HTCS_ENONE) { + return -1; + } + res = recv(socket, buffer, buffer_size, flags, error_code); + } else if (error_code != HTCS_EBADF) { + error_code = HTCS_ENOTCONN; + } + + return res; + } + + ssize_t VirtualSocketCollection::Send(s32 id, const void *buffer, size_t buffer_size, s32 flags, s32 &error_code) { + /* Setup result/error code. */ + ssize_t res = -1; + error_code = 0; + + /* Fetch the socket. */ + sf::SharedPointer socket = this->FetchSocket(id, error_code); + + /* If we found the socket, bind. */ + if (socket != nullptr) { + if (error_code != HTCS_ENONE) { + return -1; + } + res = send(socket, buffer, buffer_size, flags, error_code); + } else if (error_code != HTCS_EBADF) { + error_code = HTCS_ENOTCONN; + } + + return res; + } + + s32 VirtualSocketCollection::Connect(s32 id, const htcs::SockAddrHtcs *address, s32 &error_code) { + /* Setup result/error code. */ + s32 res = -1; + error_code = 0; + + /* Fetch the socket. */ + sf::SharedPointer socket = this->FetchSocket(id, error_code); + + /* If we found the socket, bind. */ + if (socket != nullptr) { + if (error_code != HTCS_ENONE) { + return -1; + } + res = connect(socket, address, error_code); + } else if (error_code != HTCS_EBADF) { + error_code = HTCS_EADDRNOTAVAIL; + } + + return res; + } + + s32 VirtualSocketCollection::Select(htcs::FdSet *read, htcs::FdSet *write, htcs::FdSet *except, htcs::TimeVal *timeout, s32 &error_code) { + /* Setup result/error code. */ + s32 res = -1; + s32 tmp_error_code = 0; + + /* Declare buffers. */ + s32 read_primitives[SocketCountMax]; + s32 write_primitives[SocketCountMax]; + s32 except_primitives[SocketCountMax]; + + /* Get reads. */ + s32 num_read = this->GetSockets(read_primitives, read, tmp_error_code); + if (tmp_error_code != HTCS_ENONE) { + error_code = tmp_error_code; + return res; + } + + /* Get writes. */ + s32 num_write = this->GetSockets(write_primitives, write, tmp_error_code); + if (tmp_error_code != HTCS_ENONE) { + error_code = tmp_error_code; + return res; + } + + /* Get excepts. */ + s32 num_except = this->GetSockets(except_primitives, except, tmp_error_code); + if (tmp_error_code != HTCS_ENONE) { + error_code = tmp_error_code; + return res; + } + + /* Perform the select. */ + if (num_read + num_write + num_except > 0) { + res = select(read_primitives, num_read, write_primitives, num_write, except_primitives, num_except, timeout, error_code); + + /* Set the socket primitives. */ + this->SetSockets(read, read_primitives, num_read); + this->SetSockets(write, write_primitives, num_write); + this->SetSockets(except, except_primitives, num_except); + } else { + error_code = HTCS_EINVAL; + } + + return res; + } + + s32 VirtualSocketCollection::CreateId() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Get a free id. */ + s32 res = 0; + do { + res = m_next_id++; + if (m_next_id <= 0) { + m_next_id = 1; + } + } while (this->Find(res) >= 0); + + return res; + } + + s32 VirtualSocketCollection::Add(sf::SharedPointer socket) { + /* Check that the socket isn't null. */ + if (socket == nullptr) { + return -1; + } + + /* Create the socket. */ + s32 error_code; + return this->CreateSocket(socket, error_code); + } + + void VirtualSocketCollection::Insert(s32 id, sf::SharedPointer socket) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Add the socket to the list. */ + if (m_list_count != 0) { + /* Ensure the list remains in sorder order. */ + s32 index; + for (index = m_list_count - 1; index >= 0; --index) { + if (m_socket_list[index].m_id < id) { + break; + } + m_socket_list[index + 1] = m_socket_list[index]; + } + + /* Set the socket in the list. */ + m_socket_list[index + 1].m_id = id; + m_socket_list[index + 1].m_socket = socket; + } else { + /* Set the socket in the list. */ + m_socket_list[0].m_id = id; + m_socket_list[0].m_socket = socket; + } + + /* Increment our count. */ + ++m_list_count; + } + + void VirtualSocketCollection::SetSize(s32 size) { + /* ... */ + } + + s32 VirtualSocketCollection::Find(s32 id, s32 *error_code) { + /* Perform a binary search to find the socket. */ + if (m_list_count > 0) { + s32 left = 0; + s32 right = m_list_count - 1; + while (left <= right) { + const s32 mid = (left + right) / 2; + if (m_socket_list[mid].m_id == id) { + return mid; + } else if (m_socket_list[mid].m_id > id) { + right = mid - 1; + } else /* if (m_socket_list[mid].m_id < id) */ { + left = mid + 1; + } + } + } + + /* We failed to find the socket. */ + if (error_code != nullptr) { + *error_code = HTCS_EBADF; + } + + return InvalidSocket; + } + + s32 VirtualSocketCollection::FindByPrimitive(s32 primitive) { + /* Find a socket with the desired primitive. */ + for (auto i = 0; i < m_list_size; ++i) { + if (m_socket_list[i].m_primitive == primitive) { + return i; + } + } + + return InvalidPrimitive; + } + + bool VirtualSocketCollection::HasAddr(const htcs::SockAddrHtcs *address) { + /* Try to find a matching socket. */ + for (auto i = 0; i < m_list_count; ++i) { + if (m_socket_list[i].m_address.family == address->family && + std::strcmp(m_socket_list[i].m_address.peer_name.name, address->peer_name.name) == 0 && + std::strcmp(m_socket_list[i].m_address.port_name.name, address->port_name.name) == 0) + { + return true; + } + } + return false; + } + + sf::SharedPointer VirtualSocketCollection::GetSocket(s32 id, s32 *error_code) { + sf::SharedPointer res = nullptr; + + /* Get the socket. */ + { + std::scoped_lock lk(m_mutex); + + if (const auto index = this->Find(id, error_code); index >= 0) { + res = m_socket_list[index].m_socket; + } + } + + return res; + } + + sf::SharedPointer VirtualSocketCollection::FetchSocket(s32 id, s32 &error_code) { + /* Clear the error code. */ + error_code = 0; + + /* Get the socket. */ + auto socket = this->GetSocket(id, std::addressof(error_code)); + if (socket == nullptr && error_code == HTCS_ENONE) { + socket = this->RealizeSocket(id); + } + + return socket; + } + + sf::SharedPointer VirtualSocketCollection::RealizeSocket(s32 id) { + /* Clear the error code. */ + s32 error_code = 0; + + /* Get socket. */ + sf::SharedPointer res = socket(id); + if (res != nullptr) { + /* Assign the new socket. */ + s32 index; + { + std::scoped_lock lk(m_mutex); + + index = this->Find(id, std::addressof(error_code)); + if (index >= 0) { + m_socket_list[index].SetSocket(res, error_code); + } + } + + /* If the socket was deleted, close it. */ + if (index < 0) { + s32 temp_error = 0; + close(res, temp_error); + res = nullptr; + } + } + + return res; + } + + sf::SharedPointer VirtualSocketCollection::DoAccept(sf::SharedPointer socket, s32 id, htcs::SockAddrHtcs *address, s32 &error_code) { + /* Clear the error code. */ + error_code = 0; + + /* Try to accept. */ + sf::SharedPointer new_socket = accept(socket, address, error_code); + if (error_code == HTCS_ENETDOWN) { + new_socket = accept(socket, address, error_code); + + std::scoped_lock lk(m_mutex); + if (const auto index = this->Find(id, std::addressof(error_code)); index >= 0) { + m_socket_list[index].m_socket = nullptr; + } + } + + return new_socket; + } + + s32 VirtualSocketCollection::GetSockets(s32 * const out_primitives, htcs::FdSet *set, s32 &error_code) { + /* Clear the error code. */ + error_code = 0; + s32 count = 0; + + /* Walk the fdset. */ + if (set != nullptr) { + for (auto i = 0; i < FdSetSize; ++i) { + /* If the set no longer has fds, we're done. */ + if (set->fds[i] == 0) { + break; + } + + /* Find the fd's primitive. */ + s32 primitive = InvalidPrimitive; + s32 index; + { + std::scoped_lock lk(m_mutex); + if (index = this->Find(set->fds[i], std::addressof(error_code)); index >= 0) { + /* Get the primitive, if necessary. */ + if (m_socket_list[index].m_primitive == InvalidPrimitive && m_socket_list[index].m_socket != nullptr) { + m_socket_list[index].m_socket->GetPrimitive(std::addressof(m_socket_list[index].m_primitive)); + } + + primitive = m_socket_list[index].m_primitive; + } + } + + /* Check that an error didn't occur. */ + if (error_code != HTCS_ENONE) { + return 0; + } + + /* If the primitive is invalid, try to realize the socket. */ + if (primitive == InvalidPrimitive) { + if (this->RealizeSocket(set->fds[i]) != nullptr) { + std::scoped_lock lk(m_mutex); + + /* Get the primitive. */ + if (index = this->Find(set->fds[i], std::addressof(error_code)); index >= 0) { + m_socket_list[index].m_socket->GetPrimitive(std::addressof(m_socket_list[index].m_primitive)); + + primitive = m_socket_list[index].m_primitive; + } + } + + /* Check that an error didn't occur. */ + if (error_code != HTCS_ENONE) { + return 0; + } + } + + /* Set the output primitive. */ + if (primitive != InvalidPrimitive) { + out_primitives[count++] = primitive; + } + } + } + + return count; + } + + void VirtualSocketCollection::SetSockets(htcs::FdSet *set, s32 * const primitives, s32 count) { + if (set != nullptr) { + /* Clear the set. */ + FdSetZero(set); + + /* Copy the fds. */ + for (auto i = 0; i < count; ++i) { + std::scoped_lock lk(m_mutex); + + if (const auto index = this->FindByPrimitive(primitives[i]); index >= 0) { + set->fds[i] = m_socket_list[index].m_id; + } + } + } + } + + s32 VirtualSocketCollection::CreateSocket(sf::SharedPointer socket, s32 &error_code) { + /* Clear the error code. */ + error_code = 0; + s32 id = InvalidSocket; + + /* Check that we can add to the list. */ + if (m_list_count < m_list_size) { + /* Create a new id. */ + id = this->CreateId(); + + /* Insert the socket into the list. */ + this->Insert(id, socket); + } else { + if (socket != nullptr) { + s32 tmp_error_code; + close(socket, tmp_error_code); + } + + error_code = HTCS_EMFILE; + } + + return id; + } + +} diff --git a/libraries/libstratosphere/source/htcs/client/htcs_virtual_socket_collection.hpp b/libraries/libstratosphere/source/htcs/client/htcs_virtual_socket_collection.hpp new file mode 100644 index 000000000..67161b422 --- /dev/null +++ b/libraries/libstratosphere/source/htcs/client/htcs_virtual_socket_collection.hpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htcs::client { + + struct VirtualSocket; + + class VirtualSocketCollection { + private: + void *m_buffer; + size_t m_buffer_size; + VirtualSocket *m_socket_list; + s32 m_list_count; + s32 m_list_size; + s32 m_next_id; + os::SdkMutex m_mutex; + public: + static size_t GetWorkingMemorySize(int num_sockets); + public: + explicit VirtualSocketCollection(); + ~VirtualSocketCollection(); + public: + void Init(void *buffer, size_t buffer_size); + void Clear(); + + s32 Socket(s32 &error_code); + s32 Close(s32 id, s32 &error_code); + s32 Bind(s32 id, const htcs::SockAddrHtcs *address, s32 &error_code); + s32 Listen(s32 id, s32 backlog_count, s32 &error_code); + s32 Accept(s32 id, htcs::SockAddrHtcs *address, s32 &error_code); + s32 Fcntl(s32 id, s32 command, s32 value, s32 &error_code); + + s32 Shutdown(s32 id, s32 how, s32 &error_code); + + ssize_t Recv(s32 id, void *buffer, size_t buffer_size, s32 flags, s32 &error_code); + ssize_t Send(s32 id, const void *buffer, size_t buffer_size, s32 flags, s32 &error_code); + + s32 Connect(s32 id, const htcs::SockAddrHtcs *address, s32 &error_code); + + s32 Select(htcs::FdSet *read, htcs::FdSet *write, htcs::FdSet *except, htcs::TimeVal *timeout, s32 &error_code); + private: + s32 CreateId(); + + s32 Add(sf::SharedPointer socket); + + void Insert(s32 id, sf::SharedPointer socket); + void SetSize(s32 size); + + s32 Find(s32 id, s32 *error_code = nullptr); + s32 FindByPrimitive(s32 primitive); + + bool HasAddr(const htcs::SockAddrHtcs *address); + + sf::SharedPointer GetSocket(s32 id, s32 *error_code = nullptr); + sf::SharedPointer FetchSocket(s32 id, s32 &error_code); + sf::SharedPointer RealizeSocket(s32 id); + + sf::SharedPointer DoAccept(sf::SharedPointer socket, s32 id, htcs::SockAddrHtcs *address, s32 &error_code); + + s32 GetSockets(s32 * const out_primitives, htcs::FdSet *set, s32 &error_code); + void SetSockets(htcs::FdSet *set, s32 * const primitives, s32 count); + + s32 CreateSocket(sf::SharedPointer socket, s32 &error_code); + }; + +} diff --git a/libraries/libstratosphere/source/htcs/htcs_socket.cpp b/libraries/libstratosphere/source/htcs/htcs_socket.cpp new file mode 100644 index 000000000..5884e43fb --- /dev/null +++ b/libraries/libstratosphere/source/htcs/htcs_socket.cpp @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "client/htcs_session.hpp" +#include "client/htcs_virtual_socket_collection.hpp" + +namespace ams::htcs { + + namespace { + + constinit bool g_initialized = false; + constinit bool g_enable_disconnection_emulation = false; + + constinit AllocateFunction g_allocate_function = nullptr; + constinit DeallocateFunction g_deallocate_function = nullptr; + + constinit void *g_buffer = nullptr; + constinit size_t g_buffer_size = 0; + + constinit os::TlsSlot g_tls_slot; + + constinit tma::IHtcsManager *g_manager = nullptr; + constinit tma::IHtcsManager *g_monitor = nullptr; + + constinit client::VirtualSocketCollection *g_sockets = nullptr; + + void InitializeImpl(void *buffer, size_t buffer_size, int num_sessions) { + /* Check the session count. */ + AMS_ASSERT(0 < num_sessions && num_sessions <= SessionCountMax); + + /* Initialize the manager and monitor. */ + client::InitializeSessionManager(std::addressof(g_manager), std::addressof(g_monitor)); + + /* Register the process. */ + const sf::ClientProcessId process_id{0}; + R_ABORT_UNLESS(g_manager->RegisterProcessId(process_id)); + R_ABORT_UNLESS(g_monitor->MonitorManager(process_id)); + + /* Allocate a tls slot for our last error. */ + os::SdkAllocateTlsSlot(std::addressof(g_tls_slot), nullptr); + + /* Setup the virtual socket collection. */ + AMS_ASSERT(buffer != nullptr); + g_sockets = reinterpret_cast(buffer); + std::construct_at(g_sockets); + g_sockets->Init(static_cast(buffer) + sizeof(*g_sockets), buffer_size - sizeof(*g_sockets)); + + /* Mark initialized. */ + g_initialized = true; + } + + void InitializeImpl(AllocateFunction allocate, DeallocateFunction deallocate, int num_sessions, int num_sockets) { + /* Check the session count. */ + AMS_ASSERT(0 < num_sessions && num_sessions <= SessionCountMax); + + /* Set the allocation functions. */ + g_allocate_function = allocate; + g_deallocate_function = deallocate; + + /* Allocate a buffer. */ + g_buffer_size = sizeof(client::VirtualSocketCollection) + client::VirtualSocketCollection::GetWorkingMemorySize(num_sockets); + g_buffer = g_allocate_function(g_buffer_size); + + /* Initialize. */ + InitializeImpl(g_buffer, g_buffer_size, num_sessions); + } + + } + + bool IsInitialized() { + return g_initialized; + } + + size_t GetWorkingMemorySize(int num_sockets) { + AMS_ASSERT(num_sockets <= SocketCountMax); + return sizeof(client::VirtualSocketCollection) + client::VirtualSocketCollection::GetWorkingMemorySize(num_sockets); + } + + void Initialize(AllocateFunction allocate, DeallocateFunction deallocate, int num_sessions) { + /* Check that we're not already initialized. */ + AMS_ASSERT(!IsInitialized()); + + /* Configure disconnection emulation. */ + g_enable_disconnection_emulation = true; + + /* Initialize. */ + InitializeImpl(allocate, deallocate, num_sessions, htcs::SocketCountMax); + } + + void Initialize(void *buffer, size_t buffer_size) { + /* Check that we're not already initialized. */ + AMS_ASSERT(!IsInitialized()); + + /* Configure disconnection emulation. */ + g_enable_disconnection_emulation = true; + + /* Initialize. */ + InitializeImpl(buffer, buffer_size, htcs::SessionCountMax); + } + + void InitializeForDisableDisconnectionEmulation(AllocateFunction allocate, DeallocateFunction deallocate, int num_sessions) { + /* Check that we're not already initialized. */ + AMS_ASSERT(!IsInitialized()); + + /* Configure disconnection emulation. */ + g_enable_disconnection_emulation = false; + + /* Initialize. */ + InitializeImpl(allocate, deallocate, num_sessions, htcs::SocketCountMax); + } + + void InitializeForDisableDisconnectionEmulation(void *buffer, size_t buffer_size) { + /* Check that we're not already initialized. */ + AMS_ASSERT(!IsInitialized()); + + /* Configure disconnection emulation. */ + g_enable_disconnection_emulation = false; + + /* Initialize. */ + InitializeImpl(buffer, buffer_size, htcs::SessionCountMax); + } + + void InitializeForSystem(void *buffer, size_t buffer_size, int num_sessions) { + /* Check that we're not already initialized. */ + AMS_ASSERT(!IsInitialized()); + + /* Configure disconnection emulation. */ + g_enable_disconnection_emulation = true; + + /* Initialize. */ + InitializeImpl(buffer, buffer_size, num_sessions); + } + + void Finalize() { + /* Check that we're initialized. */ + AMS_ASSERT(IsInitialized()); + + /* Set not initialized. */ + g_initialized = false; + + /* Destroy the virtual socket collection. */ + std::destroy_at(g_sockets); + g_sockets = nullptr; + + /* Free the buffer, if we have one. */ + if (g_buffer != nullptr) { + g_deallocate_function(g_buffer, g_buffer_size); + g_buffer = nullptr; + g_buffer_size = 0; + } + + /* Free the tls slot. */ + os::FreeTlsSlot(g_tls_slot); + + /* Release the manager objects. */ + sf::ReleaseSharedObject(g_manager); + sf::ReleaseSharedObject(g_monitor); + g_manager = nullptr; + g_monitor = nullptr; + + /* Finalize the bsd client sessions. */ + client::FinalizeSessionManager(); + } + +} From d0673aa2fbdb8c5c1623ceba5c86af6d6d90013f Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 19 Feb 2021 14:07:50 -0800 Subject: [PATCH 079/280] htcs: implement client socket bindings --- .../source/htcs/htcs_socket.cpp | 581 +++++++++++++++++- 1 file changed, 580 insertions(+), 1 deletion(-) diff --git a/libraries/libstratosphere/source/htcs/htcs_socket.cpp b/libraries/libstratosphere/source/htcs/htcs_socket.cpp index 5884e43fb..3178e99a1 100644 --- a/libraries/libstratosphere/source/htcs/htcs_socket.cpp +++ b/libraries/libstratosphere/source/htcs/htcs_socket.cpp @@ -37,6 +37,10 @@ namespace ams::htcs { constinit client::VirtualSocketCollection *g_sockets = nullptr; + void SetLastError(uintptr_t error_code) { + os::SetTlsValue(g_tls_slot, error_code); + } + void InitializeImpl(void *buffer, size_t buffer_size, int num_sessions) { /* Check the session count. */ AMS_ASSERT(0 < num_sessions && num_sessions <= SessionCountMax); @@ -171,8 +175,583 @@ namespace ams::htcs { g_manager = nullptr; g_monitor = nullptr; - /* Finalize the bsd client sessions. */ + /* Finalize the htcs client sessions. */ client::FinalizeSessionManager(); } + const HtcsPeerName GetPeerNameAny() { + /* Check that we have a manager. */ + AMS_ASSERT(g_manager != nullptr); + + /* Get name. */ + HtcsPeerName name; + g_manager->GetPeerNameAny(std::addressof(name)); + + return name; + } + + const HtcsPeerName GetDefaultHostName() { + /* Check that we have a manager. */ + AMS_ASSERT(g_manager != nullptr); + + /* Get name. */ + HtcsPeerName name; + g_manager->GetDefaultHostName(std::addressof(name)); + + return name; + } + + s32 GetLastError() { + /* Check that we have a manager. */ + AMS_ASSERT(g_manager != nullptr); + + return static_cast(os::GetTlsValue(g_tls_slot)); + } + + s32 Socket() { + /* Check that we have a manager. */ + AMS_ASSERT(g_manager != nullptr); + + /* Check that we have a socket collection. */ + AMS_ASSERT(g_sockets != nullptr); + + /* Perform the operation. */ + s32 error_code = 0; + const s32 ret = g_sockets->Socket(error_code); + if (ret < 0) { + SetLastError(static_cast(error_code)); + } + + return ret; + } + + s32 Close(s32 desc) { + /* Check that we have a manager. */ + AMS_ASSERT(g_manager != nullptr); + + /* Check that we have a socket collection. */ + AMS_ASSERT(g_sockets != nullptr); + + /* Perform the operation. */ + s32 error_code = 0; + const s32 ret = g_sockets->Close(desc, error_code); + if (ret < 0) { + SetLastError(static_cast(error_code)); + } + + return ret; + } + + s32 Connect(s32 desc, const SockAddrHtcs *address) { + /* Check that we have a manager. */ + AMS_ASSERT(g_manager != nullptr); + + /* Check that we have a socket collection. */ + AMS_ASSERT(g_sockets != nullptr); + + /* Check that the address family is correct. */ + AMS_ASSERT(address->family == HTCS_AF_HTCS); + + /* Perform the operation. */ + s32 error_code = 0; + const s32 ret = g_sockets->Connect(desc, address, error_code); + if (ret < 0) { + SetLastError(static_cast(error_code)); + } + + return ret; + } + + s32 Bind(s32 desc, const SockAddrHtcs *address) { + /* Check that we have a manager. */ + AMS_ASSERT(g_manager != nullptr); + + /* Check that we have a socket collection. */ + AMS_ASSERT(g_sockets != nullptr); + + /* Check that the address family is correct. */ + AMS_ASSERT(address->family == HTCS_AF_HTCS); + + /* Perform the operation. */ + s32 error_code = 0; + const s32 ret = g_sockets->Bind(desc, address, error_code); + if (ret < 0) { + SetLastError(static_cast(error_code)); + } + + return ret; + } + + s32 Listen(s32 desc, s32 backlog_count) { + /* Check that we have a manager. */ + AMS_ASSERT(g_manager != nullptr); + + /* Check that we have a socket collection. */ + AMS_ASSERT(g_sockets != nullptr); + + /* Perform the operation. */ + s32 error_code = 0; + const s32 ret = g_sockets->Listen(desc, backlog_count, error_code); + if (ret < 0) { + SetLastError(static_cast(error_code)); + } + + return ret; + } + + s32 Accept(s32 desc, SockAddrHtcs *address) { + /* Check that we have a manager. */ + AMS_ASSERT(g_manager != nullptr); + + /* Check that we have a socket collection. */ + AMS_ASSERT(g_sockets != nullptr); + + /* Ensure we have an address. */ + SockAddrHtcs tmp; + if (address == nullptr) { + address = std::addressof(tmp); + } + + /* Perform the operation. */ + s32 error_code = 0; + const s32 ret = g_sockets->Accept(desc, address, error_code); + if (ret < 0) { + SetLastError(static_cast(error_code)); + } + + return ret; + } + + s32 Shutdown(s32 desc, s32 how) { + /* Check that we have a manager. */ + AMS_ASSERT(g_manager != nullptr); + + /* Check that we have a socket collection. */ + AMS_ASSERT(g_sockets != nullptr); + + /* Perform the operation. */ + s32 error_code = 0; + const s32 ret = g_sockets->Shutdown(desc, how, error_code); + if (ret < 0) { + SetLastError(static_cast(error_code)); + } + + return ret; + } + + s32 Fcntl(s32 desc, s32 command, s32 value) { + /* Check that we have a manager. */ + AMS_ASSERT(g_manager != nullptr); + + /* Check that we have a socket collection. */ + AMS_ASSERT(g_sockets != nullptr); + + /* Perform the operation. */ + s32 error_code = 0; + const s32 ret = g_sockets->Fcntl(desc, command, value, error_code); + if (ret < 0) { + SetLastError(static_cast(error_code)); + } + + return ret; + } + + s32 Select(s32 count, FdSet *read, FdSet *write, FdSet *exception, TimeVal *timeout) { + /* Check that we have a manager. */ + AMS_ASSERT(g_manager != nullptr); + + /* Check that we have a socket collection. */ + AMS_ASSERT(g_sockets != nullptr); + + /* Check that we have some form of input. */ + if (read == nullptr && write == nullptr && exception == nullptr) { + SetLastError(static_cast(HTCS_EINVAL)); + return -1; + } + + /* Check that the timeout is valid. */ + if (timeout != nullptr && (timeout->tv_sec < 0 || timeout->tv_usec < 0)) { + SetLastError(static_cast(HTCS_EINVAL)); + return -1; + } + + /* Perform the operation. */ + s32 error_code = 0; + const s32 ret = g_sockets->Select(read, write, exception, timeout, error_code); + if (ret < 0) { + SetLastError(static_cast(error_code)); + } + + return ret; + } + + ssize_t Recv(s32 desc, void *buffer, size_t buffer_size, s32 flags) { + /* Check that we have a manager. */ + AMS_ASSERT(g_manager != nullptr); + + /* Check that we have a socket collection. */ + AMS_ASSERT(g_sockets != nullptr); + + /* Perform the operation. */ + s32 error_code = 0; + const ssize_t ret = g_sockets->Recv(desc, buffer, buffer_size, flags, error_code); + if (ret < 0) { + SetLastError(static_cast(error_code)); + } + + return ret; + } + + ssize_t Send(s32 desc, const void *buffer, size_t buffer_size, s32 flags) { + /* Check that we have a manager. */ + AMS_ASSERT(g_manager != nullptr); + + /* Check that we have a socket collection. */ + AMS_ASSERT(g_sockets != nullptr); + + /* Perform the operation. */ + s32 error_code = 0; + const ssize_t ret = g_sockets->Send(desc, buffer, buffer_size, flags, error_code); + if (ret < 0) { + SetLastError(static_cast(error_code)); + } + + return ret; + } + + void FdSetZero(FdSet *set) { + AMS_ASSERT(set != nullptr); + + std::memset(set, 0, sizeof(*set)); + } + + void FdSetSet(s32 fd, FdSet *set) { + AMS_ASSERT(set != nullptr); + + for (auto i = 0; i < FdSetSize; ++i) { + if (set->fds[i] == 0) { + set->fds[i] = fd; + break; + } + } + } + + void FdSetClr(s32 fd, FdSet *set) { + AMS_ASSERT(set != nullptr); + + for (auto i = 0; i < FdSetSize; ++i) { + if (set->fds[i] == fd) { + std::memcpy(set->fds + i, set->fds + i + 1, (FdSetSize - (i + 1)) * sizeof(fd)); + set->fds[FdSetSize - 1] = 0; + break; + } + } + } + + bool FdSetIsSet(s32 fd, const FdSet *set) { + AMS_ASSERT(set != nullptr); + + for (auto i = 0; i < FdSetSize; ++i) { + if (set->fds[i] == fd) { + return true; + } + } + + return false; + } + + namespace client { + + sf::SharedPointer socket(s32 &last_error) { + sf::SharedPointer socket = nullptr; + R_ABORT_UNLESS(g_manager->CreateSocket(std::addressof(last_error), std::addressof(socket), g_enable_disconnection_emulation)); + return socket; + } + + s32 close(sf::SharedPointer socket, s32 &last_error) { + s32 res; + socket->Close(std::addressof(last_error), std::addressof(res)); + return res; + } + + s32 bind(sf::SharedPointer socket, const htcs::SockAddrHtcs *address, s32 &last_error) { + /* Create null-terminated address. */ + htcs::SockAddrHtcs null_terminated_address; + null_terminated_address.family = address->family; + util::Strlcpy(null_terminated_address.peer_name.name, address->peer_name.name, PeerNameBufferLength); + util::Strlcpy(null_terminated_address.port_name.name, address->port_name.name, PortNameBufferLength); + + s32 res; + socket->Bind(std::addressof(last_error), std::addressof(res), null_terminated_address); + return res; + } + + s32 listen(sf::SharedPointer socket, s32 backlog_count, s32 &last_error) { + s32 res; + socket->Listen(std::addressof(last_error), std::addressof(res), backlog_count); + return res; + } + + sf::SharedPointer accept(sf::SharedPointer socket, htcs::SockAddrHtcs *address, s32 &last_error) { + /* Begin the accept. */ + sf::SharedPointer res = nullptr; + u32 task_id = 0; + sf::CopyHandle event_handle; + if (R_SUCCEEDED(socket->AcceptStart(std::addressof(task_id), std::addressof(event_handle)))) { + /* Create system event. */ + os::SystemEventType event; + os::AttachReadableHandleToSystemEvent(std::addressof(event), event_handle.GetValue(), true, os::EventClearMode_ManualClear); + + /* When we're done, clean up the event. */ + ON_SCOPE_EXIT { os::DestroySystemEvent(std::addressof(event)); }; + + /* Wait for the accept to finish. */ + os::WaitSystemEvent(std::addressof(event)); + + /* End the accept. */ + socket->AcceptResults(std::addressof(last_error), std::addressof(res), address, task_id); + } else { + /* Set error. */ + last_error = HTCS_EINTR; + } + + /* Sleep, if an error occurred. */ + if (last_error != HTCS_ENONE) { + os::SleepThread(TimeSpan::FromMilliSeconds(1)); + } + + return res; + } + + s32 fcntl(sf::SharedPointer socket, s32 command, s32 value, s32 &last_error) { + s32 res; + socket->Fcntl(std::addressof(last_error), std::addressof(res), command, value); + return res; + } + + s32 shutdown(sf::SharedPointer socket, s32 how, s32 &last_error) { + s32 res; + socket->Shutdown(std::addressof(last_error), std::addressof(res), how); + return res; + } + + s32 connect(sf::SharedPointer socket, const htcs::SockAddrHtcs *address, s32 &last_error) { + /* Create null-terminated address. */ + htcs::SockAddrHtcs null_terminated_address; + null_terminated_address.family = address->family; + util::Strlcpy(null_terminated_address.peer_name.name, address->peer_name.name, PeerNameBufferLength); + util::Strlcpy(null_terminated_address.port_name.name, address->port_name.name, PortNameBufferLength); + + s32 res; + socket->Connect(std::addressof(last_error), std::addressof(res), null_terminated_address); + return res; + } + + s32 select(s32 * const read, s32 &num_read, s32 * const write, s32 &num_write, s32 * const except, s32 &num_except, htcs::TimeVal *timeout, s32 &last_error) { + /* Determine the timeout values. */ + s64 tv_sec = -1; + s64 tv_usec = -1; + if (timeout != nullptr) { + tv_sec = timeout->tv_sec; + tv_usec = timeout->tv_usec; + } + + using InArray = sf::InMapAliasArray; + using OutArray = sf::OutMapAliasArray; + + /* Begin the select. */ + s32 res = -1; + u32 task_id = 0; + sf::CopyHandle event_handle; + if (R_SUCCEEDED(g_manager->StartSelect(std::addressof(task_id), std::addressof(event_handle), InArray(read, num_read), InArray(write, num_write), InArray(except, num_except), tv_sec, tv_usec))) { + /* Create system event. */ + os::SystemEventType event; + os::AttachReadableHandleToSystemEvent(std::addressof(event), event_handle.GetValue(), true, os::EventClearMode_ManualClear); + + /* When we're done, clean up the event. */ + ON_SCOPE_EXIT { os::DestroySystemEvent(std::addressof(event)); }; + + /* Wait for the select to finish. */ + os::WaitSystemEvent(std::addressof(event)); + + /* End the select. */ + g_manager->EndSelect(std::addressof(last_error), std::addressof(res), OutArray(read, num_read), OutArray(write, num_write), OutArray(except, num_except), task_id); + } else { + /* Set error. */ + last_error = HTCS_EINTR; + os::SleepThread(TimeSpan::FromMilliSeconds(1)); + } + + return res; + } + + namespace { + + constexpr size_t MaximumBufferSizeForSmallTransfer = 0xDFE0; + + ssize_t recvLarge(sf::SharedPointer socket, void *buffer, size_t buffer_size, s32 flags, s32 &last_error) { + /* Setup. */ + s64 res = -1; + last_error = HTCS_EINTR; + + /* Start the receive. */ + u32 task_id = 0; + sf::CopyHandle event_handle; + if (R_SUCCEEDED(socket->StartRecv(std::addressof(task_id), std::addressof(event_handle), static_cast(buffer_size), flags))) { + /* Create system event. */ + os::SystemEventType event; + os::AttachReadableHandleToSystemEvent(std::addressof(event), event_handle.GetValue(), true, os::EventClearMode_ManualClear); + + /* When we're done, clean up the event. */ + ON_SCOPE_EXIT { os::DestroySystemEvent(std::addressof(event)); }; + + /* Wait for the receive to finish. */ + os::WaitSystemEvent(std::addressof(event)); + + /* End the receive. */ + socket->EndRecv(std::addressof(last_error), std::addressof(res), sf::OutAutoSelectBuffer(buffer, buffer_size), task_id); + } else { + /* Set error. */ + last_error = HTCS_EINTR; + os::SleepThread(TimeSpan::FromMilliSeconds(1)); + } + + return static_cast(res); + } + + ssize_t sendLarge(sf::SharedPointer socket, const void *buffer, size_t buffer_size, s32 flags, s32 &last_error) { + /* Setup. */ + s64 res = -1; + last_error = HTCS_EINTR; + + /* Start the send. */ + u32 task_id = 0; + s64 max_size = 0; + sf::CopyHandle event_handle; + if (R_SUCCEEDED(socket->StartSend(std::addressof(task_id), std::addressof(event_handle), std::addressof(max_size), static_cast(buffer_size), flags))) { + /* Create system event. */ + os::SystemEventType event; + os::AttachReadableHandleToSystemEvent(std::addressof(event), event_handle.GetValue(), true, os::EventClearMode_ManualClear); + + /* When we're done, clean up the event. */ + ON_SCOPE_EXIT { os::DestroySystemEvent(std::addressof(event)); }; + + /* Send all the data. */ + bool done = false; + size_t sent = 0; + while (sent < buffer_size) { + /* Determine how much to send, this iteration. */ + const u8 *cur = static_cast(buffer) + sent; + const s64 cur_size = std::min(max_size, static_cast(buffer_size - sent)); + + /* Continue sending data. */ + s64 cur_sent = 0; + bool wait = false; + const Result result = socket->ContinueSend(std::addressof(cur_sent), std::addressof(wait), sf::InNonSecureAutoSelectBuffer(cur, cur_size), task_id); + if (cur_sent <= 0 || R_FAILED(result)) { + done = true; + break; + } + + /* Wait if we should. */ + if (wait) { + os::WaitSystemEvent(std::addressof(event)); + os::ClearSystemEvent(std::addressof(event)); + } + + /* Advance. */ + sent += cur_sent; + } + + /* Wait for the send to finish. */ + if (!done) { + os::WaitSystemEvent(std::addressof(event)); + } + + /* End the send. */ + socket->EndSend(std::addressof(last_error), std::addressof(res), task_id); + } else { + /* Set error. */ + last_error = HTCS_EINTR; + os::SleepThread(TimeSpan::FromMilliSeconds(1)); + } + + return static_cast(res); + } + + } + + ssize_t recv(sf::SharedPointer socket, void *buffer, size_t buffer_size, s32 flags, s32 &last_error) { + /* Determine how much to receive. */ + size_t recv_size = buffer_size; + + if ((flags & HTCS_MSG_WAITALL) == 0) { + recv_size = std::min(MaximumBufferSizeForSmallTransfer, buffer_size); + } + + /* Perform a large receive, if we have to. */ + if (recv_size > MaximumBufferSizeForSmallTransfer) { + return recvLarge(socket, buffer, recv_size, flags, last_error); + } + + /* Start the receive. */ + s64 res = -1; + u32 task_id = 0; + sf::CopyHandle event_handle; + if (R_SUCCEEDED(socket->RecvStart(std::addressof(task_id), std::addressof(event_handle), static_cast(recv_size), flags))) { + /* Create system event. */ + os::SystemEventType event; + os::AttachReadableHandleToSystemEvent(std::addressof(event), event_handle.GetValue(), true, os::EventClearMode_ManualClear); + + /* When we're done, clean up the event. */ + ON_SCOPE_EXIT { os::DestroySystemEvent(std::addressof(event)); }; + + /* Wait for the receive to finish. */ + os::WaitSystemEvent(std::addressof(event)); + + /* End the receive. */ + socket->RecvResults(std::addressof(last_error), std::addressof(res), sf::OutAutoSelectBuffer(buffer, recv_size), task_id); + } else { + /* Set error. */ + last_error = HTCS_EINTR; + os::SleepThread(TimeSpan::FromMilliSeconds(1)); + } + + return static_cast(res); + } + + ssize_t send(sf::SharedPointer socket, const void *buffer, size_t buffer_size, s32 flags, s32 &last_error) { + /* Perform a large send, if we have to. */ + if (buffer_size > MaximumBufferSizeForSmallTransfer) { + return sendLarge(socket, buffer, buffer_size, flags, last_error); + } + + /* Start the send. */ + s64 res = -1; + u32 task_id = 0; + sf::CopyHandle event_handle; + if (R_SUCCEEDED(socket->SendStart(std::addressof(task_id), std::addressof(event_handle), sf::InNonSecureAutoSelectBuffer(buffer, buffer_size), flags))) { + /* Create system event. */ + os::SystemEventType event; + os::AttachReadableHandleToSystemEvent(std::addressof(event), event_handle.GetValue(), true, os::EventClearMode_ManualClear); + + /* When we're done, clean up the event. */ + ON_SCOPE_EXIT { os::DestroySystemEvent(std::addressof(event)); }; + + /* Wait for the send to finish. */ + os::WaitSystemEvent(std::addressof(event)); + + /* End the send. */ + socket->SendResults(std::addressof(last_error), std::addressof(res), task_id); + } else { + /* Set error. */ + last_error = HTCS_EINTR; + os::SleepThread(TimeSpan::FromMilliSeconds(1)); + } + + return static_cast(res); + } + + } + } From 72de4d85f323a6c6b93c76d78d10ce9edfd96d37 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 19 Feb 2021 16:11:56 -0800 Subject: [PATCH 080/280] htcs: implement remaining client bindings --- .../source/htcs/client/htcs_session.cpp | 237 ++++++++++++++++++ .../source/htcs/client/htcs_session.hpp | 2 +- .../source/htcs/htcs_socket.cpp | 2 +- 3 files changed, 239 insertions(+), 2 deletions(-) create mode 100644 libraries/libstratosphere/source/htcs/client/htcs_session.cpp diff --git a/libraries/libstratosphere/source/htcs/client/htcs_session.cpp b/libraries/libstratosphere/source/htcs/client/htcs_session.cpp new file mode 100644 index 000000000..e3bc5fe1b --- /dev/null +++ b/libraries/libstratosphere/source/htcs/client/htcs_session.cpp @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcs_session.hpp" +#include + +namespace ams::htcs::client { + + namespace { + + struct HtcsObjectAllocatorTag; + using ObjectAllocator = ams::sf::ExpHeapStaticAllocator<16_KB, HtcsObjectAllocatorTag>; + using ObjectFactory = ams::sf::ObjectFactory; + + class StaticAllocatorInitializer { + public: + StaticAllocatorInitializer() { + ObjectAllocator::Initialize(lmem::CreateOption_ThreadSafe); + } + } g_static_allocator_initializer; + + class RemoteSocket { + private: + ::HtcsSocket m_s; + public: + RemoteSocket(::HtcsSocket &s) : m_s(s) { /* ... */ } + ~RemoteSocket() { ::htcsCloseSocket(std::addressof(m_s)); } + public: + Result Accept(sf::Out out_err, sf::Out> out, sf::Out out_address) { AMS_ABORT("Not Implemented"); } + Result Recv(sf::Out out_err, sf::Out out_size, const sf::OutAutoSelectBuffer &buffer, s32 flags) { AMS_ABORT("Not Implemented"); } + Result Send(sf::Out out_err, sf::Out out_size, const sf::InAutoSelectBuffer &buffer, s32 flags) { AMS_ABORT("Not Implemented"); } + Result RecvLargeStart(sf::Out out_task_id, sf::OutCopyHandle out_event, s32 unaligned_size_start, s32 unaligned_size_end, s64 aligned_size, sf::CopyHandle mem_handle, s32 flags) { AMS_ABORT("Not Implemented"); } + Result SendStartOld(sf::Out out_task_id, sf::OutCopyHandle out_event, const sf::InAutoSelectBuffer &buffer, s32 flags) { AMS_ABORT("Not Implemented"); } + Result SendLargeStart(sf::Out out_task_id, sf::OutCopyHandle out_event, const sf::InAutoSelectBuffer &start_buffer, const sf::InAutoSelectBuffer &end_buffer, sf::CopyHandle mem_handle, s64 aligned_size, s32 flags) { AMS_ABORT("Not Implemented"); } + Result ContinueSendOld(sf::Out out_size, sf::Out out_wait, const sf::InAutoSelectBuffer &buffer, u32 task_id) { AMS_ABORT("Not Implemented"); } + + Result Close(sf::Out out_err, sf::Out out_res); + Result Connect(sf::Out out_err, sf::Out out_res, const htcs::SockAddrHtcs &address); + Result Bind(sf::Out out_err, sf::Out out_res, const htcs::SockAddrHtcs &address); + Result Listen(sf::Out out_err, sf::Out out_res, s32 backlog_count); + Result Shutdown(sf::Out out_err, sf::Out out_res, s32 how); + Result Fcntl(sf::Out out_err, sf::Out out_res, s32 command, s32 value); + + Result AcceptStart(sf::Out out_task_id, sf::OutCopyHandle out_event); + Result AcceptResults(sf::Out out_err, sf::Out> out, sf::Out out_address, u32 task_id); + + Result RecvStart(sf::Out out_task_id, sf::OutCopyHandle out_event, s32 mem_size, s32 flags); + Result RecvResults(sf::Out out_err, sf::Out out_size, const sf::OutAutoSelectBuffer &buffer, u32 task_id); + + Result SendStart(sf::Out out_task_id, sf::OutCopyHandle out_event, const sf::InNonSecureAutoSelectBuffer &buffer, s32 flags); + Result SendResults(sf::Out out_err, sf::Out out_size, u32 task_id); + + Result StartSend(sf::Out out_task_id, sf::OutCopyHandle out_event, sf::Out out_max_size, s64 size, s32 flags); + Result ContinueSend(sf::Out out_size, sf::Out out_wait, const sf::InNonSecureAutoSelectBuffer &buffer, u32 task_id); + Result EndSend(sf::Out out_err, sf::Out out_size, u32 task_id); + + Result StartRecv(sf::Out out_task_id, sf::OutCopyHandle out_event, s64 size, s32 flags); + Result EndRecv(sf::Out out_err, sf::Out out_size, const sf::OutAutoSelectBuffer &buffer, u32 task_id); + + Result GetPrimitive(sf::Out out); + }; + static_assert(tma::IsISocket); + + class RemoteManager { + public: + Result Socket(sf::Out out_err, sf::Out out_sock) { AMS_ABORT("Not Implemented"); } + Result Close(sf::Out out_err, sf::Out out_res, s32 desc) { AMS_ABORT("Not Implemented"); } + Result Connect(sf::Out out_err, sf::Out out_res, s32 desc, const htcs::SockAddrHtcs &address) { AMS_ABORT("Not Implemented"); } + Result Bind(sf::Out out_err, sf::Out out_res, s32 desc, const htcs::SockAddrHtcs &address) { AMS_ABORT("Not Implemented"); } + Result Listen(sf::Out out_err, sf::Out out_res, s32 desc, s32 backlog_count) { AMS_ABORT("Not Implemented"); } + Result Accept(sf::Out out_err, sf::Out out_res, sf::Out out_address, s32 desc) { AMS_ABORT("Not Implemented"); } + Result Recv(sf::Out out_err, sf::Out out_size, const sf::OutBuffer &buffer, s32 desc, s32 flags) { AMS_ABORT("Not Implemented"); } + Result Send(sf::Out out_err, sf::Out out_size, s32 desc, const sf::InBuffer &buffer, s32 flags) { AMS_ABORT("Not Implemented"); } + Result Shutdown(sf::Out out_err, sf::Out out_res, s32 desc, s32 how) { AMS_ABORT("Not Implemented"); } + Result Fcntl(sf::Out out_err, sf::Out out_res, s32 desc, s32 command, s32 value) { AMS_ABORT("Not Implemented"); } + Result CreateSocketOld(sf::Out out_err, sf::Out> out) { AMS_ABORT("Not Implemented"); } + + Result GetPeerNameAny(sf::Out out); + Result GetDefaultHostName(sf::Out out); + Result CreateSocket(sf::Out out_err, sf::Out> out, bool enable_disconnection_emulation); + Result RegisterProcessId(const sf::ClientProcessId &client_pid); + Result MonitorManager(const sf::ClientProcessId &client_pid); + Result StartSelect(sf::Out out_task_id, sf::OutCopyHandle out_event, const sf::InMapAliasArray &read_handles, const sf::InMapAliasArray &write_handles, const sf::InMapAliasArray &exception_handles, s64 tv_sec, s64 tv_usec); + Result EndSelect(sf::Out out_err, sf::Out out_count, const sf::OutMapAliasArray &read_handles, const sf::OutMapAliasArray &write_handles, const sf::OutMapAliasArray &exception_handles, u32 task_id); + }; + static_assert(tma::IsIHtcsManager); + + Result RemoteManager::GetPeerNameAny(sf::Out out) { + static_assert(sizeof(htcs::HtcsPeerName) == sizeof(::HtcsPeerName)); + return ::htcsGetPeerNameAny(reinterpret_cast<::HtcsPeerName *>(out.GetPointer())); + } + + Result RemoteManager::GetDefaultHostName(sf::Out out) { + static_assert(sizeof(htcs::HtcsPeerName) == sizeof(::HtcsPeerName)); + return ::htcsGetDefaultHostName(reinterpret_cast<::HtcsPeerName *>(out.GetPointer())); + } + + Result RemoteManager::CreateSocket(sf::Out out_err, sf::Out> out, bool enable_disconnection_emulation) { + ::HtcsSocket libnx_socket; + R_TRY(::htcsCreateSocket(out_err.GetPointer(), std::addressof(libnx_socket), enable_disconnection_emulation)); + + *out = ObjectFactory::CreateSharedEmplaced(libnx_socket); + return ResultSuccess(); + } + + Result RemoteManager::RegisterProcessId(const sf::ClientProcessId &client_pid) { + /* Handled by libnx init. */ + return ResultSuccess(); + } + + Result RemoteManager::MonitorManager(const sf::ClientProcessId &client_pid) { + /* Handled by libnx init. */ + return ResultSuccess(); + } + + Result RemoteManager::StartSelect(sf::Out out_task_id, sf::OutCopyHandle out_event, const sf::InMapAliasArray &read_handles, const sf::InMapAliasArray &write_handles, const sf::InMapAliasArray &exception_handles, s64 tv_sec, s64 tv_usec) { + return ::htcsStartSelect(out_task_id.GetPointer(), out_event.GetHandlePointer(), read_handles.GetPointer(), read_handles.GetSize(), write_handles.GetPointer(), write_handles.GetSize(), exception_handles.GetPointer(), exception_handles.GetSize(), tv_sec, tv_usec); + } + + Result RemoteManager::EndSelect(sf::Out out_err, sf::Out out_count, const sf::OutMapAliasArray &read_handles, const sf::OutMapAliasArray &write_handles, const sf::OutMapAliasArray &exception_handles, u32 task_id) { + return ::htcsEndSelect(out_err.GetPointer(), out_count.GetPointer(), read_handles.GetPointer(), read_handles.GetSize(), write_handles.GetPointer(), write_handles.GetSize(), exception_handles.GetPointer(), exception_handles.GetSize(), task_id); + } + + Result RemoteSocket::Close(sf::Out out_err, sf::Out out_res) { + return ::htcsSocketClose(std::addressof(m_s), out_err.GetPointer(), out_res.GetPointer()); + } + + Result RemoteSocket::Connect(sf::Out out_err, sf::Out out_res, const htcs::SockAddrHtcs &address) { + static_assert(sizeof(htcs::SockAddrHtcs) == sizeof(::SockAddrHtcs)); + return ::htcsSocketConnect(std::addressof(m_s), out_err.GetPointer(), out_res.GetPointer(), reinterpret_cast(std::addressof(address))); + } + + Result RemoteSocket::Bind(sf::Out out_err, sf::Out out_res, const htcs::SockAddrHtcs &address) { + static_assert(sizeof(htcs::SockAddrHtcs) == sizeof(::SockAddrHtcs)); + return ::htcsSocketBind(std::addressof(m_s), out_err.GetPointer(), out_res.GetPointer(), reinterpret_cast(std::addressof(address))); + } + + Result RemoteSocket::Listen(sf::Out out_err, sf::Out out_res, s32 backlog_count) { + return ::htcsSocketListen(std::addressof(m_s), out_err.GetPointer(), out_res.GetPointer(), backlog_count); + } + + Result RemoteSocket::Shutdown(sf::Out out_err, sf::Out out_res, s32 how) { + return ::htcsSocketShutdown(std::addressof(m_s), out_err.GetPointer(), out_res.GetPointer(), how); + } + + Result RemoteSocket::Fcntl(sf::Out out_err, sf::Out out_res, s32 command, s32 value) { + return ::htcsSocketFcntl(std::addressof(m_s), out_err.GetPointer(), out_res.GetPointer(), command, value); + } + + + Result RemoteSocket::AcceptStart(sf::Out out_task_id, sf::OutCopyHandle out_event) { + return ::htcsSocketAcceptStart(std::addressof(m_s), out_task_id.GetPointer(), out_event.GetHandlePointer()); + } + + Result RemoteSocket::AcceptResults(sf::Out out_err, sf::Out> out, sf::Out out_address, u32 task_id) { + static_assert(sizeof(htcs::SockAddrHtcs) == sizeof(::SockAddrHtcs)); + ::HtcsSocket libnx_socket; + R_TRY(::htcsSocketAcceptResults(std::addressof(m_s), out_err.GetPointer(), std::addressof(libnx_socket), reinterpret_cast<::SockAddrHtcs *>(out_address.GetPointer()), task_id)); + + *out = ObjectFactory::CreateSharedEmplaced(libnx_socket); + return ResultSuccess(); + } + + Result RemoteSocket::RecvStart(sf::Out out_task_id, sf::OutCopyHandle out_event, s32 mem_size, s32 flags) { + return ::htcsSocketRecvStart(std::addressof(m_s), out_task_id.GetPointer(), out_event.GetHandlePointer(), mem_size, flags); + } + + Result RemoteSocket::RecvResults(sf::Out out_err, sf::Out out_size, const sf::OutAutoSelectBuffer &buffer, u32 task_id) { + return ::htcsSocketRecvResults(std::addressof(m_s), out_err.GetPointer(), out_size.GetPointer(), buffer.GetPointer(), buffer.GetSize(), task_id); + } + + Result RemoteSocket::SendStart(sf::Out out_task_id, sf::OutCopyHandle out_event, const sf::InNonSecureAutoSelectBuffer &buffer, s32 flags) { + return ::htcsSocketSendStart(std::addressof(m_s), out_task_id.GetPointer(), out_event.GetHandlePointer(), buffer.GetPointer(), buffer.GetSize(), flags); + } + + Result RemoteSocket::SendResults(sf::Out out_err, sf::Out out_size, u32 task_id) { + return ::htcsSocketSendResults(std::addressof(m_s), out_err.GetPointer(), out_size.GetPointer(), task_id); + } + + Result RemoteSocket::StartSend(sf::Out out_task_id, sf::OutCopyHandle out_event, sf::Out out_max_size, s64 size, s32 flags) { + return ::htcsSocketStartSend(std::addressof(m_s), out_task_id.GetPointer(), out_event.GetHandlePointer(), out_max_size.GetPointer(), size, flags); + } + + Result RemoteSocket::ContinueSend(sf::Out out_size, sf::Out out_wait, const sf::InNonSecureAutoSelectBuffer &buffer, u32 task_id) { + return ::htcsSocketContinueSend(std::addressof(m_s), out_size.GetPointer(), out_wait.GetPointer(), buffer.GetPointer(), buffer.GetSize(), task_id); + } + + Result RemoteSocket::EndSend(sf::Out out_err, sf::Out out_size, u32 task_id) { + return ::htcsSocketEndSend(std::addressof(m_s), out_err.GetPointer(), out_size.GetPointer(), task_id); + } + + Result RemoteSocket::StartRecv(sf::Out out_task_id, sf::OutCopyHandle out_event, s64 size, s32 flags) { + return ::htcsSocketStartRecv(std::addressof(m_s), out_task_id.GetPointer(), out_event.GetHandlePointer(), size, flags); + } + + Result RemoteSocket::EndRecv(sf::Out out_err, sf::Out out_size, const sf::OutAutoSelectBuffer &buffer, u32 task_id) { + return ::htcsSocketEndRecv(std::addressof(m_s), out_err.GetPointer(), out_size.GetPointer(), buffer.GetPointer(), buffer.GetSize(), task_id); + } + + Result RemoteSocket::GetPrimitive(sf::Out out) { + return ::htcsSocketGetPrimitive(std::addressof(m_s), out.GetPointer()); + } + + } + + void InitializeSessionManager(tma::IHtcsManager **out_manager, tma::IHtcsManager **out_monitor, u32 num_sessions) { + /* Initialize the libnx wrapper. */ + sm::DoWithSession([&] { + R_ABORT_UNLESS(::htcsInitialize(num_sessions)); + }); + + /* Create the output objects. */ + *out_manager = ObjectFactory::CreateSharedEmplaced().Detach(); + + /* Create the output objects. */ + *out_monitor = ObjectFactory::CreateSharedEmplaced().Detach(); + } + + void FinalizeSessionManager() { + /* Exit the libnx wrapper. */ + ::htcsExit(); + } + +} diff --git a/libraries/libstratosphere/source/htcs/client/htcs_session.hpp b/libraries/libstratosphere/source/htcs/client/htcs_session.hpp index bbd088e06..178a5a3fe 100644 --- a/libraries/libstratosphere/source/htcs/client/htcs_session.hpp +++ b/libraries/libstratosphere/source/htcs/client/htcs_session.hpp @@ -18,7 +18,7 @@ namespace ams::htcs::client { - void InitializeSessionManager(tma::IHtcsManager **out_manager, tma::IHtcsManager **out_monitor); + void InitializeSessionManager(tma::IHtcsManager **out_manager, tma::IHtcsManager **out_monitor, u32 num_sessions); void FinalizeSessionManager(); } diff --git a/libraries/libstratosphere/source/htcs/htcs_socket.cpp b/libraries/libstratosphere/source/htcs/htcs_socket.cpp index 3178e99a1..941347bb4 100644 --- a/libraries/libstratosphere/source/htcs/htcs_socket.cpp +++ b/libraries/libstratosphere/source/htcs/htcs_socket.cpp @@ -46,7 +46,7 @@ namespace ams::htcs { AMS_ASSERT(0 < num_sessions && num_sessions <= SessionCountMax); /* Initialize the manager and monitor. */ - client::InitializeSessionManager(std::addressof(g_manager), std::addressof(g_monitor)); + client::InitializeSessionManager(std::addressof(g_manager), std::addressof(g_monitor), num_sessions); /* Register the process. */ const sf::ClientProcessId process_id{0}; From 0ec54ed4923f4e1425e959338883ed5ef9f9a018 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 21 Feb 2021 14:31:28 -0800 Subject: [PATCH 081/280] htcs: fixes, echo server is now fully functional --- .../sf/cmif/sf_cmif_service_object_holder.hpp | 4 ++-- .../source/htc/server/rpc/htc_rpc_client.hpp | 2 +- .../source/htclow/mux/htclow_mux_task_manager.cpp | 2 +- .../source/htcs/client/htcs_session.cpp | 14 +++++++++++++- .../htcs/client/htcs_virtual_socket_collection.cpp | 5 ++--- .../source/htcs/impl/htcs_manager.cpp | 14 +++++++------- .../source/htcs/impl/htcs_service.cpp | 10 ++++++---- .../sf/hipc/sf_hipc_server_session_manager.cpp | 1 + 8 files changed, 33 insertions(+), 19 deletions(-) diff --git a/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_object_holder.hpp b/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_object_holder.hpp index d8f1c3ea1..cc9340caa 100644 --- a/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_object_holder.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_object_holder.hpp @@ -70,11 +70,11 @@ namespace ams::sf::cmif { /* Boolean operators. */ explicit constexpr operator bool() const { - return this->dispatch_meta != nullptr; + return this->srv != nullptr; } constexpr bool operator!() const { - return this->dispatch_meta == nullptr; + return this->srv == nullptr; } /* Getters. */ diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp index c4715652a..9557cb676 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp @@ -221,7 +221,7 @@ namespace ams::htc::server::rpc { } template requires IsRpcTask - Result VerifyTaskIdWitHandle(u32 task_id, s32 handle) { + Result VerifyTaskIdWithHandle(u32 task_id, s32 handle) { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp index 564271242..4f22cc972 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp @@ -119,7 +119,7 @@ namespace ams::htclow::mux { void TaskManager::NotifyReceiveData(impl::ChannelInternalType channel, size_t size) { for (auto i = 0; i < MaxTaskCount; ++i) { - if (m_valid[i] && m_tasks[i].channel == channel && m_tasks[i].size <= size) { + if (m_valid[i] && m_tasks[i].channel == channel && m_tasks[i].type == TaskType_Receive && m_tasks[i].size <= size) { this->CompleteTask(i, EventTrigger_ReceiveData); } } diff --git a/libraries/libstratosphere/source/htcs/client/htcs_session.cpp b/libraries/libstratosphere/source/htcs/client/htcs_session.cpp index e3bc5fe1b..37fc6d749 100644 --- a/libraries/libstratosphere/source/htcs/client/htcs_session.cpp +++ b/libraries/libstratosphere/source/htcs/client/htcs_session.cpp @@ -15,8 +15,13 @@ */ #include #include "htcs_session.hpp" + +extern "C" { + #include +} + namespace ams::htcs::client { namespace { @@ -32,6 +37,10 @@ namespace ams::htcs::client { } } g_static_allocator_initializer; + } + + namespace { + class RemoteSocket { private: ::HtcsSocket m_s; @@ -112,6 +121,8 @@ namespace ams::htcs::client { ::HtcsSocket libnx_socket; R_TRY(::htcsCreateSocket(out_err.GetPointer(), std::addressof(libnx_socket), enable_disconnection_emulation)); + R_SUCCEED_IF(*out_err != 0); + *out = ObjectFactory::CreateSharedEmplaced(libnx_socket); return ResultSuccess(); } @@ -160,7 +171,6 @@ namespace ams::htcs::client { return ::htcsSocketFcntl(std::addressof(m_s), out_err.GetPointer(), out_res.GetPointer(), command, value); } - Result RemoteSocket::AcceptStart(sf::Out out_task_id, sf::OutCopyHandle out_event) { return ::htcsSocketAcceptStart(std::addressof(m_s), out_task_id.GetPointer(), out_event.GetHandlePointer()); } @@ -170,6 +180,8 @@ namespace ams::htcs::client { ::HtcsSocket libnx_socket; R_TRY(::htcsSocketAcceptResults(std::addressof(m_s), out_err.GetPointer(), std::addressof(libnx_socket), reinterpret_cast<::SockAddrHtcs *>(out_address.GetPointer()), task_id)); + R_SUCCEED_IF(*out_err != 0); + *out = ObjectFactory::CreateSharedEmplaced(libnx_socket); return ResultSuccess(); } diff --git a/libraries/libstratosphere/source/htcs/client/htcs_virtual_socket_collection.cpp b/libraries/libstratosphere/source/htcs/client/htcs_virtual_socket_collection.cpp index 0c6794dd7..1ef63f254 100644 --- a/libraries/libstratosphere/source/htcs/client/htcs_virtual_socket_collection.cpp +++ b/libraries/libstratosphere/source/htcs/client/htcs_virtual_socket_collection.cpp @@ -207,7 +207,6 @@ namespace ams::htcs::client { error_code = 0; /* Create the socket. */ - sf::SharedPointer socket(nullptr); return this->CreateSocket(sf::SharedPointer{nullptr}, error_code); } @@ -579,7 +578,7 @@ namespace ams::htcs::client { /* Add the socket to the list. */ if (m_list_count != 0) { - /* Ensure the list remains in sorder order. */ + /* Ensure the list remains in sorted order. */ s32 index; for (index = m_list_count - 1; index >= 0; --index) { if (m_socket_list[index].m_id < id) { @@ -687,7 +686,7 @@ namespace ams::htcs::client { s32 error_code = 0; /* Get socket. */ - sf::SharedPointer res = socket(id); + sf::SharedPointer res = socket(error_code); if (res != nullptr) { /* Assign the new socket. */ s32 index; diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_manager.cpp b/libraries/libstratosphere/source/htcs/impl/htcs_manager.cpp index 0999b284d..f961b4f1d 100644 --- a/libraries/libstratosphere/source/htcs/impl/htcs_manager.cpp +++ b/libraries/libstratosphere/source/htcs/impl/htcs_manager.cpp @@ -45,7 +45,7 @@ namespace ams::htcs::impl { /* Set output. */ if (R_SUCCEEDED(result)) { *out_err = err; - if (out_err == 0) { + if (err == 0) { *out_desc = desc; } else { *out_desc = -1; @@ -77,7 +77,7 @@ namespace ams::htcs::impl { /* Set output. */ if (R_SUCCEEDED(result)) { *out_err = err; - if (out_err == 0) { + if (err == 0) { *out_res = 0; } else { *out_res = -1; @@ -96,7 +96,7 @@ namespace ams::htcs::impl { /* Set output. */ if (R_SUCCEEDED(result)) { *out_err = err; - if (out_err == 0) { + if (err == 0) { *out_res = 0; } else { *out_res = -1; @@ -115,7 +115,7 @@ namespace ams::htcs::impl { /* Set output. */ if (R_SUCCEEDED(result)) { *out_err = err; - if (out_err == 0) { + if (err == 0) { *out_res = 0; } else { *out_res = -1; @@ -135,7 +135,7 @@ namespace ams::htcs::impl { /* Set output. */ if (R_SUCCEEDED(result)) { *out_err = err; - if (out_err == 0) { + if (err == 0) { *out_size = recv_size; } else { *out_size = -1; @@ -155,7 +155,7 @@ namespace ams::htcs::impl { /* Set output. */ if (R_SUCCEEDED(result)) { *out_err = err; - if (out_err == 0) { + if (err == 0) { *out_size = send_size; } else { *out_size = -1; @@ -174,7 +174,7 @@ namespace ams::htcs::impl { /* Set output. */ if (R_SUCCEEDED(result)) { *out_err = err; - if (out_err == 0) { + if (err == 0) { *out_res = 0; } else { *out_res = -1; diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_service.cpp b/libraries/libstratosphere/source/htcs/impl/htcs_service.cpp index 6fd77f87a..1e046d8cc 100644 --- a/libraries/libstratosphere/source/htcs/impl/htcs_service.cpp +++ b/libraries/libstratosphere/source/htcs/impl/htcs_service.cpp @@ -259,7 +259,7 @@ namespace ams::htcs::impl { Result HtcsService::SendSmallContinue(s64 *out_size, const char *buffer, s64 buffer_size, u32 task_id, s32 desc) { /* Verify the task. */ - R_TRY(m_rpc_client->VerifyTaskIdWitHandle(task_id, desc)); + R_TRY(m_rpc_client->VerifyTaskIdWithHandle(task_id, desc)); /* Continue the task. */ R_TRY(m_rpc_client->SendContinue(task_id, buffer, buffer_size)); @@ -293,7 +293,7 @@ namespace ams::htcs::impl { Result HtcsService::SendContinue(s64 *out_size, const char *buffer, s64 buffer_size, u32 task_id, s32 desc) { /* Verify the task. */ - R_TRY(m_rpc_client->VerifyTaskIdWitHandle(task_id, desc)); + R_TRY(m_rpc_client->VerifyTaskIdWithHandle(task_id, desc)); /* Wait for the task to notify. */ m_rpc_client->WaitNotification(task_id); @@ -307,12 +307,14 @@ namespace ams::htcs::impl { R_TRY(m_data_channel_manager->Send(buffer, buffer_size, task_id)); } + /* Set output. */ + *out_size = buffer_size; return ResultSuccess(); } Result HtcsService::SendResults(s32 *out_err, s64 *out_size, u32 task_id, s32 desc) { /* Verify the task. */ - R_TRY(m_rpc_client->VerifyTaskIdWitHandle(task_id, desc)); + R_TRY(m_rpc_client->VerifyTaskIdWithHandle(task_id, desc)); /* Finish the task. */ htcs::SocketError err; @@ -337,7 +339,7 @@ namespace ams::htcs::impl { Result HtcsService::ReceiveResults(s32 *out_err, s64 *out_size, char *buffer, s64 buffer_size, u32 task_id, s32 desc) { /* Verify the task. */ - R_TRY(m_rpc_client->VerifyTaskIdWitHandle(task_id, desc)); + R_TRY(m_rpc_client->VerifyTaskIdWithHandle(task_id, desc)); /* Get the result. */ htcs::SocketError err; diff --git a/libraries/libstratosphere/source/sf/hipc/sf_hipc_server_session_manager.cpp b/libraries/libstratosphere/source/sf/hipc/sf_hipc_server_session_manager.cpp index a07196ef6..0e19a97df 100644 --- a/libraries/libstratosphere/source/sf/hipc/sf_hipc_server_session_manager.cpp +++ b/libraries/libstratosphere/source/sf/hipc/sf_hipc_server_session_manager.cpp @@ -209,6 +209,7 @@ namespace ams::sf::hipc { this->CloseSessionImpl(session); return ResultSuccess(); } + switch (GetCmifCommandType(message)) { case CmifCommandType_Close: { From ce149f996c5772601680c413c9ec5fc8d1fd02a8 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 23 Feb 2021 04:10:20 -0800 Subject: [PATCH 082/280] htc: configure usage via system setting --- config_templates/system_settings.ini | 3 +++ .../libstratosphere/source/boot2/boot2_api.cpp | 15 +++++++++++++-- .../source/htcs/impl/htcs_service.hpp | 2 -- .../ams_mitm/source/set_mitm/settings_sd_kvs.cpp | 5 +++++ 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/config_templates/system_settings.ini b/config_templates/system_settings.ini index 3059cb839..a5c29ee15 100644 --- a/config_templates/system_settings.ini +++ b/config_templates/system_settings.ini @@ -52,6 +52,9 @@ ; Controls whether dns.mitm logs to the sd card for debugging ; 0 = Disabled, 1 = Enabled ; enable_dns_mitm_debug_log = u8!0x0 +; Controls whether htc is enabled +; 0 = Disabled, 1 = Enabled +; enable_htc = u8!0x0 [hbloader] ; Controls the size of the homebrew heap when running as applet. ; If set to zero, all available applet memory is used as heap. diff --git a/libraries/libstratosphere/source/boot2/boot2_api.cpp b/libraries/libstratosphere/source/boot2/boot2_api.cpp index baa539d61..04b359045 100644 --- a/libraries/libstratosphere/source/boot2/boot2_api.cpp +++ b/libraries/libstratosphere/source/boot2/boot2_api.cpp @@ -34,7 +34,6 @@ namespace ams::boot2 { constexpr size_t NumPreSdCardLaunchPrograms = util::size(PreSdCardLaunchPrograms); constexpr ncm::SystemProgramId AdditionalLaunchPrograms[] = { - ncm::SystemProgramId::Htc, /* htc */ /* TODO: should we do boot!use_htc_gen2, with default to on in custom settings? */ ncm::SystemProgramId::Am, /* am */ ncm::SystemProgramId::NvServices, /* nvservices */ ncm::SystemProgramId::NvnFlinger, /* nvnflinger */ @@ -80,7 +79,6 @@ namespace ams::boot2 { constexpr size_t NumAdditionalLaunchPrograms = util::size(AdditionalLaunchPrograms); constexpr ncm::SystemProgramId AdditionalMaintenanceLaunchPrograms[] = { - ncm::SystemProgramId::Htc, /* htc */ ncm::SystemProgramId::Am, /* am */ ncm::SystemProgramId::NvServices, /* nvservices */ ncm::SystemProgramId::NvnFlinger, /* nvnflinger */ @@ -188,6 +186,12 @@ namespace ams::boot2 { return force_maintenance != 0; } + bool IsHtcEnabled() { + u8 enable_htc = 1; + settings::fwdbg::GetSettingsItemValue(&enable_htc, sizeof(enable_htc), "atmosphere", "enable_htc"); + return enable_htc != 0; + } + bool IsMaintenanceMode() { /* Contact set:sys, retrieve boot!force_maintenance. */ if (IsForceMaintenance()) { @@ -379,6 +383,13 @@ namespace ams::boot2 { /* Check for and forward declare non-atmosphere mitm modules. */ DetectAndDeclareFutureMitms(); + /* Device whether to launch tma or htc. */ + if (IsHtcEnabled()) { + LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Htc, ncm::StorageId::None), 0); + } else { + LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Tma, ncm::StorageId::None), 0); + } + /* Launch additional programs. */ if (maintenance) { LaunchList(AdditionalMaintenanceLaunchPrograms, NumAdditionalMaintenanceLaunchPrograms); diff --git a/libraries/libstratosphere/source/htcs/impl/htcs_service.hpp b/libraries/libstratosphere/source/htcs/impl/htcs_service.hpp index 8d656c86b..84127d5f0 100644 --- a/libraries/libstratosphere/source/htcs/impl/htcs_service.hpp +++ b/libraries/libstratosphere/source/htcs/impl/htcs_service.hpp @@ -30,8 +30,6 @@ namespace ams::htcs::impl { public: HtcsService(mem::StandardAllocator *allocator, htc::server::driver::IDriver *drv, htc::server::rpc::RpcClient *rc, rpc::DataChannelManager *dcm) : m_allocator(allocator), m_driver(drv), m_rpc_client(rc), m_data_channel_manager(dcm) { /* ... */ } - public: - /* TODO */ public: Result CreateSocket(s32 *out_err, s32 *out_desc, bool enable_disconnection_emulation); Result DestroySocket(s32 desc); diff --git a/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp b/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp index 1ed8312e5..7a294145e 100644 --- a/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp +++ b/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp @@ -362,6 +362,11 @@ namespace ams::settings::fwdbg { /* 0 = Disabled, 1 = Enabled */ R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_dns_mitm_debug_log", "u8!0x0")); + /* Controls whether htc is enabled. */ + /* TODO: Change this to default 1 when tma2 is ready for inclusion in atmosphere releases. */ + /* 0 = Disabled, 1 = Enabled */ + R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_htc", "u8!0x0")); + /* Hbloader custom settings. */ /* Controls the size of the homebrew heap when running as applet. */ From 8b32b9eadf360de1cb688f2dfa770209e143c30e Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 23 Feb 2021 04:19:15 -0800 Subject: [PATCH 083/280] kern: Increase reserved system memory, require mesosphere for htc/tma --- .../source/board/nintendo/nx/kern_k_system_control.cpp | 2 +- libraries/libstratosphere/source/boot2/boot2_api.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp index 783ef1904..8120957dd 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp @@ -397,7 +397,7 @@ namespace ams::kern::board::nintendo::nx { }(); /* Return (possibly) adjusted size. */ - constexpr size_t ExtraSystemMemoryForAtmosphere = 33_MB; + constexpr size_t ExtraSystemMemoryForAtmosphere = 40_MB; return base_pool_size - ExtraSystemMemoryForAtmosphere - KTraceBufferSize; } diff --git a/libraries/libstratosphere/source/boot2/boot2_api.cpp b/libraries/libstratosphere/source/boot2/boot2_api.cpp index 04b359045..671ad4996 100644 --- a/libraries/libstratosphere/source/boot2/boot2_api.cpp +++ b/libraries/libstratosphere/source/boot2/boot2_api.cpp @@ -384,7 +384,7 @@ namespace ams::boot2 { DetectAndDeclareFutureMitms(); /* Device whether to launch tma or htc. */ - if (IsHtcEnabled()) { + if (svc::IsKernelMesosphere() && IsHtcEnabled()) { LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Htc, ncm::StorageId::None), 0); } else { LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Tma, ncm::StorageId::None), 0); From b5ab491603665a77207253cfa1b56ba3f61872b0 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 23 Feb 2021 15:47:28 -0800 Subject: [PATCH 084/280] htc: implement htcmisc service object commands --- .../htc/server/htc_htc_service_object.cpp | 117 +++++++++++++----- .../source/htc/server/htc_htcmisc_impl.cpp | 55 ++++++++ .../source/htc/server/htc_htcmisc_impl.hpp | 9 +- .../source/htc/server/htc_observer.hpp | 3 + .../htc/server/rpc/htc_htcmisc_rpc_tasks.cpp | 7 +- .../htc/server/rpc/htc_htcmisc_rpc_tasks.hpp | 6 +- .../source/htcfs/htcfs_client.hpp | 3 + .../source/htcfs/htcfs_client_impl.cpp | 78 ++++++++++++ .../source/htcfs/htcfs_client_impl.hpp | 3 + .../source/htcfs/htcfs_header_factory.hpp | 8 ++ .../source/htcfs/htcfs_working_directory.cpp | 29 +++++ .../source/htcfs/htcfs_working_directory.hpp | 24 ++++ .../include/vapours/results/htcfs_results.hpp | 1 + 13 files changed, 302 insertions(+), 41 deletions(-) create mode 100644 libraries/libstratosphere/source/htcfs/htcfs_working_directory.cpp create mode 100644 libraries/libstratosphere/source/htcfs/htcfs_working_directory.hpp diff --git a/libraries/libstratosphere/source/htc/server/htc_htc_service_object.cpp b/libraries/libstratosphere/source/htc/server/htc_htc_service_object.cpp index be8e4c001..a93f6a353 100644 --- a/libraries/libstratosphere/source/htc/server/htc_htc_service_object.cpp +++ b/libraries/libstratosphere/source/htc/server/htc_htc_service_object.cpp @@ -15,6 +15,7 @@ */ #include #include "htc_htc_service_object.hpp" +#include "../../htcfs/htcfs_working_directory.hpp" namespace ams::htc::server { @@ -29,75 +30,125 @@ namespace ams::htc::server { } Result HtcServiceObject::GetEnvironmentVariable(sf::Out out_size, const sf::OutBuffer &out, const sf::InBuffer &name) { - AMS_ABORT("HtcServiceObject::GetEnvironmentVariable"); + /* Get the variable. */ + size_t var_size; + R_TRY(m_misc_impl.GetEnvironmentVariable(std::addressof(var_size), reinterpret_cast(out.GetPointer()), out.GetSize(), reinterpret_cast(name.GetPointer()), name.GetSize())); + + /* Check the output size. */ + R_UNLESS(util::IsIntValueRepresentable(var_size), htc::ResultUnknown()); + + /* Set the output size. */ + *out_size = static_cast(var_size); + return ResultSuccess(); } Result HtcServiceObject::GetEnvironmentVariableLength(sf::Out out_size, const sf::InBuffer &name) { - AMS_ABORT("HtcServiceObject::GetEnvironmentVariableLength"); + /* Get the variable. */ + size_t var_size; + R_TRY(m_misc_impl.GetEnvironmentVariableLength(std::addressof(var_size), reinterpret_cast(name.GetPointer()), name.GetSize())); + + /* Check the output size. */ + R_UNLESS(util::IsIntValueRepresentable(var_size), htc::ResultUnknown()); + + /* Set the output size. */ + *out_size = static_cast(var_size); + return ResultSuccess(); } Result HtcServiceObject::GetHostConnectionEvent(sf::OutCopyHandle out) { - AMS_ABORT("HtcServiceObject::GetHostConnectionEvent"); + /* Set the output handle. */ + *out = m_observer.GetConnectEvent()->GetReadableHandle(); + return ResultSuccess(); } Result HtcServiceObject::GetHostDisconnectionEvent(sf::OutCopyHandle out) { - AMS_ABORT("HtcServiceObject::GetHostDisconnectionEvent"); + /* Set the output handle. */ + *out = m_observer.GetDisconnectEvent()->GetReadableHandle(); + return ResultSuccess(); } Result HtcServiceObject::GetHostConnectionEventForSystem(sf::OutCopyHandle out) { - AMS_ABORT("HtcServiceObject::GetHostConnectionEventForSystem"); + /* NOTE: Nintendo presumably reserved this command in case they need it, but they haven't implemented it yet. */ + AMS_ABORT("HostEventForSystem not implemented."); } Result HtcServiceObject::GetHostDisconnectionEventForSystem(sf::OutCopyHandle out) { - AMS_ABORT("HtcServiceObject::GetHostDisconnectionEventForSystem"); - } - - Result HtcServiceObject::GetBridgeIpAddress(const sf::OutBuffer &out) { - AMS_ABORT("HtcServiceObject::GetBridgeIpAddress"); - } - - Result HtcServiceObject::GetBridgePort(const sf::OutBuffer &out) { - AMS_ABORT("HtcServiceObject::GetBridgePort"); - } - - Result HtcServiceObject::SetCradleAttached(bool attached) { - AMS_ABORT("HtcServiceObject::SetCradleAttached"); - } - - Result HtcServiceObject::GetBridgeSubnetMask(const sf::OutBuffer &out) { - AMS_ABORT("HtcServiceObject::GetBridgeSubnetMask"); - } - - Result HtcServiceObject::GetBridgeMacAddress(const sf::OutBuffer &out) { - AMS_ABORT("HtcServiceObject::GetBridgeMacAddress"); + /* NOTE: Nintendo presumably reserved this command in case they need it, but they haven't implemented it yet. */ + AMS_ABORT("HostEventForSystem not implemented."); } Result HtcServiceObject::GetWorkingDirectoryPath(const sf::OutBuffer &out, s32 max_len) { - AMS_ABORT("HtcServiceObject::GetWorkingDirectoryPath"); + return htcfs::GetWorkingDirectory(reinterpret_cast(out.GetPointer()), max_len); } Result HtcServiceObject::GetWorkingDirectoryPathSize(sf::Out out_size) { - AMS_ABORT("HtcServiceObject::GetWorkingDirectoryPathSize"); + return htcfs::GetWorkingDirectorySize(out_size.GetPointer()); } Result HtcServiceObject::RunOnHostStart(sf::Out out_id, sf::OutCopyHandle out, const sf::InBuffer &args) { - AMS_ABORT("HtcServiceObject::RunOnHostStart"); + /* Begin the run on host task. */ + R_TRY(m_misc_impl.RunOnHostBegin(out_id.GetPointer(), out.GetHandlePointer(), reinterpret_cast(args.GetPointer()), args.GetSize())); + + /* Add the task id to our set. */ + { + std::scoped_lock lk(m_mutex); + m_set.insert(*out_id); + } + + /* Mark the output event as managed. */ + out.SetManaged(true); + return ResultSuccess(); } Result HtcServiceObject::RunOnHostResults(sf::Out out_result, u32 id) { - AMS_ABORT("HtcServiceObject::RunOnHostResults"); + /* Verify that we have the task. */ + { + std::scoped_lock lk(m_mutex); + R_UNLESS(m_set.erase(id), htc::ResultInvalidTaskId()); + } + + /* Finish the run on host task. */ + return m_misc_impl.RunOnHostEnd(out_result.GetPointer(), id); + } + + Result HtcServiceObject::GetBridgeIpAddress(const sf::OutBuffer &out) { + /* NOTE: Atmosphere does not support HostBridge, and it's unclear if we ever will. */ + AMS_ABORT("HostBridge currently not supported."); + } + + Result HtcServiceObject::GetBridgePort(const sf::OutBuffer &out) { + /* NOTE: Atmosphere does not support HostBridge, and it's unclear if we ever will. */ + AMS_ABORT("HostBridge currently not supported."); + } + + Result HtcServiceObject::SetCradleAttached(bool attached) { + /* NOTE: Atmosphere does not support HostBridge, and it's unclear if we ever will. */ + AMS_ABORT("HostBridge currently not supported."); + } + + Result HtcServiceObject::GetBridgeSubnetMask(const sf::OutBuffer &out) { + /* NOTE: Atmosphere does not support HostBridge, and it's unclear if we ever will. */ + AMS_ABORT("HostBridge currently not supported."); + } + + Result HtcServiceObject::GetBridgeMacAddress(const sf::OutBuffer &out) { + /* NOTE: Atmosphere does not support HostBridge, and it's unclear if we ever will. */ + AMS_ABORT("HostBridge currently not supported."); } Result HtcServiceObject::SetBridgeIpAddress(const sf::InBuffer &arg) { - AMS_ABORT("HtcServiceObject::SetBridgeIpAddress"); + /* NOTE: Atmosphere does not support HostBridge, and it's unclear if we ever will. */ + AMS_ABORT("HostBridge currently not supported."); } Result HtcServiceObject::SetBridgeSubnetMask(const sf::InBuffer &arg) { - AMS_ABORT("HtcServiceObject::SetBridgeSubnetMask"); + /* NOTE: Atmosphere does not support HostBridge, and it's unclear if we ever will. */ + AMS_ABORT("HostBridge currently not supported."); } Result HtcServiceObject::SetBridgePort(const sf::InBuffer &arg) { - AMS_ABORT("HtcServiceObject::SetBridgePort"); + /* NOTE: Atmosphere does not support HostBridge, and it's unclear if we ever will. */ + AMS_ABORT("HostBridge currently not supported."); } } diff --git a/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp b/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp index 3ed5d71e7..88728eea4 100644 --- a/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp +++ b/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp @@ -76,6 +76,61 @@ namespace ams::htc::server { m_cancel_event.Signal(); } + void HtcmiscImpl::WaitTask(u32 task_id) { + return m_rpc_client.Wait(task_id); + } + + Result HtcmiscImpl::GetEnvironmentVariable(size_t *out_size, char *dst, size_t dst_size, const char *name, size_t name_size) { + /* Begin the task. */ + u32 task_id; + R_TRY(m_rpc_client.Begin(std::addressof(task_id), name, name_size)); + + /* Wait for the task to complete. */ + this->WaitTask(task_id); + + /* Finish the task. */ + R_TRY(m_rpc_client.End(task_id, out_size, dst, dst_size)); + + return ResultSuccess(); + } + + Result HtcmiscImpl::GetEnvironmentVariableLength(size_t *out_size, const char *name, size_t name_size) { + /* Begin the task. */ + u32 task_id; + R_TRY(m_rpc_client.Begin(std::addressof(task_id), name, name_size)); + + /* Wait for the task to complete. */ + this->WaitTask(task_id); + + /* Finish the task. */ + R_TRY(m_rpc_client.End(task_id, out_size)); + + return ResultSuccess(); + } + + Result HtcmiscImpl::RunOnHostBegin(u32 *out_task_id, Handle *out_event, const char *args, size_t args_size) { + /* Begin the task. */ + u32 task_id; + R_TRY(m_rpc_client.Begin(std::addressof(task_id), args, args_size)); + + /* Detach the task. */ + *out_task_id = task_id; + *out_event = m_rpc_client.DetachReadableHandle(task_id); + + return ResultSuccess(); + } + + Result HtcmiscImpl::RunOnHostEnd(s32 *out_result, u32 task_id) { + /* Finish the task. */ + s32 res; + R_TRY(m_rpc_client.End(task_id, std::addressof(res))); + + /* Set output. */ + *out_result = res; + + return ResultSuccess(); + } + void HtcmiscImpl::ClientThread() { /* Loop so long as we're not cancelled. */ while (!m_cancelled) { diff --git a/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.hpp b/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.hpp index 3317bda4c..d3e9b4e68 100644 --- a/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.hpp +++ b/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.hpp @@ -55,9 +55,16 @@ namespace ams::htc::server { void SetServerConnectionEvent(bool en); void UpdateConnectionEvent(); + + void WaitTask(u32 task_id); public: void Cancel(); - /* TODO */ + + Result GetEnvironmentVariable(size_t *out_size, char *dst, size_t dst_size, const char *name, size_t name_size); + Result GetEnvironmentVariableLength(size_t *out_size, const char *name, size_t name_size); + + Result RunOnHostBegin(u32 *out_task_id, Handle *out_event, const char *args, size_t args_size); + Result RunOnHostEnd(s32 *out_result, u32 task_id); }; } diff --git a/libraries/libstratosphere/source/htc/server/htc_observer.hpp b/libraries/libstratosphere/source/htc/server/htc_observer.hpp index ea9bf2d2d..ecd78e295 100644 --- a/libraries/libstratosphere/source/htc/server/htc_observer.hpp +++ b/libraries/libstratosphere/source/htc/server/htc_observer.hpp @@ -41,6 +41,9 @@ namespace ams::htc::server { Result Start(); void UpdateEvent(); + public: + os::SystemEvent *GetConnectEvent() { return std::addressof(m_connect_event); } + os::SystemEvent *GetDisconnectEvent() { return std::addressof(m_disconnect_event); } }; } diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_tasks.cpp b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_tasks.cpp index 95972a110..fd67ffd9d 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_tasks.cpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_tasks.cpp @@ -61,7 +61,7 @@ namespace ams::htc::server::rpc { Task::Complete(); } - Result GetEnvironmentVariableTask::GetResult(size_t *out, char *dst, size_t size) { + Result GetEnvironmentVariableTask::GetResult(size_t *out, char *dst, size_t size) const { /* Check our task state. */ AMS_ASSERT(this->GetTaskState() == RpcTaskState::Completed); @@ -166,7 +166,7 @@ namespace ams::htc::server::rpc { Task::Complete(); } - Result GetEnvironmentVariableLengthTask::GetResult(size_t *out) { + Result GetEnvironmentVariableLengthTask::GetResult(size_t *out) const { /* Check our task state. */ AMS_ASSERT(this->GetTaskState() == RpcTaskState::Completed); @@ -241,7 +241,7 @@ namespace ams::htc::server::rpc { Task::Complete(); } - Result RunOnHostTask::GetResult(int *out) { + Result RunOnHostTask::GetResult(int *out) const { *out = m_host_result; return ResultSuccess(); } @@ -290,5 +290,4 @@ namespace ams::htc::server::rpc { return m_system_event.GetBase(); } - } diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_tasks.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_tasks.hpp index 20268570f..588d5b9a0 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_tasks.hpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_tasks.hpp @@ -88,7 +88,7 @@ namespace ams::htc::server::rpc { Result SetArguments(const char *args, size_t size); void Complete(HtcmiscResult result, const char *data, size_t size); - Result GetResult(size_t *out, char *dst, size_t size); + Result GetResult(size_t *out, char *dst, size_t size) const; const char *GetName() const { return m_name; } int GetNameSize() const { return m_name_size; } @@ -110,7 +110,7 @@ namespace ams::htc::server::rpc { Result SetArguments(const char *args, size_t size); void Complete(HtcmiscResult result, const char *data, size_t size); - Result GetResult(size_t *out); + Result GetResult(size_t *out) const; const char *GetName() const { return m_name; } int GetNameSize() const { return m_name_size; } @@ -133,7 +133,7 @@ namespace ams::htc::server::rpc { Result SetArguments(const char *args, size_t size); void Complete(int host_result); - Result GetResult(int *out); + Result GetResult(int *out) const; const char *GetCommand() const { return m_command; } int GetCommandSize() const { return m_command_size; } diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client.hpp b/libraries/libstratosphere/source/htcfs/htcfs_client.hpp index 716d025c9..34165f541 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client.hpp @@ -60,6 +60,9 @@ namespace ams::htcfs { Result FlushFile(s32 handle) { return ConvertToFsResult(m_impl.FlushFile(handle)); } Result GetPriorityForFile(s32 *out, s32 handle) { return ConvertToFsResult(m_impl.GetPriorityForFile(out, handle)); } Result SetPriorityForFile(s32 priority, s32 handle) { return ConvertToFsResult(m_impl.SetPriorityForFile(priority, handle)); } + + Result GetWorkingDirectory(char *dst, size_t dst_size) { return ConvertToFsResult(m_impl.GetWorkingDirectory(dst, dst_size)); } + Result GetWorkingDirectorySize(s32 *out) { return ConvertToFsResult(m_impl.GetWorkingDirectorySize(out)); } }; void InitializeClient(htclow::HtclowManager *manager); diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp index fbeacf55c..afed4f5c5 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp @@ -1539,5 +1539,83 @@ namespace ams::htcfs { return ResultSuccess(); } + Result ClientImpl::GetWorkingDirectory(char *dst, size_t dst_size) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + m_header_factory.MakeGetWorkingDirectoryHeader(std::addressof(request)); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type)); + + /* Check that we succeeded. */ + const auto htcfs_result = ConvertHtcfsResult(response.params[0]); + if (R_FAILED(htcfs_result)) { + R_UNLESS(response.body_size == 0, htcfs::ResultUnexpectedResponseBodySize()); + return htcfs_result; + } + + /* Check that the body size is valid. */ + R_UNLESS(response.body_size < static_cast(dst_size), htcfs::ResultUnexpectedResponseBodySize()); + + /* Receive the response body. */ + R_TRY(this->ReceiveFromRpcChannel(dst, response.body_size)); + + /* Null-terminate the response body. */ + dst[response.body_size] = '\x00'; + + return ResultSuccess(); + } + + Result ClientImpl::GetWorkingDirectorySize(s32 *out) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Initialize our rpc channel. */ + R_TRY(this->InitializeRpcChannel()); + + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + m_header_factory.MakeGetWorkingDirectorySizeHeader(std::addressof(request)); + + /* Send the request to the host. */ + R_TRY(this->SendRequest(request)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type)); + + /* Check that we succeeded. */ + const auto htcfs_result = ConvertHtcfsResult(response.params[0]); + if (R_FAILED(htcfs_result)) { + R_UNLESS(response.body_size == 0, htcfs::ResultUnexpectedResponseBodySize()); + return htcfs_result; + } + + /* Check that the size is representable. */ + R_UNLESS(util::IsIntValueRepresentable(response.params[1]), htcfs::ResultInvalidSize()); + + /* Set the output size. */ + *out = static_cast(response.params[1]); + + return ResultSuccess(); + } } diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp index 219f289a1..ebe9fb428 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp @@ -90,6 +90,9 @@ namespace ams::htcfs { Result FlushFile(s32 handle); Result GetPriorityForFile(s32 *out, s32 handle); Result SetPriorityForFile(s32 priority, s32 handle); + + Result GetWorkingDirectory(char *dst, size_t dst_size); + Result GetWorkingDirectorySize(s32 *out); private: int WaitAny(htclow::ChannelState state, os::EventType *event); diff --git a/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp b/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp index b998c47eb..636f9553d 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp @@ -179,6 +179,14 @@ namespace ams::htcfs { return this->MakeRequestHeader(out, PacketType::GetEntryCount, 0, handle); } + void MakeGetWorkingDirectoryHeader(Header *out) { + return this->MakeRequestHeader(out, PacketType::GetWorkingDirectory, 0); + } + + void MakeGetWorkingDirectorySizeHeader(Header *out) { + return this->MakeRequestHeader(out, PacketType::GetWorkingDirectorySize, 0); + } + void MakeReadDirectoryHeader(Header *out, s32 handle, size_t max_out_entries) { return this->MakeRequestHeader(out, PacketType::ReadDirectory, 0, handle, max_out_entries); } diff --git a/libraries/libstratosphere/source/htcfs/htcfs_working_directory.cpp b/libraries/libstratosphere/source/htcfs/htcfs_working_directory.cpp new file mode 100644 index 000000000..22053d917 --- /dev/null +++ b/libraries/libstratosphere/source/htcfs/htcfs_working_directory.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htcfs_client.hpp" + +namespace ams::htcfs { + + Result GetWorkingDirectory(char *dst, size_t dst_size) { + return htcfs::GetClient().GetWorkingDirectory(dst, dst_size); + } + + Result GetWorkingDirectorySize(s32 *out) { + return htcfs::GetClient().GetWorkingDirectorySize(out); + } + +} diff --git a/libraries/libstratosphere/source/htcfs/htcfs_working_directory.hpp b/libraries/libstratosphere/source/htcfs/htcfs_working_directory.hpp new file mode 100644 index 000000000..53807573d --- /dev/null +++ b/libraries/libstratosphere/source/htcfs/htcfs_working_directory.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htcfs { + + Result GetWorkingDirectory(char *dst, size_t dst_size); + Result GetWorkingDirectorySize(s32 *out); + +} diff --git a/libraries/libvapours/include/vapours/results/htcfs_results.hpp b/libraries/libvapours/include/vapours/results/htcfs_results.hpp index fc35e68f9..68ca930f5 100644 --- a/libraries/libvapours/include/vapours/results/htcfs_results.hpp +++ b/libraries/libvapours/include/vapours/results/htcfs_results.hpp @@ -35,6 +35,7 @@ namespace ams::htcfs { R_DEFINE_ERROR_RESULT(UnexpectedResponseBody, 116); R_DEFINE_ERROR_RANGE(InternalError, 200, 299); + R_DEFINE_ERROR_RESULT(InvalidSize, 201); R_DEFINE_ERROR_RESULT(UnknownError, 211); R_DEFINE_ERROR_RESULT(UnsupportedProtocolVersion, 212); R_DEFINE_ERROR_RESULT(InvalidRequest, 213); From 1c974a387ca405a42c5a5dbc9f599a67eea1227b Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 24 Feb 2021 01:45:55 -0800 Subject: [PATCH 085/280] htc: implement socket driver (socket api not really impl'd yet) --- .../include/stratosphere/socket.hpp | 4 + .../stratosphere/socket/socket_api.hpp | 28 ++ .../stratosphere/socket/socket_config.hpp | 89 ++++++ .../stratosphere/socket/socket_constants.hpp | 39 +++ .../stratosphere/socket/socket_errno.hpp | 15 +- .../stratosphere/socket/socket_options.hpp | 38 +++ .../socket/socket_system_config.hpp | 60 ++++ .../stratosphere/socket/socket_types.hpp | 15 +- .../ctrl/htclow_ctrl_settings_holder.hpp | 10 +- .../htclow/driver/htclow_driver_manager.cpp | 7 +- .../htclow/driver/htclow_driver_manager.hpp | 7 +- .../htclow_driver_memory_management.cpp | 61 ++++ .../htclow_driver_memory_management.hpp | 28 ++ .../htclow_socket_discovery_manager.cpp | 151 ++++++++++ .../htclow_socket_discovery_manager.hpp | 49 +++ .../driver/htclow_socket_discovery_util.cpp | 132 ++++++++ .../driver/htclow_socket_discovery_util.hpp | 38 +++ .../htclow/driver/htclow_socket_driver.cpp | 283 ++++++++++++++++++ .../htclow/driver/htclow_socket_driver.hpp | 76 +++++ .../source/htclow/driver/htclow_usb_impl.cpp | 14 +- .../source/htclow/htclow_manager_impl.cpp | 2 +- .../source/socket/impl/socket_allocator.hpp | 2 + .../source/socket/impl/socket_api.hpp | 10 + .../socket/impl/socket_api.os.horizon.cpp | 193 +++++++++++- .../source/socket/socket_api.cpp | 12 + .../libvapours/include/vapours/results.hpp | 1 + .../vapours/results/htclow_results.hpp | 12 + .../vapours/results/socket_results.hpp | 26 ++ .../source/dns_mitm/dnsmitm_module.cpp | 5 + stratosphere/htc/htc.json | 2 +- stratosphere/htc/source/htc_main.cpp | 15 + 31 files changed, 1389 insertions(+), 35 deletions(-) create mode 100644 libraries/libstratosphere/include/stratosphere/socket/socket_config.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/socket/socket_constants.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/socket/socket_options.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/socket/socket_system_config.hpp create mode 100644 libraries/libstratosphere/source/htclow/driver/htclow_driver_memory_management.cpp create mode 100644 libraries/libstratosphere/source/htclow/driver/htclow_driver_memory_management.hpp create mode 100644 libraries/libstratosphere/source/htclow/driver/htclow_socket_discovery_manager.cpp create mode 100644 libraries/libstratosphere/source/htclow/driver/htclow_socket_discovery_manager.hpp create mode 100644 libraries/libstratosphere/source/htclow/driver/htclow_socket_discovery_util.cpp create mode 100644 libraries/libstratosphere/source/htclow/driver/htclow_socket_discovery_util.hpp create mode 100644 libraries/libstratosphere/source/htclow/driver/htclow_socket_driver.cpp create mode 100644 libraries/libstratosphere/source/htclow/driver/htclow_socket_driver.hpp create mode 100644 libraries/libvapours/include/vapours/results/socket_results.hpp diff --git a/libraries/libstratosphere/include/stratosphere/socket.hpp b/libraries/libstratosphere/include/stratosphere/socket.hpp index f42d359b9..29c9af1c9 100644 --- a/libraries/libstratosphere/include/stratosphere/socket.hpp +++ b/libraries/libstratosphere/include/stratosphere/socket.hpp @@ -18,5 +18,9 @@ #include #include #include +#include #include +#include +#include +#include #include diff --git a/libraries/libstratosphere/include/stratosphere/socket/socket_api.hpp b/libraries/libstratosphere/include/stratosphere/socket/socket_api.hpp index cc99f0aa8..a8de2535c 100644 --- a/libraries/libstratosphere/include/stratosphere/socket/socket_api.hpp +++ b/libraries/libstratosphere/include/stratosphere/socket/socket_api.hpp @@ -16,7 +16,9 @@ #pragma once #include #include +#include #include +#include namespace ams::socket { @@ -28,4 +30,30 @@ namespace ams::socket { u32 InetNtohl(u32 net); u16 InetNtohs(u16 net); + Result Initialize(const Config &config); + Result Finalize(); + + Result InitializeAllocatorForInternal(void *buffer, size_t size); + + ssize_t RecvFrom(s32 desc, void *buffer, size_t buffer_size, MsgFlag flags, SockAddr *out_address, SockLenT *out_addr_len); + ssize_t Recv(s32 desc, void *buffer, size_t buffer_size, MsgFlag flags); + + ssize_t SendTo(s32 desc, const void *buffer, size_t buffer_size, MsgFlag flags, const SockAddr *address, SockLenT len); + ssize_t Send(s32 desc, const void *buffer, size_t buffer_size, MsgFlag flags); + + s32 Shutdown(s32 desc, ShutdownMethod how); + + s32 SocketExempt(Family domain, Type type, Protocol protocol); + + s32 Accept(s32 desc, SockAddr *out_address, SockLenT *out_addr_len); + s32 Bind(s32 desc, const SockAddr *address, SockLenT len); + + s32 GetSockName(s32 desc, SockAddr *out_address, SockLenT *out_addr_len); + s32 SetSockOpt(s32 desc, Level level, Option option_name, const void *option_value, SockLenT option_size); + + s32 Listen(s32 desc, s32 backlog); + + s32 Close(s32 desc); + + } diff --git a/libraries/libstratosphere/include/stratosphere/socket/socket_config.hpp b/libraries/libstratosphere/include/stratosphere/socket/socket_config.hpp new file mode 100644 index 000000000..0879a2663 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/socket/socket_config.hpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include + +namespace ams::socket { + + constexpr ALWAYS_INLINE size_t AlignMss(size_t size) { + return util::DivideUp(size, static_cast(1500)); + } + + class Config { + private: + u32 m_version; + protected: + bool m_system; + bool m_smbp; + void *m_memory_pool; + size_t m_memory_pool_size; + size_t m_allocator_pool_size; + size_t m_tcp_initial_send_buffer_size; + size_t m_tcp_initial_receive_buffer_size; + size_t m_tcp_auto_send_buffer_size_max; + size_t m_tcp_auto_receive_buffer_size_max; + size_t m_udp_send_buffer_size; + size_t m_udp_receive_buffer_size; + int m_sb_efficiency; + int m_concurrency_count_max; + public: + constexpr Config(void *mp, size_t mp_sz, size_t ap, size_t is, size_t ir, size_t as, size_t ar, size_t us, size_t ur, int sbe, int c) + : m_version(LibraryVersion), + m_system(false), + m_smbp(false), + m_memory_pool(mp), + m_memory_pool_size(mp_sz), + m_allocator_pool_size(ap), + m_tcp_initial_send_buffer_size(is), + m_tcp_initial_receive_buffer_size(ir), + m_tcp_auto_send_buffer_size_max(as), + m_tcp_auto_receive_buffer_size_max(ar), + m_udp_send_buffer_size(us), + m_udp_receive_buffer_size(ur), + m_sb_efficiency(sbe), + m_concurrency_count_max(c) + { + /* ... */ + } + + constexpr u32 GetVersion() const { return m_version; } + constexpr bool IsSystemClient() const { return m_system; } + constexpr bool IsSmbpClient() const { return m_smbp; } + constexpr void *GetMemoryPool() const { return m_memory_pool; } + constexpr size_t GetMemoryPoolSize() const { return m_memory_pool_size; } + constexpr size_t GetAllocatorPoolSize() const { return m_allocator_pool_size; } + constexpr size_t GetTcpInitialSendBufferSize() const { return m_tcp_initial_send_buffer_size; } + constexpr size_t GetTcpInitialReceiveBufferSize() const { return m_tcp_initial_receive_buffer_size; } + constexpr size_t GetTcpAutoSendBufferSizeMax() const { return m_tcp_auto_send_buffer_size_max; } + constexpr size_t GetTcpAutoReceiveBufferSizeMax() const { return m_tcp_auto_receive_buffer_size_max; } + constexpr size_t GetUdpSendBufferSize() const { return m_udp_send_buffer_size; } + constexpr size_t GetUdpReceiveBufferSize() const { return m_udp_receive_buffer_size; } + constexpr int GetSocketBufferEfficiency() const { return m_sb_efficiency; } + constexpr int GetConcurrencyCountMax() const { return m_concurrency_count_max; } + + constexpr void SetTcpInitialSendBufferSize(size_t size) { m_tcp_initial_send_buffer_size = size; } + constexpr void SetTcpInitialReceiveBufferSize(size_t size) { m_tcp_initial_receive_buffer_size = size; } + constexpr void SetTcpAutoSendBufferSizeMax(size_t size) { m_tcp_auto_send_buffer_size_max = size; } + constexpr void SetTcpAutoReceiveBufferSizeMax(size_t size) { m_tcp_auto_receive_buffer_size_max = size; } + constexpr void SetUdpSendBufferSize(size_t size) { m_udp_send_buffer_size = size; } + constexpr void SetUdpReceiveBufferSize(size_t size) { m_udp_receive_buffer_size = size; } + constexpr void SetSocketBufferEfficiency(int sb) { AMS_ABORT_UNLESS(1 <= sb && sb <= 8); m_sb_efficiency = sb; } + constexpr void SetConcurrencyCountMax(int c) { m_concurrency_count_max = c; } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/socket/socket_constants.hpp b/libraries/libstratosphere/include/stratosphere/socket/socket_constants.hpp new file mode 100644 index 000000000..122e38f38 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/socket/socket_constants.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::socket { + + constexpr inline s32 InvalidSocket = -1; + constexpr inline s32 SocketError = -1; + + constexpr inline auto DefaultTcpAutoBufferSizeMax = 192_KB; + constexpr inline auto MinTransferMemorySize = (2 * DefaultTcpAutoBufferSizeMax + 128_KB); + constexpr inline auto MinSocketAllocatorSize = 128_KB; + constexpr inline auto MinSocketMemoryPoolSize = MinSocketAllocatorSize + MinTransferMemorySize; + constexpr inline auto MinMemHeapAllocatorSize = 16_KB; + constexpr inline auto MinimumSharedMbufPoolReservation = 4_KB; + + constexpr inline size_t MemoryPoolAlignment = 4_KB; + + constexpr inline auto ConcurrencyLimitMax = 14; + + /* TODO: Does this need to be 1 for sockets to work on lower firmware versions? */ + /* Is this value actually used/checked by bsdsockets sysmodule? */ + constexpr inline auto LibraryVersion = 7; + +} diff --git a/libraries/libstratosphere/include/stratosphere/socket/socket_errno.hpp b/libraries/libstratosphere/include/stratosphere/socket/socket_errno.hpp index faf8b2f9d..ce5513942 100644 --- a/libraries/libstratosphere/include/stratosphere/socket/socket_errno.hpp +++ b/libraries/libstratosphere/include/stratosphere/socket/socket_errno.hpp @@ -19,10 +19,21 @@ namespace ams::socket { enum class Errno : u32 { - ESuccess = 0, + ESuccess = 0, /* ... */ - ENoSpc = 28, + EAgain = 11, + ENoMem = 12, /* ... */ + EFault = 14, + /* ... */ + EInval = 22, + /* ... */ + ENoSpc = 28, + /* ... */ + EL3Hlt = 46, + /* ... */ + EOpNotSupp = 95, + ENotSup = EOpNotSupp, }; enum class HErrno : s32 { diff --git a/libraries/libstratosphere/include/stratosphere/socket/socket_options.hpp b/libraries/libstratosphere/include/stratosphere/socket/socket_options.hpp new file mode 100644 index 000000000..b7608745e --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/socket/socket_options.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::socket { + + enum class Level : s32 { + Sol_Ip = 0, + Sol_Icmp = 1, + Sol_Tcp = 6, + Sol_Udp = 17, + Sol_UdpLite = 136, + + Sol_Socket = 0xFFFF, + }; + + enum class Option : u32 { + So_Debug = (1 << 0), + /* ... */ + So_ReuseAddr = (1 << 2), + /* ... */ + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/socket/socket_system_config.hpp b/libraries/libstratosphere/include/stratosphere/socket/socket_system_config.hpp new file mode 100644 index 000000000..19b6615e3 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/socket/socket_system_config.hpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::socket { + + class SystemConfigDefault : public Config { + public: + static constexpr size_t DefaultTcpInitialSendBufferSize = 32_KB; + static constexpr size_t DefaultTcpInitialReceiveBufferSize = 64_KB; + static constexpr size_t DefaultTcpAutoSendBufferSizeMax = 256_KB; + static constexpr size_t DefaultTcpAutoReceiveBufferSizeMax = 256_KB; + static constexpr size_t DefaultUdpSendBufferSize = 9_KB; + static constexpr size_t DefaultUdpReceiveBufferSize = 42240; + static constexpr auto DefaultSocketBufferEfficiency = 2; + static constexpr auto DefaultConcurrency = 8; + static constexpr size_t DefaultAllocatorPoolSize = 128_KB; + + static constexpr size_t PerTcpSocketWorstCaseMemoryPoolSize = [] { + constexpr size_t WorstCaseTcpSendBufferSize = AlignMss(std::max(DefaultTcpInitialSendBufferSize, DefaultTcpAutoSendBufferSizeMax)); + constexpr size_t WorstCaseTcpReceiveBufferSize = AlignMss(std::max(DefaultTcpInitialReceiveBufferSize, DefaultTcpAutoReceiveBufferSizeMax)); + + return util::AlignUp(WorstCaseTcpSendBufferSize * DefaultSocketBufferEfficiency + WorstCaseTcpReceiveBufferSize * DefaultSocketBufferEfficiency, os::MemoryPageSize); + }(); + + static constexpr size_t PerUdpSocketWorstCaseMemoryPoolSize = [] { + constexpr size_t WorstCaseUdpSendBufferSize = AlignMss(DefaultUdpSendBufferSize); + constexpr size_t WorstCaseUdpReceiveBufferSize = AlignMss(DefaultUdpReceiveBufferSize); + + return util::AlignUp(WorstCaseUdpSendBufferSize * DefaultSocketBufferEfficiency + WorstCaseUdpReceiveBufferSize * DefaultSocketBufferEfficiency, os::MemoryPageSize); + }(); + public: + constexpr SystemConfigDefault(void *mp, size_t mp_sz, size_t ap, int c=DefaultConcurrency) + : Config(mp, mp_sz, ap, + DefaultTcpInitialSendBufferSize, DefaultTcpInitialReceiveBufferSize, + DefaultTcpAutoSendBufferSizeMax, DefaultTcpAutoReceiveBufferSizeMax, + DefaultUdpSendBufferSize, DefaultUdpReceiveBufferSize, + DefaultSocketBufferEfficiency, c) + { + /* Mark as system. */ + m_system = true; + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/socket/socket_types.hpp b/libraries/libstratosphere/include/stratosphere/socket/socket_types.hpp index c8bbb9ddf..08faab3ba 100644 --- a/libraries/libstratosphere/include/stratosphere/socket/socket_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/socket/socket_types.hpp @@ -79,6 +79,19 @@ namespace ams::socket { Pf_Max = Af_Max }; + enum class MsgFlag : s32 { + MsgFlag_None = (0 << 0), + /* ... */ + MsgFlag_WaitAll = (1 << 6), + /* ... */ + }; + + enum class ShutdownMethod : u32 { + Shut_Rd = 0, + Shut_Wr = 1, + Shut_RdWr = 2, + }; + struct HostEnt { char *h_name; char **h_aliases; @@ -98,7 +111,7 @@ namespace ams::socket { Ai_NumericHost = (1 << 2), Ai_NumericServ = (1 << 3), - Ai_AddrConfig = (1 << 10), + Ai_AddrConfig = (1 << 10), }; struct SockAddr { diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_settings_holder.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_settings_holder.hpp index ca4561008..3768b2558 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_settings_holder.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_settings_holder.hpp @@ -20,12 +20,12 @@ namespace ams::htclow::ctrl { class SettingsHolder { private: - char m_hardware_type[0x40]; - char m_target_name[0x40]; - char m_serial_number[0x40]; - char m_firmware_version[0x40]; + char m_hardware_type[0x40]{}; + char m_target_name[0x40]{}; + char m_serial_number[0x40]{}; + char m_firmware_version[0x40]{}; public: - SettingsHolder() { /* ... */ } + constexpr SettingsHolder() = default; void LoadSettings(); diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp index 2f7220387..3e847c16d 100644 --- a/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp +++ b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp @@ -32,10 +32,9 @@ namespace ams::htclow::driver { m_open_driver = m_debug_driver; break; case impl::DriverType::Socket: - //m_socket_driver.Open(); - //m_open_driver = std::addressof(m_socket_driver); - //break; - return htclow::ResultUnknownDriverType(); + m_socket_driver.Open(); + m_open_driver = std::addressof(m_socket_driver); + break; case impl::DriverType::Usb: m_usb_driver.Open(); m_open_driver = std::addressof(m_usb_driver); diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp index f19d46d1e..faa3f046a 100644 --- a/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp +++ b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp @@ -16,6 +16,7 @@ #pragma once #include #include "htclow_i_driver.hpp" +#include "htclow_socket_driver.hpp" #include "htclow_usb_driver.hpp" namespace ams::htclow::driver { @@ -23,14 +24,14 @@ namespace ams::htclow::driver { class DriverManager { private: std::optional m_driver_type{}; - IDriver *m_debug_driver; - /* TODO: SocketDriver m_socket_driver; */ + IDriver *m_debug_driver{}; + SocketDriver m_socket_driver; UsbDriver m_usb_driver{}; /* TODO: PlainChannelDriver m_plain_channel_driver; */ os::SdkMutex m_mutex{}; IDriver *m_open_driver{}; public: - DriverManager() = default; + DriverManager(mem::StandardAllocator *allocator) : m_socket_driver(allocator) { /* ... */ } Result OpenDriver(impl::DriverType driver_type); void CloseDriver(); diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_driver_memory_management.cpp b/libraries/libstratosphere/source/htclow/driver/htclow_driver_memory_management.cpp new file mode 100644 index 000000000..64b3fe8ba --- /dev/null +++ b/libraries/libstratosphere/source/htclow/driver/htclow_driver_memory_management.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htclow_driver_memory_management.hpp" + +namespace ams::htclow::driver { + + namespace { + + constexpr inline size_t RequiredAlignment = std::max(os::ThreadStackAlignment, os::MemoryPageSize); + + using SocketConfigType = socket::SystemConfigDefault; + + /* TODO: If we ever use resolvers, increase this. */ + constexpr inline size_t SocketAllocatorSize = 4_KB; + constexpr inline size_t SocketMemoryPoolSize = util::AlignUp(SocketConfigType::PerTcpSocketWorstCaseMemoryPoolSize + SocketConfigType::PerUdpSocketWorstCaseMemoryPoolSize, os::MemoryPageSize); + + constexpr inline size_t SocketRequiredSize = util::AlignUp(SocketMemoryPoolSize + SocketAllocatorSize, os::MemoryPageSize); + constexpr inline size_t UsbRequiredSize = 2 * UsbDmaBufferSize + UsbIndicationThreadStackSize; + static_assert(util::IsAligned(UsbDmaBufferSize, RequiredAlignment)); + + constexpr inline size_t RequiredSize = std::max(SocketRequiredSize, UsbRequiredSize); + static_assert(util::IsAligned(RequiredSize, os::MemoryPageSize)); + + /* Declare the memory pool. */ + alignas(RequiredAlignment) constinit u8 g_driver_memory[RequiredSize]; + + constexpr inline const socket::SystemConfigDefault SocketConfig(g_driver_memory, RequiredSize, SocketAllocatorSize); + + } + + void *GetUsbReceiveBuffer() { + return g_driver_memory; + } + + void *GetUsbSendBuffer() { + return g_driver_memory + UsbDmaBufferSize; + } + + void *GetUsbIndicationThreadStack() { + return g_driver_memory + 2 * UsbDmaBufferSize; + } + + void InitializeSocketApiForSocketDriver() { + R_ABORT_UNLESS(socket::Initialize(SocketConfig)); + } + +} diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_driver_memory_management.hpp b/libraries/libstratosphere/source/htclow/driver/htclow_driver_memory_management.hpp new file mode 100644 index 000000000..9e9c3bae6 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/driver/htclow_driver_memory_management.hpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htclow::driver { + + constexpr inline size_t UsbDmaBufferSize = 0x80000; + constexpr inline size_t UsbIndicationThreadStackSize = 16_KB; + + void *GetUsbReceiveBuffer(); + void *GetUsbSendBuffer(); + void *GetUsbIndicationThreadStack(); + +} diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_socket_discovery_manager.cpp b/libraries/libstratosphere/source/htclow/driver/htclow_socket_discovery_manager.cpp new file mode 100644 index 000000000..8f9e14743 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/driver/htclow_socket_discovery_manager.cpp @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htclow_socket_discovery_manager.hpp" +#include "htclow_socket_discovery_util.hpp" + +namespace ams::htclow::driver { + + namespace { + + constexpr inline u32 BeaconQueryServiceId = 0xB48F5C51; + + } + + void SocketDiscoveryManager::OnDriverOpen() { + /* Create our socket. */ + m_socket = socket::SocketExempt(socket::Family::Af_Inet, socket::Type::Sock_Dgram, socket::Protocol::IpProto_Udp); + AMS_ABORT_UNLESS(m_socket != -1); + + /* Mark driver open. */ + m_driver_closed = false; + + /* Create our thread. */ + R_ABORT_UNLESS(os::CreateThread(std::addressof(m_discovery_thread), ThreadEntry, this, m_thread_stack, os::MemoryPageSize, AMS_GET_SYSTEM_THREAD_PRIORITY(htc, HtclowDiscovery))); + + /* Set our thread name. */ + os::SetThreadNamePointer(std::addressof(m_discovery_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, HtclowDiscovery)); + + /* Start our thread. */ + os::StartThread(std::addressof(m_discovery_thread)); + } + + void SocketDiscoveryManager::OnDriverClose() { + /* Mark driver closed. */ + m_driver_closed = true; + + /* Shutdown our socket. */ + socket::Shutdown(m_socket, socket::ShutdownMethod::Shut_RdWr); + + /* Close our socket. */ + socket::Close(m_socket); + + /* Destroy our thread. */ + os::WaitThread(std::addressof(m_discovery_thread)); + os::DestroyThread(std::addressof(m_discovery_thread)); + } + + void SocketDiscoveryManager::OnSocketAcceptBegin(u16 port) { + /* ... */ + } + + void SocketDiscoveryManager::OnSocketAcceptEnd() { + /* ... */ + } + + void SocketDiscoveryManager::ThreadFunc() { + for (this->DoDiscovery(); !m_driver_closed; this->DoDiscovery()) { + /* Check if the driver is closed five times. */ + for (size_t i = 0; i < 5; ++i) { + os::SleepThread(TimeSpan::FromSeconds(1)); + if (m_driver_closed) { + return; + } + } + } + } + + Result SocketDiscoveryManager::DoDiscovery() { + /* Ensure we close our socket if we fail. */ + auto socket_guard = SCOPE_GUARD { socket::Close(m_socket); }; + + /* Create sockaddr for our socket. */ + const socket::SockAddrIn sockaddr = { + .sin_len = 0, + .sin_family = socket::Family::Af_Inet, + .sin_port = socket::InetHtons(20181), + .sin_addr = { socket::InetHtonl(0) }, + }; + + /* Bind our socket. */ + const auto bind_res = socket::Bind(m_socket, reinterpret_cast(std::addressof(sockaddr)), sizeof(sockaddr)); + R_UNLESS(bind_res != 0, htclow::ResultSocketBindError()); + + /* Loop processing beacon queries. */ + while (true) { + /* Receive a tmipc query header. */ + TmipcHeader header; + socket::SockAddr recv_sockaddr; + socket::SockLenT recv_sockaddr_len = sizeof(recv_sockaddr); + const auto recv_res = socket::RecvFrom(m_socket, std::addressof(header), sizeof(header), socket::MsgFlag::MsgFlag_None, std::addressof(recv_sockaddr), std::addressof(recv_sockaddr_len)); + + /* Check that our receive was valid. */ + R_UNLESS(recv_res >= 0, htclow::ResultSocketReceiveFromError()); + R_UNLESS(recv_sockaddr_len == sizeof(recv_sockaddr), htclow::ResultSocketReceiveFromError()); + + /* Check we received a packet header. */ + if (recv_res != sizeof(header)) { + continue; + } + + /* Check that we received a correctly versioned BeaconQuery packet. */ + /* NOTE: Nintendo checks this *after* the following receive, but this seems saner. */ + if (header.version != TmipcVersion || header.service_id != BeaconQueryServiceId) { + continue; + } + + /* Receive the packet body, if there is one. */ + char packet_data[0x120]; + + /* NOTE: Nintendo does not check this... */ + if (header.data_len > sizeof(packet_data)) { + continue; + } + + if (header.data_len > 0) { + const auto body_res = socket::RecvFrom(m_socket, packet_data, header.data_len, socket::MsgFlag::MsgFlag_None, std::addressof(recv_sockaddr), std::addressof(recv_sockaddr_len)); + R_UNLESS(body_res >= 0, htclow::ResultSocketReceiveFromError()); + R_UNLESS(recv_sockaddr_len == sizeof(recv_sockaddr), htclow::ResultSocketReceiveFromError()); + + if (body_res != header.data_len) { + continue; + } + } + + /* Make our beacon response packet. */ + const auto len = MakeBeaconResponsePacket(packet_data, sizeof(packet_data)); + + /* Send the beacon response data. */ + const auto send_res = socket::SendTo(m_socket, packet_data, len, socket::MsgFlag::MsgFlag_None, std::addressof(recv_sockaddr), sizeof(recv_sockaddr)); + R_UNLESS(send_res >= 0, htclow::ResultSocketSendToError()); + } + + /* This can never happen, as the above loop should be infinite, but completion logic is here for posterity. */ + socket_guard.Cancel(); + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_socket_discovery_manager.hpp b/libraries/libstratosphere/source/htclow/driver/htclow_socket_discovery_manager.hpp new file mode 100644 index 000000000..a457e4dc7 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/driver/htclow_socket_discovery_manager.hpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htclow::driver { + + class SocketDiscoveryManager { + private: + bool m_driver_closed; + mem::StandardAllocator *m_allocator; + void *m_thread_stack; + os::ThreadType m_discovery_thread; + s32 m_socket; + public: + SocketDiscoveryManager(mem::StandardAllocator *allocator) + : m_driver_closed(false), m_allocator(allocator), m_thread_stack(allocator->Allocate(os::MemoryPageSize, os::ThreadStackAlignment)) + { + /* ... */ + } + private: + static void ThreadEntry(void *arg) { + static_cast(arg)->ThreadFunc(); + } + + void ThreadFunc(); + + Result DoDiscovery(); + public: + void OnDriverOpen(); + void OnDriverClose(); + void OnSocketAcceptBegin(u16 port); + void OnSocketAcceptEnd(); + }; + +} diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_socket_discovery_util.cpp b/libraries/libstratosphere/source/htclow/driver/htclow_socket_discovery_util.cpp new file mode 100644 index 000000000..2e32d12f0 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/driver/htclow_socket_discovery_util.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htclow_socket_discovery_util.hpp" +#include "../ctrl/htclow_ctrl_settings_holder.hpp" + +namespace ams::htclow::driver { + + namespace { + + constexpr inline u32 AutoConnectIpv4RequestServiceId = 0x834C775A; + constexpr inline u32 BeaconResponseServiceId = 0xA6F7FA96; + + constexpr const char MakeAutoConnectIpv4RequestPacketFormat[] = + "{\r\n" + " \"Address\" : \"%u.%u.%u.%u\",\r\n" + " \"Port\" : %u,\r\n" + " \"HW\" : \"%s\",\r\n" + " \"SN\" : \"%s\"\r\n" + "}\r\n"; + + constexpr const char BeaconResponsePacketFormat[] = + "{\r\n" + " \"Gen\" : 2,\r\n" + " \"Spec \": \"%s\",\r\n" + " \"MAC\" : \"00:00:00:00:00:00\",\r\n" + " \"Conn\" : \"TCP\",\r\n" + " \"HW\" : \"%s\",\r\n" + " \"Name\" : \"%s\",\r\n" + " \"SN\" : \"%s\",\r\n" + " \"FW\" : \"%s\"\r\n" + "}\r\n"; + + constinit os::SdkMutex g_settings_holder_mutex; + constinit bool g_settings_holder_initialized = false; + constinit htclow::ctrl::SettingsHolder g_settings_holder; + + void InitializeSettingsHolder() { + std::scoped_lock lk(g_settings_holder_mutex); + + if (!g_settings_holder_initialized) { + g_settings_holder.LoadSettings(); + g_settings_holder_initialized = true; + } + } + + } + + s32 MakeAutoConnectIpv4RequestPacket(char *dst, size_t dst_size, const socket::SockAddrIn &sockaddr) { + /* Initialize the settings holder. */ + InitializeSettingsHolder(); + + /* Create the packet header. */ + TmipcHeader header = { + .service_id = AutoConnectIpv4RequestServiceId, + .version = TmipcVersion, + }; + + /* Create the packet body. */ + std::scoped_lock lk(g_settings_holder_mutex); + const auto addr = sockaddr.sin_addr.s_addr; + + char packet_body[0x100]; + const auto ideal_len = util::SNPrintf(packet_body, sizeof(packet_body), MakeAutoConnectIpv4RequestPacketFormat, + (addr >> 0) & 0xFF, (addr >> 8) & 0xFF, (addr >> 16) & 0xFF, (addr >> 24) & 0xFF, + socket::InetNtohs(sockaddr.sin_port), + g_settings_holder.GetHardwareType(), + "" /* Nintendo passes empty string as serial number here. */ + ); + + /* Determine actual usable body length. */ + header.data_len = std::max(ideal_len, sizeof(packet_body)); + + /* Check that the packet will fit. */ + AMS_ABORT_UNLESS(sizeof(header) + header.data_len <= dst_size); + + /* Copy the formatted header. */ + std::memcpy(dst, std::addressof(header), sizeof(header)); + std::memcpy(dst + sizeof(header), packet_body, header.data_len); + + return header.data_len; + } + + s32 MakeBeaconResponsePacket(char *dst, size_t dst_size) { + /* Initialize the settings holder. */ + InitializeSettingsHolder(); + + /* Create the packet header. */ + TmipcHeader header = { + .service_id = BeaconResponseServiceId, + .version = TmipcVersion, + }; + + /* Create the packet body. */ + std::scoped_lock lk(g_settings_holder_mutex); + + char packet_body[0x100]; + const auto ideal_len = util::SNPrintf(packet_body, sizeof(packet_body), BeaconResponsePacketFormat, + g_settings_holder.GetSpec(), + g_settings_holder.GetHardwareType(), + g_settings_holder.GetTargetName(), + g_settings_holder.GetSerialNumber(), + g_settings_holder.GetFirmwareVersion() + ); + + /* Determine actual usable body length. */ + header.data_len = std::max(ideal_len, sizeof(packet_body)); + + /* Check that the packet will fit. */ + AMS_ABORT_UNLESS(sizeof(header) + header.data_len <= dst_size); + + /* Copy the formatted header. */ + std::memcpy(dst, std::addressof(header), sizeof(header)); + std::memcpy(dst + sizeof(header), packet_body, header.data_len); + + return header.data_len; + } + +} diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_socket_discovery_util.hpp b/libraries/libstratosphere/source/htclow/driver/htclow_socket_discovery_util.hpp new file mode 100644 index 000000000..96ef4d453 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/driver/htclow_socket_discovery_util.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htclow::driver { + + constexpr inline u8 TmipcVersion = 5; + + struct TmipcHeader { + u32 service_id; + u32 reserved_00; + u16 reserved_01; + u8 reserved_02; + u8 version; + u32 data_len; + u32 reserved[4]; + }; + static_assert(util::is_pod::value); + static_assert(sizeof(TmipcHeader) == 0x20); + + s32 MakeAutoConnectIpv4RequestPacket(char *dst, size_t dst_size, const socket::SockAddrIn &sockaddr); + s32 MakeBeaconResponsePacket(char *dst, size_t dst_size); + +} diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_socket_driver.cpp b/libraries/libstratosphere/source/htclow/driver/htclow_socket_driver.cpp new file mode 100644 index 000000000..ed430414f --- /dev/null +++ b/libraries/libstratosphere/source/htclow/driver/htclow_socket_driver.cpp @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htclow_socket_driver.hpp" +#include "htclow_socket_discovery_util.hpp" + +namespace ams::htclow::driver { + + Result SocketDriver::ConnectThread() { + /* Do auto connect, if we should. */ + if (m_auto_connect_reserved) { + this->DoAutoConnect(); + } + + /* Get the socket's name. */ + socket::SockAddrIn sockaddr; + socket::SockLenT sockaddr_len = sizeof(sockaddr); + R_UNLESS(socket::GetSockName(m_server_socket, reinterpret_cast(std::addressof(sockaddr)), std::addressof(sockaddr_len)) == 0, htclow::ResultSocketGetSockNameError()); + + /* Accept. */ + m_discovery_manager.OnSocketAcceptBegin(sockaddr.sin_port); + + sockaddr_len = sizeof(m_server_sockaddr); + const auto client_desc = socket::Accept(m_server_socket, reinterpret_cast(std::addressof(m_server_sockaddr)), std::addressof(sockaddr_len)); + + m_discovery_manager.OnSocketAcceptEnd(); + + /* Check accept result. */ + R_UNLESS(client_desc >= 0, htclow::ResultSocketAcceptError()); + + /* Setup client socket. */ + R_TRY(this->SetupClientSocket(client_desc)); + + return ResultSuccess(); + } + + Result SocketDriver::CreateServerSocket() { + /* Check that we don't have a server socket. */ + AMS_ASSERT(!m_server_socket_valid); + + /* Create the socket. */ + const auto desc = socket::SocketExempt(socket::Family::Af_Inet, socket::Type::Sock_Stream, socket::Protocol::IpProto_Tcp); + R_UNLESS(desc != -1, htclow::ResultSocketSocketExemptError()); + + /* Be sure that we close the socket if we don't succeed. */ + auto socket_guard = SCOPE_GUARD { socket::Close(desc); }; + + /* Create sockaddr for our socket. */ + const socket::SockAddrIn sockaddr = { + .sin_len = 0, + .sin_family = socket::Family::Af_Inet, + .sin_port = socket::InetHtons(20180), + .sin_addr = { socket::InetHtonl(0) }, + }; + + /* Enable local address reuse. */ + { + u32 enable = 1; + const auto res = socket::SetSockOpt(desc, socket::Level::Sol_Socket, socket::Option::So_ReuseAddr, std::addressof(enable), sizeof(enable)); + AMS_ABORT_UNLESS(res == 0); + } + + /* Bind the socket. */ + const auto bind_res = socket::Bind(desc, reinterpret_cast(std::addressof(sockaddr)), sizeof(sockaddr)); + R_UNLESS(bind_res == 0, htclow::ResultSocketBindError()); + + /* Listen on the socket. */ + const auto listen_res = socket::Listen(desc, 1); + R_UNLESS(listen_res == 0, htclow::ResultSocketListenError()); + + /* We succeeded. */ + socket_guard.Cancel(); + m_server_socket = desc; + m_server_socket_valid = true; + + return ResultSuccess(); + } + + void SocketDriver::DestroyServerSocket() { + if (m_server_socket_valid) { + socket::Shutdown(m_server_socket, socket::ShutdownMethod::Shut_RdWr); + socket::Close(m_server_socket); + m_server_socket_valid = false; + } + } + + Result SocketDriver::SetupClientSocket(s32 desc) { + std::scoped_lock lk(m_mutex); + + /* Check that we don't have a client socket. */ + AMS_ASSERT(!m_client_socket_valid); + + /* Be sure that we close the socket if we don't succeed. */ + auto socket_guard = SCOPE_GUARD { socket::Close(desc); }; + + /* Enable debug logging for the socket. */ + u32 debug = 1; + const auto res = socket::SetSockOpt(desc, socket::Level::Sol_Tcp, socket::Option::So_Debug, std::addressof(debug), sizeof(debug)); + R_UNLESS(res >= 0, htclow::ResultSocketSetSockOptError()); + + /* We succeeded. */ + socket_guard.Cancel(); + m_client_socket = desc; + m_client_socket_valid = true; + + return ResultSuccess(); + } + + bool SocketDriver::IsAutoConnectReserved() { + return m_auto_connect_reserved; + } + + void SocketDriver::ReserveAutoConnect() { + std::scoped_lock lk(m_mutex); + + if (m_client_socket_valid) { + /* Save our client sockaddr. */ + socket::SockLenT sockaddr_len = sizeof(m_saved_client_sockaddr); + if (socket::GetSockName(m_server_socket, reinterpret_cast(std::addressof(m_saved_client_sockaddr)), std::addressof(sockaddr_len)) != 0) { + return; + } + + /* Save our server sockaddr. */ + m_saved_server_sockaddr = m_server_sockaddr; + + /* Mark auto-connect reserved. */ + m_auto_connect_reserved = true; + } + } + + void SocketDriver::DoAutoConnect() { + /* Clear auto-connect reserved. */ + m_auto_connect_reserved = false; + + /* Create udb socket. */ + const auto desc = socket::SocketExempt(socket::Family::Af_Inet, socket::Type::Sock_Dgram, socket::Protocol::IpProto_Udp); + if (desc == -1) { + return; + } + + /* Clean up the desc when we're done. */ + ON_SCOPE_EXIT { socket::Close(desc); }; + + /* Create auto-connect packet. */ + char auto_connect_packet[0x120]; + s32 len; + { + const socket::SockAddrIn sockaddr = { + .sin_family = socket::Family::Af_Inet, + .sin_port = m_saved_client_sockaddr.sin_port, + .sin_addr = m_saved_client_sockaddr.sin_addr, + }; + + len = htclow::driver::MakeAutoConnectIpv4RequestPacket(auto_connect_packet, sizeof(auto_connect_packet), sockaddr); + } + + /* Send the auto-connect packet to the host on port 20181. */ + const socket::SockAddrIn sockaddr = { + .sin_family = socket::Family::Af_Inet, + .sin_port = socket::InetHtons(20181), + .sin_addr = m_saved_server_sockaddr.sin_addr, + }; + + /* Send the auto-connect packet. */ + socket::SendTo(desc, auto_connect_packet, len, socket::MsgFlag::MsgFlag_None, reinterpret_cast(std::addressof(sockaddr)), sizeof(sockaddr)); + } + + Result SocketDriver::Open() { + m_discovery_manager.OnDriverOpen(); + return ResultSuccess(); + } + + void SocketDriver::Close() { + m_discovery_manager.OnDriverClose(); + } + + Result SocketDriver::Connect(os::EventType *event) { + /* Allocate a temporary thread stack. */ + void *stack = m_allocator->Allocate(os::MemoryPageSize, os::ThreadStackAlignment); + ON_SCOPE_EXIT { m_allocator->Free(stack); }; + + /* Try to create a server socket. */ + R_TRY(this->CreateServerSocket()); + + /* Prepare to run our connect thread. */ + m_event.Clear(); + + /* Run our connect thread. */ + { + /* Create the thread. */ + os::ThreadType connect_thread; + R_ABORT_UNLESS(os::CreateThread(std::addressof(connect_thread), ConnectThreadEntry, this, stack, os::MemoryPageSize, AMS_GET_SYSTEM_THREAD_PRIORITY(htc, HtclowTcpServer))); + + /* Set the thread's name. */ + os::SetThreadNamePointer(std::addressof(connect_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, HtclowTcpServer)); + + /* Start the thread. */ + os::StartThread(std::addressof(connect_thread)); + + /* Check if we should cancel the connection. */ + if (os::WaitAny(event, m_event.GetBase()) == 0) { + this->DestroyServerSocket(); + } + + /* Wait for the connect thread to finish. */ + os::WaitThread(std::addressof(connect_thread)); + + /* Destroy the connection thread. */ + os::DestroyThread(std::addressof(connect_thread)); + + /* Destroy the server socket. */ + this->DestroyServerSocket(); + } + + /* Return our connection result. */ + return m_connect_result; + } + + void SocketDriver::Shutdown() { + std::scoped_lock lk(m_mutex); + + /* Shut down our client socket, if we need to. */ + if (m_client_socket_valid) { + socket::Shutdown(m_client_socket, socket::ShutdownMethod::Shut_RdWr); + socket::Close(m_client_socket); + m_client_socket_valid = 0; + } + } + + Result SocketDriver::Send(const void *src, int src_size) { + /* Check the input size. */ + R_UNLESS(src_size >= 0, htclow::ResultInvalidArgument()); + + /* Repeatedly send data until it's all sent. */ + ssize_t cur_sent; + for (ssize_t sent = 0; sent < src_size; sent += cur_sent) { + cur_sent = socket::Send(m_client_socket, static_cast(src) + sent, src_size - sent, socket::MsgFlag::MsgFlag_None); + R_UNLESS(cur_sent > 0, htclow::ResultSocketSendError()); + } + + return ResultSuccess(); + } + + Result SocketDriver::Receive(void *dst, int dst_size) { + /* Check the input size. */ + R_UNLESS(dst_size >= 0, htclow::ResultInvalidArgument()); + + /* Repeatedly receive data until it's all sent. */ + ssize_t cur_recv; + for (ssize_t received = 0; received < dst_size; received += cur_recv) { + cur_recv = socket::Recv(m_client_socket, static_cast(dst) + received, dst_size - received, socket::MsgFlag::MsgFlag_None); + R_UNLESS(cur_recv > 0, htclow::ResultSocketReceiveError()); + } + + return ResultSuccess(); + } + + void SocketDriver::CancelSendReceive() { + this->Shutdown(); + } + + void SocketDriver::Suspend() { + this->ReserveAutoConnect(); + } + + void SocketDriver::Resume() { + /* ... */ + } + +} diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_socket_driver.hpp b/libraries/libstratosphere/source/htclow/driver/htclow_socket_driver.hpp new file mode 100644 index 000000000..1ae8ad54e --- /dev/null +++ b/libraries/libstratosphere/source/htclow/driver/htclow_socket_driver.hpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "htclow_i_driver.hpp" +#include "htclow_socket_discovery_manager.hpp" + +namespace ams::htclow::driver { + + class SocketDriver final : public IDriver { + private: + mem::StandardAllocator *m_allocator; + SocketDiscoveryManager m_discovery_manager; + socket::SockAddrIn m_server_sockaddr; + socket::SockAddrIn m_saved_server_sockaddr; + socket::SockAddrIn m_saved_client_sockaddr; + os::Event m_event; + Result m_connect_result; + os::SdkMutex m_mutex; + s32 m_server_socket; + s32 m_client_socket; + bool m_server_socket_valid; + bool m_client_socket_valid; + bool m_auto_connect_reserved; + public: + SocketDriver(mem::StandardAllocator *allocator) + : m_allocator(allocator), m_discovery_manager(m_allocator), m_event(os::EventClearMode_ManualClear), + m_connect_result(), m_mutex(), m_server_socket(), m_client_socket(), m_server_socket_valid(false), + m_client_socket_valid(false), m_auto_connect_reserved(false) + { + /* ... */ + } + private: + static void ConnectThreadEntry(void *arg) { + auto * const driver = static_cast(arg); + + driver->m_connect_result = driver->ConnectThread(); + driver->m_event.Signal(); + } + + Result ConnectThread(); + private: + Result CreateServerSocket(); + void DestroyServerSocket(); + + Result SetupClientSocket(s32 desc); + + bool IsAutoConnectReserved(); + void ReserveAutoConnect(); + void DoAutoConnect(); + public: + virtual Result Open() override; + virtual void Close() override; + virtual Result Connect(os::EventType *event) override; + virtual void Shutdown() override; + virtual Result Send(const void *src, int src_size) override; + virtual Result Receive(void *dst, int dst_size) override; + virtual void CancelSendReceive() override; + virtual void Suspend() override; + virtual void Resume() override; + }; + +} diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_usb_impl.cpp b/libraries/libstratosphere/source/htclow/driver/htclow_usb_impl.cpp index 76b55f1ce..ab769752b 100644 --- a/libraries/libstratosphere/source/htclow/driver/htclow_usb_impl.cpp +++ b/libraries/libstratosphere/source/htclow/driver/htclow_usb_impl.cpp @@ -15,6 +15,7 @@ */ #include #include "htclow_usb_impl.hpp" +#include "htclow_driver_memory_management.hpp" namespace ams::htclow::driver { @@ -179,11 +180,8 @@ namespace ams::htclow::driver { .wBytesPerInterval = 0x0000, }; - constexpr size_t UsbDmaBufferSize = 0x60000; - - alignas(os::MemoryPageSize) constinit u8 g_usb_receive_buffer[UsbDmaBufferSize]; - alignas(os::MemoryPageSize) constinit u8 g_usb_send_buffer[UsbDmaBufferSize]; - alignas(os::ThreadStackAlignment) constinit u8 g_usb_indication_thread_stack[16_KB]; + constinit void *g_usb_receive_buffer = nullptr; + constinit void *g_usb_send_buffer = nullptr; constinit UsbAvailabilityChangeCallback g_availability_change_callback = nullptr; constinit void *g_availability_change_param = nullptr; @@ -346,6 +344,10 @@ namespace ams::htclow::driver { /* Set the interface as initialized. */ g_usb_interface_initialized = true; + /* Get the dma buffers. */ + g_usb_receive_buffer = GetUsbReceiveBuffer(); + g_usb_send_buffer = GetUsbSendBuffer(); + /* If we fail somewhere, finalize. */ auto init_guard = SCOPE_GUARD { FinalizeUsbInterface(); }; @@ -385,7 +387,7 @@ namespace ams::htclow::driver { R_TRY(ConvertUsbDriverResult(InitializeDsEndpoints())); /* Create the indication thread. */ - R_ABORT_UNLESS(os::CreateThread(std::addressof(g_usb_indication_thread), &UsbIndicationThreadFunction, nullptr, g_usb_indication_thread_stack, sizeof(g_usb_indication_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(htc, HtclowUsbIndication))); + R_ABORT_UNLESS(os::CreateThread(std::addressof(g_usb_indication_thread), &UsbIndicationThreadFunction, nullptr, GetUsbIndicationThreadStack(), UsbIndicationThreadStackSize, AMS_GET_SYSTEM_THREAD_PRIORITY(htc, HtclowUsbIndication))); /* Set the thread name. */ os::SetThreadNamePointer(std::addressof(g_usb_indication_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, HtclowUsbIndication)); diff --git a/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp b/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp index d4f6d2e8a..8d4c32508 100644 --- a/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp +++ b/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp @@ -20,7 +20,7 @@ namespace ams::htclow { HtclowManagerImpl::HtclowManagerImpl(mem::StandardAllocator *allocator) - : m_packet_factory(allocator), m_driver_manager(), m_mux(std::addressof(m_packet_factory), std::addressof(m_ctrl_state_machine)), + : m_packet_factory(allocator), m_driver_manager(allocator), m_mux(std::addressof(m_packet_factory), std::addressof(m_ctrl_state_machine)), m_ctrl_packet_factory(allocator), m_ctrl_state_machine(), m_ctrl_service(std::addressof(m_ctrl_packet_factory), std::addressof(m_ctrl_state_machine), std::addressof(m_mux)), m_worker(allocator, std::addressof(m_mux), std::addressof(m_ctrl_service)), m_listener(allocator, std::addressof(m_mux), std::addressof(m_ctrl_service), std::addressof(m_worker)), diff --git a/libraries/libstratosphere/source/socket/impl/socket_allocator.hpp b/libraries/libstratosphere/source/socket/impl/socket_allocator.hpp index d65838676..bd00f1ce0 100644 --- a/libraries/libstratosphere/source/socket/impl/socket_allocator.hpp +++ b/libraries/libstratosphere/source/socket/impl/socket_allocator.hpp @@ -18,6 +18,8 @@ namespace ams::socket::impl { + constexpr inline auto MinimumHeapAlignment = 0x10; + void *Alloc(size_t size); void *Calloc(size_t num, size_t size); void Free(void *ptr); diff --git a/libraries/libstratosphere/source/socket/impl/socket_api.hpp b/libraries/libstratosphere/source/socket/impl/socket_api.hpp index 292b4bdc9..74124bfd6 100644 --- a/libraries/libstratosphere/source/socket/impl/socket_api.hpp +++ b/libraries/libstratosphere/source/socket/impl/socket_api.hpp @@ -20,9 +20,19 @@ namespace ams::socket::impl { Errno GetLastError(); void SetLastError(Errno err); + bool HeapIsAvailable(int generation); + int GetHeapGeneration(); + u32 InetHtonl(u32 host); u16 InetHtons(u16 host); u32 InetNtohl(u32 net); u16 InetNtohs(u16 net); + Result Initialize(const Config &config); + Result Finalize(); + + Result InitializeAllocatorForInternal(void *buffer, size_t size); + + s32 Shutdown(s32 desc, ShutdownMethod how); + } diff --git a/libraries/libstratosphere/source/socket/impl/socket_api.os.horizon.cpp b/libraries/libstratosphere/source/socket/impl/socket_api.os.horizon.cpp index ed4fa9873..ffca3e38c 100644 --- a/libraries/libstratosphere/source/socket/impl/socket_api.os.horizon.cpp +++ b/libraries/libstratosphere/source/socket/impl/socket_api.os.horizon.cpp @@ -17,35 +17,99 @@ #include "socket_api.hpp" #include "socket_allocator.hpp" +extern "C" { + +#include + +} + namespace ams::socket::impl { + namespace { + + constinit bool g_initialized = false; + + constinit os::SdkMutex g_heap_mutex; + + constinit lmem::HeapHandle g_heap_handle = nullptr; + constinit int g_heap_generation = -1; + + ALWAYS_INLINE bool IsInitialized() { + return g_initialized; + } + + } + void *Alloc(size_t size) { - /* TODO: expheap, heap generation. */ - return ams::Malloc(size); + std::scoped_lock lk(g_heap_mutex); + + AMS_ASSERT(g_heap_generation > 0); + + void *ptr = nullptr; + + if (!g_heap_handle) { + socket::impl::SetLastError(Errno::EOpNotSupp); + } else if ((ptr = lmem::AllocateFromExpHeap(g_heap_handle, size)) == nullptr) { + socket::impl::SetLastError(Errno::EOpNotSupp); + } + + return ptr; } void *Calloc(size_t num, size_t size) { - if (void *ptr = Alloc(size * num); ptr != nullptr) { - std::memset(ptr, 0, size * num); - return ptr; + std::scoped_lock lk(g_heap_mutex); + + AMS_ASSERT(g_heap_generation > 0); + + void *ptr = nullptr; + + if (!g_heap_handle) { + socket::impl::SetLastError(Errno::EOpNotSupp); + } else if ((ptr = lmem::AllocateFromExpHeap(g_heap_handle, size * num)) == nullptr) { + socket::impl::SetLastError(Errno::EOpNotSupp); } else { - return nullptr; + std::memset(ptr, 0, size * num); } + + return ptr; } void Free(void *ptr) { - /* TODO: expheap, heap generation. */ - return ams::Free(ptr); + std::scoped_lock lk(g_heap_mutex); + + AMS_ASSERT(g_heap_generation > 0); + + if (!g_heap_handle) { + socket::impl::SetLastError(Errno::EOpNotSupp); + } else if (ptr != nullptr) { + lmem::FreeToExpHeap(g_heap_handle, ptr); + } + } + + bool HeapIsAvailable(int generation) { + std::scoped_lock lk(g_heap_mutex); + + return g_heap_handle && g_heap_generation == generation; + } + + int GetHeapGeneration() { + std::scoped_lock lk(g_heap_mutex); + + return g_heap_generation; } Errno GetLastError() { - /* TODO: check that client library is initialized. */ - return static_cast(errno); + if (AMS_LIKELY(IsInitialized())) { + return static_cast(errno); + } else { + return Errno::EInval; + } } void SetLastError(Errno err) { - /* TODO: check that client library is initialized. */ - errno = static_cast(err); + if (AMS_LIKELY(IsInitialized())) { + errno = static_cast(err); + } } u32 InetHtonl(u32 host) { @@ -80,4 +144,109 @@ namespace ams::socket::impl { } } + namespace { + + void InitializeHeapImpl(void *buffer, size_t size) { + /* NOTE: Nintendo uses both CreateOption_ThreadSafe *and* a global heap mutex. */ + /* This is unnecessary, and using a single SdkMutex is more performant, since we're not recursive. */ + std::scoped_lock lk(g_heap_mutex); + + g_heap_handle = lmem::CreateExpHeap(buffer, size, lmem::CreateOption_None); + } + + Result InitializeCommon(const Config &config) { + /* Check pre-conditions. */ + AMS_ABORT_UNLESS(!IsInitialized()); + AMS_ABORT_UNLESS(config.GetMemoryPool() != nullptr); + AMS_ABORT_UNLESS(1 <= config.GetConcurrencyCountMax() && config.GetConcurrencyCountMax() <= ConcurrencyLimitMax); + if (!config.IsSmbpClient()) { AMS_ABORT_UNLESS(config.GetAllocatorPoolSize() < config.GetMemoryPoolSize()); } + AMS_ABORT_UNLESS(util::IsAligned(config.GetMemoryPoolSize(), os::MemoryPageSize)); + AMS_ABORT_UNLESS(util::IsAligned(config.GetAllocatorPoolSize(), os::MemoryPageSize)); + AMS_ABORT_UNLESS(config.GetAllocatorPoolSize() >= 4_KB); + if (!config.IsSystemClient()) { + R_UNLESS(config.GetMemoryPoolSize() >= socket::MinSocketMemoryPoolSize, socket::ResultInsufficientProvidedMemory()); + } + + const size_t transfer_memory_size = config.GetMemoryPoolSize() - config.GetAllocatorPoolSize(); + if (!config.IsSmbpClient()) { + R_UNLESS(transfer_memory_size >= socket::MinMemHeapAllocatorSize, socket::ResultInsufficientProvidedMemory()); + } else { + R_UNLESS(config.GetMemoryPoolSize() >= socket::MinimumSharedMbufPoolReservation, socket::ResultInsufficientProvidedMemory()); + } + + /* Initialize the allocator heap. */ + InitializeHeapImpl(static_cast(config.GetMemoryPool()) + transfer_memory_size, config.GetAllocatorPoolSize()); + + /* Initialize libnx. */ + { + const ::BsdInitConfig libnx_config = { + .version = config.GetVersion(), + .tmem_buffer = config.GetMemoryPool(), + .tmem_buffer_size = transfer_memory_size, + + .tcp_tx_buf_size = static_cast(config.GetTcpInitialSendBufferSize()), + .tcp_rx_buf_size = static_cast(config.GetTcpInitialReceiveBufferSize()), + .tcp_tx_buf_max_size = static_cast(config.GetTcpAutoSendBufferSizeMax()), + .tcp_rx_buf_max_size = static_cast(config.GetTcpAutoReceiveBufferSizeMax()), + + .udp_tx_buf_size = static_cast(config.GetUdpSendBufferSize()), + .udp_rx_buf_size = static_cast(config.GetUdpReceiveBufferSize()), + + .sb_efficiency = static_cast(config.GetSocketBufferEfficiency()), + }; + + const auto service_type = config.IsSystemClient() ? (1 << 1) : (1 << 0); + + sm::DoWithSession([&] { + R_ABORT_UNLESS(::bsdInitialize(std::addressof(libnx_config), static_cast(config.GetConcurrencyCountMax()), service_type)); + }); + } + + /* Set the heap generation. */ + g_heap_generation = (g_heap_generation + 1) % MinimumHeapAlignment; + + /* TODO: socket::resolver::EnableResolverCalls()? Not necessary in our case (htc), but consider calling it. */ + + return ResultSuccess(); + } + + } + + Result Initialize(const Config &config) { + return InitializeCommon(config); + } + + Result Finalize() { + /* Check pre-conditions. */ + AMS_ABORT_UNLESS(IsInitialized()); + + /* TODO: If we support statistics, kill the statistics thread. */ + + /* TODO: socket::resolver::DisableResolverCalls()? */ + + /* Finalize libnx. */ + ::bsdExit(); + + /* Finalize the heap. */ + lmem::HeapHandle heap_handle; + { + std::scoped_lock lk(g_heap_mutex); + + heap_handle = g_heap_handle; + g_heap_handle = nullptr; + } + lmem::DestroyExpHeap(heap_handle); + + return ResultSuccess(); + } + + Result InitializeAllocatorForInternal(void *buffer, size_t size) { + /* Check pre-conditions. */ + AMS_ABORT_UNLESS(util::IsAligned(size, os::MemoryPageSize)); + AMS_ABORT_UNLESS(size >= 4_KB); + + InitializeHeapImpl(buffer, size); + return ResultSuccess(); + } + } diff --git a/libraries/libstratosphere/source/socket/socket_api.cpp b/libraries/libstratosphere/source/socket/socket_api.cpp index f1171c2c3..41a25c543 100644 --- a/libraries/libstratosphere/source/socket/socket_api.cpp +++ b/libraries/libstratosphere/source/socket/socket_api.cpp @@ -42,4 +42,16 @@ namespace ams::socket { return impl::InetNtohs(net); } + Result Initialize(const Config &config) { + return impl::Initialize(config); + } + + Result Finalize() { + return impl::Finalize(); + } + + Result InitializeAllocatorForInternal(void *buffer, size_t size) { + return impl::InitializeAllocatorForInternal(buffer, size); + } + } diff --git a/libraries/libvapours/include/vapours/results.hpp b/libraries/libvapours/include/vapours/results.hpp index 406e1ec24..ad89ea157 100644 --- a/libraries/libvapours/include/vapours/results.hpp +++ b/libraries/libvapours/include/vapours/results.hpp @@ -59,6 +59,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libvapours/include/vapours/results/htclow_results.hpp b/libraries/libvapours/include/vapours/results/htclow_results.hpp index eb90e2b90..7a57eb1e7 100644 --- a/libraries/libvapours/include/vapours/results/htclow_results.hpp +++ b/libraries/libvapours/include/vapours/results/htclow_results.hpp @@ -52,6 +52,18 @@ namespace ams::htclow { R_DEFINE_ERROR_RANGE(DriverError, 1200, 1999); R_DEFINE_ERROR_RESULT(DriverOpened, 1201); + R_DEFINE_ERROR_RANGE(SocketDriverError, 1300, 1399); + R_DEFINE_ERROR_RESULT(SocketSocketExemptError, 1301); + R_DEFINE_ERROR_RESULT(SocketBindError, 1302); + R_DEFINE_ERROR_RESULT(SocketListenError, 1304); + R_DEFINE_ERROR_RESULT(SocketAcceptError, 1305); + R_DEFINE_ERROR_RESULT(SocketReceiveError, 1306); + R_DEFINE_ERROR_RESULT(SocketSendError, 1307); + R_DEFINE_ERROR_RESULT(SocketReceiveFromError, 1308); + R_DEFINE_ERROR_RESULT(SocketSendToError, 1309); + R_DEFINE_ERROR_RESULT(SocketSetSockOptError, 1310); + R_DEFINE_ERROR_RESULT(SocketGetSockNameError, 1311); + R_DEFINE_ERROR_RANGE(UsbDriverError, 1400, 1499); R_DEFINE_ERROR_RESULT(UsbDriverUnknownError, 1401); R_DEFINE_ERROR_RESULT(UsbDriverBusyError, 1402); diff --git a/libraries/libvapours/include/vapours/results/socket_results.hpp b/libraries/libvapours/include/vapours/results/socket_results.hpp new file mode 100644 index 000000000..c11271018 --- /dev/null +++ b/libraries/libvapours/include/vapours/results/socket_results.hpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +namespace ams::socket { + + R_DEFINE_NAMESPACE_RESULT_MODULE(27); + + R_DEFINE_ERROR_RESULT(InsufficientProvidedMemory, 1); + +} diff --git a/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_module.cpp b/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_module.cpp index 2aeeb93e6..82e361374 100644 --- a/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_module.cpp +++ b/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_module.cpp @@ -39,6 +39,8 @@ namespace ams::mitm::socket::resolver { virtual Result OnNeedsToAccept(int port_index, Server *server) override; }; + alignas(os::MemoryPageSize) constinit u8 g_resolver_allocator_buffer[16_KB]; + ServerManager g_server_manager; Result ServerManager::OnNeedsToAccept(int port_index, Server *server) { @@ -121,6 +123,9 @@ namespace ams::mitm::socket::resolver { return; } + /* Initialize the socket allocator. */ + ams::socket::InitializeAllocatorForInternal(g_resolver_allocator_buffer, sizeof(g_resolver_allocator_buffer)); + /* Initialize debug. */ resolver::InitializeDebug(ShouldEnableDebugLog()); diff --git a/stratosphere/htc/htc.json b/stratosphere/htc/htc.json index 95d24bfdd..2bbde7bef 100644 --- a/stratosphere/htc/htc.json +++ b/stratosphere/htc/htc.json @@ -15,7 +15,7 @@ "filesystem_access": { "permissions": "0xFFFFFFFFFFFFFFFF" }, - "service_access": ["pcie", "psc:m", "set:cal", "set:fd", "set:sys", "usb:ds", "fsp-srv"], + "service_access": ["pcie", "psc:m", "set:cal", "set:fd", "set:sys", "usb:ds", "fsp-srv", "bsd:s"], "service_host": ["file_io", "htc", "htcs"], "kernel_capabilities": [{ "type": "kernel_flags", diff --git a/stratosphere/htc/source/htc_main.cpp b/stratosphere/htc/source/htc_main.cpp index 5f2bc88fd..cf36bdb30 100644 --- a/stratosphere/htc/source/htc_main.cpp +++ b/stratosphere/htc/source/htc_main.cpp @@ -200,6 +200,10 @@ namespace ams::htc { return htclow::impl::DriverType::HostBridge; } else if (std::strstr(transport, "plainchannel")) { return htclow::impl::DriverType::PlainChannel; + } else if (std::strstr(transport, "socket")) { + /* NOTE: Nintendo does not actually allow socket driver to be selected. */ + /* Should we disallow this? Undesirable, because people will want to use docked tma. */ + return htclow::impl::DriverType::Socket; } else { return DefaultHtclowDriverType; } @@ -230,6 +234,12 @@ namespace ams::htc { } +namespace ams::htclow::driver { + + void InitializeSocketApiForSocketDriver(); + +} + int main(int argc, char **argv) { /* Set thread name. */ @@ -240,6 +250,11 @@ int main(int argc, char **argv) const auto driver_type = htc::GetHtclowDriverType(); htclow::HtclowManagerHolder::SetDefaultDriver(driver_type); + /* If necessary, initialize the socket driver. */ + if (driver_type == htclow::impl::DriverType::Socket) { + htclow::driver::InitializeSocketApiForSocketDriver(); + } + /* Initialize the htclow manager. */ htclow::HtclowManagerHolder::AddReference(); ON_SCOPE_EXIT { htclow::HtclowManagerHolder::Release(); }; From 64c7c6b2a5ea80e9996c8d9502a54eb4c3032a72 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 24 Feb 2021 02:28:57 -0800 Subject: [PATCH 086/280] ams: implement socket api for htclow socket driver --- .../source/socket/impl/socket_api.hpp | 17 + .../socket/impl/socket_api.os.horizon.cpp | 322 +++++++++++++++++- .../source/socket/socket_api.cpp | 48 +++ .../include/vapours/results/hipc_results.hpp | 3 +- 4 files changed, 387 insertions(+), 3 deletions(-) diff --git a/libraries/libstratosphere/source/socket/impl/socket_api.hpp b/libraries/libstratosphere/source/socket/impl/socket_api.hpp index 74124bfd6..d45c2c93d 100644 --- a/libraries/libstratosphere/source/socket/impl/socket_api.hpp +++ b/libraries/libstratosphere/source/socket/impl/socket_api.hpp @@ -33,6 +33,23 @@ namespace ams::socket::impl { Result InitializeAllocatorForInternal(void *buffer, size_t size); + ssize_t RecvFrom(s32 desc, void *buffer, size_t buffer_size, MsgFlag flags, SockAddr *out_address, SockLenT *out_addr_len); + ssize_t Recv(s32 desc, void *buffer, size_t buffer_size, MsgFlag flags); + + ssize_t SendTo(s32 desc, const void *buffer, size_t buffer_size, MsgFlag flags, const SockAddr *address, SockLenT len); + ssize_t Send(s32 desc, const void *buffer, size_t buffer_size, MsgFlag flags); + s32 Shutdown(s32 desc, ShutdownMethod how); + s32 SocketExempt(Family domain, Type type, Protocol protocol); + + s32 Accept(s32 desc, SockAddr *out_address, SockLenT *out_addr_len); + s32 Bind(s32 desc, const SockAddr *address, SockLenT len); + + s32 GetSockName(s32 desc, SockAddr *out_address, SockLenT *out_addr_len); + s32 SetSockOpt(s32 desc, Level level, Option option_name, const void *option_value, SockLenT option_size); + + s32 Listen(s32 desc, s32 backlog); + + s32 Close(s32 desc); } diff --git a/libraries/libstratosphere/source/socket/impl/socket_api.os.horizon.cpp b/libraries/libstratosphere/source/socket/impl/socket_api.os.horizon.cpp index ffca3e38c..9efa5e3ef 100644 --- a/libraries/libstratosphere/source/socket/impl/socket_api.os.horizon.cpp +++ b/libraries/libstratosphere/source/socket/impl/socket_api.os.horizon.cpp @@ -50,7 +50,7 @@ namespace ams::socket::impl { if (!g_heap_handle) { socket::impl::SetLastError(Errno::EOpNotSupp); } else if ((ptr = lmem::AllocateFromExpHeap(g_heap_handle, size)) == nullptr) { - socket::impl::SetLastError(Errno::EOpNotSupp); + socket::impl::SetLastError(Errno::ENoMem); } return ptr; @@ -66,7 +66,7 @@ namespace ams::socket::impl { if (!g_heap_handle) { socket::impl::SetLastError(Errno::EOpNotSupp); } else if ((ptr = lmem::AllocateFromExpHeap(g_heap_handle, size * num)) == nullptr) { - socket::impl::SetLastError(Errno::EOpNotSupp); + socket::impl::SetLastError(Errno::ENoMem); } else { std::memset(ptr, 0, size * num); } @@ -210,6 +210,43 @@ namespace ams::socket::impl { return ResultSuccess(); } + ALWAYS_INLINE struct sockaddr *ConvertForLibnx(SockAddr *addr) { + static_assert(sizeof(SockAddr) == sizeof(struct sockaddr)); + static_assert(alignof(SockAddr) == alignof(struct sockaddr)); + return reinterpret_cast(addr); + } + + ALWAYS_INLINE const struct sockaddr *ConvertForLibnx(const SockAddr *addr) { + static_assert(sizeof(SockAddr) == sizeof(struct sockaddr)); + static_assert(alignof(SockAddr) == alignof(struct sockaddr)); + return reinterpret_cast(addr); + } + + static_assert(std::same_as); + + Errno TranslateResultToBsdErrorImpl(const Result &result) { + if (R_SUCCEEDED(result)) { + return Errno::ESuccess; + } else if (svc::ResultInvalidCurrentMemory::Includes(result) || svc::ResultOutOfAddressSpace::Includes(result)) { + return Errno::EFault; + } else if (sf::hipc::ResultCommunicationError::Includes(result)) { + return Errno::EL3Hlt; + } else if (sf::hipc::ResultOutOfResource::Includes(result)) { + return Errno::EAgain; + } else { + R_ABORT_UNLESS(result); + return static_cast(-1); + } + } + + ALWAYS_INLINE void TranslateResultToBsdError(Errno &bsd_error, int &result) { + Errno translate_error = Errno::ESuccess; + if ((translate_error = TranslateResultToBsdErrorImpl(static_cast<::ams::Result>(::g_bsdResult))) != Errno::ESuccess) { + bsd_error = translate_error; + result = -1; + } + } + } Result Initialize(const Config &config) { @@ -249,4 +286,285 @@ namespace ams::socket::impl { return ResultSuccess(); } + ssize_t RecvFrom(s32 desc, void *buffer, size_t buffer_size, MsgFlag flags, SockAddr *out_address, SockLenT *out_addr_len) { + /* Check pre-conditions. */ + AMS_ABORT_UNLESS(IsInitialized()); + + /* Check input. */ + if (buffer_size == 0) { + return 0; + } else if (buffer == nullptr) { + socket::impl::SetLastError(Errno::EInval); + return -1; + } else if (buffer_size > std::numeric_limits::max()) { + socket::impl::SetLastError(Errno::EFault); + return -1; + } + + /* If this is just a normal receive call, perform a normal receive. */ + if (out_address == nullptr || out_addr_len == nullptr || *out_addr_len == 0) { + return impl::Recv(desc, buffer, buffer_size, flags); + } + + /* Perform the call. */ + socklen_t length; + Errno error = Errno::ESuccess; + int result = ::bsdRecvFrom(desc, buffer, buffer_size, static_cast(flags), ConvertForLibnx(out_address), std::addressof(length)); + TranslateResultToBsdError(error, result); + + if (result >= 0) { + *out_addr_len = length; + } else { + socket::impl::SetLastError(error); + } + + return result; + } + + ssize_t Recv(s32 desc, void *buffer, size_t buffer_size, MsgFlag flags) { + /* Check pre-conditions. */ + AMS_ABORT_UNLESS(IsInitialized()); + + /* Check input. */ + if (buffer_size == 0) { + return 0; + } else if (buffer == nullptr) { + socket::impl::SetLastError(Errno::EInval); + return -1; + } else if (buffer_size > std::numeric_limits::max()) { + socket::impl::SetLastError(Errno::EFault); + return -1; + } + + /* Perform the call. */ + Errno error = Errno::ESuccess; + int result = ::bsdRecv(desc, buffer, buffer_size, static_cast(flags)); + TranslateResultToBsdError(error, result); + + if (result < 0) { + socket::impl::SetLastError(error); + } + + return result; + } + + ssize_t SendTo(s32 desc, const void *buffer, size_t buffer_size, MsgFlag flags, const SockAddr *address, SockLenT len) { + /* Check pre-conditions. */ + AMS_ABORT_UNLESS(IsInitialized()); + + /* If this is a normal send, perform a normal send. */ + if (address == nullptr || len == 0) { + return impl::Send(desc, buffer, buffer_size, flags); + } + + /* Check input. */ + if (buffer_size == 0) { + return 0; + } else if (buffer == nullptr) { + socket::impl::SetLastError(Errno::EInval); + return -1; + } else if (buffer_size > std::numeric_limits::max()) { + socket::impl::SetLastError(Errno::EFault); + return -1; + } + + /* Perform the call. */ + Errno error = Errno::ESuccess; + int result = ::bsdSendTo(desc, buffer, buffer_size, static_cast(flags), ConvertForLibnx(address), len); + TranslateResultToBsdError(error, result); + + if (result < 0) { + socket::impl::SetLastError(error); + } + + return result; + } + + ssize_t Send(s32 desc, const void *buffer, size_t buffer_size, MsgFlag flags) { + /* Check pre-conditions. */ + AMS_ABORT_UNLESS(IsInitialized()); + + /* Check input. */ + if (buffer_size == 0) { + return 0; + } else if (buffer == nullptr) { + socket::impl::SetLastError(Errno::EInval); + return -1; + } else if (buffer_size > std::numeric_limits::max()) { + socket::impl::SetLastError(Errno::EFault); + return -1; + } + + /* Perform the call. */ + Errno error = Errno::ESuccess; + int result = ::bsdSend(desc, buffer, buffer_size, static_cast(flags)); + TranslateResultToBsdError(error, result); + + if (result < 0) { + socket::impl::SetLastError(error); + } + + return result; + } + + s32 Shutdown(s32 desc, ShutdownMethod how) { + /* Check pre-conditions. */ + AMS_ABORT_UNLESS(IsInitialized()); + + /* Perform the call. */ + Errno error = Errno::ESuccess; + int result = ::bsdShutdown(desc, static_cast(how)); + TranslateResultToBsdError(error, result); + + if (result < 0) { + socket::impl::SetLastError(error); + } + + return result; + } + + s32 SocketExempt(Family domain, Type type, Protocol protocol) { + /* Check pre-conditions. */ + AMS_ABORT_UNLESS(IsInitialized()); + + /* Perform the call. */ + Errno error = Errno::ESuccess; + int result = ::bsdSocketExempt(static_cast(domain), static_cast(type), static_cast(protocol)); + TranslateResultToBsdError(error, result); + + if (result < 0) { + socket::impl::SetLastError(error); + } + + return result; + } + + s32 Accept(s32 desc, SockAddr *out_address, SockLenT *out_addr_len) { + /* Check pre-conditions. */ + AMS_ABORT_UNLESS(IsInitialized()); + + /* Check input. */ + if (out_address == nullptr && out_addr_len != nullptr && *out_addr_len != 0) { + socket::impl::SetLastError(Errno::EFault); + return -1; + } + + socklen_t addrlen = static_cast((out_address && out_addr_len) ? *out_addr_len : 0); + + /* Perform the call. */ + Errno error = Errno::ESuccess; + int result = ::bsdAccept(desc, ConvertForLibnx(out_address), std::addressof(addrlen)); + TranslateResultToBsdError(error, result); + + if (result >= 0) { + if (out_addr_len != nullptr) { + *out_addr_len = addrlen; + } + } else { + socket::impl::SetLastError(error); + } + + return result; + } + s32 Bind(s32 desc, const SockAddr *address, SockLenT len) { + /* Check pre-conditions. */ + AMS_ABORT_UNLESS(IsInitialized()); + + /* Check input. */ + if (address == nullptr || len == 0) { + socket::impl::SetLastError(Errno::EInval); + return -1; + } + + /* Perform the call. */ + Errno error = Errno::ESuccess; + int result = ::bsdBind(desc, ConvertForLibnx(address), len); + TranslateResultToBsdError(error, result); + + if (result < 0) { + socket::impl::SetLastError(error); + } + + return result; + } + + s32 GetSockName(s32 desc, SockAddr *out_address, SockLenT *out_addr_len) { + /* Check pre-conditions. */ + AMS_ABORT_UNLESS(IsInitialized()); + + /* Check input. */ + if (out_address == nullptr || out_addr_len == nullptr || *out_addr_len == 0) { + socket::impl::SetLastError(Errno::EInval); + return -1; + } + + /* Perform the call. */ + socklen_t length; + Errno error = Errno::ESuccess; + int result = ::bsdGetSockName(desc, ConvertForLibnx(out_address), std::addressof(length)); + TranslateResultToBsdError(error, result); + + if (result >= 0) { + *out_addr_len = length; + } else { + socket::impl::SetLastError(error); + } + + return result; + } + + s32 SetSockOpt(s32 desc, Level level, Option option_name, const void *option_value, SockLenT option_size) { + /* Check pre-conditions. */ + AMS_ABORT_UNLESS(IsInitialized()); + + /* Check input. */ + if (option_value == nullptr) { + socket::impl::SetLastError(Errno::EInval); + return -1; + } + + /* Perform the call. */ + Errno error = Errno::ESuccess; + int result = ::bsdSetSockOpt(desc, static_cast(level), static_cast(option_name), option_value, option_size); + TranslateResultToBsdError(error, result); + + if (result < 0) { + socket::impl::SetLastError(error); + } + + return result; + } + + s32 Listen(s32 desc, s32 backlog) { + /* Check pre-conditions. */ + AMS_ABORT_UNLESS(IsInitialized()); + + /* Perform the call. */ + Errno error = Errno::ESuccess; + int result = ::bsdListen(desc, backlog); + TranslateResultToBsdError(error, result); + + if (result < 0) { + socket::impl::SetLastError(error); + } + + return result; + } + + s32 Close(s32 desc) { + /* Check pre-conditions. */ + AMS_ABORT_UNLESS(IsInitialized()); + + /* Perform the call. */ + Errno error = Errno::ESuccess; + int result = ::bsdClose(desc); + TranslateResultToBsdError(error, result); + + if (result < 0) { + socket::impl::SetLastError(error); + } + + return result; + } + } diff --git a/libraries/libstratosphere/source/socket/socket_api.cpp b/libraries/libstratosphere/source/socket/socket_api.cpp index 41a25c543..0723f766b 100644 --- a/libraries/libstratosphere/source/socket/socket_api.cpp +++ b/libraries/libstratosphere/source/socket/socket_api.cpp @@ -54,4 +54,52 @@ namespace ams::socket { return impl::InitializeAllocatorForInternal(buffer, size); } + ssize_t RecvFrom(s32 desc, void *buffer, size_t buffer_size, MsgFlag flags, SockAddr *out_address, SockLenT *out_addr_len){ + return impl::RecvFrom(desc, buffer, buffer_size, flags, out_address, out_addr_len); + } + + ssize_t Recv(s32 desc, void *buffer, size_t buffer_size, MsgFlag flags) { + return impl::Recv(desc, buffer, buffer_size, flags); + } + + ssize_t SendTo(s32 desc, const void *buffer, size_t buffer_size, MsgFlag flags, const SockAddr *address, SockLenT len) { + return impl::SendTo(desc, buffer, buffer_size, flags, address, len); + } + + ssize_t Send(s32 desc, const void *buffer, size_t buffer_size, MsgFlag flags) { + return impl::Send(desc, buffer, buffer_size, flags); + } + + s32 Shutdown(s32 desc, ShutdownMethod how) { + return impl::Shutdown(desc, how); + } + + s32 SocketExempt(Family domain, Type type, Protocol protocol) { + return impl::SocketExempt(domain, type, protocol); + } + + s32 Accept(s32 desc, SockAddr *out_address, SockLenT *out_addr_len) { + return impl::Accept(desc, out_address, out_addr_len); + } + + s32 Bind(s32 desc, const SockAddr *address, SockLenT len) { + return impl::Bind(desc, address, len); + } + + s32 GetSockName(s32 desc, SockAddr *out_address, SockLenT *out_addr_len) { + return impl::GetSockName(desc, out_address, out_addr_len); + } + + s32 SetSockOpt(s32 desc, Level level, Option option_name, const void *option_value, SockLenT option_size) { + return impl::SetSockOpt(desc, level, option_name, option_value, option_size); + } + + s32 Listen(s32 desc, s32 backlog) { + return impl::Listen(desc, backlog); + } + + s32 Close(s32 desc) { + return impl::Close(desc); + } + } diff --git a/libraries/libvapours/include/vapours/results/hipc_results.hpp b/libraries/libvapours/include/vapours/results/hipc_results.hpp index dec237e1a..84fcc4744 100644 --- a/libraries/libvapours/include/vapours/results/hipc_results.hpp +++ b/libraries/libvapours/include/vapours/results/hipc_results.hpp @@ -28,7 +28,8 @@ namespace ams::sf::hipc { R_DEFINE_ERROR_RESULT(OutOfDomains, 200); - R_DEFINE_ERROR_RESULT(SessionClosed, 301); + R_DEFINE_ABSTRACT_ERROR_RANGE(CommunicationError, 300, 349); + R_DEFINE_ERROR_RESULT(SessionClosed, 301); R_DEFINE_ERROR_RESULT(InvalidRequestSize, 402); R_DEFINE_ERROR_RESULT(UnknownCommandType, 403); From d8faa37de0132f0e97a9006dbde8d37fef98a5d5 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 24 Feb 2021 03:51:35 -0800 Subject: [PATCH 087/280] socket: fix config size calculations --- .../include/stratosphere/socket/socket_config.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/libstratosphere/include/stratosphere/socket/socket_config.hpp b/libraries/libstratosphere/include/stratosphere/socket/socket_config.hpp index 0879a2663..0110cd967 100644 --- a/libraries/libstratosphere/include/stratosphere/socket/socket_config.hpp +++ b/libraries/libstratosphere/include/stratosphere/socket/socket_config.hpp @@ -21,7 +21,7 @@ namespace ams::socket { constexpr ALWAYS_INLINE size_t AlignMss(size_t size) { - return util::DivideUp(size, static_cast(1500)); + return util::DivideUp(size, static_cast(1500)) * static_cast(1500); } class Config { From 953246a175f01f6ad139e28faf77431631777a96 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 24 Feb 2021 04:05:45 -0800 Subject: [PATCH 088/280] htc: disable socket driver, needs design thought before we can turn it on for real. --- .../htclow/driver/htclow_driver_memory_management.cpp | 2 +- stratosphere/htc/source/htc_main.cpp | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_driver_memory_management.cpp b/libraries/libstratosphere/source/htclow/driver/htclow_driver_memory_management.cpp index 64b3fe8ba..6638ad10a 100644 --- a/libraries/libstratosphere/source/htclow/driver/htclow_driver_memory_management.cpp +++ b/libraries/libstratosphere/source/htclow/driver/htclow_driver_memory_management.cpp @@ -38,7 +38,7 @@ namespace ams::htclow::driver { /* Declare the memory pool. */ alignas(RequiredAlignment) constinit u8 g_driver_memory[RequiredSize]; - constexpr inline const socket::SystemConfigDefault SocketConfig(g_driver_memory, RequiredSize, SocketAllocatorSize); + constexpr inline const socket::SystemConfigDefault SocketConfig(g_driver_memory, RequiredSize, SocketAllocatorSize, 2); } diff --git a/stratosphere/htc/source/htc_main.cpp b/stratosphere/htc/source/htc_main.cpp index cf36bdb30..cfe5db814 100644 --- a/stratosphere/htc/source/htc_main.cpp +++ b/stratosphere/htc/source/htc_main.cpp @@ -203,7 +203,15 @@ namespace ams::htc { } else if (std::strstr(transport, "socket")) { /* NOTE: Nintendo does not actually allow socket driver to be selected. */ /* Should we disallow this? Undesirable, because people will want to use docked tma. */ - return htclow::impl::DriverType::Socket; + + /* TODO: Right now, SocketDriver causes a hang on init. This is because */ + /* the socket driver requires wi-fi, but wi-fi can't happen until the system is fully up. */ + /* The system can't initialize fully until we acknowledge power state events. */ + /* We can't acknowledge power state events until our driver is online. */ + /* Resolving this chicken-and-egg problem without compromising design will require thought. */ + + //return htclow::impl::DriverType::Socket; + return DefaultHtclowDriverType; } else { return DefaultHtclowDriverType; } From a739e3fb20cb5beb4df06210f6b7e70755cbf68e Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 24 Feb 2021 04:08:15 -0800 Subject: [PATCH 089/280] docs: remove deprecated hid mitm from settings template --- config_templates/system_settings.ini | 6 ------ 1 file changed, 6 deletions(-) diff --git a/config_templates/system_settings.ini b/config_templates/system_settings.ini index a5c29ee15..abdc73c5e 100644 --- a/config_templates/system_settings.ini +++ b/config_templates/system_settings.ini @@ -32,12 +32,6 @@ ; NOTE: EXPERIMENTAL ; If you do not know what you are doing, do not touch this yet. ; fsmitm_redirect_saves_to_sd = u8!0x0 -; Controls whether to enable the deprecated hid mitm -; to fix compatibility with old homebrew. -; 0 = Do not enable, 1 = Enable. -; Please note this setting may be removed in a -; future release of Atmosphere. -; enable_deprecated_hid_mitm = u8!0x0 ; Controls whether am sees system settings "DebugModeFlag" as ; enabled or disabled. ; 0 = Disabled (not debug mode), 1 = Enabled (debug mode) From cee1ecd06f2463c460ab1d2450b0cc985eabe5d7 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 24 Feb 2021 04:17:00 -0800 Subject: [PATCH 090/280] tio: add stub sysmodule to host target io server --- .../impl/ams_system_thread_definitions.hpp | 5 + .../ncm/ncm_system_content_meta_id.hpp | 2 + stratosphere/TioServer/Makefile | 113 ++++++++++++++++++ stratosphere/TioServer/TioServer.json | 83 +++++++++++++ stratosphere/TioServer/source/tio_main.cpp | 113 ++++++++++++++++++ 5 files changed, 316 insertions(+) create mode 100644 stratosphere/TioServer/Makefile create mode 100644 stratosphere/TioServer/TioServer.json create mode 100644 stratosphere/TioServer/source/tio_main.cpp diff --git a/libraries/libstratosphere/include/stratosphere/ams/impl/ams_system_thread_definitions.hpp b/libraries/libstratosphere/include/stratosphere/ams/impl/ams_system_thread_definitions.hpp index 1c3bca1a3..bf7210586 100644 --- a/libraries/libstratosphere/include/stratosphere/ams/impl/ams_system_thread_definitions.hpp +++ b/libraries/libstratosphere/include/stratosphere/ams/impl/ams_system_thread_definitions.hpp @@ -144,6 +144,11 @@ namespace ams::impl { AMS_DEFINE_SYSTEM_THREAD(10, tma, BridgePcieDriver); + /* DevServer/TioServer. */ + AMS_DEFINE_SYSTEM_THREAD(21, TioServer, Main); + AMS_DEFINE_SYSTEM_THREAD(21, TioServer, FileServerHtcsServer); + AMS_DEFINE_SYSTEM_THREAD(21, TioServer, SdCardObserver); + #undef AMS_DEFINE_SYSTEM_THREAD diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_system_content_meta_id.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_system_content_meta_id.hpp index 894c556fe..f8028b078 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_system_content_meta_id.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_system_content_meta_id.hpp @@ -101,6 +101,7 @@ namespace ams::ncm { static const SystemProgramId Manu; static const SystemProgramId Htc; + static const SystemProgramId DevServer; }; struct AtmosphereProgramId { @@ -202,6 +203,7 @@ namespace ams::ncm { inline constexpr const SystemProgramId SystemProgramId::Manu = { 0x010000000000B14Aul }; inline constexpr const SystemProgramId SystemProgramId::Htc = { 0x010000000000B240ul }; + inline constexpr const SystemProgramId SystemProgramId::DevServer = { 0x010000000000D623ul }; inline constexpr bool IsSystemProgramId(const ProgramId &program_id) { return (SystemProgramId::Start <= program_id && program_id <= SystemProgramId::End) || IsAtmosphereProgramId(program_id); diff --git a/stratosphere/TioServer/Makefile b/stratosphere/TioServer/Makefile new file mode 100644 index 000000000..aab86a63a --- /dev/null +++ b/stratosphere/TioServer/Makefile @@ -0,0 +1,113 @@ +#--------------------------------------------------------------------------------- +# pull in common stratosphere sysmodule configuration +#--------------------------------------------------------------------------------- +include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../libraries/config/templates/stratosphere.mk + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(call FIND_SOURCE_FILES,$(SOURCES),c) +CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp) +SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),s) + +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC) + +ifeq ($(strip $(CONFIG_JSON)),) + jsons := $(wildcard *.json) + ifneq (,$(findstring $(TARGET).json,$(jsons))) + export APP_JSON := $(TOPDIR)/$(TARGET).json + else + ifneq (,$(findstring config.json,$(jsons))) + export APP_JSON := $(TOPDIR)/config.json + endif + endif +else + export APP_JSON := $(TOPDIR)/$(CONFIG_JSON) +endif + +.PHONY: $(BUILD) clean all + +#--------------------------------------------------------------------------------- +all: $(BUILD) + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).nsp $(TARGET).npdm $(TARGET).nso $(TARGET).elf + + +#--------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +all : $(OUTPUT).nsp + +ifeq ($(strip $(APP_JSON)),) +$(OUTPUT).nsp : $(OUTPUT).nso +else +$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm +endif + +$(OUTPUT).nso : $(OUTPUT).elf + +$(OUTPUT).elf : $(OFILES) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/stratosphere/TioServer/TioServer.json b/stratosphere/TioServer/TioServer.json new file mode 100644 index 000000000..05dea993b --- /dev/null +++ b/stratosphere/TioServer/TioServer.json @@ -0,0 +1,83 @@ +{ + "name": "TioServer", + "title_id": "0x010000000000D623", + "title_id_range_min": "0x010000000000D623", + "title_id_range_max": "0x010000000000D623", + "main_thread_stack_size": "0x00004000", + "main_thread_priority": 49, + "default_cpu_id": 3, + "process_category": 0, + "is_retail": true, + "pool_partition": 2, + "is_64_bit": true, + "address_space_type": 3, + "disable_device_address_space_merge": true, + "filesystem_access": { + "permissions": "0xFFFFFFFFFFFFFFFF" + }, + "service_access": ["fatal:u", "fsp-srv", "htcs"], + "service_host": [], + "kernel_capabilities": [{ + "type": "kernel_flags", + "value": { + "highest_thread_priority": 63, + "lowest_thread_priority": 24, + "lowest_cpu_id": 3, + "highest_cpu_id": 3 + } + }, { + "type": "syscalls", + "value": { + "svcSetHeapSize": "0x01", + "svcSetMemoryPermission": "0x02", + "svcSetMemoryAttribute": "0x03", + "svcMapMemory": "0x04", + "svcUnmapMemory": "0x05", + "svcQueryMemory": "0x06", + "svcExitProcess": "0x07", + "svcCreateThread": "0x08", + "svcStartThread": "0x09", + "svcExitThread": "0x0a", + "svcSleepThread": "0x0b", + "svcGetThreadPriority": "0x0c", + "svcSetThreadPriority": "0x0d", + "svcGetThreadCoreMask": "0x0e", + "svcSetThreadCoreMask": "0x0f", + "svcGetCurrentProcessorNumber": "0x10", + "svcSignalEvent": "0x11", + "svcClearEvent": "0x12", + "svcMapSharedMemory": "0x13", + "svcUnmapSharedMemory": "0x14", + "svcCreateTransferMemory": "0x15", + "svcCloseHandle": "0x16", + "svcResetSignal": "0x17", + "svcWaitSynchronization": "0x18", + "svcCancelSynchronization": "0x19", + "svcArbitrateLock": "0x1a", + "svcArbitrateUnlock": "0x1b", + "svcWaitProcessWideKeyAtomic": "0x1c", + "svcSignalProcessWideKey": "0x1d", + "svcGetSystemTick": "0x1e", + "svcConnectToNamedPort": "0x1f", + "svcSendSyncRequestLight": "0x20", + "svcSendSyncRequest": "0x21", + "svcSendSyncRequestWithUserBuffer": "0x22", + "svcSendAsyncRequestWithUserBuffer": "0x23", + "svcGetProcessId": "0x24", + "svcGetThreadId": "0x25", + "svcBreak": "0x26", + "svcOutputDebugString": "0x27", + "svcReturnFromException": "0x28", + "svcGetInfo": "0x29", + "svcWaitForAddress": "0x34", + "svcSignalToAddress": "0x35", + "svcCallSecureMonitor": "0x7f" + } + }, { + "type": "min_kernel_version", + "value": "0x0030" + }, { + "type": "handle_table_size", + "value": 0 + }] +} \ No newline at end of file diff --git a/stratosphere/TioServer/source/tio_main.cpp b/stratosphere/TioServer/source/tio_main.cpp new file mode 100644 index 000000000..bd2844517 --- /dev/null +++ b/stratosphere/TioServer/source/tio_main.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +extern "C" { + extern u32 __start__; + + u32 __nx_applet_type = AppletType_None; + u32 __nx_fs_num_sessions = 1; + + #define INNER_HEAP_SIZE 0x0 + size_t nx_inner_heap_size = INNER_HEAP_SIZE; + char nx_inner_heap[INNER_HEAP_SIZE]; + + void __libnx_initheap(void); + void __appInit(void); + void __appExit(void); + + void *__libnx_alloc(size_t size); + void *__libnx_aligned_alloc(size_t alignment, size_t size); + void __libnx_free(void *mem); +} + +namespace ams { + + ncm::ProgramId CurrentProgramId = ncm::SystemProgramId::DevServer; + +} + +using namespace ams; + +#define AMS_TIO_SERVER_USE_FATAL_ERROR 1 + +#if AMS_TIO_SERVER_USE_FATAL_ERROR + +extern "C" { + + /* Exception handling. */ + alignas(16) u8 __nx_exception_stack[ams::os::MemoryPageSize]; + u64 __nx_exception_stack_size = sizeof(__nx_exception_stack); + void __libnx_exception_handler(ThreadExceptionDump *ctx); + +} + +void __libnx_exception_handler(ThreadExceptionDump *ctx) { + ams::CrashHandler(ctx); +} + +#endif + +void __appInit(void) { + hos::InitializeForStratosphere(); + + /* TODO */ +} + +void __appExit(void) { + /* TODO */ +} + +namespace ams { + + void *Malloc(size_t size) { + AMS_ABORT("ams::Malloc was called"); + } + + void Free(void *ptr) { + AMS_ABORT("ams::Free was called"); + } + +} + +void *operator new(size_t size) { + AMS_ABORT("operator new(size_t) was called"); +} + +void operator delete(void *p) { + AMS_ABORT("operator delete(void *) was called"); +} + +void *__libnx_alloc(size_t size) { + AMS_ABORT("__libnx_alloc was called"); +} + +void *__libnx_aligned_alloc(size_t alignment, size_t size) { + AMS_ABORT("__libnx_aligned_alloc was called"); +} + +void __libnx_free(void *mem) { + AMS_ABORT("__libnx_free was called"); +} + +int main(int argc, char **argv) +{ + /* Set thread name. */ + os::SetThreadNamePointer(os::GetCurrentThread(), AMS_GET_SYSTEM_THREAD_NAME(TioServer, Main)); + AMS_ASSERT(os::GetThreadPriority(os::GetCurrentThread()) == AMS_GET_SYSTEM_THREAD_PRIORITY(TioServer, Main)); + + return 0; +} From 6ce2076d923207e24d1e7dc891eefa58dbec2cf4 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 24 Feb 2021 07:16:41 -0800 Subject: [PATCH 091/280] tio: implement server/dispatch logic. --- .../TioServer/source/tio_file_server.cpp | 150 ++++++++++++++++++ .../TioServer/source/tio_file_server.hpp | 25 +++ .../source/tio_file_server_htcs_server.cpp | 94 +++++++++++ .../source/tio_file_server_htcs_server.hpp | 47 ++++++ .../source/tio_file_server_packet.hpp | 66 ++++++++ .../source/tio_file_server_processor.cpp | 137 ++++++++++++++++ .../source/tio_file_server_processor.hpp | 48 ++++++ stratosphere/TioServer/source/tio_main.cpp | 69 +++++++- 8 files changed, 634 insertions(+), 2 deletions(-) create mode 100644 stratosphere/TioServer/source/tio_file_server.cpp create mode 100644 stratosphere/TioServer/source/tio_file_server.hpp create mode 100644 stratosphere/TioServer/source/tio_file_server_htcs_server.cpp create mode 100644 stratosphere/TioServer/source/tio_file_server_htcs_server.hpp create mode 100644 stratosphere/TioServer/source/tio_file_server_packet.hpp create mode 100644 stratosphere/TioServer/source/tio_file_server_processor.cpp create mode 100644 stratosphere/TioServer/source/tio_file_server_processor.hpp diff --git a/stratosphere/TioServer/source/tio_file_server.cpp b/stratosphere/TioServer/source/tio_file_server.cpp new file mode 100644 index 000000000..8c02dcfab --- /dev/null +++ b/stratosphere/TioServer/source/tio_file_server.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "tio_file_server.hpp" +#include "tio_file_server_packet.hpp" +#include "tio_file_server_htcs_server.hpp" +#include "tio_file_server_processor.hpp" + +namespace ams::tio { + + namespace { + + constexpr inline auto NumDispatchThreads = 2; + constexpr inline auto DispatchThreadPriority = 21; + constexpr inline size_t RequestBufferSize = 1_MB + util::AlignUp(0x40 + fs::EntryNameLengthMax, 1_KB); + + struct FileServerRequest { + int socket; + FileServerRequestHeader header; + u8 body[RequestBufferSize]; + }; + + constexpr const char HtcsPortName[] = "iywys@$TioServer_FileServer"; + + alignas(os::ThreadStackAlignment) u8 g_server_stack[os::MemoryPageSize]; + alignas(os::ThreadStackAlignment) u8 g_dispatch_stacks[NumDispatchThreads][os::MemoryPageSize]; + + constinit FileServerHtcsServer g_file_server_htcs_server; + constinit FileServerProcessor g_file_server_processor(g_file_server_htcs_server); + + constinit os::ThreadType g_file_server_dispatch_threads[NumDispatchThreads]; + + constinit FileServerRequest g_requests[NumDispatchThreads]; + + constinit os::MessageQueueType g_free_mq; + constinit os::MessageQueueType g_dispatch_mq; + + constinit uintptr_t g_free_mq_storage[NumDispatchThreads]; + constinit uintptr_t g_dispatch_mq_storage[NumDispatchThreads]; + + void OnFileServerHtcsSocketAccepted(int fd) { + /* Service requests, while we can. */ + while (true) { + /* Receive a free request. */ + uintptr_t request_address; + os::ReceiveMessageQueue(std::addressof(request_address), std::addressof(g_free_mq)); + + /* Ensure we manage our request properly. */ + auto req_guard = SCOPE_GUARD { os::SendMessageQueue(std::addressof(g_free_mq), request_address); }; + + /* Receive the request header. */ + FileServerRequest *request = reinterpret_cast(request_address); + if (htcs::Recv(fd, std::addressof(request->header), sizeof(request->header), htcs::HTCS_MSG_WAITALL) != sizeof(request->header)) { + break; + } + + /* Receive the request body, if necessary. */ + if (request->header.body_size > 0) { + if (htcs::Recv(fd, request->body, request->header.body_size, htcs::HTCS_MSG_WAITALL) != sizeof(request->header.body_size)) { + break; + } + } + + /* Dispatch the request. */ + req_guard.Cancel(); + request->socket = fd; + os::SendMessageQueue(std::addressof(g_dispatch_mq), request_address); + } + + /* Our socket is no longer making requests, so close it. */ + htcs::Close(fd); + + /* Clean up any server resources. */ + g_file_server_processor.Unmount(); + } + + void FileServerDispatchThreadFunction(void *) { + while (true) { + /* Receive a request. */ + uintptr_t request_address; + os::ReceiveMessageQueue(std::addressof(request_address), std::addressof(g_dispatch_mq)); + + /* Process the request. */ + FileServerRequest *request = reinterpret_cast(request_address); + if (!g_file_server_processor.ProcessRequest(std::addressof(request->header), request->body, request->socket)) { + htcs::Close(request->socket); + } + + /* Free the request. */ + os::SendMessageQueue(std::addressof(g_free_mq), request_address); + } + } + + } + + void InitializeFileServer() { + /* Initialize the htcs server. */ + g_file_server_htcs_server.Initialize(HtcsPortName, g_server_stack, sizeof(g_server_stack), OnFileServerHtcsSocketAccepted); + + /* TODO: Initialize SD card observer. */ + + /* Initialize the command processor. */ + g_file_server_processor.SetInserted(false); + g_file_server_processor.SetRequestBufferSize(RequestBufferSize); + + /* Initialize the dispatch message queues. */ + os::InitializeMessageQueue(std::addressof(g_free_mq), g_free_mq_storage, util::size(g_free_mq_storage)); + os::InitializeMessageQueue(std::addressof(g_dispatch_mq), g_dispatch_mq_storage, util::size(g_dispatch_mq_storage)); + + /* Begin with all requests free. */ + for (auto i = 0; i < NumDispatchThreads; ++i) { + os::SendMessageQueue(std::addressof(g_free_mq), reinterpret_cast(g_requests + i)); + } + + /* Initialize the dispatch threads. */ + /* NOTE: Nintendo does not name these threads. */ + for (auto i = 0; i < NumDispatchThreads; ++i) { + R_ABORT_UNLESS(os::CreateThread(g_file_server_dispatch_threads + i, FileServerDispatchThreadFunction, nullptr, g_dispatch_stacks + i, sizeof(g_dispatch_stacks[i]), DispatchThreadPriority)); + } + } + + void StartFileServer() { + /* Start the htcs server. */ + g_file_server_htcs_server.Start(); + + /* Start the dispatch threads. */ + for (auto i = 0; i < NumDispatchThreads; ++i) { + os::StartThread(g_file_server_dispatch_threads + i); + } + } + + void WaitFileServer() { + /* Wait for the htcs server to finish. */ + g_file_server_htcs_server.Wait(); + } + +} \ No newline at end of file diff --git a/stratosphere/TioServer/source/tio_file_server.hpp b/stratosphere/TioServer/source/tio_file_server.hpp new file mode 100644 index 000000000..f20803042 --- /dev/null +++ b/stratosphere/TioServer/source/tio_file_server.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::tio { + + void InitializeFileServer(); + void StartFileServer(); + void WaitFileServer(); + +} \ No newline at end of file diff --git a/stratosphere/TioServer/source/tio_file_server_htcs_server.cpp b/stratosphere/TioServer/source/tio_file_server_htcs_server.cpp new file mode 100644 index 000000000..91b65b2d0 --- /dev/null +++ b/stratosphere/TioServer/source/tio_file_server_htcs_server.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "tio_file_server_htcs_server.hpp" + +namespace ams::tio { + + void FileServerHtcsServer::Initialize(const char *port_name, void *thread_stack, size_t thread_stack_size, SocketAcceptedCallback on_socket_accepted) { + /* Set our port name. */ + std::strcpy(m_port_name.name, port_name); + + /* Set our callback. */ + m_on_socket_accepted = on_socket_accepted; + + /* Setup our thread. */ + R_ABORT_UNLESS(os::CreateThread(std::addressof(m_thread), ThreadEntry, this, thread_stack, thread_stack_size, AMS_GET_SYSTEM_THREAD_PRIORITY(TioServer, FileServerHtcsServer))); + + /* Set our thread name pointer. */ + os::SetThreadNamePointer(std::addressof(m_thread), AMS_GET_SYSTEM_THREAD_NAME(TioServer, FileServerHtcsServer)); + } + + void FileServerHtcsServer::Start() { + os::StartThread(std::addressof(m_thread)); + } + void FileServerHtcsServer::Wait() { + os::WaitThread(std::addressof(m_thread)); + } + + void FileServerHtcsServer::ThreadFunc() { + /* Loop forever, servicing sockets. */ + while (true) { + /* Get a socket. */ + int fd; + while ((fd = htcs::Socket()) == -1) { + os::SleepThread(TimeSpan::FromSeconds(1)); + } + + /* Ensure we cleanup the socket when we're done with it. */ + ON_SCOPE_EXIT { + htcs::Close(fd); + os::SleepThread(TimeSpan::FromSeconds(1)); + }; + + /* Create a sock addr for our server. */ + htcs::SockAddrHtcs addr; + addr.family = htcs::HTCS_AF_HTCS; + addr.peer_name = htcs::GetPeerNameAny(); + addr.port_name = m_port_name; + + /* Bind. */ + if (htcs::Bind(fd, std::addressof(addr)) == -1) { + continue; + } + + /* Listen on our port. */ + while (htcs::Listen(fd, 0) == 0) { + /* Continue accepting clients, so long as we can. */ + int client_fd; + while (true) { + /* Try to accept a client. */ + if (client_fd = htcs::Accept(fd, std::addressof(addr)); client_fd < 0) { + break; + } + + /* Handle the client. */ + m_on_socket_accepted(client_fd); + } + + /* NOTE: This seems unnecessary (client_fd guaranteed < 0 here), but Nintendo does it. */ + htcs::Close(client_fd); + } + } + } + + ssize_t FileServerHtcsServer::Send(s32 desc, const void *buffer, size_t buffer_size, s32 flags) { + AMS_ASSERT(m_mutex.IsLockedByCurrentThread()); + + return htcs::Send(desc, buffer, buffer_size, flags); + } + +} \ No newline at end of file diff --git a/stratosphere/TioServer/source/tio_file_server_htcs_server.hpp b/stratosphere/TioServer/source/tio_file_server_htcs_server.hpp new file mode 100644 index 000000000..b1ab62f2d --- /dev/null +++ b/stratosphere/TioServer/source/tio_file_server_htcs_server.hpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::tio { + + using SocketAcceptedCallback = void(*)(s32 desc); + + class FileServerHtcsServer { + private: + SocketAcceptedCallback m_on_socket_accepted; + htcs::HtcsPortName m_port_name; + os::ThreadType m_thread; + os::SdkMutex m_mutex; + public: + constexpr FileServerHtcsServer() : m_on_socket_accepted(nullptr), m_port_name{}, m_thread{}, m_mutex{} { /* ... */ } + private: + static void ThreadEntry(void *arg) { + static_cast(arg)->ThreadFunc(); + } + + void ThreadFunc(); + public: + os::SdkMutex &GetMutex() { return m_mutex; } + public: + void Initialize(const char *port_name, void *thread_stack, size_t thread_stack_size, SocketAcceptedCallback on_socket_accepted); + void Start(); + void Wait(); + + ssize_t Send(s32 desc, const void *buffer, size_t buffer_size, s32 flags); + }; + +} \ No newline at end of file diff --git a/stratosphere/TioServer/source/tio_file_server_packet.hpp b/stratosphere/TioServer/source/tio_file_server_packet.hpp new file mode 100644 index 000000000..17af28f70 --- /dev/null +++ b/stratosphere/TioServer/source/tio_file_server_packet.hpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::tio { + + enum class PacketType : u32 { + /* Control commands. */ + Connect = 0, + Disconnect = 1, + + /* Direct filesystem access. */ + CreateDirectory = 500, + DeleteDirectory = 501, + DeleteDirectoryRecursively = 502, + OpenDirectory = 503, + CloseDirectory = 504, + RenameDirectory = 505, + CreateFile = 506, + DeleteFile = 507, + OpenFile = 508, + FlushFile = 509, + CloseFile = 510, + RenameFile = 511, + ReadFile = 512, + WriteFile = 513, + GetEntryType = 514, + ReadDirectory = 515, + GetFileSize = 516, + SetFileSize = 517, + GetTotalSpaceSize = 518, + GetFreeSpaceSize = 519, + + /* Utilities. */ + Stat = 1000, + ListDirectory = 1001, + }; + + struct FileServerRequestHeader { + u64 request_id; + PacketType packet_type; + u32 body_size; + }; + + struct FileServerResponseHeader { + u64 request_id; + Result result; + u32 body_size; + }; + static_assert(sizeof(FileServerRequestHeader) == sizeof(FileServerResponseHeader)); + +} \ No newline at end of file diff --git a/stratosphere/TioServer/source/tio_file_server_processor.cpp b/stratosphere/TioServer/source/tio_file_server_processor.cpp new file mode 100644 index 000000000..5618f1854 --- /dev/null +++ b/stratosphere/TioServer/source/tio_file_server_processor.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "tio_file_server_processor.hpp" + +namespace ams::tio { + + namespace { + + constexpr inline int ProtocolVersion = 1; + + } + + void FileServerProcessor::Unmount() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Close all our directories. */ + for (size_t i = 0; i < m_open_directory_count; ++i) { + fs::CloseDirectory(m_directories[i]); + m_directories[i] = {}; + } + + /* Close all our files. */ + for (size_t i = 0; i < m_open_file_count; ++i) { + fs::CloseFile(m_files[i]); + m_files[i] = {}; + } + + /* If we're mounted, unmount the sd card. */ + if (m_is_mounted) { + m_is_mounted = false; + fs::Unmount("sd"); + } + } + + bool FileServerProcessor::ProcessRequest(FileServerRequestHeader *header, u8 *body, int socket) { + /* Declare a response header for us to use. */ + FileServerResponseHeader response_header = { + .request_id = header->request_id, + .result = ResultSuccess(), + .body_size = 0, + }; + + /* Handle the special control commands. */ + if (header->packet_type == PacketType::Connect) { + /* If the SD card isn't already mounted, try to mount it. */ + if (!m_is_mounted) { + /* Mount the sd card. */ + m_is_mounted = !fs::ResultSdCardAccessFailed::Includes(fs::MountSdCard("sd")); + + /* Prepare the response. */ + char *response_body = reinterpret_cast(body); + util::SNPrintf(response_body, 0x100, "{\"bufferSize\":%zu, \"sdcardMounted\":%s, \"sdcardInserted\":%s, \"version\":%d}", + m_request_buffer_size, + m_is_mounted ? "true" : "false", + m_is_inserted ? "true" : "false", + ProtocolVersion); + + /* Get the response length. */ + response_header.body_size = std::strlen(response_body); + } + + return this->SendResponse(response_header, body, socket); + } else if (header->packet_type == PacketType::Disconnect) { + /* If we need to, unmount the sd card. */ + if (m_is_mounted) { + this->Unmount(); + } + + /* Send the response. */ + return this->SendResponse(response_header, body, socket); + } + + /* TODO: Handle remaining packet types. */ + return false; + // + //switch (header->packet_type) { + // case PacketType::CreateDirectory: + // case PacketType::DeleteDirectory: + // case PacketType::DeleteDirectoryRecursively: + // case PacketType::OpenDirectory: + // case PacketType::CloseDirectory: + // case PacketType::RenameDirectory: + // case PacketType::CreateFile: + // case PacketType::DeleteFile: + // case PacketType::OpenFile: + // case PacketType::FlushFile: + // case PacketType::CloseFile: + // case PacketType::RenameFile: + // case PacketType::ReadFile: + // case PacketType::WriteFile: + // case PacketType::GetEntryType: + // case PacketType::ReadDirectory: + // case PacketType::GetFileSize: + // case PacketType::SetFileSize: + // case PacketType::GetTotalSpaceSize: + // case PacketType::GetFreeSpaceSize: + // case PacketType::Stat: + // case PacketType::ListDirectory: + // /* TODO */ + // return false; + //} + } + + bool FileServerProcessor::SendResponse(const FileServerResponseHeader &header, const void *body, int socket) { + /* Lock our server. */ + std::scoped_lock lk(m_htcs_server.GetMutex()); + + /* Send the response header. */ + if (m_htcs_server.Send(socket, std::addressof(header), sizeof(header), 0) != sizeof(header)) { + return false; + } + + /* If we don't have a body, we're done. */ + if (header.body_size == 0) { + return true; + } + + /* Send the body. */ + return m_htcs_server.Send(socket, body, header.body_size, 0) == header.body_size; + } + +} \ No newline at end of file diff --git a/stratosphere/TioServer/source/tio_file_server_processor.hpp b/stratosphere/TioServer/source/tio_file_server_processor.hpp new file mode 100644 index 000000000..8ec5c1ac5 --- /dev/null +++ b/stratosphere/TioServer/source/tio_file_server_processor.hpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "tio_file_server_htcs_server.hpp" +#include "tio_file_server_packet.hpp" + +namespace ams::tio { + + class FileServerProcessor { + private: + bool m_is_inserted{}; + bool m_is_mounted{}; + size_t m_request_buffer_size{}; + FileServerHtcsServer &m_htcs_server; + size_t m_open_file_count{}; + size_t m_open_directory_count{}; + fs::FileHandle m_files[0x80]{}; + fs::DirectoryHandle m_directories[0x80]{}; + os::SdkMutex m_fs_mutex{}; + os::SdkMutex m_mutex{}; + public: + constexpr FileServerProcessor(FileServerHtcsServer &htcs_server) : m_htcs_server(htcs_server) { /* ... */ } + + void SetInserted(bool ins) { m_is_inserted = ins; } + void SetRequestBufferSize(size_t size) { m_request_buffer_size = size; } + public: + bool ProcessRequest(FileServerRequestHeader *header, u8 *body, int socket); + + void Unmount(); + private: + bool SendResponse(const FileServerResponseHeader &header, const void *body, int socket); + }; + +} \ No newline at end of file diff --git a/stratosphere/TioServer/source/tio_main.cpp b/stratosphere/TioServer/source/tio_main.cpp index bd2844517..9bf97dac4 100644 --- a/stratosphere/TioServer/source/tio_main.cpp +++ b/stratosphere/TioServer/source/tio_main.cpp @@ -14,6 +14,7 @@ * along with this program. If not, see . */ #include +#include "tio_file_server.hpp" extern "C" { extern u32 __start__; @@ -61,14 +62,63 @@ void __libnx_exception_handler(ThreadExceptionDump *ctx) { #endif +namespace ams::tio { + + namespace { + + alignas(0x40) constinit u8 g_fs_heap_buffer[64_KB]; + alignas(0x40) constinit u8 g_htcs_buffer[1_KB]; + lmem::HeapHandle g_fs_heap_handle; + + void *AllocateForFs(size_t size) { + return lmem::AllocateFromExpHeap(g_fs_heap_handle, size); + } + + void DeallocateForFs(void *p, size_t size) { + return lmem::FreeToExpHeap(g_fs_heap_handle, p); + } + + void InitializeFsHeap() { + /* Setup fs allocator. */ + g_fs_heap_handle = lmem::CreateExpHeap(g_fs_heap_buffer, sizeof(g_fs_heap_buffer), lmem::CreateOption_ThreadSafe); + } + + } + +} + +void __libnx_initheap(void) { + void* addr = nx_inner_heap; + size_t size = nx_inner_heap_size; + + /* Newlib */ + extern char* fake_heap_start; + extern char* fake_heap_end; + + fake_heap_start = (char*)addr; + fake_heap_end = (char*)addr + size; + + ams::tio::InitializeFsHeap(); +} + void __appInit(void) { hos::InitializeForStratosphere(); - /* TODO */ + /* Initialize FS heap. */ + fs::SetAllocator(tio::AllocateForFs, tio::DeallocateForFs); + + /* Disable FS auto-abort. */ + fs::SetEnabledAutoAbort(false); + + sm::DoWithSession([&]() { + R_ABORT_UNLESS(fsInitialize()); + }); + + ams::CheckApiVersion(); } void __appExit(void) { - /* TODO */ + fsExit(); } namespace ams { @@ -109,5 +159,20 @@ int main(int argc, char **argv) os::SetThreadNamePointer(os::GetCurrentThread(), AMS_GET_SYSTEM_THREAD_NAME(TioServer, Main)); AMS_ASSERT(os::GetThreadPriority(os::GetCurrentThread()) == AMS_GET_SYSTEM_THREAD_PRIORITY(TioServer, Main)); + /* Initialize htcs. */ + constexpr auto HtcsSocketCountMax = 2; + const size_t buffer_size = htcs::GetWorkingMemorySize(HtcsSocketCountMax); + AMS_ABORT_UNLESS(sizeof(tio::g_htcs_buffer) >= buffer_size); + htcs::InitializeForSystem(tio::g_htcs_buffer, buffer_size, HtcsSocketCountMax); + + /* Initialize the file server. */ + tio::InitializeFileServer(); + + /* Start the file server. */ + tio::StartFileServer(); + + /* Wait for the file server to finish. */ + tio::WaitFileServer(); + return 0; } From 3cbd99a709ee2c933fee135032a8d52647c59392 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 24 Feb 2021 09:22:13 -0800 Subject: [PATCH 092/280] tio: implement all command processor logic --- .../source/tio_file_server_packet.hpp | 123 +++- .../source/tio_file_server_processor.cpp | 670 +++++++++++++++++- 2 files changed, 756 insertions(+), 37 deletions(-) diff --git a/stratosphere/TioServer/source/tio_file_server_packet.hpp b/stratosphere/TioServer/source/tio_file_server_packet.hpp index 17af28f70..bfca282b7 100644 --- a/stratosphere/TioServer/source/tio_file_server_packet.hpp +++ b/stratosphere/TioServer/source/tio_file_server_packet.hpp @@ -63,4 +63,125 @@ namespace ams::tio { }; static_assert(sizeof(FileServerRequestHeader) == sizeof(FileServerResponseHeader)); -} \ No newline at end of file + struct CreateDirectoryParam { + u32 path_len; + char path[]; + }; + + struct DeleteDirectoryParam { + u32 path_len; + char path[]; + }; + + struct DeleteDirectoryRecursivelyParam { + u32 path_len; + char path[]; + }; + + struct OpenDirectoryParam { + u32 path_len; + fs::OpenDirectoryMode open_mode; + char path[]; + }; + static_assert(sizeof(OpenDirectoryParam) == 0x8); + + struct CloseDirectoryParam { + u64 handle; + }; + + struct RenameDirectoryParam { + u32 old_len; + u32 new_len; + char data[]; + }; + + struct CreateFileParam { + s64 size; + u32 path_len; + fs::CreateOption option; + char path[]; + }; + static_assert(sizeof(CreateFileParam) == 0x10); + + struct DeleteFileParam { + u32 path_len; + char path[]; + }; + + struct OpenFileParam { + u32 path_len; + fs::OpenMode mode; + char path[]; + }; + static_assert(sizeof(OpenFileParam) == 0x8); + + struct FlushFileParam { + u64 handle; + }; + + struct CloseFileParam { + u64 handle; + }; + + struct RenameFileParam { + u32 old_len; + u32 new_len; + char data[]; + }; + + struct ReadFileParam { + u64 handle; + s64 offset; + u64 size; + fs::ReadOption option; + }; + static_assert(sizeof(ReadFileParam) == 0x20); + + struct WriteFileParam { + u64 handle; + s64 offset; + u64 size; + fs::WriteOption option; + }; + static_assert(sizeof(WriteFileParam) == 0x20); + + struct GetEntryTypeParam { + u32 path_len; + char path[]; + }; + + struct ReadDirectoryParam { + u64 handle; + s64 count; + }; + + struct GetFileSizeParam { + u64 handle; + }; + + struct SetFileSizeParam { + u64 handle; + s64 size; + }; + + struct GetTotalSpaceSizeParam { + u32 path_len; + char path[]; + }; + + struct GetFreeSpaceSizeParam { + u32 path_len; + char path[]; + }; + + struct StatParam { + u32 path_len; + char path[]; + }; + + struct ListDirectoryParam { + u32 path_len; + char path[]; + }; + +} diff --git a/stratosphere/TioServer/source/tio_file_server_processor.cpp b/stratosphere/TioServer/source/tio_file_server_processor.cpp index 5618f1854..05deb6c25 100644 --- a/stratosphere/TioServer/source/tio_file_server_processor.cpp +++ b/stratosphere/TioServer/source/tio_file_server_processor.cpp @@ -29,16 +29,28 @@ namespace ams::tio { std::scoped_lock lk(m_mutex); /* Close all our directories. */ - for (size_t i = 0; i < m_open_directory_count; ++i) { - fs::CloseDirectory(m_directories[i]); - m_directories[i] = {}; + if (m_open_directory_count > 0) { + for (size_t i = 0; i < util::size(m_directories); ++i) { + if (m_directories[i].handle != nullptr) { + fs::CloseDirectory(m_directories[i]); + m_directories[i] = {}; + --m_open_directory_count; + } + } } + AMS_ABORT_UNLESS(m_open_directory_count == 0); /* Close all our files. */ - for (size_t i = 0; i < m_open_file_count; ++i) { - fs::CloseFile(m_files[i]); - m_files[i] = {}; + if (m_open_file_count > 0) { + for (size_t i = 0; i < util::size(m_files); ++i) { + if (m_files[i].handle != nullptr) { + fs::CloseFile(m_files[i]); + m_files[i] = {}; + --m_open_file_count; + } + } } + AMS_ABORT_UNLESS(m_open_file_count == 0); /* If we're mounted, unmount the sd card. */ if (m_is_mounted) { @@ -85,35 +97,621 @@ namespace ams::tio { return this->SendResponse(response_header, body, socket); } - /* TODO: Handle remaining packet types. */ - return false; - // - //switch (header->packet_type) { - // case PacketType::CreateDirectory: - // case PacketType::DeleteDirectory: - // case PacketType::DeleteDirectoryRecursively: - // case PacketType::OpenDirectory: - // case PacketType::CloseDirectory: - // case PacketType::RenameDirectory: - // case PacketType::CreateFile: - // case PacketType::DeleteFile: - // case PacketType::OpenFile: - // case PacketType::FlushFile: - // case PacketType::CloseFile: - // case PacketType::RenameFile: - // case PacketType::ReadFile: - // case PacketType::WriteFile: - // case PacketType::GetEntryType: - // case PacketType::ReadDirectory: - // case PacketType::GetFileSize: - // case PacketType::SetFileSize: - // case PacketType::GetTotalSpaceSize: - // case PacketType::GetFreeSpaceSize: - // case PacketType::Stat: - // case PacketType::ListDirectory: - // /* TODO */ - // return false; - //} + /* The SD card must be inserted and mounted for us to process requests. */ + if (m_is_inserted && m_is_mounted) { + switch (header->packet_type) { + case PacketType::CreateDirectory: + { + /* Get the parameters. */ + const auto *param = reinterpret_cast(body); + if (header->body_size != sizeof(*param) + param->path_len) { + return false; + } + + /* Create the directory. */ + response_header.result = fs::CreateDirectory(param->path); + } + break; + case PacketType::DeleteDirectory: + { + /* Get the parameters. */ + const auto *param = reinterpret_cast(body); + if (header->body_size != sizeof(*param) + param->path_len) { + return false; + } + + /* Delete the directory. */ + response_header.result = fs::DeleteDirectory(param->path); + } + break; + case PacketType::DeleteDirectoryRecursively: + { + /* Get the parameters. */ + const auto *param = reinterpret_cast(body); + if (header->body_size != sizeof(*param) + param->path_len) { + return false; + } + + /* Delete the directory. */ + response_header.result = fs::DeleteDirectoryRecursively(param->path); + } + break; + case PacketType::OpenDirectory: + { + /* Get the parameters. */ + const auto *param = reinterpret_cast(body); + if (header->body_size != sizeof(*param) + param->path_len) { + return false; + } + + /* Open the directory. */ + fs::DirectoryHandle handle; + response_header.result = fs::OpenDirectory(std::addressof(handle), param->path, param->open_mode); + if (R_SUCCEEDED(response_header.result)) { + std::scoped_lock lk(m_mutex); + + if (m_open_directory_count < util::size(m_directories)) { + /* Insert the directory into our table. */ + u64 index = std::numeric_limits::max(); + for (size_t i = 0; i < util::size(m_directories); ++i) { + if (m_directories[i].handle == nullptr) { + m_directories[i] = handle; + index = i; + ++m_open_directory_count; + break; + } + } + AMS_ABORT_UNLESS(index < util::size(m_directories)); + + /* Return the index. */ + response_header.body_size = sizeof(index); + std::memcpy(body, std::addressof(index), sizeof(index)); + } else { + /* We can't actually open the directory. */ + fs::CloseDirectory(handle); + + response_header.result = fs::ResultOpenCountLimit(); + } + } + } + break; + case PacketType::CloseDirectory: + { + /* Get the parameters. */ + const auto *param = reinterpret_cast(body); + if (header->body_size != sizeof(*param)) { + return false; + } + + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Check that the directory handle is valid. */ + if (param->handle >= util::size(m_directories) || m_directories[param->handle].handle == nullptr) { + response_header.result = fs::ResultDataCorrupted(); + break; + } + + /* Lock the filesystem. */ + std::scoped_lock lk2(m_fs_mutex); + + /* Close the directory. */ + fs::CloseDirectory(m_directories[param->handle]); + m_directories[param->handle].handle = {}; + --m_open_directory_count; + } + break; + case PacketType::RenameDirectory: + { + /* Get the parameters. */ + const auto *param = reinterpret_cast(body); + if (header->body_size != sizeof(*param) + param->old_len + param->new_len) { + return false; + } + + /* Delete the directory. */ + const char *old_path = param->data + 0; + const char *new_path = param->data + param->old_len; + response_header.result = fs::RenameDirectory(old_path, new_path); + } + break; + case PacketType::CreateFile: + { + /* Get the parameters. */ + const auto *param = reinterpret_cast(body); + if (header->body_size != sizeof(*param) + param->path_len) { + return false; + } + + /* Create the file. */ + response_header.result = fs::CreateFile(param->path, param->size, static_cast(param->option)); + } + break; + case PacketType::DeleteFile: + { + /* Get the parameters. */ + const auto *param = reinterpret_cast(body); + if (header->body_size != sizeof(*param) + param->path_len) { + return false; + } + + /* Delete the file. */ + response_header.result = fs::DeleteFile(param->path); + } + break; + case PacketType::OpenFile: + { + /* Get the parameters. */ + const auto *param = reinterpret_cast(body); + if (header->body_size != sizeof(*param) + param->path_len) { + return false; + } + + /* Open the file. */ + fs::FileHandle handle; + response_header.result = fs::OpenFile(std::addressof(handle), param->path, param->mode); + if (R_SUCCEEDED(response_header.result)) { + std::scoped_lock lk(m_mutex); + + if (m_open_file_count < util::size(m_files)) { + /* Insert the file into our table. */ + u64 index = std::numeric_limits::max(); + for (size_t i = 0; i < util::size(m_files); ++i) { + if (m_files[i].handle == nullptr) { + m_files[i] = handle; + index = i; + ++m_open_file_count; + break; + } + } + AMS_ABORT_UNLESS(index < util::size(m_files)); + + /* Return the index. */ + response_header.body_size = sizeof(index); + std::memcpy(body, std::addressof(index), sizeof(index)); + } else { + /* We can't actually open the file. */ + fs::CloseFile(handle); + + response_header.result = fs::ResultOpenCountLimit(); + } + } + } + break; + case PacketType::FlushFile: + { + /* Get the parameters. */ + const auto *param = reinterpret_cast(body); + if (header->body_size != sizeof(*param)) { + return false; + } + + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Check that the file handle is valid. */ + if (param->handle >= util::size(m_files) || m_files[param->handle].handle == nullptr) { + response_header.result = fs::ResultDataCorrupted(); + break; + } + + /* Lock the filesystem. */ + std::scoped_lock lk2(m_fs_mutex); + + /* Flush the file. */ + response_header.result = fs::FlushFile(m_files[param->handle]); + } + break; + case PacketType::CloseFile: + { + /* Get the parameters. */ + const auto *param = reinterpret_cast(body); + if (header->body_size != sizeof(*param)) { + return false; + } + + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Check that the file handle is valid. */ + if (param->handle >= util::size(m_files) || m_files[param->handle].handle == nullptr) { + response_header.result = fs::ResultDataCorrupted(); + break; + } + + /* Lock the filesystem. */ + std::scoped_lock lk2(m_fs_mutex); + + /* Close the directory. */ + fs::CloseFile(m_files[param->handle]); + m_files[param->handle].handle = {}; + --m_open_file_count; + } + break; + case PacketType::RenameFile: + { + /* Get the parameters. */ + const auto *param = reinterpret_cast(body); + if (header->body_size != sizeof(*param) + param->old_len + param->new_len) { + return false; + } + + /* Delete the directory. */ + const char *old_path = param->data + 0; + const char *new_path = param->data + param->old_len; + response_header.result = fs::RenameFile(old_path, new_path); + } + break; + case PacketType::ReadFile: + { + /* Get the parameters. */ + const auto param = *reinterpret_cast(body); + if (header->body_size != sizeof(param)) { + return false; + } + + /* Check that the read is valid. */ + if (param.size + sizeof(u64) > m_request_buffer_size) { + response_header.result = fs::ResultDataCorrupted(); + break; + } + + /* Prepare response variables. */ + u64 *out_size = reinterpret_cast(body); + void *dst = out_size + 1; + + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Check that the file handle is valid. */ + if (param.handle >= util::size(m_files) || m_files[param.handle].handle == nullptr) { + response_header.result = fs::ResultDataCorrupted(); + break; + } + + /* Read the file. */ + size_t read_size; + response_header.result = fs::ReadFile(std::addressof(read_size), m_files[param.handle], param.offset, dst, param.size); + + if (R_SUCCEEDED(response_header.result)) { + *out_size = read_size; + response_header.body_size = sizeof(u64) + read_size; + } + } + break; + case PacketType::WriteFile: + { + /* Get the parameters. */ + const auto *param = reinterpret_cast(body); + if (header->body_size != sizeof(*param) + param->size) { + return false; + } + + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Check that the file handle is valid. */ + if (param->handle >= util::size(m_files) || m_files[param->handle].handle == nullptr) { + response_header.result = fs::ResultDataCorrupted(); + break; + } + + /* Lock the filesystem. */ + std::scoped_lock lk2(m_fs_mutex); + + /* Write the file. */ + response_header.result = fs::WriteFile(m_files[param->handle], param->offset, body + sizeof(*param), param->size, param->option); + } + break; + case PacketType::GetEntryType: + { + /* Get the parameters. */ + const auto *param = reinterpret_cast(body); + if (header->body_size != sizeof(*param) + param->path_len) { + return false; + } + + /* Get the entry type. */ + fs::DirectoryEntryType type; + response_header.result = fs::GetEntryType(std::addressof(type), param->path); + + if (R_SUCCEEDED(response_header.result)) { + /* Return the type. */ + response_header.body_size = sizeof(type); + std::memcpy(body, std::addressof(type), sizeof(type)); + + static_assert(sizeof(type) == sizeof(u32)); + } + } + break; + case PacketType::ReadDirectory: + { + /* Get the parameters. */ + const auto param = *reinterpret_cast(body); + if (header->body_size != sizeof(param)) { + return false; + } + + /* Check that the read is valid. */ + if (sizeof(s64) + param.count * sizeof(fs::DirectoryEntry) > m_request_buffer_size) { + response_header.result = fs::ResultDataCorrupted(); + break; + } + + /* Prepare response variables. */ + s64 *out_count = reinterpret_cast(body); + fs::DirectoryEntry *dst = reinterpret_cast(out_count + 1); + + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Check that the directory handle is valid. */ + if (param.handle >= util::size(m_directories) || m_directories[param.handle].handle == nullptr) { + response_header.result = fs::ResultDataCorrupted(); + break; + } + + /* Read the directory. */ + response_header.result = fs::ReadDirectory(out_count, dst, m_directories[param.handle], param.count); + + if (R_SUCCEEDED(response_header.result)) { + response_header.body_size = sizeof(s64) + *out_count * sizeof(fs::DirectoryEntry); + } + } + break; + case PacketType::GetFileSize: + { + /* Get the parameters. */ + const auto *param = reinterpret_cast(body); + if (header->body_size != sizeof(*param)) { + return false; + } + + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Check that the file handle is valid. */ + if (param->handle >= util::size(m_files) || m_files[param->handle].handle == nullptr) { + response_header.result = fs::ResultDataCorrupted(); + break; + } + + /* Get the file size. */ + response_header.result = fs::GetFileSize(reinterpret_cast(body), m_files[param->handle]); + + if (R_SUCCEEDED(response_header.result)) { + response_header.body_size = sizeof(s64); + } + } + break; + case PacketType::SetFileSize: + { + /* Get the parameters. */ + const auto *param = reinterpret_cast(body); + if (header->body_size != sizeof(*param)) { + return false; + } + + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Check that the file handle is valid. */ + if (param->handle >= util::size(m_files) || m_files[param->handle].handle == nullptr) { + response_header.result = fs::ResultDataCorrupted(); + break; + } + + /* Lock the filesystem. */ + std::scoped_lock lk2(m_fs_mutex); + + /* Get the file size. */ + response_header.result = fs::SetFileSize(m_files[param->handle], param->size); + } + break; + case PacketType::GetTotalSpaceSize: + { + /* Get the parameters. */ + const auto *param = reinterpret_cast(body); + if (header->body_size != sizeof(*param) + param->path_len) { + return false; + } + + /* Get the total space size. */ + s64 size; + response_header.result = fs::GetTotalSpaceSize(std::addressof(size), param->path); + + if (R_SUCCEEDED(response_header.result)) { + /* Return the size. */ + response_header.body_size = sizeof(size); + std::memcpy(body, std::addressof(size), sizeof(size)); + } + } + break; + case PacketType::GetFreeSpaceSize: + { + /* Get the parameters. */ + const auto *param = reinterpret_cast(body); + if (header->body_size != sizeof(*param) + param->path_len) { + return false; + } + + /* Get the free space size. */ + s64 size; + response_header.result = fs::GetFreeSpaceSize(std::addressof(size), param->path); + + if (R_SUCCEEDED(response_header.result)) { + /* Return the size. */ + response_header.body_size = sizeof(size); + std::memcpy(body, std::addressof(size), sizeof(size)); + } + } + break; + case PacketType::Stat: + { + /* Get the parameters. */ + const auto *param = reinterpret_cast(body); + if (header->body_size != sizeof(*param) + param->path_len) { + return false; + } + + /* Prepare a response stat structure. */ + struct { + fs::DirectoryEntryType type; + s64 file_size; + fs::FileTimeStampRaw file_timestamp; + } out = {}; + static_assert(sizeof(out) == 0x30); + + /* Get the entry type. */ + response_header.result = fs::GetEntryType(std::addressof(out.type), param->path); + if (R_FAILED(response_header.result)) { + break; + } + + /* If the path is a file, get further information. */ + if (out.type == fs::DirectoryEntryType_File) { + /* Try to get the file size. */ + { + fs::FileHandle handle; + const auto open_result = fs::OpenFile(std::addressof(handle), param->path, fs::OpenMode_Read); + if (R_SUCCEEDED(open_result)) { + ON_SCOPE_EXIT { fs::CloseFile(handle); }; + + response_header.result = fs::GetFileSize(std::addressof(out.file_size), handle); + if (R_FAILED(response_header.result)) { + break; + } + } else { + if (fs::ResultTargetLocked::Includes(open_result)) { + out.file_size = 0; + } else { + response_header.result = open_result; + break; + } + } + } + + /* Get the file timestamp. */ + response_header.result = fs::GetFileTimeStampRawForDebug(std::addressof(out.file_timestamp), param->path); + if (R_FAILED(response_header.result)) { + break; + } + } + + /* If we successfully got the stat information, send it as response. */ + if (R_SUCCEEDED(response_header.result)) { + response_header.body_size = sizeof(out); + std::memcpy(body, std::addressof(out), sizeof(out)); + } + } + break; + case PacketType::ListDirectory: + { + /* Get the parameters. */ + const auto *param = reinterpret_cast(body); + if (header->body_size != sizeof(*param) + param->path_len) { + return false; + } + + /* Open the directory. */ + fs::DirectoryHandle handle; + response_header.result = fs::OpenDirectory(std::addressof(handle), param->path, fs::OpenDirectoryMode_All); + if (R_FAILED(response_header.result)) { + break; + } + + /* When we're done, close the handle. */ + ON_SCOPE_EXIT { fs::CloseDirectory(handle); }; + + /* Get the directory entry count. */ + s64 count; + response_header.result = fs::GetDirectoryEntryCount(std::addressof(count), handle); + if (R_FAILED(response_header.result)) { + break; + } + /* Determine whether we can send the response in one go. */ + const size_t needed_size = sizeof(s64) + sizeof(u64) + sizeof(fs::DirectoryEntry) * count; + if (needed_size <= m_request_buffer_size) { + /* We can perform the entire read in one send. */ + struct { + s64 count; + u64 size; + fs::DirectoryEntry entries[]; + } *out = reinterpret_cast(body); + + s64 read_count; + response_header.result = fs::ReadDirectory(std::addressof(read_count), out->entries, handle, count); + if (R_FAILED(response_header.result)) { + break; + } + + /* Set the output. */ + out->count = read_count; + out->size = read_count * sizeof(fs::DirectoryEntry); + + /* Set the response body size. */ + response_header.body_size = sizeof(out) + out->size; + } else { + /* We have to use multiple sends. */ + /* Lock our server. */ + std::scoped_lock lk(m_htcs_server.GetMutex()); + + /* Send the response header. */ + response_header.body_size = needed_size; + if (m_htcs_server.Send(socket, std::addressof(header), sizeof(header), 0) != sizeof(header)) { + return false; + } + + /* Send the body header. */ + struct { + s64 count; + u64 size; + } out = { count, count * sizeof(fs::DirectoryEntry) }; + if (m_htcs_server.Send(socket, std::addressof(out), sizeof(out), 0) != sizeof(out)) { + return false; + } + + /* Loop sending entries. */ + s64 remaining = count; + do { + /* Determine how many entries we can read. */ + const s64 cur = std::min(remaining, static_cast(m_request_buffer_size / sizeof(fs::DirectoryEntry))); + + /* NOTE: Nintendo does not check the output of this call. */ + s64 read_count = 0; + fs::ReadDirectory(std::addressof(read_count), reinterpret_cast(body), handle, cur); + + /* Send the current entries. */ + const ssize_t cur_size = read_count * sizeof(fs::DirectoryEntry); + if (m_htcs_server.Send(socket, body, cur_size, 0) != cur_size) { + return false; + } + + /* Advance. */ + remaining -= read_count; + } while (remaining > 0); + + /* We've sent the entirety of our response, so early return. */ + return true; + } + } + break; + default: + /* Unsupported packet. */ + return false; + } + + /* Send the response. */ + return this->SendResponse(response_header, body, socket); + } else if (m_is_mounted) { + /* The SD card is mounted but not inserted, so we should unmount it. */ + this->Unmount(); + } + + /* We failed to process the request due to SD card not being inserted or mounted. */ + response_header.result = fs::ResultSdCardAccessFailed(); + + return this->SendResponse(response_header, body, socket); } bool FileServerProcessor::SendResponse(const FileServerResponseHeader &header, const void *body, int socket) { @@ -134,4 +732,4 @@ namespace ams::tio { return m_htcs_server.Send(socket, body, header.body_size, 0) == header.body_size; } -} \ No newline at end of file +} From 0da3b2b2732084fec8f14217e9e63f1c285e7f9f Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 24 Feb 2021 09:57:41 -0800 Subject: [PATCH 093/280] tio: implement SdCardObserver (finishes sysmodule) --- .../include/stratosphere/fs.hpp | 1 + .../stratosphere/fs/fs_i_event_notifier.hpp | 33 ++++++++++ .../include/stratosphere/fs/fs_sd_card.hpp | 6 ++ .../include/stratosphere/fssrv.hpp | 1 + .../fssrv/sf/fssrv_sf_i_event_notifier.hpp | 23 +++++++ .../libstratosphere/source/fs/fs_sd_card.cpp | 27 ++++++++ .../impl/fs_event_notifier_object_adapter.hpp | 59 +++++++++++++++++ .../TioServer/source/tio_file_server.cpp | 13 +++- .../TioServer/source/tio_sd_card_observer.cpp | 64 +++++++++++++++++++ .../TioServer/source/tio_sd_card_observer.hpp | 43 +++++++++++++ 10 files changed, 268 insertions(+), 2 deletions(-) create mode 100644 libraries/libstratosphere/include/stratosphere/fs/fs_i_event_notifier.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssrv/sf/fssrv_sf_i_event_notifier.hpp create mode 100644 libraries/libstratosphere/source/fs/impl/fs_event_notifier_object_adapter.hpp create mode 100644 stratosphere/TioServer/source/tio_sd_card_observer.cpp create mode 100644 stratosphere/TioServer/source/tio_sd_card_observer.hpp diff --git a/libraries/libstratosphere/include/stratosphere/fs.hpp b/libraries/libstratosphere/include/stratosphere/fs.hpp index 4428ac11c..3b81c8af3 100644 --- a/libraries/libstratosphere/include/stratosphere/fs.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs.hpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_i_event_notifier.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_i_event_notifier.hpp new file mode 100644 index 000000000..375ed1afd --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_i_event_notifier.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "fs_common.hpp" + +namespace ams::fs { + + class IEventNotifier { + public: + virtual ~IEventNotifier() { /* ... */ } + + Result BindEvent(os::SystemEventType *out, os::EventClearMode clear_mode) { + AMS_ASSERT(out != nullptr); + return this->DoBindEvent(out, clear_mode); + } + private: + virtual Result DoBindEvent(os::SystemEventType *out, os::EventClearMode clear_mode) = 0; + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_sd_card.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_sd_card.hpp index e27c12195..183f7bea5 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_sd_card.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_sd_card.hpp @@ -18,8 +18,14 @@ namespace ams::fs { + class IEventNotifier; + Result MountSdCard(const char *name); Result MountSdCardErrorReportDirectoryForAtmosphere(const char *name); + Result OpenSdCardDetectionEventNotifier(std::unique_ptr *out); + + bool IsSdCardInserted(); + } diff --git a/libraries/libstratosphere/include/stratosphere/fssrv.hpp b/libraries/libstratosphere/include/stratosphere/fssrv.hpp index a2fb95b3d..70fa77bba 100644 --- a/libraries/libstratosphere/include/stratosphere/fssrv.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssrv.hpp @@ -17,6 +17,7 @@ #pragma once #include #include +#include #include #include #include diff --git a/libraries/libstratosphere/include/stratosphere/fssrv/sf/fssrv_sf_i_event_notifier.hpp b/libraries/libstratosphere/include/stratosphere/fssrv/sf/fssrv_sf_i_event_notifier.hpp new file mode 100644 index 000000000..090369985 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssrv/sf/fssrv_sf_i_event_notifier.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +#define AMS_FSSRV_I_EVENT_NOTIFIER_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 0, Result, GetEventHandle, (ams::sf::OutCopyHandle out), (out)) + +AMS_SF_DEFINE_INTERFACE(ams::fssrv::sf, IEventNotifier, AMS_FSSRV_I_EVENT_NOTIFIER_INTERFACE_INFO) diff --git a/libraries/libstratosphere/source/fs/fs_sd_card.cpp b/libraries/libstratosphere/source/fs/fs_sd_card.cpp index d001cef8c..b11982b11 100644 --- a/libraries/libstratosphere/source/fs/fs_sd_card.cpp +++ b/libraries/libstratosphere/source/fs/fs_sd_card.cpp @@ -15,6 +15,7 @@ */ #include #include "fsa/fs_mount_utils.hpp" +#include "impl/fs_event_notifier_object_adapter.hpp" namespace ams::fs { @@ -87,4 +88,30 @@ namespace ams::fs { return fsa::Register(name, std::move(subdir_fs)); } + Result OpenSdCardDetectionEventNotifier(std::unique_ptr *out) { + /* Try to open an event notifier. */ + FsEventNotifier notifier; + AMS_FS_R_TRY(fsOpenSdCardDetectionEventNotifier(std::addressof(notifier))); + + /* Create an event notifier adapter. */ + auto adapter = std::make_unique(notifier); + R_UNLESS(adapter != nullptr, fs::ResultAllocationFailureInSdCardB()); + + *out = std::move(adapter); + return ResultSuccess(); + } + + bool IsSdCardInserted() { + /* Open device operator. */ + FsDeviceOperator device_operator; + AMS_FS_R_ABORT_UNLESS(::fsOpenDeviceOperator(std::addressof(device_operator))); + ON_SCOPE_EXIT { ::fsDeviceOperatorClose(std::addressof(device_operator)); }; + + /* Get SD card inserted. */ + bool inserted; + AMS_FS_R_ABORT_UNLESS(::fsDeviceOperatorIsSdCardInserted(std::addressof(device_operator), std::addressof(inserted))); + + return inserted; + } + } diff --git a/libraries/libstratosphere/source/fs/impl/fs_event_notifier_object_adapter.hpp b/libraries/libstratosphere/source/fs/impl/fs_event_notifier_object_adapter.hpp new file mode 100644 index 000000000..ea0cc64f0 --- /dev/null +++ b/libraries/libstratosphere/source/fs/impl/fs_event_notifier_object_adapter.hpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::fs::impl { + + class EventNotifierObjectAdapter final : public ::ams::fs::IEventNotifier, public ::ams::fs::impl::Newable { + private: + sf::SharedPointer m_object; + public: + EventNotifierObjectAdapter(sf::SharedPointer &&obj) : m_object(obj) { /* ... */ } + virtual ~EventNotifierObjectAdapter() { /* ... */ } + private: + virtual Result DoBindEvent(os::SystemEventType *out, os::EventClearMode clear_mode) override { + /* Get the handle. */ + sf::CopyHandle handle; + AMS_FS_R_TRY(m_object->GetEventHandle(std::addressof(handle))); + + /* Create the system event. */ + os::AttachReadableHandleToSystemEvent(out, handle, true, clear_mode); + + return ResultSuccess(); + } + }; + + class RemoteEventNotifierObjectAdapter final : public ::ams::fs::IEventNotifier, public ::ams::fs::impl::Newable { + private: + ::FsEventNotifier m_notifier; + public: + RemoteEventNotifierObjectAdapter(::FsEventNotifier &n) : m_notifier(n) { /* ... */ } + virtual ~RemoteEventNotifierObjectAdapter() { fsEventNotifierClose(std::addressof(m_notifier)); } + private: + virtual Result DoBindEvent(os::SystemEventType *out, os::EventClearMode clear_mode) override { + /* Get the handle. */ + ::Event e; + R_TRY(fsEventNotifierGetEventHandle(std::addressof(m_notifier), std::addressof(e), false)); + + /* Create the system event. */ + os::AttachReadableHandleToSystemEvent(out, e.revent, true, clear_mode); + + return ResultSuccess(); + } + }; + +} diff --git a/stratosphere/TioServer/source/tio_file_server.cpp b/stratosphere/TioServer/source/tio_file_server.cpp index 8c02dcfab..2b5309c8a 100644 --- a/stratosphere/TioServer/source/tio_file_server.cpp +++ b/stratosphere/TioServer/source/tio_file_server.cpp @@ -18,6 +18,7 @@ #include "tio_file_server_packet.hpp" #include "tio_file_server_htcs_server.hpp" #include "tio_file_server_processor.hpp" +#include "tio_sd_card_observer.hpp" namespace ams::tio { @@ -36,10 +37,12 @@ namespace ams::tio { constexpr const char HtcsPortName[] = "iywys@$TioServer_FileServer"; alignas(os::ThreadStackAlignment) u8 g_server_stack[os::MemoryPageSize]; + alignas(os::ThreadStackAlignment) u8 g_observer_stack[os::MemoryPageSize]; alignas(os::ThreadStackAlignment) u8 g_dispatch_stacks[NumDispatchThreads][os::MemoryPageSize]; constinit FileServerHtcsServer g_file_server_htcs_server; constinit FileServerProcessor g_file_server_processor(g_file_server_htcs_server); + constinit SdCardObserver g_sd_card_observer; constinit os::ThreadType g_file_server_dispatch_threads[NumDispatchThreads]; @@ -51,6 +54,10 @@ namespace ams::tio { constinit uintptr_t g_free_mq_storage[NumDispatchThreads]; constinit uintptr_t g_dispatch_mq_storage[NumDispatchThreads]; + void OnSdCardInsertionChanged(bool inserted) { + g_file_server_processor.SetInserted(inserted); + } + void OnFileServerHtcsSocketAccepted(int fd) { /* Service requests, while we can. */ while (true) { @@ -110,10 +117,12 @@ namespace ams::tio { /* Initialize the htcs server. */ g_file_server_htcs_server.Initialize(HtcsPortName, g_server_stack, sizeof(g_server_stack), OnFileServerHtcsSocketAccepted); - /* TODO: Initialize SD card observer. */ + /* Initialize SD card observer. */ + g_sd_card_observer.Initialize(g_observer_stack, sizeof(g_observer_stack)); + g_sd_card_observer.SetCallback(OnSdCardInsertionChanged); /* Initialize the command processor. */ - g_file_server_processor.SetInserted(false); + g_file_server_processor.SetInserted(g_sd_card_observer.IsSdCardInserted()); g_file_server_processor.SetRequestBufferSize(RequestBufferSize); /* Initialize the dispatch message queues. */ diff --git a/stratosphere/TioServer/source/tio_sd_card_observer.cpp b/stratosphere/TioServer/source/tio_sd_card_observer.cpp new file mode 100644 index 000000000..981b7aecf --- /dev/null +++ b/stratosphere/TioServer/source/tio_sd_card_observer.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "tio_sd_card_observer.hpp" + +namespace ams::tio { + + void SdCardObserver::Initialize(void *thread_stack, size_t thread_stack_size) { + /* Setup our thread. */ + R_ABORT_UNLESS(os::CreateThread(std::addressof(m_thread), ThreadEntry, this, thread_stack, thread_stack_size, AMS_GET_SYSTEM_THREAD_PRIORITY(TioServer, SdCardObserver))); + + /* Set our thread name pointer. */ + os::SetThreadNamePointer(std::addressof(m_thread), AMS_GET_SYSTEM_THREAD_NAME(TioServer, SdCardObserver)); + + /* Set our initial insertion state. */ + m_inserted = fs::IsSdCardInserted(); + } + + void SdCardObserver::SetCallback(SdCardInsertionCallback callback) { + /* Check that we don't already have a callback. */ + AMS_ABORT_UNLESS(m_callback == nullptr); + + /* Set our callback. */ + m_callback = callback; + } + + void SdCardObserver::ThreadFunc() { + /* Open detection event notifier. */ + std::unique_ptr notifier; + R_ABORT_UNLESS(fs::OpenSdCardDetectionEventNotifier(std::addressof(notifier))); + + /* Bind the detection event. */ + os::SystemEventType event; + R_ABORT_UNLESS(notifier->BindEvent(std::addressof(event), os::EventClearMode_AutoClear)); + + /* Loop, waiting for insertion events. */ + while (true) { + /* Wait for an event. */ + os::WaitSystemEvent(std::addressof(event)); + + /* Update our insertion state. */ + m_inserted = fs::IsSdCardInserted(); + + /* Invoke our callback. */ + if (m_callback) { + m_callback(m_inserted); + } + } + } + +} diff --git a/stratosphere/TioServer/source/tio_sd_card_observer.hpp b/stratosphere/TioServer/source/tio_sd_card_observer.hpp new file mode 100644 index 000000000..4347d9846 --- /dev/null +++ b/stratosphere/TioServer/source/tio_sd_card_observer.hpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::tio { + + using SdCardInsertionCallback = void(*)(bool inserted); + + class SdCardObserver { + private: + bool m_inserted; + SdCardInsertionCallback m_callback; + os::ThreadType m_thread; + public: + constexpr SdCardObserver() : m_inserted(false), m_callback(nullptr), m_thread{} { /* ... */ } + + bool IsSdCardInserted() const { return m_inserted; } + private: + static void ThreadEntry(void *arg) { + static_cast(arg)->ThreadFunc(); + } + + void ThreadFunc(); + public: + void Initialize(void *thread_stack, size_t thread_stack_size); + void SetCallback(SdCardInsertionCallback callback); + }; + +} \ No newline at end of file From 97875c7d2f03ae10535502c2c591a38c0b427cab Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 24 Feb 2021 10:12:25 -0800 Subject: [PATCH 094/280] tio: fix bug in body receive --- stratosphere/TioServer/source/tio_file_server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stratosphere/TioServer/source/tio_file_server.cpp b/stratosphere/TioServer/source/tio_file_server.cpp index 2b5309c8a..8dee7ddb6 100644 --- a/stratosphere/TioServer/source/tio_file_server.cpp +++ b/stratosphere/TioServer/source/tio_file_server.cpp @@ -76,7 +76,7 @@ namespace ams::tio { /* Receive the request body, if necessary. */ if (request->header.body_size > 0) { - if (htcs::Recv(fd, request->body, request->header.body_size, htcs::HTCS_MSG_WAITALL) != sizeof(request->header.body_size)) { + if (htcs::Recv(fd, request->body, request->header.body_size, htcs::HTCS_MSG_WAITALL) != request->header.body_size) { break; } } From c7e4f963e8ec9fa8fbd894b67111ee9d735f3e42 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 24 Feb 2021 10:16:44 -0800 Subject: [PATCH 095/280] fs: fix GetFileTimeStampRawForDebug --- .../source/fs/fsa/fs_user_filesystem_for_debug.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/libstratosphere/source/fs/fsa/fs_user_filesystem_for_debug.cpp b/libraries/libstratosphere/source/fs/fsa/fs_user_filesystem_for_debug.cpp index d10e5be6b..af95ddcc4 100644 --- a/libraries/libstratosphere/source/fs/fsa/fs_user_filesystem_for_debug.cpp +++ b/libraries/libstratosphere/source/fs/fsa/fs_user_filesystem_for_debug.cpp @@ -37,7 +37,7 @@ namespace ams::fs { } Result GetFileTimeStampRawForDebug(FileTimeStampRaw *out, const char *path) { - AMS_FS_R_TRY(GetFileTimeStampRawForDebug(out, path)); + AMS_FS_R_TRY(impl::GetFileTimeStampRawForDebug(out, path)); return ResultSuccess(); } From 18031ae1073580494e3ad253a866987194a208e7 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 24 Feb 2021 21:34:00 -0800 Subject: [PATCH 096/280] tio: fix wrong body size on optimized ListDirectory --- stratosphere/TioServer/source/tio_file_server_processor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stratosphere/TioServer/source/tio_file_server_processor.cpp b/stratosphere/TioServer/source/tio_file_server_processor.cpp index 05deb6c25..939f59e48 100644 --- a/stratosphere/TioServer/source/tio_file_server_processor.cpp +++ b/stratosphere/TioServer/source/tio_file_server_processor.cpp @@ -650,7 +650,7 @@ namespace ams::tio { out->size = read_count * sizeof(fs::DirectoryEntry); /* Set the response body size. */ - response_header.body_size = sizeof(out) + out->size; + response_header.body_size = sizeof(*out) + out->size; } else { /* We have to use multiple sends. */ /* Lock our server. */ From 35c816d62f38ad0cded0fcec929ae52777cc92f3 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 25 Feb 2021 11:21:34 -0800 Subject: [PATCH 097/280] htclow: fix ordering of channels, uninitialized bug in service json parse --- .../include/stratosphere/htclow/impl/htclow_internal_types.hpp | 2 +- .../source/htclow/ctrl/htclow_service_channel_parser.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/libstratosphere/include/stratosphere/htclow/impl/htclow_internal_types.hpp b/libraries/libstratosphere/include/stratosphere/htclow/impl/htclow_internal_types.hpp index bee584a85..7657e31a4 100644 --- a/libraries/libstratosphere/include/stratosphere/htclow/impl/htclow_internal_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/htclow/impl/htclow_internal_types.hpp @@ -43,7 +43,7 @@ namespace ams::htclow::impl { } constexpr ALWAYS_INLINE bool operator<(const ChannelInternalType &lhs, const ChannelInternalType &rhs) { - return lhs.module_id < rhs.module_id || lhs.reserved < rhs.reserved || lhs.channel_id < rhs.channel_id; + return std::tie(lhs.module_id, lhs.reserved, lhs.channel_id) < std::tie(rhs.module_id, rhs.reserved, rhs.channel_id); } constexpr ALWAYS_INLINE bool operator>(const ChannelInternalType &lhs, const ChannelInternalType &rhs) { diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_service_channel_parser.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_service_channel_parser.cpp index 48d27b132..8a6ab286e 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_service_channel_parser.cpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_service_channel_parser.cpp @@ -139,7 +139,7 @@ namespace ams::htclow::ctrl { void ParseServiceChannel(s16 *out_version, impl::ChannelInternalType *out_channels, int *out_num_channels, int max_channels, void *str, size_t str_size) { /* Parse the JSON. */ const char *channel_strs[0x20]; - int num_channels; + int num_channels = 0; ParseBody(out_version, channel_strs, std::addressof(num_channels), util::size(channel_strs), str, str_size); /* Parse the channel strings. */ From c9015581caa4bd2cd2176992c3c939791630c83e Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 26 Feb 2021 08:08:05 -0800 Subject: [PATCH 098/280] boot2: fix tma launch when htc is disabled --- libraries/libstratosphere/source/boot2/boot2_api.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/libstratosphere/source/boot2/boot2_api.cpp b/libraries/libstratosphere/source/boot2/boot2_api.cpp index 671ad4996..a9bddc42e 100644 --- a/libraries/libstratosphere/source/boot2/boot2_api.cpp +++ b/libraries/libstratosphere/source/boot2/boot2_api.cpp @@ -387,7 +387,7 @@ namespace ams::boot2 { if (svc::IsKernelMesosphere() && IsHtcEnabled()) { LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Htc, ncm::StorageId::None), 0); } else { - LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Tma, ncm::StorageId::None), 0); + LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Tma, ncm::StorageId::BuiltInSystem), 0); } /* Launch additional programs. */ From a6729171d36f3d76745b3cda1924f68578b9cc0d Mon Sep 17 00:00:00 2001 From: SciresM Date: Mon, 1 Mar 2021 14:18:27 -0800 Subject: [PATCH 099/280] set.mitm: fake compatibility for usb!usb30_force_enabled on 9.0.0+ (#1391) * set.mitm: fake compatibility for usb!usb30_force_enabled on 9.0.0+ * set.mitm: add value meaning comment for usb!usb30_force_enabled * loader: pretend to be polite about patch ordering --- config_templates/system_settings.ini | 8 +- .../program/source/smc/secmon_smc_info.cpp | 4 + .../program/source/smc/secmon_smc_info.hpp | 1 + fusee/fusee-secondary/src/exocfg.h | 1 + fusee/fusee-secondary/src/nxboot.c | 27 +++++++ .../secmon/secmon_monitor_context.hpp | 2 + .../include/stratosphere/spl/spl_api.hpp | 4 + .../include/stratosphere/spl/spl_types.hpp | 40 ++++----- .../source/boot2/boot2_api.cpp | 15 +++- .../source/set_mitm/settings_sd_kvs.cpp | 3 + .../source/ldr_embedded_usb_patches.inc | 37 +++++++++ stratosphere/loader/source/ldr_patcher.cpp | 81 ++++++++++++++++++- stratosphere/loader/source/ldr_patcher.hpp | 3 + .../loader/source/ldr_process_creation.cpp | 3 + 14 files changed, 205 insertions(+), 24 deletions(-) create mode 100644 stratosphere/loader/source/ldr_embedded_usb_patches.inc diff --git a/config_templates/system_settings.ini b/config_templates/system_settings.ini index abdc73c5e..f02491abb 100644 --- a/config_templates/system_settings.ini +++ b/config_templates/system_settings.ini @@ -1,9 +1,13 @@ -; Disable uploading error reports to Nintendo [eupld] +; Disable uploading error reports to Nintendo ; upload_enabled = u8!0x0 +[usb] +; Enable USB 3.0 superspeed for homebrew +; 0 = USB 3.0 support is system default (usually disabled), 1 = USB 3.0 support is enabled. +; usb30_force_enabled = u8!0x0 +[ro] ; Control whether RO should ease its validation of NROs. ; (note: this is normally not necessary, and ips patches can be used.) -[ro] ; ease_nro_restriction = u8!0x1 ; Atmosphere custom settings [atmosphere] diff --git a/exosphere/program/source/smc/secmon_smc_info.cpp b/exosphere/program/source/smc/secmon_smc_info.cpp index 468b134fb..de7fbeb72 100644 --- a/exosphere/program/source/smc/secmon_smc_info.cpp +++ b/exosphere/program/source/smc/secmon_smc_info.cpp @@ -286,6 +286,10 @@ namespace ams::secmon::smc { /* Get the log configuration. */ args.r[1] = (static_cast(static_cast(secmon::GetLogPort())) << 32) | static_cast(secmon::GetLogBaudRate()); break; + case ConfigItem::ExosphereForceEnableUsb30: + /* Get whether usb 3.0 should be force-enabled. */ + args.r[1] = GetSecmonConfiguration().IsUsb30ForceEnabled(); + break; default: return SmcResult::InvalidArgument; } diff --git a/exosphere/program/source/smc/secmon_smc_info.hpp b/exosphere/program/source/smc/secmon_smc_info.hpp index 12870324e..c7481775f 100644 --- a/exosphere/program/source/smc/secmon_smc_info.hpp +++ b/exosphere/program/source/smc/secmon_smc_info.hpp @@ -50,6 +50,7 @@ namespace ams::secmon::smc { ExosphereEmummcType = 65007, ExospherePayloadAddress = 65008, ExosphereLogConfiguration = 65009, + ExosphereForceEnableUsb30 = 65010, }; SmcResult SmcGetConfigUser(SmcArguments &args); diff --git a/fusee/fusee-secondary/src/exocfg.h b/fusee/fusee-secondary/src/exocfg.h index 36cca04dc..42885f8ec 100644 --- a/fusee/fusee-secondary/src/exocfg.h +++ b/fusee/fusee-secondary/src/exocfg.h @@ -33,6 +33,7 @@ #define EXOSPHERE_FLAG_ENABLE_USERMODE_PMU_ACCESS (1 << 4u) #define EXOSPHERE_FLAG_BLANK_PRODINFO (1 << 5u) #define EXOSPHERE_FLAG_ALLOW_WRITING_TO_CAL_SYSMMC (1 << 6u) +#define EXOSPHERE_FLAG_FORCE_ENABLE_USB_30 (1 << 7u) #define EXOSPHERE_LOG_FLAG_INVERTED (1 << 0u) diff --git a/fusee/fusee-secondary/src/nxboot.c b/fusee/fusee-secondary/src/nxboot.c index e63fa0950..ee81427e1 100644 --- a/fusee/fusee-secondary/src/nxboot.c +++ b/fusee/fusee-secondary/src/nxboot.c @@ -256,6 +256,24 @@ static int stratosphere_ini_handler(void *user, const char *section, const char return 1; } +static int system_settings_ini_handler(void *user, const char *section, const char *name, const char *value) { + uint32_t *flags = (uint32_t *)user; + if (strcmp(section, "usb") == 0) { + if (strcmp(name, "usb30_force_enabled") == 0) { + if (strcmp(value, "u8!0x1") == 0) { + *flags |= EXOSPHERE_FLAG_FORCE_ENABLE_USB_30; + } else if (strcmp(value, "u8!0x0") == 0) { + *flags &= ~(EXOSPHERE_FLAG_FORCE_ENABLE_USB_30); + } + } else { + return 0; + } + } else { + return 0; + } + return 1; +} + static bool is_nca_present(const char *nca_name) { char path[0x100]; snprintf(path, sizeof(path), "system:/contents/registered/%s.nca", nca_name); @@ -537,6 +555,15 @@ static void nxboot_configure_exosphere(uint32_t target_firmware, unsigned int ke /* Apply lcd vendor. */ exo_cfg.lcd_vendor = display_get_lcd_vendor(); + /* Read and parse system settings.ini to determine usb 3.0 enable. */ + char *settings_ini = calloc(1, 0x20000); + if (read_from_file(settings_ini, 0x1FFFF, "atmosphere/config/system_settings.ini")) { + if (ini_parse_string(settings_ini, system_settings_ini_handler, &exo_cfg.flags[0]) < 0) { + fatal_error("[NXBOOT] Failed to parse system_settings.ini!\n"); + } + } + free(settings_ini); + if ((exo_cfg.target_firmware < ATMOSPHERE_TARGET_FIRMWARE_MIN) || (exo_cfg.target_firmware > ATMOSPHERE_TARGET_FIRMWARE_MAX)) { fatal_error("[NXBOOT] Invalid Exosphere target firmware!\n"); } diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_monitor_context.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_monitor_context.hpp index 6c974b1c7..1bfcfb955 100644 --- a/libraries/libexosphere/include/exosphere/secmon/secmon_monitor_context.hpp +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_monitor_context.hpp @@ -29,6 +29,7 @@ namespace ams::secmon { SecureMonitorConfigurationFlag_EnableUserModePerformanceCounterAccess = (1u << 4), SecureMonitorConfigurationFlag_ShouldUseBlankCalibrationBinary = (1u << 5), SecureMonitorConfigurationFlag_AllowWritingToCalibrationBinarySysmmc = (1u << 6), + SecureMonitorConfigurationFlag_ForceEnableUsb30 = (1u << 7), SecureMonitorConfigurationFlag_Default = SecureMonitorConfigurationFlag_IsDevelopmentFunctionEnabledForKernel, }; @@ -101,6 +102,7 @@ namespace ams::secmon { constexpr bool EnableUserModePerformanceCounterAccess() const { return (this->flags[0] & SecureMonitorConfigurationFlag_EnableUserModePerformanceCounterAccess) != 0; } constexpr bool ShouldUseBlankCalibrationBinary() const { return (this->flags[0] & SecureMonitorConfigurationFlag_ShouldUseBlankCalibrationBinary) != 0; } constexpr bool AllowWritingToCalibrationBinarySysmmc() const { return (this->flags[0] & SecureMonitorConfigurationFlag_AllowWritingToCalibrationBinarySysmmc) != 0; } + constexpr bool IsUsb30ForceEnabled() const { return (this->flags[0] & SecureMonitorConfigurationFlag_ForceEnableUsb30) != 0; } constexpr bool IsDevelopmentFunctionEnabled(bool for_kern) const { return for_kern ? this->IsDevelopmentFunctionEnabledForKernel() : this->IsDevelopmentFunctionEnabledForUser(); } }; diff --git a/libraries/libstratosphere/include/stratosphere/spl/spl_api.hpp b/libraries/libstratosphere/include/stratosphere/spl/spl_api.hpp index 506290b39..f38a0d1ec 100644 --- a/libraries/libstratosphere/include/stratosphere/spl/spl_api.hpp +++ b/libraries/libstratosphere/include/stratosphere/spl/spl_api.hpp @@ -78,6 +78,10 @@ namespace ams::spl { return ::ams::spl::GetConfigBool(::ams::spl::ConfigItem::DisableProgramVerification); } + inline bool IsUsb30ForceEnabled() { + return ::ams::spl::GetConfigBool(::ams::spl::ConfigItem::ExosphereForceEnableUsb30); + } + Result SetBootReason(BootReasonValue boot_reason); Result GetBootReason(BootReasonValue *out); diff --git a/libraries/libstratosphere/include/stratosphere/spl/spl_types.hpp b/libraries/libstratosphere/include/stratosphere/spl/spl_types.hpp index 3e1f2d74e..fa510ab53 100644 --- a/libraries/libstratosphere/include/stratosphere/spl/spl_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/spl/spl_types.hpp @@ -223,26 +223,30 @@ namespace ams::spl { Package2Hash = 17, /* Extension config items for exosphere. */ - ExosphereApiVersion = 65000, - ExosphereNeedsReboot = 65001, - ExosphereNeedsShutdown = 65002, - ExosphereGitCommitHash = 65003, - ExosphereHasRcmBugPatch = 65004, - ExosphereBlankProdInfo = 65005, - ExosphereAllowCalWrites = 65006, - ExosphereEmummcType = 65007, - ExospherePayloadAddress = 65008, + ExosphereApiVersion = 65000, + ExosphereNeedsReboot = 65001, + ExosphereNeedsShutdown = 65002, + ExosphereGitCommitHash = 65003, + ExosphereHasRcmBugPatch = 65004, + ExosphereBlankProdInfo = 65005, + ExosphereAllowCalWrites = 65006, + ExosphereEmummcType = 65007, + ExospherePayloadAddress = 65008, + ExosphereLogConfiguration = 65009, + ExosphereForceEnableUsb30 = 65010, }; } /* Extensions to libnx spl config item enum. */ -constexpr inline SplConfigItem SplConfigItem_ExosphereApiVersion = static_cast(65000); -constexpr inline SplConfigItem SplConfigItem_ExosphereNeedsReboot = static_cast(65001); -constexpr inline SplConfigItem SplConfigItem_ExosphereNeedsShutdown = static_cast(65002); -constexpr inline SplConfigItem SplConfigItem_ExosphereGitCommitHash = static_cast(65003); -constexpr inline SplConfigItem SplConfigItem_ExosphereHasRcmBugPatch = static_cast(65004); -constexpr inline SplConfigItem SplConfigItem_ExosphereBlankProdInfo = static_cast(65005); -constexpr inline SplConfigItem SplConfigItem_ExosphereAllowCalWrites = static_cast(65006); -constexpr inline SplConfigItem SplConfigItem_ExosphereEmummcType = static_cast(65007); -constexpr inline SplConfigItem SplConfigItem_ExospherePayloadAddress = static_cast(65008); +constexpr inline SplConfigItem SplConfigItem_ExosphereApiVersion = static_cast(65000); +constexpr inline SplConfigItem SplConfigItem_ExosphereNeedsReboot = static_cast(65001); +constexpr inline SplConfigItem SplConfigItem_ExosphereNeedsShutdown = static_cast(65002); +constexpr inline SplConfigItem SplConfigItem_ExosphereGitCommitHash = static_cast(65003); +constexpr inline SplConfigItem SplConfigItem_ExosphereHasRcmBugPatch = static_cast(65004); +constexpr inline SplConfigItem SplConfigItem_ExosphereBlankProdInfo = static_cast(65005); +constexpr inline SplConfigItem SplConfigItem_ExosphereAllowCalWrites = static_cast(65006); +constexpr inline SplConfigItem SplConfigItem_ExosphereEmummcType = static_cast(65007); +constexpr inline SplConfigItem SplConfigItem_ExospherePayloadAddress = static_cast(65008); +constexpr inline SplConfigItem SplConfigItem_ExosphereLogConfiguration = static_cast(65009); +constexpr inline SplConfigItem SplConfigItem_ExosphereForceEnableUsb30 = static_cast(65010); diff --git a/libraries/libstratosphere/source/boot2/boot2_api.cpp b/libraries/libstratosphere/source/boot2/boot2_api.cpp index a9bddc42e..6e2879b86 100644 --- a/libraries/libstratosphere/source/boot2/boot2_api.cpp +++ b/libraries/libstratosphere/source/boot2/boot2_api.cpp @@ -307,6 +307,10 @@ namespace ams::boot2 { }); } + bool IsUsbRequiredToMountSdCard() { + return hos::GetVersion() >= hos::Version_9_0_0; + } + } /* Boot2 API. */ @@ -347,8 +351,10 @@ namespace ams::boot2 { /* Launch pcv. */ LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Pcv, ncm::StorageId::BuiltInSystem), 0); - /* Launch usb. */ - LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Usb, ncm::StorageId::BuiltInSystem), 0); + /* On 9.0.0+, FS depends on the USB sysmodule having been launched in order to mount the SD card. */ + if (IsUsbRequiredToMountSdCard()) { + LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Usb, ncm::StorageId::BuiltInSystem), 0); + } } /* Wait for the SD card required services to be ready. */ @@ -371,6 +377,11 @@ namespace ams::boot2 { void LaunchPostSdCardBootPrograms() { /* This code is normally run by boot2. */ + /* Launch the usb system module, if we haven't already. */ + if (!IsUsbRequiredToMountSdCard()) { + LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Usb, ncm::StorageId::BuiltInSystem), 0); + } + /* Find out whether we are maintenance mode. */ const bool maintenance = IsMaintenanceMode(); if (maintenance) { diff --git a/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp b/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp index 7a294145e..8e15ae3f9 100644 --- a/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp +++ b/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp @@ -310,6 +310,9 @@ namespace ams::settings::fwdbg { /* Disable uploading error reports to Nintendo. */ R_ABORT_UNLESS(ParseSettingsItemValue("eupld", "upload_enabled", "u8!0x0")); + /* Enable USB 3.0 superspeed for homebrew */ + R_ABORT_UNLESS(ParseSettingsItemValue("usb", "usb30_force_enabled", spl::IsUsb30ForceEnabled() ? "u8!0x1" : "u8!0x0")); + /* Control whether RO should ease its validation of NROs. */ /* (note: this is normally not necessary, and ips patches can be used.) */ R_ABORT_UNLESS(ParseSettingsItemValue("ro", "ease_nro_restriction", "u8!0x1")); diff --git a/stratosphere/loader/source/ldr_embedded_usb_patches.inc b/stratosphere/loader/source/ldr_embedded_usb_patches.inc new file mode 100644 index 000000000..946d65990 --- /dev/null +++ b/stratosphere/loader/source/ldr_embedded_usb_patches.inc @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018-2021 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* Patch fallback case to mov w0, #1 rather than retrieving settings flag. */ +constexpr inline const EmbeddedPatchEntry Usb30ForceEnablePatches_9_0_0[] = { + { 0x521C, "\x20\x00\x80\x52", 4 }, +}; + +/* Patch fallback case to mov w0, #1 rather than retrieving settings flag. */ +constexpr inline const EmbeddedPatchEntry Usb30ForceEnablePatches_10_0_0[] = { + { 0x5494, "\x20\x00\x80\x52", 4 }, +}; + +/* Patch getter functions to return 1. */ +constexpr inline const EmbeddedPatchEntry Usb30ForceEnablePatches_11_0_0[] = { + { 0x85DC, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 }, + { 0x866C, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 }, +}; + +constexpr inline const EmbeddedPatch Usb30ForceEnablePatches[] = { + { ParseModuleId("C0D3F4E87E8B0FE9BBE9F1968A20767F3DC08E03"), util::size(Usb30ForceEnablePatches_9_0_0), Usb30ForceEnablePatches_9_0_0 }, + { ParseModuleId("B9C700CA8335F8BAA0D2041D8D09F772890BA988"), util::size(Usb30ForceEnablePatches_10_0_0), Usb30ForceEnablePatches_10_0_0 }, + { ParseModuleId("95BAF06A69650C215A5DD50CF8BD2A603E7AD3C2"), util::size(Usb30ForceEnablePatches_11_0_0), Usb30ForceEnablePatches_11_0_0 }, +}; \ No newline at end of file diff --git a/stratosphere/loader/source/ldr_patcher.cpp b/stratosphere/loader/source/ldr_patcher.cpp index a9b14da9d..99385fc51 100644 --- a/stratosphere/loader/source/ldr_patcher.cpp +++ b/stratosphere/loader/source/ldr_patcher.cpp @@ -30,8 +30,12 @@ namespace ams::ldr { constexpr const char * const LoaderSdMountName = "#amsldr-sdpatch"; static_assert(sizeof(LoaderSdMountName) <= fs::MountNameLengthMax); - os::Mutex g_ldr_sd_lock(false); - bool g_mounted_sd; + constinit os::SdkMutex g_ldr_sd_lock; + constinit bool g_mounted_sd; + + constinit os::SdkMutex g_embedded_patch_lock; + constinit bool g_got_embedded_patch_settings; + constinit bool g_force_enable_usb30; bool EnsureSdCardMounted() { std::scoped_lock lk(g_ldr_sd_lock); @@ -51,6 +55,59 @@ namespace ams::ldr { return (g_mounted_sd = true); } + bool IsUsb30ForceEnabled() { + std::scoped_lock lk(g_embedded_patch_lock); + + if (!g_got_embedded_patch_settings) { + g_force_enable_usb30 = spl::IsUsb30ForceEnabled(); + g_got_embedded_patch_settings = true; + } + + return g_force_enable_usb30; + } + + consteval u8 ParseNybble(char c) { + AMS_ASSUME(('0' <= c && c <= '9') || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f')); + if ('0' <= c && c <= '9') { + return c - '0' + 0x0; + } else if ('A' <= c && c <= 'F') { + return c - 'A' + 0xA; + } else /* if ('a' <= c && c <= 'f') */ { + return c - 'a' + 0xa; + } + } + + consteval ro::ModuleId ParseModuleId(const char *str) { + /* Parse a static module id. */ + ro::ModuleId module_id = {}; + + size_t ofs = 0; + while (str[0] != 0) { + AMS_ASSUME(ofs < sizeof(module_id)); + AMS_ASSUME(str[1] != 0); + + module_id.build_id[ofs] = (ParseNybble(str[0]) << 4) | (ParseNybble(str[1]) << 0); + + str += 2; + ofs++; + } + + return module_id; + } + + struct EmbeddedPatchEntry { + uintptr_t offset; + const void * const data; + size_t size; + }; + + struct EmbeddedPatch { + ro::ModuleId module_id; + size_t num_entries; + const EmbeddedPatchEntry *entries; + }; + + #include "ldr_embedded_usb_patches.inc" } @@ -65,4 +122,24 @@ namespace ams::ldr { ams::patcher::LocateAndApplyIpsPatchesToModule(LoaderSdMountName, NsoPatchesDirectory, NsoPatchesProtectedSize, NsoPatchesProtectedOffset, &module_id, reinterpret_cast(mapped_nso), mapped_size); } + /* Apply embedded patches. */ + void ApplyEmbeddedPatchesToModule(const u8 *build_id, uintptr_t mapped_nso, size_t mapped_size) { + /* Make module id. */ + ro::ModuleId module_id; + std::memcpy(&module_id.build_id, build_id, sizeof(module_id.build_id)); + + if (IsUsb30ForceEnabled()) { + for (const auto &patch : Usb30ForceEnablePatches) { + if (std::memcmp(std::addressof(patch.module_id), std::addressof(module_id), sizeof(module_id)) == 0) { + for (size_t i = 0; i < patch.num_entries; ++i) { + const auto &entry = patch.entries[i]; + if (entry.offset + entry.size <= mapped_size) { + std::memcpy(reinterpret_cast(mapped_nso + entry.offset), entry.data, entry.size); + } + } + } + } + } + } + } \ No newline at end of file diff --git a/stratosphere/loader/source/ldr_patcher.hpp b/stratosphere/loader/source/ldr_patcher.hpp index 54d7d7142..1b73a1eea 100644 --- a/stratosphere/loader/source/ldr_patcher.hpp +++ b/stratosphere/loader/source/ldr_patcher.hpp @@ -21,4 +21,7 @@ namespace ams::ldr { /* Apply IPS patches. */ void LocateAndApplyIpsPatchesToModule(const u8 *build_id, uintptr_t mapped_nso, size_t mapped_size); + /* Apply embedded patches. */ + void ApplyEmbeddedPatchesToModule(const u8 *build_id, uintptr_t mapped_nso, size_t mapped_size); + } \ No newline at end of file diff --git a/stratosphere/loader/source/ldr_process_creation.cpp b/stratosphere/loader/source/ldr_process_creation.cpp index a6a7e2b05..821c5390e 100644 --- a/stratosphere/loader/source/ldr_process_creation.cpp +++ b/stratosphere/loader/source/ldr_process_creation.cpp @@ -534,6 +534,9 @@ namespace ams::ldr { std::memset(reinterpret_cast(map_address + ro_end), 0, nso_header->rw_dst_offset - ro_end); std::memset(reinterpret_cast(map_address + rw_end), 0, nso_header->bss_size); + /* Apply embedded patches. */ + ApplyEmbeddedPatchesToModule(nso_header->build_id, map_address, nso_size); + /* Apply IPS patches. */ LocateAndApplyIpsPatchesToModule(nso_header->build_id, map_address, nso_size); } From deb4aece9a400c9559f8e3de297c6192e94425d3 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 11 Mar 2021 12:53:17 -0800 Subject: [PATCH 100/280] kern: fix inverted conditional in KDebugBase::SetThreadContext --- libraries/libmesosphere/source/kern_k_debug_base.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/libraries/libmesosphere/source/kern_k_debug_base.cpp b/libraries/libmesosphere/source/kern_k_debug_base.cpp index c41c87f13..7e9d61557 100644 --- a/libraries/libmesosphere/source/kern_k_debug_base.cpp +++ b/libraries/libmesosphere/source/kern_k_debug_base.cpp @@ -557,8 +557,12 @@ namespace ams::kern { /* Verify that the thread's svc state is valid. */ if (thread->IsCallingSvc()) { - R_UNLESS(thread->GetSvcId() != svc::SvcId_Break, svc::ResultInvalidState()); - R_UNLESS(thread->GetSvcId() != svc::SvcId_ReturnFromException, svc::ResultInvalidState()); + const u8 svc_id = thread->GetSvcId(); + + const bool is_valid_svc = svc_id == svc::SvcId_Break || + svc_id == svc::SvcId_ReturnFromException; + + R_UNLESS(is_valid_svc, svc::ResultInvalidState()); } /* Set the thread context. */ From 021d4c88fabdce852750971bedb0adc74531b08f Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 13 Mar 2021 15:08:38 -0800 Subject: [PATCH 101/280] kern: use fix usage of incorrect page table for UserBuffer ipc --- libraries/libmesosphere/source/kern_k_server_session.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/libmesosphere/source/kern_k_server_session.cpp b/libraries/libmesosphere/source/kern_k_server_session.cpp index d2d67cbab..e49f78ebb 100644 --- a/libraries/libmesosphere/source/kern_k_server_session.cpp +++ b/libraries/libmesosphere/source/kern_k_server_session.cpp @@ -771,6 +771,9 @@ namespace ams::kern { /* NOTE: Session is used only for debugging, and so may go unused. */ MESOSPHERE_UNUSED(session); + /* NOTE: Source page table is not used, and so may go unused. */ + MESOSPHERE_UNUSED(src_page_table); + /* Determine the message buffers. */ u32 *dst_msg_ptr, *src_msg_ptr; bool dst_user, src_user; @@ -907,7 +910,7 @@ namespace ams::kern { /* If the fast part of the copy didn't get everything, perform the slow part of the copy. */ if (fast_size < raw_size) { - R_TRY(src_page_table.CopyMemoryFromHeapToHeap(dst_page_table, dst_message_buffer + max_fast_size, raw_size - fast_size, + R_TRY(dst_page_table.CopyMemoryFromHeapToHeap(dst_page_table, dst_message_buffer + max_fast_size, raw_size - fast_size, KMemoryState_FlagReferenceCounted, KMemoryState_FlagReferenceCounted, dst_perm, KMemoryAttribute_Uncached, KMemoryAttribute_None, @@ -921,7 +924,7 @@ namespace ams::kern { constexpr KMemoryPermission DestinationPermission = static_cast(KMemoryPermission_NotMapped | KMemoryPermission_KernelReadWrite); /* Copy the memory. */ - R_TRY(src_page_table.CopyMemoryFromUserToLinear(dst_message_buffer + offset_words, raw_size, + R_TRY(dst_page_table.CopyMemoryFromUserToLinear(dst_message_buffer + offset_words, raw_size, KMemoryState_FlagReferenceCounted, KMemoryState_FlagReferenceCounted, DestinationPermission, KMemoryAttribute_Uncached, KMemoryAttribute_None, From 5362ee945027bd0d839c5aecd5564b62294de922 Mon Sep 17 00:00:00 2001 From: SciresM Date: Tue, 16 Mar 2021 17:13:30 -0700 Subject: [PATCH 102/280] [tma2] [Ongoing] Continue implementing modules for tma2. (#1388) * cs: add stub sysmodule to host command shell server * cs: implement logic for main (linker error paradise, for now) * cs: implement more of the system module's skeleton * htcs: update client type names for libnx pr merge --- .../libstratosphere/include/stratosphere.hpp | 2 + .../impl/ams_system_thread_definitions.hpp | 13 + .../include/stratosphere/cs.hpp | 23 ++ .../stratosphere/cs/cs_audio_server.hpp | 23 ++ .../stratosphere/cs/cs_command_processor.hpp | 30 +++ .../include/stratosphere/cs/cs_hid_server.hpp | 23 ++ .../cs/cs_remote_video_server.hpp | 23 ++ .../stratosphere/cs/cs_target_io_server.hpp | 23 ++ .../include/stratosphere/htc.hpp | 4 + .../stratosphere/htc/tenv/htc_tenv.hpp | 25 ++ .../htc/tenv/htc_tenv_i_service.hpp | 26 ++ .../htc/tenv/htc_tenv_i_service_manager.hpp | 25 ++ .../htc/tenv/htc_tenv_service_manager.hpp | 31 +++ .../stratosphere/htc/tenv/htc_tenv_types.hpp | 25 ++ .../include/stratosphere/scs.hpp | 24 ++ .../scs/scs_command_processor.hpp | 83 +++++++ .../stratosphere/scs/scs_server_manager.hpp | 52 ++++ .../include/stratosphere/scs/scs_shell.hpp | 36 +++ .../stratosphere/scs/scs_shell_server.hpp | 41 ++++ .../include/stratosphere/scs/scs_tenv.hpp | 23 ++ .../source/boot2/boot2_api.cpp | 1 + .../source/cs/cs_audio_server.cpp | 24 ++ .../source/cs/cs_command_processor.cpp | 31 +++ .../source/cs/cs_hid_server.cpp | 24 ++ .../source/cs/cs_remote_video_server.cpp | 24 ++ .../source/cs/cs_target_io_server.cpp | 26 ++ .../source/htc/tenv/htc_tenv.cpp | 26 ++ .../source/htc/tenv/htc_tenv_service.cpp | 36 +++ .../source/htc/tenv/htc_tenv_service.hpp | 33 +++ .../htc/tenv/htc_tenv_service_manager.cpp | 27 ++ .../htc/tenv/impl/htc_tenv_allocator.cpp | 56 +++++ .../htc/tenv/impl/htc_tenv_allocator.hpp | 42 ++++ .../source/htcs/client/htcs_session.cpp | 12 +- .../source/scs/scs_command_processor.cpp | 99 ++++++++ .../source/scs/scs_server_manager.cpp | 38 +++ .../libstratosphere/source/scs/scs_shell.cpp | 152 ++++++++++++ .../source/scs/scs_shell_server.cpp | 41 ++++ .../libstratosphere/source/scs/scs_tenv.cpp | 52 ++++ .../libvapours/include/vapours/results.hpp | 2 + .../include/vapours/results/cs_results.hpp | 28 +++ .../include/vapours/results/scs_results.hpp | 28 +++ stratosphere/cs/Makefile | 113 +++++++++ stratosphere/cs/cs.json | 89 +++++++ stratosphere/cs/source/cs_main.cpp | 232 ++++++++++++++++++ 44 files changed, 1785 insertions(+), 6 deletions(-) create mode 100644 libraries/libstratosphere/include/stratosphere/cs.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/cs/cs_audio_server.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/cs/cs_command_processor.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/cs/cs_hid_server.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/cs/cs_remote_video_server.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/cs/cs_target_io_server.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv_i_service.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv_i_service_manager.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv_service_manager.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv_types.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/scs.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/scs/scs_command_processor.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/scs/scs_server_manager.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/scs/scs_shell.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/scs/scs_shell_server.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/scs/scs_tenv.hpp create mode 100644 libraries/libstratosphere/source/cs/cs_audio_server.cpp create mode 100644 libraries/libstratosphere/source/cs/cs_command_processor.cpp create mode 100644 libraries/libstratosphere/source/cs/cs_hid_server.cpp create mode 100644 libraries/libstratosphere/source/cs/cs_remote_video_server.cpp create mode 100644 libraries/libstratosphere/source/cs/cs_target_io_server.cpp create mode 100644 libraries/libstratosphere/source/htc/tenv/htc_tenv.cpp create mode 100644 libraries/libstratosphere/source/htc/tenv/htc_tenv_service.cpp create mode 100644 libraries/libstratosphere/source/htc/tenv/htc_tenv_service.hpp create mode 100644 libraries/libstratosphere/source/htc/tenv/htc_tenv_service_manager.cpp create mode 100644 libraries/libstratosphere/source/htc/tenv/impl/htc_tenv_allocator.cpp create mode 100644 libraries/libstratosphere/source/htc/tenv/impl/htc_tenv_allocator.hpp create mode 100644 libraries/libstratosphere/source/scs/scs_command_processor.cpp create mode 100644 libraries/libstratosphere/source/scs/scs_server_manager.cpp create mode 100644 libraries/libstratosphere/source/scs/scs_shell.cpp create mode 100644 libraries/libstratosphere/source/scs/scs_shell_server.cpp create mode 100644 libraries/libstratosphere/source/scs/scs_tenv.cpp create mode 100644 libraries/libvapours/include/vapours/results/cs_results.hpp create mode 100644 libraries/libvapours/include/vapours/results/scs_results.hpp create mode 100644 stratosphere/cs/Makefile create mode 100644 stratosphere/cs/cs.json create mode 100644 stratosphere/cs/source/cs_main.cpp diff --git a/libraries/libstratosphere/include/stratosphere.hpp b/libraries/libstratosphere/include/stratosphere.hpp index d032cab3c..2e0743841 100644 --- a/libraries/libstratosphere/include/stratosphere.hpp +++ b/libraries/libstratosphere/include/stratosphere.hpp @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -77,6 +78,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libstratosphere/include/stratosphere/ams/impl/ams_system_thread_definitions.hpp b/libraries/libstratosphere/include/stratosphere/ams/impl/ams_system_thread_definitions.hpp index bf7210586..3cec1f265 100644 --- a/libraries/libstratosphere/include/stratosphere/ams/impl/ams_system_thread_definitions.hpp +++ b/libraries/libstratosphere/include/stratosphere/ams/impl/ams_system_thread_definitions.hpp @@ -144,6 +144,19 @@ namespace ams::impl { AMS_DEFINE_SYSTEM_THREAD(10, tma, BridgePcieDriver); + /* cs/scs. */ + AMS_DEFINE_SYSTEM_THREAD(20, cs, Main); + AMS_DEFINE_SYSTEM_THREAD(20, cs, HidctlService); + AMS_DEFINE_SYSTEM_THREAD(20, cs, HidctlLegacyServer); + AMS_DEFINE_SYSTEM_THREAD(20, cs, AudioServer); + AMS_DEFINE_SYSTEM_THREAD(10, cs, GrcVideoSender); + AMS_DEFINE_SYSTEM_THREAD(10, cs, GrcVideoReader); + AMS_DEFINE_SYSTEM_THREAD(10, cs, GrcAudioSender); + AMS_DEFINE_SYSTEM_THREAD(10, cs, GrcAudioReader); + + AMS_DEFINE_SYSTEM_THREAD(21, scs, ShellServer); + AMS_DEFINE_SYSTEM_THREAD(21, scs, ShellEventHandler); + /* DevServer/TioServer. */ AMS_DEFINE_SYSTEM_THREAD(21, TioServer, Main); AMS_DEFINE_SYSTEM_THREAD(21, TioServer, FileServerHtcsServer); diff --git a/libraries/libstratosphere/include/stratosphere/cs.hpp b/libraries/libstratosphere/include/stratosphere/cs.hpp new file mode 100644 index 000000000..9e2c0cbee --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/cs.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include +#include +#include +#include + +#include diff --git a/libraries/libstratosphere/include/stratosphere/cs/cs_audio_server.hpp b/libraries/libstratosphere/include/stratosphere/cs/cs_audio_server.hpp new file mode 100644 index 000000000..ccb5088d4 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/cs/cs_audio_server.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::cs { + + void InitializeAudioServer(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/cs/cs_command_processor.hpp b/libraries/libstratosphere/include/stratosphere/cs/cs_command_processor.hpp new file mode 100644 index 000000000..22912c523 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/cs/cs_command_processor.hpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::cs { + + using CommandHeader = scs::CommandHeader; + using ResponseHeader = scs::ResponseHeader; + + class CommandProcessor : public scs::CommandProcessor { + public: + virtual bool ProcessCommand(const CommandHeader &header, const u8 *body, s32 socket) override; + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/cs/cs_hid_server.hpp b/libraries/libstratosphere/include/stratosphere/cs/cs_hid_server.hpp new file mode 100644 index 000000000..4d2c95070 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/cs/cs_hid_server.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::cs { + + void InitializeHidServer(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/cs/cs_remote_video_server.hpp b/libraries/libstratosphere/include/stratosphere/cs/cs_remote_video_server.hpp new file mode 100644 index 000000000..a38ce23de --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/cs/cs_remote_video_server.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::cs { + + void InitializeRemoteVideoServer(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/cs/cs_target_io_server.hpp b/libraries/libstratosphere/include/stratosphere/cs/cs_target_io_server.hpp new file mode 100644 index 000000000..a163b661c --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/cs/cs_target_io_server.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::cs { + + void InitializeTargetIoServer(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/htc.hpp b/libraries/libstratosphere/include/stratosphere/htc.hpp index 748ba3f6b..6a77b25f0 100644 --- a/libraries/libstratosphere/include/stratosphere/htc.hpp +++ b/libraries/libstratosphere/include/stratosphere/htc.hpp @@ -17,3 +17,7 @@ #include #include + +#include +#include +#include diff --git a/libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv.hpp b/libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv.hpp new file mode 100644 index 000000000..24825d2c5 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htc::tenv { + + void Initialize(AllocateFunction allocate, DeallocateFunction deallocate); + Result RegisterDefinitionFilePath(os::ProcessId process_id, const char *path, size_t size); + void UnregisterDefinitionFilePath(os::ProcessId process_id); + +} diff --git a/libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv_i_service.hpp b/libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv_i_service.hpp new file mode 100644 index 000000000..dcf1742ee --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv_i_service.hpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include + +#define AMS_HTC_TENV_I_SERVICE_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 0, Result, GetVariable, (sf::Out out_size, const sf::OutBuffer &out_buffer, const htc::tenv::VariableName &name), (out_size, out_buffer, name)) \ + AMS_SF_METHOD_INFO(C, H, 1, Result, GetVariableLength, (sf::Out out_size,const htc::tenv::VariableName &name), (out_size, name)) \ + AMS_SF_METHOD_INFO(C, H, 2, Result, WaitUntilVariableAvailable, (s64 timeout_ms), (timeout_ms)) + +AMS_SF_DEFINE_INTERFACE(ams::htc::tenv, IService, AMS_HTC_TENV_I_SERVICE_INTERFACE_INFO) diff --git a/libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv_i_service_manager.hpp b/libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv_i_service_manager.hpp new file mode 100644 index 000000000..f0a11accf --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv_i_service_manager.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include + +#define AMS_HTC_TENV_I_SERVICE_MANAGER_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 0, Result, GetServiceInterface, (sf::Out> out, const sf::ClientProcessId &process_id), (out, process_id)) + +AMS_SF_DEFINE_INTERFACE(ams::htc::tenv, IServiceManager, AMS_HTC_TENV_I_SERVICE_MANAGER_INTERFACE_INFO) diff --git a/libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv_service_manager.hpp b/libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv_service_manager.hpp new file mode 100644 index 000000000..ecfae7ea9 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv_service_manager.hpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include + +namespace ams::htc::tenv { + + constexpr inline sm::ServiceName ServiceName = sm::ServiceName::Encode("htc:tenv"); + + class ServiceManager { + public: + Result GetServiceInterface(sf::Out> out, const sf::ClientProcessId &process_id); + }; + static_assert(htc::tenv::IsIServiceManager); + +} diff --git a/libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv_types.hpp b/libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv_types.hpp new file mode 100644 index 000000000..fbfcaaa33 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/htc/tenv/htc_tenv_types.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htc::tenv { + + struct VariableName { + char str[0x40]; + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/scs.hpp b/libraries/libstratosphere/include/stratosphere/scs.hpp new file mode 100644 index 000000000..e3a7902fd --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/scs.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include +#include + +#include +#include + +#include diff --git a/libraries/libstratosphere/include/stratosphere/scs/scs_command_processor.hpp b/libraries/libstratosphere/include/stratosphere/scs/scs_command_processor.hpp new file mode 100644 index 000000000..adfe97b39 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/scs/scs_command_processor.hpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::scs { + + struct CommandHeader { + u64 id; + u32 command; + u32 body_size; + }; + + struct ResponseHeader { + u64 id; + u32 response; + u32 body_size; + }; + + class CommandProcessor { + protected: + enum Command { + Command_None = 0, + Command_LaunchProgramFromHost = 1, + Command_TerminateProcesses = 2, + Command_GetFirmwareVersion = 3, + Command_Reboot = 4, + Command_SetSafeMode = 5, + Command_RegisterTenvDefinitionFilePath = 6, + Command_TerminateApplication = 7, + Command_Shutdown = 8, + Command_SubscribeProcessEvent = 9, + Command_GetTitleName = 10, + Command_ControlVirtualTemperature = 11, + Command_LaunchInstalledApplication = 12, + Command_LaunchGameCardApplication = 13, + Command_LaunchInstalledSystemProcess = 14, + Command_TakeScreenShot = 15, + Command_TakeForegroundScreenShot = 16, + Command_SimulateGameCardDetection = 17, + Command_SimulateSdCardDetection = 18, + Command_DumpRunningApplication = 19, + }; + + enum Response { + Response_None = 0, + Response_Success = 1, + Response_Error = 2, + /* ... */ + }; + public: + constexpr CommandProcessor() = default; + + void Initialize(); + public: + virtual bool ProcessCommand(const CommandHeader &header, const u8 *body, s32 socket); + protected: + static void SendSuccess(s32 socket, const CommandHeader &header); + static void SendErrorResult(s32 socket, const CommandHeader &header, Result result); + private: + static void SendErrorResult(s32 socket, u64 id, Result result); + + static void OnProcessStart(u64 id, s32 socket, os::ProcessId process_id); + static void OnProcessExit(u64 id, s32 socket, os::ProcessId process_id); + static void OnProcessJitDebug(u64 id, s32 socket, os::ProcessId process_id); + }; + + os::SdkMutex &GetHtcsSendMutex(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/scs/scs_server_manager.hpp b/libraries/libstratosphere/include/stratosphere/scs/scs_server_manager.hpp new file mode 100644 index 000000000..b86865c31 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/scs/scs_server_manager.hpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::scs { + + enum Port { + Port_HtcTenv, + Port_Count, + }; + + constexpr inline int SessionCount[Port_Count] = { + 6, + }; + + constexpr inline auto MaxSessions = [] { + auto total = 0; + for (const auto sessions : SessionCount) { + total += sessions; + } + return total; + }(); + + struct ServerOptions { + static constexpr size_t PointerBufferSize = 0; + static constexpr size_t MaxDomains = 6; + static constexpr size_t MaxDomainObjects = 16; + }; + + class ServerManager final : public sf::hipc::ServerManager { + /* ... */ + }; + + ServerManager *GetServerManager(); + void StartServer(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/scs/scs_shell.hpp b/libraries/libstratosphere/include/stratosphere/scs/scs_shell.hpp new file mode 100644 index 000000000..9ba1caf22 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/scs/scs_shell.hpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include + +namespace ams::scs { + + using ProcessEventHandler = void(*)(u64 id, s32 socket, os::ProcessId process_id); + + void InitializeShell(); + + void RegisterCommonProcessEventHandler(ProcessEventHandler on_start, ProcessEventHandler on_exit, ProcessEventHandler on_jit_debug); + + bool RegisterSocket(s32 socket); + void UnregisterSocket(s32 socket); + + Result LaunchProgram(os::ProcessId *out, ncm::ProgramId program_id, const void *args, size_t args_size, u32 process_flags); + + Result SubscribeProcessEvent(s32 socket, bool is_register, u64 id); + +} diff --git a/libraries/libstratosphere/include/stratosphere/scs/scs_shell_server.hpp b/libraries/libstratosphere/include/stratosphere/scs/scs_shell_server.hpp new file mode 100644 index 000000000..2cba7808e --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/scs/scs_shell_server.hpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include + +namespace ams::scs { + + class ShellServer { + private: + htcs::HtcsPortName m_port_name; + os::ThreadType m_thread; + u8 m_buffer[64_KB]; + CommandProcessor *m_command_processor; + private: + static void ThreadEntry(void *arg) { reinterpret_cast(arg)->DoShellServer(); } + + void DoShellServer(); + public: + constexpr ShellServer() = default; + public: + void Initialize(const char *port_name, void *stack, size_t stack_size, CommandProcessor *command_processor); + void Start(); + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/scs/scs_tenv.hpp b/libraries/libstratosphere/include/stratosphere/scs/scs_tenv.hpp new file mode 100644 index 000000000..8a06ceb3e --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/scs/scs_tenv.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::scs { + + void InitializeTenvServiceManager(); + +} diff --git a/libraries/libstratosphere/source/boot2/boot2_api.cpp b/libraries/libstratosphere/source/boot2/boot2_api.cpp index 6e2879b86..5c5c16ed7 100644 --- a/libraries/libstratosphere/source/boot2/boot2_api.cpp +++ b/libraries/libstratosphere/source/boot2/boot2_api.cpp @@ -397,6 +397,7 @@ namespace ams::boot2 { /* Device whether to launch tma or htc. */ if (svc::IsKernelMesosphere() && IsHtcEnabled()) { LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Htc, ncm::StorageId::None), 0); + LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Cs, ncm::StorageId::None), 0); } else { LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Tma, ncm::StorageId::BuiltInSystem), 0); } diff --git a/libraries/libstratosphere/source/cs/cs_audio_server.cpp b/libraries/libstratosphere/source/cs/cs_audio_server.cpp new file mode 100644 index 000000000..bd716364e --- /dev/null +++ b/libraries/libstratosphere/source/cs/cs_audio_server.cpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::cs { + + void InitializeAudioServer() { + /* TODO: Support audio server. */ + } + +} diff --git a/libraries/libstratosphere/source/cs/cs_command_processor.cpp b/libraries/libstratosphere/source/cs/cs_command_processor.cpp new file mode 100644 index 000000000..2fd4e9e6d --- /dev/null +++ b/libraries/libstratosphere/source/cs/cs_command_processor.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::cs { + + bool CommandProcessor::ProcessCommand(const CommandHeader &header, const u8 *body, s32 socket) { + switch (header.command) { + /* TODO: Command support. */ + default: + scs::CommandProcessor::ProcessCommand(header, body, socket); + break; + } + + return true; + } + +} diff --git a/libraries/libstratosphere/source/cs/cs_hid_server.cpp b/libraries/libstratosphere/source/cs/cs_hid_server.cpp new file mode 100644 index 000000000..2aabab5df --- /dev/null +++ b/libraries/libstratosphere/source/cs/cs_hid_server.cpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::cs { + + void InitializeHidServer() { + /* TODO: Support hid redirection server. */ + } + +} diff --git a/libraries/libstratosphere/source/cs/cs_remote_video_server.cpp b/libraries/libstratosphere/source/cs/cs_remote_video_server.cpp new file mode 100644 index 000000000..f00454fa0 --- /dev/null +++ b/libraries/libstratosphere/source/cs/cs_remote_video_server.cpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::cs { + + void InitializeRemoteVideoServer() { + /* TODO: Support remote video server. */ + } + +} diff --git a/libraries/libstratosphere/source/cs/cs_target_io_server.cpp b/libraries/libstratosphere/source/cs/cs_target_io_server.cpp new file mode 100644 index 000000000..284b8fb04 --- /dev/null +++ b/libraries/libstratosphere/source/cs/cs_target_io_server.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::cs { + + void InitializeTargetIoServer() { + /* Launch target io server. */ + os::ProcessId process_id; + scs::LaunchProgram(std::addressof(process_id), ncm::SystemProgramId::DevServer, nullptr, 0, 0); + } + +} diff --git a/libraries/libstratosphere/source/htc/tenv/htc_tenv.cpp b/libraries/libstratosphere/source/htc/tenv/htc_tenv.cpp new file mode 100644 index 000000000..4e31262a3 --- /dev/null +++ b/libraries/libstratosphere/source/htc/tenv/htc_tenv.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "impl/htc_tenv_allocator.hpp" + +namespace ams::htc::tenv { + + void Initialize(AllocateFunction allocate, DeallocateFunction deallocate) { + /* Initialize the library allocator. */ + impl::InitializeAllocator(allocate, deallocate); + } + +} diff --git a/libraries/libstratosphere/source/htc/tenv/htc_tenv_service.cpp b/libraries/libstratosphere/source/htc/tenv/htc_tenv_service.cpp new file mode 100644 index 000000000..ad316748e --- /dev/null +++ b/libraries/libstratosphere/source/htc/tenv/htc_tenv_service.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htc_tenv_service.hpp" + +namespace ams::htc::tenv { + + Result Service::GetVariable(sf::Out out_size, const sf::OutBuffer &out_buffer, const htc::tenv::VariableName &name) { + /* TODO */ + AMS_ABORT("Service::GetVariable"); + } + + Result Service::GetVariableLength(sf::Out out_size,const htc::tenv::VariableName &name) { + /* TODO */ + AMS_ABORT("Service::GetVariableLength"); + } + + Result Service::WaitUntilVariableAvailable(s64 timeout_ms) { + /* TODO */ + AMS_ABORT("Service::WaitUntilVariableAvailable"); + } + +} diff --git a/libraries/libstratosphere/source/htc/tenv/htc_tenv_service.hpp b/libraries/libstratosphere/source/htc/tenv/htc_tenv_service.hpp new file mode 100644 index 000000000..9ce3230bb --- /dev/null +++ b/libraries/libstratosphere/source/htc/tenv/htc_tenv_service.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htc::tenv { + + class Service { + private: + os::ProcessId m_process_id; + public: + constexpr Service(os::ProcessId pid) : m_process_id(pid) { /* ... */ } + public: + Result GetVariable(sf::Out out_size, const sf::OutBuffer &out_buffer, const htc::tenv::VariableName &name); + Result GetVariableLength(sf::Out out_size,const htc::tenv::VariableName &name); + Result WaitUntilVariableAvailable(s64 timeout_ms); + }; + static_assert(htc::tenv::IsIService); + +} diff --git a/libraries/libstratosphere/source/htc/tenv/htc_tenv_service_manager.cpp b/libraries/libstratosphere/source/htc/tenv/htc_tenv_service_manager.cpp new file mode 100644 index 000000000..1a954712f --- /dev/null +++ b/libraries/libstratosphere/source/htc/tenv/htc_tenv_service_manager.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "impl/htc_tenv_allocator.hpp" +#include "htc_tenv_service.hpp" + +namespace ams::htc::tenv { + + Result ServiceManager::GetServiceInterface(sf::Out> out, const sf::ClientProcessId &process_id) { + *out = impl::SfObjectFactory::CreateSharedEmplaced(process_id.GetValue()); + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/htc/tenv/impl/htc_tenv_allocator.cpp b/libraries/libstratosphere/source/htc/tenv/impl/htc_tenv_allocator.cpp new file mode 100644 index 000000000..93c2cc86b --- /dev/null +++ b/libraries/libstratosphere/source/htc/tenv/impl/htc_tenv_allocator.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htc_tenv_allocator.hpp" + +namespace ams::htc::tenv::impl { + + namespace { + + constinit AllocateFunction g_allocate = nullptr; + constinit DeallocateFunction g_deallocate = nullptr; + + } + + void InitializeAllocator(AllocateFunction allocate, DeallocateFunction deallocate) { + /* Check that we don't already have allocator functions. */ + AMS_ASSERT(g_allocate == nullptr); + AMS_ASSERT(g_deallocate == nullptr); + + /* Set our allocator functions. */ + g_allocate = allocate; + g_deallocate = deallocate; + + /* Check that we have allocator functions. */ + AMS_ASSERT(g_allocate != nullptr); + AMS_ASSERT(g_deallocate != nullptr); + } + + void *Allocate(size_t size) { + /* Check that we have an allocator. */ + AMS_ASSERT(g_allocate != nullptr); + + return g_allocate(size); + } + + void Deallocate(void *p, size_t size) { + /* Check that we have a deallocator. */ + AMS_ASSERT(g_deallocate != nullptr); + + return g_deallocate(p, size); + } + +} diff --git a/libraries/libstratosphere/source/htc/tenv/impl/htc_tenv_allocator.hpp b/libraries/libstratosphere/source/htc/tenv/impl/htc_tenv_allocator.hpp new file mode 100644 index 000000000..f48f2056a --- /dev/null +++ b/libraries/libstratosphere/source/htc/tenv/impl/htc_tenv_allocator.hpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::htc::tenv::impl { + + void InitializeAllocator(AllocateFunction allocate, DeallocateFunction deallocate); + + void *Allocate(size_t size); + void Deallocate(void *p, size_t size); + + class SfAllocator { + public: + constexpr ALWAYS_INLINE SfAllocator() { /* ... */ } + + void *Allocate(size_t size) { + return ::ams::htc::tenv::impl::Allocate(size); + } + + void Deallocate(void *p, size_t size) { + return ::ams::htc::tenv::impl::Deallocate(p, size); + } + }; + + using SfPolicy = sf::StatelessAllocationPolicy; + using SfObjectFactory = sf::ObjectFactory; + +} diff --git a/libraries/libstratosphere/source/htcs/client/htcs_session.cpp b/libraries/libstratosphere/source/htcs/client/htcs_session.cpp index 37fc6d749..c41276bb0 100644 --- a/libraries/libstratosphere/source/htcs/client/htcs_session.cpp +++ b/libraries/libstratosphere/source/htcs/client/htcs_session.cpp @@ -150,13 +150,13 @@ namespace ams::htcs::client { } Result RemoteSocket::Connect(sf::Out out_err, sf::Out out_res, const htcs::SockAddrHtcs &address) { - static_assert(sizeof(htcs::SockAddrHtcs) == sizeof(::SockAddrHtcs)); - return ::htcsSocketConnect(std::addressof(m_s), out_err.GetPointer(), out_res.GetPointer(), reinterpret_cast(std::addressof(address))); + static_assert(sizeof(htcs::SockAddrHtcs) == sizeof(::HtcsSockAddr)); + return ::htcsSocketConnect(std::addressof(m_s), out_err.GetPointer(), out_res.GetPointer(), reinterpret_cast(std::addressof(address))); } Result RemoteSocket::Bind(sf::Out out_err, sf::Out out_res, const htcs::SockAddrHtcs &address) { - static_assert(sizeof(htcs::SockAddrHtcs) == sizeof(::SockAddrHtcs)); - return ::htcsSocketBind(std::addressof(m_s), out_err.GetPointer(), out_res.GetPointer(), reinterpret_cast(std::addressof(address))); + static_assert(sizeof(htcs::SockAddrHtcs) == sizeof(::HtcsSockAddr)); + return ::htcsSocketBind(std::addressof(m_s), out_err.GetPointer(), out_res.GetPointer(), reinterpret_cast(std::addressof(address))); } Result RemoteSocket::Listen(sf::Out out_err, sf::Out out_res, s32 backlog_count) { @@ -176,9 +176,9 @@ namespace ams::htcs::client { } Result RemoteSocket::AcceptResults(sf::Out out_err, sf::Out> out, sf::Out out_address, u32 task_id) { - static_assert(sizeof(htcs::SockAddrHtcs) == sizeof(::SockAddrHtcs)); + static_assert(sizeof(htcs::SockAddrHtcs) == sizeof(::HtcsSockAddr)); ::HtcsSocket libnx_socket; - R_TRY(::htcsSocketAcceptResults(std::addressof(m_s), out_err.GetPointer(), std::addressof(libnx_socket), reinterpret_cast<::SockAddrHtcs *>(out_address.GetPointer()), task_id)); + R_TRY(::htcsSocketAcceptResults(std::addressof(m_s), out_err.GetPointer(), std::addressof(libnx_socket), reinterpret_cast<::HtcsSockAddr *>(out_address.GetPointer()), task_id)); R_SUCCEED_IF(*out_err != 0); diff --git a/libraries/libstratosphere/source/scs/scs_command_processor.cpp b/libraries/libstratosphere/source/scs/scs_command_processor.cpp new file mode 100644 index 000000000..b3da75937 --- /dev/null +++ b/libraries/libstratosphere/source/scs/scs_command_processor.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::scs { + + namespace { + + struct ResponseError { + ResponseHeader header; + u32 result; + }; + + constinit os::SdkMutex g_htcs_send_mutex; + + } + + os::SdkMutex &GetHtcsSendMutex() { + return g_htcs_send_mutex; + } + + void CommandProcessor::SendSuccess(s32 socket, const CommandHeader &header) { + /* Build the response. */ + const ResponseHeader response = { + .id = header.id, + .response = Response_Success, + .body_size = 0, + }; + + /* Send the response. */ + std::scoped_lock lk(GetHtcsSendMutex()); + htcs::Send(socket, std::addressof(response), sizeof(response), 0); + } + + void CommandProcessor::SendErrorResult(s32 socket, const CommandHeader &header, Result result) { + return SendErrorResult(socket, header.id, result); + } + + void CommandProcessor::SendErrorResult(s32 socket, u64 id, Result result) { + /* Build the response. */ + const ResponseError response = { + .header = { + .id = id, + .response = Response_Error, + .body_size = sizeof(response) - sizeof(response.header), + }, + .result = result.GetValue(), + }; + + /* Send the response. */ + std::scoped_lock lk(GetHtcsSendMutex()); + htcs::Send(socket, std::addressof(response), sizeof(response), 0); + } + + void CommandProcessor::OnProcessStart(u64 id, s32 socket, os::ProcessId process_id) { + /* TODO */ + AMS_ABORT("CommandProcessor::OnProcessStart"); + } + + void CommandProcessor::OnProcessExit(u64 id, s32 socket, os::ProcessId process_id) { + /* TODO */ + AMS_ABORT("CommandProcessor::OnProcessExit"); + } + + void CommandProcessor::OnProcessJitDebug(u64 id, s32 socket, os::ProcessId process_id) { + /* TODO */ + AMS_ABORT("CommandProcessor::OnProcessJitDebug"); + } + + void CommandProcessor::Initialize() { + /* Register our process event handlers. */ + scs::RegisterCommonProcessEventHandler(OnProcessStart, OnProcessExit, OnProcessJitDebug); + } + + bool CommandProcessor::ProcessCommand(const CommandHeader &header, const u8 *body, s32 socket) { + switch (header.command) { + /* TODO: Support commands. */ + default: + SendErrorResult(socket, header, scs::ResultUnknownCommand()); + break; + } + + return true; + } + +} diff --git a/libraries/libstratosphere/source/scs/scs_server_manager.cpp b/libraries/libstratosphere/source/scs/scs_server_manager.cpp new file mode 100644 index 000000000..0a9fd2d94 --- /dev/null +++ b/libraries/libstratosphere/source/scs/scs_server_manager.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::scs { + + namespace { + + ServerManager g_server_manager; + + } + + ServerManager *GetServerManager() { + return std::addressof(g_server_manager); + } + + void StartServer() { + /* Start the server. */ + g_server_manager.ResumeProcessing(); + + /* Loop processing the server. */ + g_server_manager.LoopProcess(); + } + +} diff --git a/libraries/libstratosphere/source/scs/scs_shell.cpp b/libraries/libstratosphere/source/scs/scs_shell.cpp new file mode 100644 index 000000000..0f511064f --- /dev/null +++ b/libraries/libstratosphere/source/scs/scs_shell.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::scs { + + namespace { + + struct SocketInfo { + u64 id; + s32 socket; + }; + + struct ProgramInfo { + os::ProcessId process_id; + u64 id; + s32 socket; + s32 info_id; + bool _18; + bool _19; + bool _1A; + }; + + constexpr inline auto MaxSocketInfo = 2; + constexpr inline auto MaxProgramInfo = 64; + + class SocketInfoManager { + private: + SocketInfo m_infos[MaxProgramInfo]; + int m_count; + public: + constexpr SocketInfoManager() = default; + + void Initialize() { + /* Clear our count. */ + m_count = 0; + } + }; + + class ProgramInfoManager { + private: + s32 m_next_info_id; + ProgramInfo m_infos[MaxProgramInfo]; + int m_count; + public: + constexpr ProgramInfoManager() = default; + + void Initialize() { + /* Reset our next id. */ + m_next_info_id = 1; + + /* Clear our count. */ + m_count = 0; + } + }; + + alignas(os::ThreadStackAlignment) constinit u8 g_thread_stack[os::MemoryPageSize]; + constinit os::ThreadType g_thread; + + constinit ProcessEventHandler g_common_start_handler; + constinit ProcessEventHandler g_common_exit_handler; + constinit ProcessEventHandler g_common_jit_debug_handler; + + constinit SocketInfoManager g_socket_info_manager; + constinit ProgramInfoManager g_program_info_manager; + + void EventHandlerThread(void *) { + /* TODO */ + AMS_ABORT("scs::EventHandlerThread"); + } + + void StartEventHandlerThread() { + /* Create the handler thread. */ + R_ABORT_UNLESS(os::CreateThread(std::addressof(g_thread), EventHandlerThread, nullptr, g_thread_stack, sizeof(g_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(scs, ShellEventHandler))); + + /* Set the handler thread's name. */ + os::SetThreadNamePointer(std::addressof(g_thread), AMS_GET_SYSTEM_THREAD_NAME(scs, ShellEventHandler)); + + /* Start the handler thread. */ + os::StartThread(std::addressof(g_thread)); + } + + Result PrepareToLaunchProgram(ncm::ProgramId program_id, const void *args, size_t args_size) { + /* Set the arguments. */ + R_TRY_CATCH(ldr::SetProgramArgument(program_id, args, args_size)) { + R_CATCH(ldr::ResultTooManyArguments) { + /* There are too many arguments already registered. Flush the arguments queue. */ + R_TRY(ldr::FlushArguments()); + + /* Try again. */ + R_TRY(ldr::SetProgramArgument(program_id, args, args_size)); + } + } R_END_TRY_CATCH; + + return ResultSuccess(); + } + + void FlushProgramArgument(ncm::ProgramId program_id) { + /* Ensure there are no arguments for the program. */ + ldr::SetProgramArgument(program_id, "", 1); + } + + } + + void InitializeShell() { + /* Initialize our managers. */ + g_socket_info_manager.Initialize(); + g_program_info_manager.Initialize(); + + /* Start our event handler. */ + StartEventHandlerThread(); + } + + void RegisterCommonProcessEventHandler(ProcessEventHandler on_start, ProcessEventHandler on_exit, ProcessEventHandler on_jit_debug) { + g_common_start_handler = on_start; + g_common_exit_handler = on_exit; + g_common_jit_debug_handler = on_jit_debug; + } + + bool RegisterSocket(s32 socket); + void UnregisterSocket(s32 socket); + + Result LaunchProgram(os::ProcessId *out, ncm::ProgramId program_id, const void *args, size_t args_size, u32 process_flags) { + /* Set up the arguments. */ + PrepareToLaunchProgram(program_id, args, args_size); + + /* Ensure arguments are managed correctly. */ + ON_SCOPE_EXIT { FlushProgramArgument(program_id); }; + + /* Launch the program. */ + const ncm::ProgramLocation loc = ncm::ProgramLocation::Make(program_id, ncm::StorageId::BuiltInSystem); + R_TRY(pgl::LaunchProgram(out, loc, process_flags | pm::LaunchFlags_SignalOnExit, 0)); + + return ResultSuccess(); + } + + Result SubscribeProcessEvent(s32 socket, bool is_register, u64 id); + +} diff --git a/libraries/libstratosphere/source/scs/scs_shell_server.cpp b/libraries/libstratosphere/source/scs/scs_shell_server.cpp new file mode 100644 index 000000000..286fa31d9 --- /dev/null +++ b/libraries/libstratosphere/source/scs/scs_shell_server.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::scs { + + void ShellServer::Initialize(const char *port_name, void *stack, size_t stack_size, CommandProcessor *command_processor) { + /* Set our variables. */ + m_command_processor = command_processor; + std::strcpy(m_port_name.name, port_name); + + /* Create our thread. */ + R_ABORT_UNLESS(os::CreateThread(std::addressof(m_thread), ThreadEntry, this, stack, stack_size, AMS_GET_SYSTEM_THREAD_PRIORITY(scs, ShellServer))); + + /* Set our thread's name. */ + os::SetThreadNamePointer(std::addressof(m_thread), AMS_GET_SYSTEM_THREAD_NAME(scs, ShellServer)); + } + + void ShellServer::Start() { + os::StartThread(std::addressof(m_thread)); + } + + void ShellServer::DoShellServer() { + /* TODO */ + AMS_ABORT("ShellServer::DoShellServer"); + } + +} diff --git a/libraries/libstratosphere/source/scs/scs_tenv.cpp b/libraries/libstratosphere/source/scs/scs_tenv.cpp new file mode 100644 index 000000000..7569517d8 --- /dev/null +++ b/libraries/libstratosphere/source/scs/scs_tenv.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::scs { + + namespace { + + alignas(os::MemoryPageSize) constinit u8 g_tenv_heap_storage[48_KB]; + constinit lmem::HeapHandle g_tenv_heap_handle = nullptr; + constinit os::SdkMutex g_mutex; + + void InitializeExpHeap() { + std::scoped_lock lk(g_mutex); + g_tenv_heap_handle = lmem::CreateExpHeap(g_tenv_heap_storage, sizeof(g_tenv_heap_storage), lmem::CreateOption_None); + } + + void *Allocate(size_t size) { + std::scoped_lock lk(g_mutex); + void *mem = lmem::AllocateFromExpHeap(g_tenv_heap_handle, size); + return mem; + } + + void Deallocate(void *p, size_t size) { + std::scoped_lock lk(g_mutex); + lmem::FreeToExpHeap(g_tenv_heap_handle, p); + } + + } + + void InitializeTenvServiceManager() { + /* Initialize the tenv heap. */ + InitializeExpHeap(); + + /* Initialize the tenv library. */ + htc::tenv::Initialize(Allocate, Deallocate); + } + +} diff --git a/libraries/libvapours/include/vapours/results.hpp b/libraries/libvapours/include/vapours/results.hpp index ad89ea157..7786f7f23 100644 --- a/libraries/libvapours/include/vapours/results.hpp +++ b/libraries/libvapours/include/vapours/results.hpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +56,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libvapours/include/vapours/results/cs_results.hpp b/libraries/libvapours/include/vapours/results/cs_results.hpp new file mode 100644 index 000000000..0c1a67ab4 --- /dev/null +++ b/libraries/libvapours/include/vapours/results/cs_results.hpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +namespace ams::cs { + + R_DEFINE_NAMESPACE_RESULT_MODULE(204); + + R_DEFINE_ERROR_RESULT(UnknownCommand, 2); + R_DEFINE_ERROR_RESULT(OutOfResource, 4); + R_DEFINE_ERROR_RESULT(NoSocket, 7); + +} diff --git a/libraries/libvapours/include/vapours/results/scs_results.hpp b/libraries/libvapours/include/vapours/results/scs_results.hpp new file mode 100644 index 000000000..8981847a8 --- /dev/null +++ b/libraries/libvapours/include/vapours/results/scs_results.hpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +namespace ams::scs { + + R_DEFINE_NAMESPACE_RESULT_MODULE(204); + + R_DEFINE_ERROR_RESULT(UnknownCommand, 2); + R_DEFINE_ERROR_RESULT(OutOfResource, 4); + R_DEFINE_ERROR_RESULT(NoSocket, 7); + +} diff --git a/stratosphere/cs/Makefile b/stratosphere/cs/Makefile new file mode 100644 index 000000000..aab86a63a --- /dev/null +++ b/stratosphere/cs/Makefile @@ -0,0 +1,113 @@ +#--------------------------------------------------------------------------------- +# pull in common stratosphere sysmodule configuration +#--------------------------------------------------------------------------------- +include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../libraries/config/templates/stratosphere.mk + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(call FIND_SOURCE_FILES,$(SOURCES),c) +CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp) +SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),s) + +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC) + +ifeq ($(strip $(CONFIG_JSON)),) + jsons := $(wildcard *.json) + ifneq (,$(findstring $(TARGET).json,$(jsons))) + export APP_JSON := $(TOPDIR)/$(TARGET).json + else + ifneq (,$(findstring config.json,$(jsons))) + export APP_JSON := $(TOPDIR)/config.json + endif + endif +else + export APP_JSON := $(TOPDIR)/$(CONFIG_JSON) +endif + +.PHONY: $(BUILD) clean all + +#--------------------------------------------------------------------------------- +all: $(BUILD) + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).nsp $(TARGET).npdm $(TARGET).nso $(TARGET).elf + + +#--------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +all : $(OUTPUT).nsp + +ifeq ($(strip $(APP_JSON)),) +$(OUTPUT).nsp : $(OUTPUT).nso +else +$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm +endif + +$(OUTPUT).nso : $(OUTPUT).elf + +$(OUTPUT).elf : $(OFILES) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/stratosphere/cs/cs.json b/stratosphere/cs/cs.json new file mode 100644 index 000000000..4cd06eb68 --- /dev/null +++ b/stratosphere/cs/cs.json @@ -0,0 +1,89 @@ +{ + "name": "cs", + "title_id": "0x0100000000000017", + "title_id_range_min": "0x0100000000000017", + "title_id_range_max": "0x0100000000000017", + "main_thread_stack_size": "0x00004000", + "main_thread_priority": 48, + "default_cpu_id": 3, + "process_category": 0, + "is_retail": false, + "pool_partition": 2, + "is_64_bit": true, + "address_space_type": 3, + "disable_device_address_space_merge": true, + "filesystem_access": { + "permissions": "0xFFFFFFFFFFFFFFFF" + }, + "service_access": ["appletAE", "lr", "ldr:shel", "pgl", "ns:am2", "ns:dev", "htcs", "hid", "hid:dbg", "hid:sys", "set", "set:sys", "lr", "ldr:shel", "ncm", "arp:r", "fsp-srv", "bpc", "tc", "fatal:u", "caps:sc", "audrec:u", "auddev", "grc:d"], + "service_host": ["htc:tenv"], + "kernel_capabilities": [{ + "type": "kernel_flags", + "value": { + "highest_thread_priority": 63, + "lowest_thread_priority": 24, + "lowest_cpu_id": 3, + "highest_cpu_id": 3 + } + }, { + "type": "syscalls", + "value": { + "svcSetHeapSize": "0x01", + "svcSetMemoryPermission": "0x02", + "svcSetMemoryAttribute": "0x03", + "svcMapMemory": "0x04", + "svcUnmapMemory": "0x05", + "svcQueryMemory": "0x06", + "svcExitProcess": "0x07", + "svcCreateThread": "0x08", + "svcStartThread": "0x09", + "svcExitThread": "0x0a", + "svcSleepThread": "0x0b", + "svcGetThreadPriority": "0x0c", + "svcSetThreadPriority": "0x0d", + "svcGetThreadCoreMask": "0x0e", + "svcSetThreadCoreMask": "0x0f", + "svcGetCurrentProcessorNumber": "0x10", + "svcSignalEvent": "0x11", + "svcClearEvent": "0x12", + "svcMapSharedMemory": "0x13", + "svcUnmapSharedMemory": "0x14", + "svcCreateTransferMemory": "0x15", + "svcCloseHandle": "0x16", + "svcResetSignal": "0x17", + "svcWaitSynchronization": "0x18", + "svcCancelSynchronization": "0x19", + "svcArbitrateLock": "0x1a", + "svcArbitrateUnlock": "0x1b", + "svcWaitProcessWideKeyAtomic": "0x1c", + "svcSignalProcessWideKey": "0x1d", + "svcGetSystemTick": "0x1e", + "svcConnectToNamedPort": "0x1f", + "svcSendSyncRequestLight": "0x20", + "svcSendSyncRequest": "0x21", + "svcSendSyncRequestWithUserBuffer": "0x22", + "svcSendAsyncRequestWithUserBuffer": "0x23", + "svcGetProcessId": "0x24", + "svcGetThreadId": "0x25", + "svcBreak": "0x26", + "svcOutputDebugString": "0x27", + "svcReturnFromException": "0x28", + "svcGetInfo": "0x29", + "svcWaitForAddress": "0x34", + "svcSignalToAddress": "0x35", + "svcCreateSession": "0x40", + "svcAcceptSession": "0x41", + "svcReplyAndReceiveLight": "0x42", + "svcReplyAndReceive": "0x43", + "svcReplyAndReceiveWithUserBuffer": "0x44", + "svcCreateEvent": "0x45", + "svcCallSecureMonitor": "0x7f" + } + }, { + "type": "min_kernel_version", + "value": "0x0030" + }, { + "type": "handle_table_size", + "value": 0 + }] +} \ No newline at end of file diff --git a/stratosphere/cs/source/cs_main.cpp b/stratosphere/cs/source/cs_main.cpp new file mode 100644 index 000000000..479f82012 --- /dev/null +++ b/stratosphere/cs/source/cs_main.cpp @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +extern "C" { + extern u32 __start__; + + u32 __nx_applet_type = AppletType_None; + u32 __nx_fs_num_sessions = 1; + + #define INNER_HEAP_SIZE 0x0 + size_t nx_inner_heap_size = INNER_HEAP_SIZE; + char nx_inner_heap[INNER_HEAP_SIZE]; + + void __libnx_initheap(void); + void __appInit(void); + void __appExit(void); + + void *__libnx_alloc(size_t size); + void *__libnx_aligned_alloc(size_t alignment, size_t size); + void __libnx_free(void *mem); +} + +namespace ams { + + ncm::ProgramId CurrentProgramId = ncm::SystemProgramId::Cs; + +} + +using namespace ams; + +#define AMS_CS_SERVER_USE_FATAL_ERROR 1 + +#if AMS_CS_SERVER_USE_FATAL_ERROR + +extern "C" { + + /* Exception handling. */ + alignas(16) u8 __nx_exception_stack[ams::os::MemoryPageSize]; + u64 __nx_exception_stack_size = sizeof(__nx_exception_stack); + void __libnx_exception_handler(ThreadExceptionDump *ctx); + +} + +void __libnx_exception_handler(ThreadExceptionDump *ctx) { + ams::CrashHandler(ctx); +} + +#endif + +namespace ams::cs { + + namespace { + + alignas(os::ThreadStackAlignment) constinit u8 g_shell_stack[4_KB]; + alignas(os::ThreadStackAlignment) constinit u8 g_runner_stack[4_KB]; + + alignas(os::MemoryPageSize) constinit u8 g_heap_memory[32_KB]; + + alignas(0x40) constinit u8 g_htcs_buffer[1_KB]; + + constinit os::SdkMutex g_heap_mutex; + constinit lmem::HeapHandle g_heap_handle; + + void *Allocate(size_t size) { + std::scoped_lock lk(g_heap_mutex); + void *mem = lmem::AllocateFromExpHeap(g_heap_handle, size); + return mem; + } + + void Deallocate(void *p, size_t size) { + std::scoped_lock lk(g_heap_mutex); + lmem::FreeToExpHeap(g_heap_handle, p); + } + + void InitializeHeap() { + std::scoped_lock lk(g_heap_mutex); + g_heap_handle = lmem::CreateExpHeap(g_heap_memory, sizeof(g_heap_memory), lmem::CreateOption_None); + } + + } + +} + +void __libnx_initheap(void) { + void* addr = nx_inner_heap; + size_t size = nx_inner_heap_size; + + /* Newlib */ + extern char* fake_heap_start; + extern char* fake_heap_end; + + fake_heap_start = (char*)addr; + fake_heap_end = (char*)addr + size; + + cs::InitializeHeap(); +} + +void __appInit(void) { + hos::InitializeForStratosphere(); + + fs::SetAllocator(cs::Allocate, cs::Deallocate); + + sm::DoWithSession([&]() { + R_ABORT_UNLESS(fsInitialize()); + lr::Initialize(); + R_ABORT_UNLESS(ldr::InitializeForShell()); + R_ABORT_UNLESS(pgl::Initialize()); + /* TODO: Other services? */ + }); + + ams::CheckApiVersion(); +} + +void __appExit(void) { + /* TODO: Other services? */ + pgl::Finalize(); + ldr::FinalizeForShell(); + lr::Finalize(); + fsExit(); +} + +namespace ams { + + void *Malloc(size_t size) { + AMS_ABORT("ams::Malloc was called"); + } + + void Free(void *ptr) { + AMS_ABORT("ams::Free was called"); + } + +} + +void *operator new(size_t size) { + AMS_ABORT("operator new(size_t) was called"); +} + +void operator delete(void *p) { + AMS_ABORT("operator delete(void *) was called"); +} + +void *__libnx_alloc(size_t size) { + AMS_ABORT("__libnx_alloc was called"); +} + +void *__libnx_aligned_alloc(size_t alignment, size_t size) { + AMS_ABORT("__libnx_aligned_alloc was called"); +} + +void __libnx_free(void *mem) { + AMS_ABORT("__libnx_free was called"); +} + +namespace ams::cs { + + namespace { + + constinit ::ams::cs::CommandProcessor g_command_processor; + + constinit ::ams::scs::ShellServer g_shell_server; + constinit ::ams::scs::ShellServer g_runner_server; + + constinit sf::UnmanagedServiceObject g_tenv_service_manager; + + } + +} + +int main(int argc, char **argv) +{ + using namespace ams::cs; + + /* Set thread name. */ + os::SetThreadNamePointer(os::GetCurrentThread(), AMS_GET_SYSTEM_THREAD_NAME(cs, Main)); + AMS_ASSERT(os::GetThreadPriority(os::GetCurrentThread()) == AMS_GET_SYSTEM_THREAD_PRIORITY(cs, Main)); + + /* Initialize htcs. */ + constexpr auto HtcsSocketCountMax = 6; + const size_t buffer_size = htcs::GetWorkingMemorySize(2 * HtcsSocketCountMax); + AMS_ABORT_UNLESS(sizeof(g_htcs_buffer) >= buffer_size); + htcs::InitializeForSystem(g_htcs_buffer, buffer_size, HtcsSocketCountMax); + + /* Initialize audio server. */ + cs::InitializeAudioServer(); + + /* Initialize remote video server. */ + cs::InitializeRemoteVideoServer(); + + /* Initialize hid server. */ + cs::InitializeHidServer(); + + /* Initialize target io server. */ + cs::InitializeTargetIoServer(); + + /* Initialize command processor. */ + g_command_processor.Initialize(); + + /* Setup scs. */ + scs::InitializeShell(); + + /* Setup target environment service. */ + scs::InitializeTenvServiceManager(); + + /* Initialize the shell servers. */ + g_shell_server.Initialize("iywys@$cs", g_shell_stack, sizeof(g_shell_stack), std::addressof(g_command_processor)); + g_shell_server.Start(); + + g_runner_server.Initialize("iywys@$csForRunnerTools", g_runner_stack, sizeof(g_runner_stack), std::addressof(g_command_processor)); + g_runner_server.Start(); + + /* Register htc:tenv. */ + R_ABORT_UNLESS(scs::GetServerManager()->RegisterObjectForServer(g_tenv_service_manager.GetShared(), htc::tenv::ServiceName, scs::SessionCount[scs::Port_HtcTenv])); + + /* Start the scs ipc server. */ + scs::StartServer(); + + return 0; +} From a7564cf303d1e97d801f8f3f4efc0f1bea493ad7 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 17 Mar 2021 17:48:30 -0700 Subject: [PATCH 103/280] kern: add extension InfoType for retrieving current process handle. --- .../source/svc/kern_svc_info.cpp | 19 +++++++++++++++++++ .../include/vapours/svc/svc_types_common.hpp | 1 + 2 files changed, 20 insertions(+) diff --git a/libraries/libmesosphere/source/svc/kern_svc_info.cpp b/libraries/libmesosphere/source/svc/kern_svc_info.cpp index 60a72fbc0..308871fa0 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_info.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_info.cpp @@ -276,6 +276,25 @@ namespace ams::kern::svc { } } break; + case ams::svc::InfoType_MesosphereCurrentProcess: + { + /* Verify the input handle is invalid. */ + R_UNLESS(handle == ams::svc::InvalidHandle, svc::ResultInvalidHandle()); + + /* Verify the sub-type is valid. */ + R_UNLESS(info_subtype == 0, svc::ResultInvalidCombination()); + + /* Get the handle table. */ + KHandleTable &handle_table = GetCurrentProcess().GetHandleTable(); + + /* Get a new handle for the current process. */ + ams::svc::Handle tmp; + R_TRY(handle_table.Add(std::addressof(tmp), GetCurrentProcessPointer())); + + /* Set the output. */ + *out = tmp; + } + break; default: { /* For debug, log the invalid info call. */ diff --git a/libraries/libvapours/include/vapours/svc/svc_types_common.hpp b/libraries/libvapours/include/vapours/svc/svc_types_common.hpp index b533d4e03..069518989 100644 --- a/libraries/libvapours/include/vapours/svc/svc_types_common.hpp +++ b/libraries/libvapours/include/vapours/svc/svc_types_common.hpp @@ -159,6 +159,7 @@ namespace ams::svc { InfoType_FreeThreadCount = 24, InfoType_MesosphereMeta = 65000, + InfoType_MesosphereCurrentProcess = 65001, InfoType_ThreadTickCount = 0xF0000002, }; From 3afd9a737cf8a48c58462d5c6432c83364dc7e79 Mon Sep 17 00:00:00 2001 From: Adubbz Date: Fri, 19 Mar 2021 14:14:03 +1100 Subject: [PATCH 104/280] daybreak: Added a warning when resetting to factory settings --- troposphere/daybreak/source/ui.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/troposphere/daybreak/source/ui.cpp b/troposphere/daybreak/source/ui.cpp index e7e726f27..2dc96f32f 100644 --- a/troposphere/daybreak/source/ui.cpp +++ b/troposphere/daybreak/source/ui.cpp @@ -477,7 +477,7 @@ namespace dbk { } if (R_FAILED(rc = splGetConfig(static_cast(ExosphereEmummcType), &is_emummc))) { - ChangeMenu(std::make_shared("An error has occurred", "Failed to chech emuMMC status.", rc)); + ChangeMenu(std::make_shared("An error has occurred", "Failed to check emuMMC status.", rc)); return; } @@ -941,10 +941,18 @@ namespace dbk { break; } + std::shared_ptr next_menu; + if (g_exfat_supported) { - ChangeMenu(std::make_shared(g_current_menu)); + next_menu = std::make_shared(g_current_menu); } else { - ChangeMenu(std::make_shared(g_current_menu, std::make_shared(g_current_menu), "Ready to begin update installation", "Are you sure you want to proceed?")); + next_menu = std::make_shared(g_current_menu, std::make_shared(g_current_menu), "Ready to begin update installation", "Are you sure you want to proceed?"); + } + + if (g_reset_to_factory) { + ChangeMenu(std::make_shared(g_current_menu, next_menu, "Warning: Factory reset selected", "Saves and installed games will be permanently deleted.")); + } else { + ChangeMenu(next_menu); } } From 79e4c82d7ed47ee723292a785289043703eca773 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 21 Mar 2021 02:51:59 -0700 Subject: [PATCH 105/280] ams: distribute sysmodules in single file as stratosphere.romfs --- Makefile | 49 +++++----- .../libstratosphere/source/fs/fs_code.cpp | 91 ++++++++++++++++++- .../ncm_submission_package_install_task.cpp | 3 + 3 files changed, 118 insertions(+), 25 deletions(-) diff --git a/Makefile b/Makefile index d1db70423..75cc6e781 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,9 @@ +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=devkitPro) +endif + +include $(DEVKITPRO)/devkitA64/base_tools + TOPTARGETS := all clean dist-no-debug dist AMSBRANCH := $(shell git symbolic-ref --short HEAD) AMSHASH := $(shell git rev-parse --short HEAD) @@ -56,15 +62,6 @@ dist-no-debug: all mkdir atmosphere-$(AMSVER)/atmosphere mkdir atmosphere-$(AMSVER)/sept mkdir atmosphere-$(AMSVER)/switch - mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000008 - mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/010000000000000D - mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/010000000000002B - mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000032 - mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000034 - mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000036 - mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000037 - mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/010000000000003C - mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000042 mkdir -p atmosphere-$(AMSVER)/atmosphere/fatal_errors mkdir -p atmosphere-$(AMSVER)/atmosphere/config_templates mkdir -p atmosphere-$(AMSVER)/atmosphere/config @@ -84,19 +81,27 @@ dist-no-debug: all cp config_templates/exosphere.ini atmosphere-$(AMSVER)/atmosphere/config_templates/exosphere.ini cp -r config_templates/kip_patches atmosphere-$(AMSVER)/atmosphere/kip_patches cp -r config_templates/hbl_html atmosphere-$(AMSVER)/atmosphere/hbl_html - cp stratosphere/boot2/boot2.nsp atmosphere-$(AMSVER)/atmosphere/contents/0100000000000008/exefs.nsp - cp stratosphere/dmnt/dmnt.nsp atmosphere-$(AMSVER)/atmosphere/contents/010000000000000D/exefs.nsp - cp stratosphere/erpt/erpt.nsp atmosphere-$(AMSVER)/atmosphere/contents/010000000000002B/exefs.nsp - cp stratosphere/eclct.stub/eclct.stub.nsp atmosphere-$(AMSVER)/atmosphere/contents/0100000000000032/exefs.nsp - cp stratosphere/fatal/fatal.nsp atmosphere-$(AMSVER)/atmosphere/contents/0100000000000034/exefs.nsp - cp stratosphere/creport/creport.nsp atmosphere-$(AMSVER)/atmosphere/contents/0100000000000036/exefs.nsp - cp stratosphere/ro/ro.nsp atmosphere-$(AMSVER)/atmosphere/contents/0100000000000037/exefs.nsp - cp stratosphere/jpegdec/jpegdec.nsp atmosphere-$(AMSVER)/atmosphere/contents/010000000000003C/exefs.nsp - cp stratosphere/pgl/pgl.nsp atmosphere-$(AMSVER)/atmosphere/contents/0100000000000042/exefs.nsp - mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000032/flags - touch atmosphere-$(AMSVER)/atmosphere/contents/0100000000000032/flags/boot2.flag - mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000037/flags - touch atmosphere-$(AMSVER)/atmosphere/contents/0100000000000037/flags/boot2.flag + mkdir -p atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/0100000000000008 + mkdir -p atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/0100000000000008 + mkdir -p atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/010000000000000D + mkdir -p atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/010000000000002B + mkdir -p atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/0100000000000032 + mkdir -p atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/0100000000000034 + mkdir -p atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/0100000000000036 + mkdir -p atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/0100000000000037 + mkdir -p atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/010000000000003C + mkdir -p atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/0100000000000042 + cp stratosphere/boot2/boot2.nsp atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/0100000000000008/exefs.nsp + cp stratosphere/dmnt/dmnt.nsp atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/010000000000000D/exefs.nsp + cp stratosphere/erpt/erpt.nsp atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/010000000000002B/exefs.nsp + cp stratosphere/eclct.stub/eclct.stub.nsp atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/0100000000000032/exefs.nsp + cp stratosphere/fatal/fatal.nsp atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/0100000000000034/exefs.nsp + cp stratosphere/creport/creport.nsp atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/0100000000000036/exefs.nsp + cp stratosphere/ro/ro.nsp atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/0100000000000037/exefs.nsp + cp stratosphere/jpegdec/jpegdec.nsp atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/010000000000003C/exefs.nsp + cp stratosphere/pgl/pgl.nsp atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/0100000000000042/exefs.nsp + @build_romfs atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs atmosphere-$(AMSVER)/atmosphere/stratosphere.romfs + rm -r atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs cp troposphere/reboot_to_payload/reboot_to_payload.nro atmosphere-$(AMSVER)/switch/reboot_to_payload.nro cp troposphere/daybreak/daybreak.nro atmosphere-$(AMSVER)/switch/daybreak.nro cd atmosphere-$(AMSVER); zip -r ../atmosphere-$(AMSVER).zip ./*; cd ../; diff --git a/libraries/libstratosphere/source/fs/fs_code.cpp b/libraries/libstratosphere/source/fs/fs_code.cpp index 3e706728d..248a6b082 100644 --- a/libraries/libstratosphere/source/fs/fs_code.cpp +++ b/libraries/libstratosphere/source/fs/fs_code.cpp @@ -20,6 +20,55 @@ namespace ams::fs { namespace { + constinit os::SdkMutex g_mount_stratosphere_romfs_lock; + constinit bool g_mounted_stratosphere_romfs = false; + + constinit TYPED_STORAGE(FileHandleStorage) g_stratosphere_romfs_storage = {}; + constinit TYPED_STORAGE(RomFsFileSystem) g_stratosphere_romfs_fs = {}; + + Result EnsureStratosphereRomfsMounted() { + std::scoped_lock lk(g_mount_stratosphere_romfs_lock); + + if (AMS_UNLIKELY(!g_mounted_stratosphere_romfs)) { + /* Mount the SD card. */ + R_TRY(fs::MountSdCard("#strat-romfs-sd")); + auto sd_guard = SCOPE_GUARD { fs::Unmount("#strat-romfs-sd"); }; + + /* Open sd:/atmosphere/stratosphere.romfs. */ + fs::FileHandle stratosphere_romfs_file; + R_TRY(fs::OpenFile(std::addressof(stratosphere_romfs_file), "#strat-romfs-sd:/atmosphere/stratosphere.romfs", fs::OpenMode_Read)); + + /* Setup the storage. */ + /* NOTE: This owns the file, and so on failure it will be closed appropriately. */ + std::construct_at(GetPointer(g_stratosphere_romfs_storage), stratosphere_romfs_file, true); + auto storage_guard = SCOPE_GUARD { std::destroy_at(GetPointer(g_stratosphere_romfs_storage)); }; + + /* Create the filesystem. */ + std::construct_at(GetPointer(g_stratosphere_romfs_fs)); + auto fs_guard = SCOPE_GUARD { std::destroy_at(GetPointer(g_stratosphere_romfs_fs)); }; + + /* Initialize the filesystem. */ + R_TRY(GetReference(g_stratosphere_romfs_fs).Initialize(GetPointer(g_stratosphere_romfs_storage), nullptr, 0, false)); + + /* We succeeded, and so stratosphere.romfs is mounted. */ + fs_guard.Cancel(); + storage_guard.Cancel(); + sd_guard.Cancel(); + + g_mounted_stratosphere_romfs = true; + } + + return ResultSuccess(); + } + + fsa::IFileSystem &GetStratosphereRomFsFileSystem() { + /* Ensure that stratosphere.romfs is mounted. */ + /* NOTE: Abort is used here to ensure that atmosphere's filesystem is structurally valid. */ + R_ABORT_UNLESS(EnsureStratosphereRomfsMounted()); + + return GetReference(g_stratosphere_romfs_fs); + } + Result OpenCodeFileSystemImpl(CodeVerificationData *out_verification_data, std::unique_ptr *out, const char *path, ncm::ProgramId program_id) { /* Print a path suitable for the remote service. */ fssrv::sf::Path sf_path; @@ -62,10 +111,46 @@ namespace ams::fs { return OpenPackageFileSystemImpl(out, sf_path.str); } - Result OpenSdCardCodeOrCodeFileSystemImpl(CodeVerificationData *out_verification_data, std::unique_ptr *out, const char *path, ncm::ProgramId program_id) { + Result OpenStratosphereCodeFileSystemImpl(std::unique_ptr *out, ncm::ProgramId program_id) { + /* Ensure we don't access the SD card too early. */ + R_UNLESS(cfg::IsSdCardInitialized(), fs::ResultSdCardNotPresent()); + + /* Open the program's package. */ + std::unique_ptr package_file; + { + /* Get the stratosphere.romfs filesystem. */ + auto &romfs_fs = GetStratosphereRomFsFileSystem(); + + /* Print a path to the program's package. */ + fssrv::sf::Path sf_path; + R_TRY(FspPathPrintf(std::addressof(sf_path), "/contents/%016lX/exefs.nsp", program_id.value)); + + /* Open the package within stratosphere.romfs. */ + R_TRY(romfs_fs.OpenFile(std::addressof(package_file), sf_path.str, fs::OpenMode_Read)); + } + + /* Create a file storage for the program's package. */ + auto package_storage = std::make_shared(std::move(package_file)); + R_UNLESS(package_storage != nullptr, fs::ResultAllocationFailureInCodeA()); + + /* Create a partition filesystem. */ + auto package_fs = std::make_unique(); + R_UNLESS(package_fs != nullptr, fs::ResultAllocationFailureInCodeA()); + + /* Initialize the partition filesystem. */ + R_TRY(package_fs->Initialize(package_storage)); + + *out = std::move(package_fs); + return ResultSuccess(); + } + + Result OpenSdCardCodeOrStratosphereCodeOrCodeFileSystemImpl(CodeVerificationData *out_verification_data, std::unique_ptr *out, const char *path, ncm::ProgramId program_id) { /* If we can open an sd card code fs, use it. */ R_SUCCEED_IF(R_SUCCEEDED(OpenSdCardCodeFileSystemImpl(out, program_id))); + /* If we can open a stratosphere code fs, use it. */ + R_SUCCEED_IF(R_SUCCEEDED(OpenStratosphereCodeFileSystemImpl(out, program_id))); + /* Otherwise, fall back to a normal code fs. */ return OpenCodeFileSystemImpl(out_verification_data, out, path, program_id); } @@ -239,7 +324,7 @@ namespace ams::fs { /* Open the code filesystem. */ std::unique_ptr fsa; - R_TRY(OpenSdCardCodeOrCodeFileSystemImpl(out_verification_data, std::addressof(fsa), path, program_id)); + R_TRY(OpenSdCardCodeOrStratosphereCodeOrCodeFileSystemImpl(out_verification_data, std::addressof(fsa), path, program_id)); this->code_fs.emplace(std::move(fsa), program_id, is_specific); this->program_id = program_id; @@ -326,7 +411,7 @@ namespace ams::fs { /* Open the code file system. */ std::unique_ptr fsa; - R_TRY(OpenSdCardCodeOrCodeFileSystemImpl(out, std::addressof(fsa), path, program_id)); + R_TRY(OpenSdCardCodeOrStratosphereCodeOrCodeFileSystemImpl(out, std::addressof(fsa), path, program_id)); /* Create a wrapper fs. */ auto wrap_fsa = std::make_unique(std::move(fsa), program_id, false); diff --git a/libraries/libstratosphere/source/ncm/ncm_submission_package_install_task.cpp b/libraries/libstratosphere/source/ncm/ncm_submission_package_install_task.cpp index 37f702277..2c3a6cd8d 100644 --- a/libraries/libstratosphere/source/ncm/ncm_submission_package_install_task.cpp +++ b/libraries/libstratosphere/source/ncm/ncm_submission_package_install_task.cpp @@ -38,6 +38,9 @@ namespace ams::ncm { auto partition_file_system = std::make_unique(); R_UNLESS(partition_file_system != nullptr, ncm::ResultAllocationFailed()); + /* Initialize the partition file system. */ + R_TRY(partition_file_system->Initialize(std::addressof(this->storage))); + /* Create a mount name and register the file system. */ auto mount_name = impl::CreateUniqueMountName(); R_TRY(fs::fsa::Register(mount_name.str, std::move(partition_file_system))); From c8404e84523dc393c73b2bd019d50c1ce4f7025c Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 21 Mar 2021 04:12:30 -0700 Subject: [PATCH 106/280] boot2: clean up pre-0.19.0 ams contents on upgrade --- Makefile | 44 ++++++----- .../include/stratosphere/cfg/cfg_api.hpp | 1 + .../source/boot2/boot2_api.cpp | 79 ++++++++++++++++++- .../libstratosphere/source/cfg/cfg_flags.cpp | 26 ++++++ .../libstratosphere/source/fs/fs_code.cpp | 2 +- 5 files changed, 127 insertions(+), 25 deletions(-) diff --git a/Makefile b/Makefile index 75cc6e781..2d248175a 100644 --- a/Makefile +++ b/Makefile @@ -65,6 +65,8 @@ dist-no-debug: all mkdir -p atmosphere-$(AMSVER)/atmosphere/fatal_errors mkdir -p atmosphere-$(AMSVER)/atmosphere/config_templates mkdir -p atmosphere-$(AMSVER)/atmosphere/config + mkdir -p atmosphere-$(AMSVER)/atmosphere/flags + touch atmosphere-$(AMSVER)/atmosphere/flags/clean_stratosphere_for_0.19.0.flag cp fusee/fusee-primary/fusee-primary.bin atmosphere-$(AMSVER)/atmosphere/reboot_payload.bin cp fusee/fusee-mtc/fusee-mtc.bin atmosphere-$(AMSVER)/atmosphere/fusee-mtc.bin cp fusee/fusee-secondary/fusee-secondary-experimental.bin atmosphere-$(AMSVER)/atmosphere/fusee-secondary.bin @@ -81,27 +83,27 @@ dist-no-debug: all cp config_templates/exosphere.ini atmosphere-$(AMSVER)/atmosphere/config_templates/exosphere.ini cp -r config_templates/kip_patches atmosphere-$(AMSVER)/atmosphere/kip_patches cp -r config_templates/hbl_html atmosphere-$(AMSVER)/atmosphere/hbl_html - mkdir -p atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/0100000000000008 - mkdir -p atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/0100000000000008 - mkdir -p atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/010000000000000D - mkdir -p atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/010000000000002B - mkdir -p atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/0100000000000032 - mkdir -p atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/0100000000000034 - mkdir -p atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/0100000000000036 - mkdir -p atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/0100000000000037 - mkdir -p atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/010000000000003C - mkdir -p atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/0100000000000042 - cp stratosphere/boot2/boot2.nsp atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/0100000000000008/exefs.nsp - cp stratosphere/dmnt/dmnt.nsp atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/010000000000000D/exefs.nsp - cp stratosphere/erpt/erpt.nsp atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/010000000000002B/exefs.nsp - cp stratosphere/eclct.stub/eclct.stub.nsp atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/0100000000000032/exefs.nsp - cp stratosphere/fatal/fatal.nsp atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/0100000000000034/exefs.nsp - cp stratosphere/creport/creport.nsp atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/0100000000000036/exefs.nsp - cp stratosphere/ro/ro.nsp atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/0100000000000037/exefs.nsp - cp stratosphere/jpegdec/jpegdec.nsp atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/010000000000003C/exefs.nsp - cp stratosphere/pgl/pgl.nsp atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs/contents/0100000000000042/exefs.nsp - @build_romfs atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs atmosphere-$(AMSVER)/atmosphere/stratosphere.romfs - rm -r atmosphere-$(AMSVER)/atmosphere/stratosphere_romfs + mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000008 + mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000008 + mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000000D + mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000002B + mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000032 + mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000034 + mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000036 + mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000037 + mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000003C + mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000042 + cp stratosphere/boot2/boot2.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000008/exefs.nsp + cp stratosphere/dmnt/dmnt.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000000D/exefs.nsp + cp stratosphere/erpt/erpt.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000002B/exefs.nsp + cp stratosphere/eclct.stub/eclct.stub.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000032/exefs.nsp + cp stratosphere/fatal/fatal.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000034/exefs.nsp + cp stratosphere/creport/creport.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000036/exefs.nsp + cp stratosphere/ro/ro.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000037/exefs.nsp + cp stratosphere/jpegdec/jpegdec.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000003C/exefs.nsp + cp stratosphere/pgl/pgl.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000042/exefs.nsp + @build_romfs atmosphere-$(AMSVER)/stratosphere_romfs atmosphere-$(AMSVER)/atmosphere/stratosphere.romfs + rm -r atmosphere-$(AMSVER)/stratosphere_romfs cp troposphere/reboot_to_payload/reboot_to_payload.nro atmosphere-$(AMSVER)/switch/reboot_to_payload.nro cp troposphere/daybreak/daybreak.nro atmosphere-$(AMSVER)/switch/daybreak.nro cd atmosphere-$(AMSVER); zip -r ../atmosphere-$(AMSVER).zip ./*; cd ../; diff --git a/libraries/libstratosphere/include/stratosphere/cfg/cfg_api.hpp b/libraries/libstratosphere/include/stratosphere/cfg/cfg_api.hpp index 5cb515619..3d7b13b27 100644 --- a/libraries/libstratosphere/include/stratosphere/cfg/cfg_api.hpp +++ b/libraries/libstratosphere/include/stratosphere/cfg/cfg_api.hpp @@ -40,6 +40,7 @@ namespace ams::cfg { bool HasFlag(const sm::MitmProcessInfo &process_info, const char *flag); bool HasContentSpecificFlag(ncm::ProgramId program_id, const char *flag); bool HasGlobalFlag(const char *flag); + Result DeleteGlobalFlag(const char *flag); /* HBL Configuration utilities. */ bool HasHblFlag(const char *flag); diff --git a/libraries/libstratosphere/source/boot2/boot2_api.cpp b/libraries/libstratosphere/source/boot2/boot2_api.cpp index 5c5c16ed7..d660ac5e0 100644 --- a/libraries/libstratosphere/source/boot2/boot2_api.cpp +++ b/libraries/libstratosphere/source/boot2/boot2_api.cpp @@ -23,7 +23,7 @@ namespace ams::boot2 { /* psc, bus, pcv is the minimal set of required programs to get SD card. */ /* bus depends on pcie, and pcv depends on settings. */ - constexpr ncm::SystemProgramId PreSdCardLaunchPrograms[] = { + constexpr const ncm::SystemProgramId PreSdCardLaunchPrograms[] = { ncm::SystemProgramId::Psc, /* psc */ ncm::SystemProgramId::Pcie, /* pcie */ ncm::SystemProgramId::Bus, /* bus */ @@ -33,7 +33,7 @@ namespace ams::boot2 { }; constexpr size_t NumPreSdCardLaunchPrograms = util::size(PreSdCardLaunchPrograms); - constexpr ncm::SystemProgramId AdditionalLaunchPrograms[] = { + constexpr const ncm::SystemProgramId AdditionalLaunchPrograms[] = { ncm::SystemProgramId::Am, /* am */ ncm::SystemProgramId::NvServices, /* nvservices */ ncm::SystemProgramId::NvnFlinger, /* nvnflinger */ @@ -78,7 +78,7 @@ namespace ams::boot2 { }; constexpr size_t NumAdditionalLaunchPrograms = util::size(AdditionalLaunchPrograms); - constexpr ncm::SystemProgramId AdditionalMaintenanceLaunchPrograms[] = { + constexpr const ncm::SystemProgramId AdditionalMaintenanceLaunchPrograms[] = { ncm::SystemProgramId::Am, /* am */ ncm::SystemProgramId::NvServices, /* nvservices */ ncm::SystemProgramId::NvnFlinger, /* nvnflinger */ @@ -311,6 +311,57 @@ namespace ams::boot2 { return hos::GetVersion() >= hos::Version_9_0_0; } + /* Prior to 0.19.0, we distributed system modules inside /atmosphere/contents/. */ + /* We need to clean these up, so that we don't break horribly on first upgrade. */ + constexpr const ncm::SystemProgramId StratosphereSystemModulesForPostZeroPointNineteenPointZeroCleanup[] = { + ncm::SystemProgramId::Boot2, + ncm::SystemProgramId::Creport, + ncm::SystemProgramId::Dmnt, + ncm::SystemProgramId::Eclct, + ncm::SystemProgramId::Erpt, + ncm::SystemProgramId::Fatal, + ncm::SystemProgramId::JpegDec, + ncm::SystemProgramId::Pgl, + ncm::SystemProgramId::Ro, + }; + + alignas(0x40) constinit u8 g_fs_cleanup_buffer[4_KB]; + lmem::HeapHandle g_fs_cleanup_heap_handle; + + void *AllocateForFsForCleanup(size_t size) { + return lmem::AllocateFromExpHeap(g_fs_cleanup_heap_handle, size); + } + + void DeallocateForFsForCleanup(void *p, size_t size) { + return lmem::FreeToExpHeap(g_fs_cleanup_heap_handle, p); + } + + void InitializeFsHeapForCleanup() { + g_fs_cleanup_heap_handle = lmem::CreateExpHeap(g_fs_cleanup_buffer, sizeof(g_fs_cleanup_buffer), lmem::CreateOption_None); + fs::SetAllocator(AllocateForFsForCleanup, DeallocateForFsForCleanup); + } + + void CleanupSdCardSystemProgramsForUpgradeToZeroPointNineteenPointZero() { + /* Temporarily mount the SD card. */ + R_ABORT_UNLESS(fs::MountSdCard("sdmc")); + ON_SCOPE_EXIT { fs::Unmount("sdmc"); }; + + for (const auto program_id : StratosphereSystemModulesForPostZeroPointNineteenPointZeroCleanup) { + /* Get the program's contents path. */ + char path[fs::EntryNameLengthMax]; + util::SNPrintf(path, sizeof(path), "sdmc:/atmosphere/contents/%016lx/", program_id.value); + + /* Check if we have old contents. */ + bool has_dir; + R_ABORT_UNLESS(fs::HasDirectory(std::addressof(has_dir), path)); + + /* Cleanup the old contents, if we have them. */ + if (has_dir) { + R_ABORT_UNLESS(fs::DeleteDirectoryRecursively(path)); + } + } + } + } /* Boot2 API. */ @@ -370,6 +421,28 @@ namespace ams::boot2 { } } + /* Perform cleanup to faciliate upgrade to 0.19.0. */ + /* NOTE: This will be removed in a future atmosphere revision. */ + { + /* Setup FS heap for cleanup. */ + InitializeFsHeapForCleanup(); + + /* Temporarily initialize fs. */ + sm::DoWithSession([&] { + R_ABORT_UNLESS(fsInitialize()); + }); + ON_SCOPE_EXIT { fsExit(); }; + + /* Wait for the sd card to be available. */ + cfg::WaitSdCardInitialized(); + + /* Cleanup. */ + if (cfg::HasGlobalFlag("clean_stratosphere_for_0.19.0")) { + CleanupSdCardSystemProgramsForUpgradeToZeroPointNineteenPointZero(); + R_ABORT_UNLESS(cfg::DeleteGlobalFlag("clean_stratosphere_for_0.19.0")); + } + } + /* Launch Atmosphere boot2, using NcmStorageId_None to force SD card boot. */ LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Boot2, ncm::StorageId::None), 0); } diff --git a/libraries/libstratosphere/source/cfg/cfg_flags.cpp b/libraries/libstratosphere/source/cfg/cfg_flags.cpp index be0b1bb03..3ef313f10 100644 --- a/libraries/libstratosphere/source/cfg/cfg_flags.cpp +++ b/libraries/libstratosphere/source/cfg/cfg_flags.cpp @@ -52,6 +52,26 @@ namespace ams::cfg { return has_file; } + Result DeleteFlagFile(const char *flag_path) { + /* We need the SD card to be available to delete anything. */ + AMS_ABORT_UNLESS(IsSdCardInitialized()); + + /* Mount the sd card. */ + char mount_name[fs::MountNameLengthMax + 1]; + GetFlagMountName(mount_name); + R_TRY(fs::MountSdCard(mount_name)); + ON_SCOPE_EXIT { fs::Unmount(mount_name); }; + + /* Get the flag path. */ + char full_path[fs::EntryNameLengthMax + 1]; + util::SNPrintf(full_path, sizeof(full_path), "%s:/%s", mount_name, flag_path[0] == '/' ? flag_path + 1 : flag_path); + + /* Delete the file. */ + R_TRY(fs::DeleteFile(full_path)); + + return ResultSuccess(); + } + } /* Flag utilities. */ @@ -77,4 +97,10 @@ namespace ams::cfg { return HasGlobalFlag(hbl_flag); } + Result DeleteGlobalFlag(const char *flag) { + char global_flag[fs::EntryNameLengthMax + 1]; + util::SNPrintf(global_flag, sizeof(global_flag) - 1, "/atmosphere/flags/%s.flag", flag); + return DeleteFlagFile(global_flag); + } + } diff --git a/libraries/libstratosphere/source/fs/fs_code.cpp b/libraries/libstratosphere/source/fs/fs_code.cpp index 248a6b082..0fe32294d 100644 --- a/libraries/libstratosphere/source/fs/fs_code.cpp +++ b/libraries/libstratosphere/source/fs/fs_code.cpp @@ -123,7 +123,7 @@ namespace ams::fs { /* Print a path to the program's package. */ fssrv::sf::Path sf_path; - R_TRY(FspPathPrintf(std::addressof(sf_path), "/contents/%016lX/exefs.nsp", program_id.value)); + R_TRY(FspPathPrintf(std::addressof(sf_path), "/atmosphere/contents/%016lX/exefs.nsp", program_id.value)); /* Open the package within stratosphere.romfs. */ R_TRY(romfs_fs.OpenFile(std::addressof(package_file), sf_path.str, fs::OpenMode_Read)); From 8d9174b2272c754f6c47c2e2f0ab9fafb4c2b7ae Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 21 Mar 2021 13:16:49 -0700 Subject: [PATCH 107/280] ams: bump version to 0.19.0. Release (probably) not actually imminent, I just don't want to forget. --- libraries/libvapours/include/vapours/ams/ams_api_version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/libvapours/include/vapours/ams/ams_api_version.h b/libraries/libvapours/include/vapours/ams/ams_api_version.h index a2fef79b1..d45da8ef8 100644 --- a/libraries/libvapours/include/vapours/ams/ams_api_version.h +++ b/libraries/libvapours/include/vapours/ams/ams_api_version.h @@ -16,8 +16,8 @@ #pragma once #define ATMOSPHERE_RELEASE_VERSION_MAJOR 0 -#define ATMOSPHERE_RELEASE_VERSION_MINOR 18 -#define ATMOSPHERE_RELEASE_VERSION_MICRO 1 +#define ATMOSPHERE_RELEASE_VERSION_MINOR 19 +#define ATMOSPHERE_RELEASE_VERSION_MICRO 0 #define ATMOSPHERE_RELEASE_VERSION ATMOSPHERE_RELEASE_VERSION_MAJOR, ATMOSPHERE_RELEASE_VERSION_MINOR, ATMOSPHERE_RELEASE_VERSION_MICRO From aff0da942765a8cb98245fb614f3f88dcef6072f Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 21 Mar 2021 18:47:30 -0700 Subject: [PATCH 108/280] ams: remove TYPED_STORAGE() macro in favor of template --- .../include/mesosphere/kern_k_code_memory.hpp | 2 +- .../mesosphere/kern_k_transfer_memory.hpp | 2 +- .../ddsf/ddsf_device_code_entry.hpp | 2 +- .../stratosphere/ncm/ncm_install_progress.hpp | 2 +- .../os/impl/os_internal_condition_variable.hpp | 2 +- .../os/impl/os_internal_critical_section.hpp | 2 +- .../stratosphere/os/os_thread_types.hpp | 2 +- .../stratosphere/os/os_timer_event_types.hpp | 2 +- .../stratosphere/powctl/powctl_session_api.hpp | 2 +- .../sf/cmif/sf_cmif_domain_manager.hpp | 4 ++-- .../sf/hipc/sf_hipc_server_manager.hpp | 4 ++-- .../sf/impl/sf_impl_command_serialization.hpp | 2 +- .../libstratosphere/source/fs/fs_code.cpp | 4 ++-- .../fssystem_file_system_proxy_api.cpp | 12 ++++++------ .../htc/server/htc_htcmisc_hipc_server.cpp | 2 +- .../source/htcfs/htcfs_client.cpp | 2 +- .../htclow/mux/htclow_mux_channel_impl_map.hpp | 2 +- .../source/os/impl/os_resource_manager.cpp | 2 +- .../source/os/impl/os_resource_manager.hpp | 2 +- .../source/os/impl/os_waitable_holder_impl.hpp | 18 +++++++++--------- .../pgl/srv/pgl_srv_shell_event_observer.hpp | 4 ++-- .../source/sf/hipc/sf_hipc_mitm_query_api.cpp | 2 +- .../include/vapours/util/util_bounded_map.hpp | 2 +- .../vapours/util/util_intrusive_list.hpp | 4 ++-- .../util/util_intrusive_red_black_tree.hpp | 4 ++-- .../vapours/util/util_parent_of_member.hpp | 4 ++-- .../vapours/util/util_typed_storage.hpp | 12 +++++------- ...dmmc_sdmmc_controller.board.nintendo_nx.hpp | 2 +- .../dmnt/source/cheat/impl/dmnt_cheat_api.cpp | 2 +- .../impl/dmnt_cheat_debug_events_manager.cpp | 2 +- .../pm/source/impl/pm_process_manager.cpp | 2 +- 31 files changed, 55 insertions(+), 57 deletions(-) diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_code_memory.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_code_memory.hpp index 6f94c8f2f..0875216ac 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_code_memory.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_code_memory.hpp @@ -23,7 +23,7 @@ namespace ams::kern { class KCodeMemory final : public KAutoObjectWithSlabHeapAndContainer { MESOSPHERE_AUTOOBJECT_TRAITS(KCodeMemory, KAutoObject); private: - TYPED_STORAGE(KPageGroup) m_page_group; + util::TypedStorage m_page_group; KProcess *m_owner; KProcessAddress m_address; KLightLock m_lock; diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_transfer_memory.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_transfer_memory.hpp index 831b35824..0abb0dbce 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_transfer_memory.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_transfer_memory.hpp @@ -23,7 +23,7 @@ namespace ams::kern { class KTransferMemory final : public KAutoObjectWithSlabHeapAndContainer { MESOSPHERE_AUTOOBJECT_TRAITS(KTransferMemory, KAutoObject); private: - TYPED_STORAGE(KPageGroup) m_page_group; + util::TypedStorage m_page_group; KProcess *m_owner; KProcessAddress m_address; KLightLock m_lock; diff --git a/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_device_code_entry.hpp b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_device_code_entry.hpp index 376601bbb..2459e394b 100644 --- a/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_device_code_entry.hpp +++ b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_device_code_entry.hpp @@ -49,7 +49,7 @@ namespace ams::ddsf { NON_MOVEABLE(DeviceCodeEntryHolder); private: util::IntrusiveListNode list_node; - TYPED_STORAGE(DeviceCodeEntry) entry_storage; + util::TypedStorage entry_storage; bool is_constructed; public: using ListTraits = util::IntrusiveListMemberTraitsDeferredAssert<&DeviceCodeEntryHolder::list_node>; diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_progress.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_progress.hpp index ba70fe1fc..e91309d24 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_progress.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_progress.hpp @@ -29,7 +29,7 @@ namespace ams::ncm { struct InstallProgress { InstallProgressState state; u8 pad[3]; - TYPED_STORAGE(Result) last_result; + util::TypedStorage last_result; s64 installed_size; s64 total_size; diff --git a/libraries/libstratosphere/include/stratosphere/os/impl/os_internal_condition_variable.hpp b/libraries/libstratosphere/include/stratosphere/os/impl/os_internal_condition_variable.hpp index 33a34a706..2a48e06bf 100644 --- a/libraries/libstratosphere/include/stratosphere/os/impl/os_internal_condition_variable.hpp +++ b/libraries/libstratosphere/include/stratosphere/os/impl/os_internal_condition_variable.hpp @@ -53,6 +53,6 @@ namespace ams::os::impl { } }; - using InternalConditionVariableStorage = TYPED_STORAGE(InternalConditionVariable); + using InternalConditionVariableStorage = util::TypedStorage; } diff --git a/libraries/libstratosphere/include/stratosphere/os/impl/os_internal_critical_section.hpp b/libraries/libstratosphere/include/stratosphere/os/impl/os_internal_critical_section.hpp index d1b0871f6..ed49fbac5 100644 --- a/libraries/libstratosphere/include/stratosphere/os/impl/os_internal_critical_section.hpp +++ b/libraries/libstratosphere/include/stratosphere/os/impl/os_internal_critical_section.hpp @@ -57,6 +57,6 @@ namespace ams::os::impl { } }; - using InternalCriticalSectionStorage = TYPED_STORAGE(InternalCriticalSection); + using InternalCriticalSectionStorage = util::TypedStorage; } diff --git a/libraries/libstratosphere/include/stratosphere/os/os_thread_types.hpp b/libraries/libstratosphere/include/stratosphere/os/os_thread_types.hpp index 08f1f6329..b0ddb9648 100644 --- a/libraries/libstratosphere/include/stratosphere/os/os_thread_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/os/os_thread_types.hpp @@ -44,7 +44,7 @@ namespace ams::os { State_Terminated = 4, }; - TYPED_STORAGE(util::IntrusiveListNode) all_threads_node; + util::TypedStorage all_threads_node; util::TypedStorage waitlist; uintptr_t reserved[4]; u8 state; diff --git a/libraries/libstratosphere/include/stratosphere/os/os_timer_event_types.hpp b/libraries/libstratosphere/include/stratosphere/os/os_timer_event_types.hpp index 44e465429..975445d78 100644 --- a/libraries/libstratosphere/include/stratosphere/os/os_timer_event_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/os/os_timer_event_types.hpp @@ -29,7 +29,7 @@ namespace ams::os { } struct TimerEventType { - using TimeSpanStorage = TYPED_STORAGE(TimeSpan); + using TimeSpanStorage = util::TypedStorage; enum State { State_NotInitialized = 0, diff --git a/libraries/libstratosphere/include/stratosphere/powctl/powctl_session_api.hpp b/libraries/libstratosphere/include/stratosphere/powctl/powctl_session_api.hpp index 7c0e3fbd6..495ad9f05 100644 --- a/libraries/libstratosphere/include/stratosphere/powctl/powctl_session_api.hpp +++ b/libraries/libstratosphere/include/stratosphere/powctl/powctl_session_api.hpp @@ -35,7 +35,7 @@ namespace ams::powctl { struct Session { bool has_session; - TYPED_STORAGE(impl::SessionImpl) impl_storage; + util::TypedStorage impl_storage; Session() : has_session(false) { /* ... */ } }; diff --git a/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_manager.hpp b/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_manager.hpp index 99b8e201c..6f3aea555 100644 --- a/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_manager.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_manager.hpp @@ -77,8 +77,8 @@ namespace ams::sf::cmif { virtual ServiceObjectHolder GetObject(DomainObjectId id) override final; }; public: - using DomainEntryStorage = TYPED_STORAGE(Entry); - using DomainStorage = TYPED_STORAGE(Domain); + using DomainEntryStorage = util::TypedStorage; + using DomainStorage = util::TypedStorage; private: class EntryManager { private: diff --git a/libraries/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_manager.hpp b/libraries/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_manager.hpp index 19986c90a..d611f5d1b 100644 --- a/libraries/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_manager.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_manager.hpp @@ -260,9 +260,9 @@ namespace ams::sf::hipc { private: /* Resource storage. */ os::Mutex resource_mutex; - TYPED_STORAGE(Server) server_storages[MaxServers]; + util::TypedStorage server_storages[MaxServers]; bool server_allocated[MaxServers]; - TYPED_STORAGE(ServerSession) session_storages[MaxSessions]; + util::TypedStorage session_storages[MaxSessions]; bool session_allocated[MaxSessions]; u8 pointer_buffer_storage[0x10 + (MaxSessions * ManagerOptions::PointerBufferSize)]; u8 saved_message_storage[0x10 + (MaxSessions * hipc::TlsMessageBufferSize)]; diff --git a/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp b/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp index 9a0a83e41..f293d15ce 100644 --- a/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp @@ -684,7 +684,7 @@ namespace ams::sf::impl { private: std::array in_object_holders; std::array out_object_holders; - std::array), NumOutObjects> out_shared_pointers; + std::array>, NumOutObjects> out_shared_pointers; std::array out_object_ids; public: constexpr InOutObjectHolder() : in_object_holders(), out_object_holders() { diff --git a/libraries/libstratosphere/source/fs/fs_code.cpp b/libraries/libstratosphere/source/fs/fs_code.cpp index 0fe32294d..e58acb3e3 100644 --- a/libraries/libstratosphere/source/fs/fs_code.cpp +++ b/libraries/libstratosphere/source/fs/fs_code.cpp @@ -23,8 +23,8 @@ namespace ams::fs { constinit os::SdkMutex g_mount_stratosphere_romfs_lock; constinit bool g_mounted_stratosphere_romfs = false; - constinit TYPED_STORAGE(FileHandleStorage) g_stratosphere_romfs_storage = {}; - constinit TYPED_STORAGE(RomFsFileSystem) g_stratosphere_romfs_fs = {}; + constinit util::TypedStorage g_stratosphere_romfs_storage = {}; + constinit util::TypedStorage g_stratosphere_romfs_fs = {}; Result EnsureStratosphereRomfsMounted() { std::scoped_lock lk(g_mount_stratosphere_romfs_lock); diff --git a/libraries/libstratosphere/source/fssystem/fssystem_file_system_proxy_api.cpp b/libraries/libstratosphere/source/fssystem/fssystem_file_system_proxy_api.cpp index 8bc1dd43d..fdb76c479 100644 --- a/libraries/libstratosphere/source/fssystem/fssystem_file_system_proxy_api.cpp +++ b/libraries/libstratosphere/source/fssystem/fssystem_file_system_proxy_api.cpp @@ -68,19 +68,19 @@ namespace ams::fssystem { alignas(os::MemoryPageSize) u8 g_device_buffer[DeviceBufferSize]; alignas(os::MemoryPageSize) u8 g_buffer_pool[BufferPoolSize]; - TYPED_STORAGE(mem::StandardAllocator) g_buffer_allocator; - TYPED_STORAGE(fssrv::MemoryResourceFromStandardAllocator) g_allocator; + util::TypedStorage g_buffer_allocator; + util::TypedStorage g_allocator; /* TODO: Nintendo uses os::SetMemoryHeapSize (svc::SetHeapSize) and os::AllocateMemoryBlock for the BufferManager heap. */ /* It's unclear how we should handle this in ams.mitm (especially hoping to reuse some logic for fs reimpl). */ /* Should we be doing the same(?) */ - TYPED_STORAGE(fssystem::FileSystemBufferManager) g_buffer_manager; + util::TypedStorage g_buffer_manager; alignas(os::MemoryPageSize) u8 g_buffer_manager_heap[BufferManagerHeapSize]; /* FileSystem creators. */ - TYPED_STORAGE(fssrv::fscreator::RomFileSystemCreator) g_rom_fs_creator; - TYPED_STORAGE(fssrv::fscreator::PartitionFileSystemCreator) g_partition_fs_creator; - TYPED_STORAGE(fssrv::fscreator::StorageOnNcaCreator) g_storage_on_nca_creator; + util::TypedStorage g_rom_fs_creator; + util::TypedStorage g_partition_fs_creator; + util::TypedStorage g_storage_on_nca_creator; fssrv::fscreator::FileSystemCreatorInterfaces g_fs_creator_interfaces = {}; diff --git a/libraries/libstratosphere/source/htc/server/htc_htcmisc_hipc_server.cpp b/libraries/libstratosphere/source/htc/server/htc_htcmisc_hipc_server.cpp index f9ebb2b86..6ee4fda59 100644 --- a/libraries/libstratosphere/source/htc/server/htc_htcmisc_hipc_server.cpp +++ b/libraries/libstratosphere/source/htc/server/htc_htcmisc_hipc_server.cpp @@ -28,7 +28,7 @@ namespace ams::htc::server { using ServerOptions = sf::hipc::DefaultServerManagerOptions; using ServerManager = sf::hipc::ServerManager; - constinit TYPED_STORAGE(ServerManager) g_server_manager_storage; + constinit util::TypedStorage g_server_manager_storage; constinit ServerManager *g_server_manager = nullptr; constinit HtcmiscImpl *g_misc_impl = nullptr; diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client.cpp b/libraries/libstratosphere/source/htcfs/htcfs_client.cpp index 27972f265..37a4dc93f 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client.cpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client.cpp @@ -20,7 +20,7 @@ namespace ams::htcfs { namespace { - constinit TYPED_STORAGE(Client) g_client_storage; + constinit util::TypedStorage g_client_storage; constinit bool g_initialized; } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp index dee89de16..a5ac0f30a 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp @@ -35,7 +35,7 @@ namespace ams::htclow::mux { os::Event *m_event; u8 m_map_buffer[MapRequiredMemorySize]; MapType m_map; - TYPED_STORAGE(ChannelImpl) m_channel_storage[MaxChannelCount]; + util::TypedStorage m_channel_storage[MaxChannelCount]; bool m_storage_valid[MaxChannelCount]; public: ChannelImplMap(PacketFactory *pf, ctrl::HtcctrlStateMachine *sm, TaskManager *tm, os::Event *ev); diff --git a/libraries/libstratosphere/source/os/impl/os_resource_manager.cpp b/libraries/libstratosphere/source/os/impl/os_resource_manager.cpp index 6ff528a31..4252a7cb5 100644 --- a/libraries/libstratosphere/source/os/impl/os_resource_manager.cpp +++ b/libraries/libstratosphere/source/os/impl/os_resource_manager.cpp @@ -18,6 +18,6 @@ namespace ams::os::impl { - constinit TYPED_STORAGE(OsResourceManager) ResourceManagerHolder::s_resource_manager_storage = {}; + constinit util::TypedStorage ResourceManagerHolder::s_resource_manager_storage = {}; } diff --git a/libraries/libstratosphere/source/os/impl/os_resource_manager.hpp b/libraries/libstratosphere/source/os/impl/os_resource_manager.hpp index b79babfbf..0845ff3f8 100644 --- a/libraries/libstratosphere/source/os/impl/os_resource_manager.hpp +++ b/libraries/libstratosphere/source/os/impl/os_resource_manager.hpp @@ -42,7 +42,7 @@ namespace ams::os::impl { class ResourceManagerHolder { private: - static TYPED_STORAGE(OsResourceManager) s_resource_manager_storage; + static util::TypedStorage s_resource_manager_storage; private: constexpr ResourceManagerHolder() { /* ... */ } public: diff --git a/libraries/libstratosphere/source/os/impl/os_waitable_holder_impl.hpp b/libraries/libstratosphere/source/os/impl/os_waitable_holder_impl.hpp index 90a3e4631..c47983027 100644 --- a/libraries/libstratosphere/source/os/impl/os_waitable_holder_impl.hpp +++ b/libraries/libstratosphere/source/os/impl/os_waitable_holder_impl.hpp @@ -27,15 +27,15 @@ namespace ams::os::impl { struct WaitableHolderImpl { union { - TYPED_STORAGE(WaitableHolderOfHandle) holder_of_handle_storage; - TYPED_STORAGE(WaitableHolderOfEvent) holder_of_event_storage; - TYPED_STORAGE(WaitableHolderOfInterProcessEvent) holder_of_inter_process_event_storage; - TYPED_STORAGE(WaitableHolderOfInterruptEvent) holder_of_interrupt_event_storage; - TYPED_STORAGE(WaitableHolderOfTimerEvent) holder_of_timer_event_storage; - TYPED_STORAGE(WaitableHolderOfThread) holder_of_thread_storage; - TYPED_STORAGE(WaitableHolderOfSemaphore) holder_of_semaphore_storage; - TYPED_STORAGE(WaitableHolderOfMessageQueueForNotFull) holder_of_mq_for_not_full_storage; - TYPED_STORAGE(WaitableHolderOfMessageQueueForNotEmpty) holder_of_mq_for_not_empty_storage; + util::TypedStorage holder_of_handle_storage; + util::TypedStorage holder_of_event_storage; + util::TypedStorage holder_of_inter_process_event_storage; + util::TypedStorage holder_of_interrupt_event_storage; + util::TypedStorage holder_of_timer_event_storage; + util::TypedStorage holder_of_thread_storage; + util::TypedStorage holder_of_semaphore_storage; + util::TypedStorage holder_of_mq_for_not_full_storage; + util::TypedStorage holder_of_mq_for_not_empty_storage; }; }; diff --git a/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell_event_observer.hpp b/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell_event_observer.hpp index 7b730589c..71e7e653f 100644 --- a/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell_event_observer.hpp +++ b/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell_event_observer.hpp @@ -41,10 +41,10 @@ namespace ams::pgl::srv { os::MessageQueue message_queue; uintptr_t queue_buffer[QueueCapacity]; os::SystemEvent event; - TYPED_STORAGE(lmem::HeapCommonHead) heap_head; + util::TypedStorage heap_head; lmem::HeapHandle heap_handle; pm::ProcessEventInfo event_info_data[QueueCapacity]; - TYPED_STORAGE(ShellEventObserverHolder) holder; + util::TypedStorage holder; public: ShellEventObserver(); ~ShellEventObserver(); diff --git a/libraries/libstratosphere/source/sf/hipc/sf_hipc_mitm_query_api.cpp b/libraries/libstratosphere/source/sf/hipc/sf_hipc_mitm_query_api.cpp index e5d3f8a60..c641c6b5c 100644 --- a/libraries/libstratosphere/source/sf/hipc/sf_hipc_mitm_query_api.cpp +++ b/libraries/libstratosphere/source/sf/hipc/sf_hipc_mitm_query_api.cpp @@ -50,7 +50,7 @@ namespace ams::sf::hipc::impl { constinit os::ThreadType g_query_server_process_thread; constexpr size_t MaxServers = 0; - TYPED_STORAGE(sf::hipc::ServerManager) g_query_server_storage; + util::TypedStorage> g_query_server_storage; } diff --git a/libraries/libvapours/include/vapours/util/util_bounded_map.hpp b/libraries/libvapours/include/vapours/util/util_bounded_map.hpp index 41f7593c0..5d0ab6ee5 100644 --- a/libraries/libvapours/include/vapours/util/util_bounded_map.hpp +++ b/libraries/libvapours/include/vapours/util/util_bounded_map.hpp @@ -25,7 +25,7 @@ namespace ams::util { class BoundedMap { private: std::array, N> keys; - std::array values; + std::array, N> values; private: ALWAYS_INLINE void FreeEntry(size_t i) { this->keys[i].reset(); diff --git a/libraries/libvapours/include/vapours/util/util_intrusive_list.hpp b/libraries/libvapours/include/vapours/util/util_intrusive_list.hpp index 737c9586b..eefe1b7ff 100644 --- a/libraries/libvapours/include/vapours/util/util_intrusive_list.hpp +++ b/libraries/libvapours/include/vapours/util/util_intrusive_list.hpp @@ -569,7 +569,7 @@ namespace ams::util { return util::GetParentReference(&node); } private: - static constexpr TYPED_STORAGE(Derived) DerivedStorage = {}; + static constexpr TypedStorage DerivedStorage = {}; static_assert(std::addressof(GetParent(GetNode(GetReference(DerivedStorage)))) == GetPointer(DerivedStorage)); }; @@ -582,7 +582,7 @@ namespace ams::util { using ListType = IntrusiveList; static constexpr bool IsValid() { - TYPED_STORAGE(Derived) DerivedStorage = {}; + TypedStorage DerivedStorage = {}; return std::addressof(GetParent(GetNode(GetReference(DerivedStorage)))) == GetPointer(DerivedStorage); } private: diff --git a/libraries/libvapours/include/vapours/util/util_intrusive_red_black_tree.hpp b/libraries/libvapours/include/vapours/util/util_intrusive_red_black_tree.hpp index 192c8bd79..f43b2fc0b 100644 --- a/libraries/libvapours/include/vapours/util/util_intrusive_red_black_tree.hpp +++ b/libraries/libvapours/include/vapours/util/util_intrusive_red_black_tree.hpp @@ -498,7 +498,7 @@ namespace ams::util { return util::GetParentPointer(node); } private: - static constexpr TYPED_STORAGE(Derived) DerivedStorage = {}; + static constexpr TypedStorage DerivedStorage = {}; static_assert(GetParent(GetNode(GetPointer(DerivedStorage))) == GetPointer(DerivedStorage)); }; @@ -513,7 +513,7 @@ namespace ams::util { using TreeTypeImpl = impl::IntrusiveRedBlackTreeImpl; static constexpr bool IsValid() { - TYPED_STORAGE(Derived) DerivedStorage = {}; + TypedStorage DerivedStorage = {}; return GetParent(GetNode(GetPointer(DerivedStorage))) == GetPointer(DerivedStorage); } private: diff --git a/libraries/libvapours/include/vapours/util/util_parent_of_member.hpp b/libraries/libvapours/include/vapours/util/util_parent_of_member.hpp index a1600680d..19b364785 100644 --- a/libraries/libvapours/include/vapours/util/util_parent_of_member.hpp +++ b/libraries/libvapours/include/vapours/util/util_parent_of_member.hpp @@ -63,7 +63,7 @@ namespace ams::util { union Union { char c; UnionHolder first_union; - TYPED_STORAGE(ParentType) parent; + TypedStorage parent; /* This coerces the active member to be c. */ constexpr Union() : c() { /* ... */ } @@ -110,7 +110,7 @@ namespace ams::util { template struct OffsetOfCalculator { static constexpr std::ptrdiff_t OffsetOf(MemberType ParentType::*member) { - constexpr TYPED_STORAGE(ParentType) Holder = {}; + constexpr TypedStorage Holder = {}; const auto *parent = GetPointer(Holder); const auto *target = std::addressof(parent->*member); return static_cast(static_cast(target)) - static_cast(static_cast(parent)); diff --git a/libraries/libvapours/include/vapours/util/util_typed_storage.hpp b/libraries/libvapours/include/vapours/util/util_typed_storage.hpp index de1db3f80..cfbb9505d 100644 --- a/libraries/libvapours/include/vapours/util/util_typed_storage.hpp +++ b/libraries/libvapours/include/vapours/util/util_typed_storage.hpp @@ -20,30 +20,28 @@ namespace ams::util { - template + template struct TypedStorage { typename std::aligned_storage::type _storage; }; - #define TYPED_STORAGE(...) ::ams::util::TypedStorage<__VA_ARGS__, sizeof(__VA_ARGS__), alignof(__VA_ARGS__)> - template - static constexpr ALWAYS_INLINE T *GetPointer(TYPED_STORAGE(T) &ts) { + static constexpr ALWAYS_INLINE T *GetPointer(TypedStorage &ts) { return static_cast(static_cast(std::addressof(ts._storage))); } template - static constexpr ALWAYS_INLINE const T *GetPointer(const TYPED_STORAGE(T) &ts) { + static constexpr ALWAYS_INLINE const T *GetPointer(const TypedStorage &ts) { return static_cast(static_cast(std::addressof(ts._storage))); } template - static constexpr ALWAYS_INLINE T &GetReference(TYPED_STORAGE(T) &ts) { + static constexpr ALWAYS_INLINE T &GetReference(TypedStorage &ts) { return *GetPointer(ts); } template - static constexpr ALWAYS_INLINE const T &GetReference(const TYPED_STORAGE(T) &ts) { + static constexpr ALWAYS_INLINE const T &GetReference(const TypedStorage &ts) { return *GetPointer(ts); } diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_sdmmc_controller.board.nintendo_nx.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_sdmmc_controller.board.nintendo_nx.hpp index d007d9dd0..d731842fc 100644 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_sdmmc_controller.board.nintendo_nx.hpp +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_sdmmc_controller.board.nintendo_nx.hpp @@ -237,7 +237,7 @@ namespace ams::sdmmc::impl { #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) bool is_pcv_control; #endif - TYPED_STORAGE(PowerController) power_controller_storage; + util::TypedStorage power_controller_storage; PowerController *power_controller; private: Result PowerOnForRegisterControl(BusPower bus_power); diff --git a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp index 79f3a8b84..5fda294ae 100644 --- a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp +++ b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp @@ -1148,7 +1148,7 @@ namespace ams::dmnt::cheat::impl { /* Manager global. */ - TYPED_STORAGE(CheatProcessManager) g_cheat_process_manager; + util::TypedStorage g_cheat_process_manager; } diff --git a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_debug_events_manager.cpp b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_debug_events_manager.cpp index 5da5d609d..07df7f143 100644 --- a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_debug_events_manager.cpp +++ b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_debug_events_manager.cpp @@ -143,7 +143,7 @@ namespace ams::dmnt::cheat::impl { }; /* Manager global. */ - TYPED_STORAGE(DebugEventsManager) g_events_manager; + util::TypedStorage g_events_manager; } diff --git a/stratosphere/pm/source/impl/pm_process_manager.cpp b/stratosphere/pm/source/impl/pm_process_manager.cpp index 200dfd4bf..5cc2c09c4 100644 --- a/stratosphere/pm/source/impl/pm_process_manager.cpp +++ b/stratosphere/pm/source/impl/pm_process_manager.cpp @@ -78,7 +78,7 @@ namespace ams::pm::impl { NON_MOVEABLE(ProcessInfoAllocator); static_assert(MaxProcessInfos >= 0x40, "MaxProcessInfos is too small."); private: - TYPED_STORAGE(ProcessInfo) process_info_storages[MaxProcessInfos]; + util::TypedStorage process_info_storages[MaxProcessInfos]; bool process_info_allocated[MaxProcessInfos]; os::Mutex lock; private: From d84dcb653d0491a7b6e09ffdfb0e41b3f6f514c3 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 21 Mar 2021 20:30:40 -0700 Subject: [PATCH 109/280] ams: prefer construct_at/destroy_at over placement new/explicit destructor --- .../mesosphere/kern_k_dynamic_slab_heap.hpp | 2 +- .../include/mesosphere/kern_k_slab_heap.hpp | 2 +- .../source/kern_k_code_memory.cpp | 7 ++-- .../source/kern_k_memory_layout.cpp | 2 +- .../source/kern_k_transfer_memory.cpp | 7 ++-- .../ddsf/ddsf_device_code_entry.hpp | 4 +- .../sf/cmif/sf_cmif_domain_manager.hpp | 3 +- .../sf/impl/sf_impl_command_serialization.hpp | 4 +- .../sf/sf_object_impl_factory.hpp | 2 +- .../ddsf/ddsf_device_code_entry_manager.cpp | 4 +- .../fssystem_file_system_buffer_manager.cpp | 4 +- .../fssystem_file_system_proxy_api.cpp | 12 +++--- .../board/nintendo/nx/gpio_driver_api.cpp | 10 ++--- .../source/gpio/driver/gpio_pad_api.cpp | 6 +-- .../nintendo/nx/impl/i2c_i_allocator.hpp | 4 +- .../source/i2c/driver/i2c_driver_bus_api.cpp | 6 +-- .../lmem/impl/lmem_impl_common_heap.cpp | 4 +- .../source/lmem/impl/lmem_impl_exp_heap.cpp | 13 +++--- .../impl/heap/mem_impl_heap_central_heap.cpp | 12 +++--- .../source/mem/mem_standard_allocator.cpp | 2 +- .../source/os/impl/os_inter_process_event.cpp | 4 +- .../source/os/impl/os_resource_manager.hpp | 2 +- .../source/os/impl/os_thread_manager.cpp | 10 ++--- .../source/os/os_condition_variable.cpp | 4 +- .../libstratosphere/source/os/os_event.cpp | 14 +++---- .../source/os/os_interrupt_event.cpp | 6 +-- .../source/os/os_message_queue.cpp | 20 ++++----- .../libstratosphere/source/os/os_mutex.cpp | 4 +- .../libstratosphere/source/os/os_rw_lock.cpp | 12 +++--- .../source/os/os_semaphore.cpp | 12 +++--- .../source/os/os_system_event.cpp | 4 +- .../source/os/os_timer_event.cpp | 14 +++---- .../source/os/os_transfer_memory_api.cpp | 4 +- .../libstratosphere/source/os/os_waitable.cpp | 8 ++-- .../pgl/srv/pgl_srv_shell_event_observer.cpp | 5 +-- .../source/powctl/powctl_session_api.cpp | 6 +-- .../nx/impl/pwm_impl_pwm_driver_api.cpp | 4 +- .../pwm/driver/pwm_driver_channel_api.cpp | 6 +-- .../source/sf/cmif/sf_cmif_domain_manager.cpp | 7 ++-- .../source/sf/hipc/sf_hipc_mitm_query_api.cpp | 4 +- .../hipc/sf_hipc_server_session_manager.cpp | 36 ++++++++-------- .../include/vapours/util/util_bounded_map.hpp | 10 ++--- .../include/vapours/util/util_scope_guard.hpp | 2 + .../vapours/util/util_typed_storage.hpp | 42 +++++++++++++++++++ ...mmc_sdmmc_controller.board.nintendo_nx.cpp | 4 +- .../creport/source/creport_crash_report.cpp | 8 ++-- .../dmnt/source/cheat/impl/dmnt_cheat_api.cpp | 6 +-- .../impl/dmnt_cheat_debug_events_manager.cpp | 2 +- .../pm/source/impl/pm_process_manager.cpp | 18 ++++---- 49 files changed, 217 insertions(+), 171 deletions(-) diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_dynamic_slab_heap.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_dynamic_slab_heap.hpp index c7e0ad45c..3dc14f704 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_dynamic_slab_heap.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_dynamic_slab_heap.hpp @@ -113,7 +113,7 @@ namespace ams::kern { if (AMS_LIKELY(allocated != nullptr)) { /* Construct the object. */ - new (allocated) T(); + std::construct_at(allocated); /* Update our tracking. */ size_t used = m_used.fetch_add(1) + 1; diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_slab_heap.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_slab_heap.hpp index d3eb06678..6afa5c94d 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_slab_heap.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_slab_heap.hpp @@ -216,7 +216,7 @@ namespace ams::kern { T *Allocate() { T *obj = reinterpret_cast(this->AllocateImpl()); if (AMS_LIKELY(obj != nullptr)) { - new (obj) T(); + std::construct_at(obj); } return obj; } diff --git a/libraries/libmesosphere/source/kern_k_code_memory.cpp b/libraries/libmesosphere/source/kern_k_code_memory.cpp index e1b027783..8fc88e13a 100644 --- a/libraries/libmesosphere/source/kern_k_code_memory.cpp +++ b/libraries/libmesosphere/source/kern_k_code_memory.cpp @@ -23,12 +23,11 @@ namespace ams::kern { /* Set members. */ m_owner = GetCurrentProcessPointer(); - /* Initialize the page group. */ + /* Get the owner page table. */ auto &page_table = m_owner->GetPageTable(); - new (GetPointer(m_page_group)) KPageGroup(page_table.GetBlockInfoManager()); - /* Ensure that our page group's state is valid on exit. */ - auto pg_guard = SCOPE_GUARD { GetReference(m_page_group).~KPageGroup(); }; + /* Construct the page group, guarding to make sure our state is valid on exit. */ + auto pg_guard = util::ConstructAtGuarded(m_page_group, page_table.GetBlockInfoManager()); /* Lock the memory. */ R_TRY(page_table.LockForCodeMemory(GetPointer(m_page_group), addr, size)); diff --git a/libraries/libmesosphere/source/kern_k_memory_layout.cpp b/libraries/libmesosphere/source/kern_k_memory_layout.cpp index f2ccd9dc2..cdc56fb63 100644 --- a/libraries/libmesosphere/source/kern_k_memory_layout.cpp +++ b/libraries/libmesosphere/source/kern_k_memory_layout.cpp @@ -37,7 +37,7 @@ namespace ams::kern { /* Create the new region. */ KMemoryRegion *region = std::addressof(this->region_heap[this->num_regions++]); - new (region) KMemoryRegion(std::forward(args)...); + std::construct_at(region, std::forward(args)...); return region; } diff --git a/libraries/libmesosphere/source/kern_k_transfer_memory.cpp b/libraries/libmesosphere/source/kern_k_transfer_memory.cpp index 283383a87..0abf544f5 100644 --- a/libraries/libmesosphere/source/kern_k_transfer_memory.cpp +++ b/libraries/libmesosphere/source/kern_k_transfer_memory.cpp @@ -23,12 +23,11 @@ namespace ams::kern { /* Set members. */ m_owner = GetCurrentProcessPointer(); - /* Initialize the page group. */ + /* Get the owner page table. */ auto &page_table = m_owner->GetPageTable(); - new (GetPointer(m_page_group)) KPageGroup(page_table.GetBlockInfoManager()); - /* Ensure that our page group's state is valid on exit. */ - auto pg_guard = SCOPE_GUARD { GetReference(m_page_group).~KPageGroup(); }; + /* Construct the page group, guarding to make sure our state is valid on exit. */ + auto pg_guard = util::ConstructAtGuarded(m_page_group, page_table.GetBlockInfoManager()); /* Lock the memory. */ R_TRY(page_table.LockForTransferMemory(GetPointer(m_page_group), addr, size, ConvertToKMemoryPermission(own_perm))); diff --git a/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_device_code_entry.hpp b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_device_code_entry.hpp index 2459e394b..e1d74fbc3 100644 --- a/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_device_code_entry.hpp +++ b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_device_code_entry.hpp @@ -80,7 +80,7 @@ namespace ams::ddsf { DeviceCodeEntry &Construct(DeviceCode dc, IDevice *dev) { AMS_ASSERT(!this->IsConstructed()); - DeviceCodeEntry *entry = new (GetPointer(this->entry_storage)) DeviceCodeEntry(dc, dev); + DeviceCodeEntry *entry = util::ConstructAt(this->entry_storage, dc, dev); this->is_constructed = true; return *entry; } @@ -91,7 +91,7 @@ namespace ams::ddsf { void Destroy() { AMS_ASSERT(this->IsConstructed()); - GetReference(this->entry_storage).~DeviceCodeEntry(); + util::DestroyAt(this->entry_storage); this->is_constructed = false; } diff --git a/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_manager.hpp b/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_manager.hpp index 6f3aea555..e59d54caf 100644 --- a/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_manager.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_manager.hpp @@ -127,7 +127,8 @@ namespace ams::sf::cmif { if (storage == nullptr) { return nullptr; } - return new (storage) Domain(this); + + return std::construct_at(static_cast(storage), this); } public: static void DestroyDomainServiceObject(DomainServiceObject *obj) { diff --git a/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp b/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp index f293d15ce..19f760b75 100644 --- a/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp @@ -732,12 +732,12 @@ namespace ams::sf::impl { template SharedPointer *GetOutObjectSharedPointer() { static_assert(sizeof(SharedPointer) == sizeof(SharedPointer)); - return static_cast *>(static_cast(&out_shared_pointers[Index])); + return static_cast *>(static_cast(GetPointer(out_shared_pointers[Index]))); } template Out> GetOutObject() { - auto sp = new (GetOutObjectSharedPointer()) SharedPointer; + auto sp = std::construct_at(GetOutObjectSharedPointer()); return Out>(sp, &this->out_object_ids[Index]); } diff --git a/libraries/libstratosphere/include/stratosphere/sf/sf_object_impl_factory.hpp b/libraries/libstratosphere/include/stratosphere/sf/sf_object_impl_factory.hpp index cf6ffe24b..059eca8db 100644 --- a/libraries/libstratosphere/include/stratosphere/sf/sf_object_impl_factory.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf/sf_object_impl_factory.hpp @@ -113,7 +113,7 @@ namespace ams::sf { void DisposeImpl() { Allocator *a = this->GetAllocator(); - this->~Object(); + std::destroy_at(this); operator delete(this, a); } public: diff --git a/libraries/libstratosphere/source/ddsf/ddsf_device_code_entry_manager.cpp b/libraries/libstratosphere/source/ddsf/ddsf_device_code_entry_manager.cpp index 93f37ea9d..72338075b 100644 --- a/libraries/libstratosphere/source/ddsf/ddsf_device_code_entry_manager.cpp +++ b/libraries/libstratosphere/source/ddsf/ddsf_device_code_entry_manager.cpp @@ -36,7 +36,7 @@ namespace ams::ddsf { R_UNLESS(holder_storage != nullptr, ddsf::ResultOutOfResource()); /* Initialize the new holder. */ - auto *holder = new (static_cast(holder_storage)) DeviceCodeEntryHolder; + auto *holder = std::construct_at(static_cast(holder_storage)); holder->Construct(device_code, device); /* Link the new holder. */ @@ -60,7 +60,7 @@ namespace ams::ddsf { if (cur->Get().GetDeviceCode() == device_code) { /* Destroy and deallocate the holder. */ cur->Destroy(); - cur->~DeviceCodeEntryHolder(); + std::destroy_at(cur); this->memory_resource->Deallocate(cur, sizeof(*cur)); erased = true; diff --git a/libraries/libstratosphere/source/fssystem/buffers/fssystem_file_system_buffer_manager.cpp b/libraries/libstratosphere/source/fssystem/buffers/fssystem_file_system_buffer_manager.cpp index aebc91960..19b74a6cf 100644 --- a/libraries/libstratosphere/source/fssystem/buffers/fssystem_file_system_buffer_manager.cpp +++ b/libraries/libstratosphere/source/fssystem/buffers/fssystem_file_system_buffer_manager.cpp @@ -88,8 +88,8 @@ namespace ams::fssystem { if (this->external_attr_info_buffer == nullptr) { new_info = new AttrInfo(attr.GetLevel(), 1, size); } else if (0 <= attr.GetLevel() && attr.GetLevel() < this->external_attr_info_count) { - const auto buffer = this->external_attr_info_buffer + attr.GetLevel() * sizeof(AttrInfo); - new_info = new (buffer) AttrInfo(attr.GetLevel(), 1, size); + void *buffer = this->external_attr_info_buffer + attr.GetLevel() * sizeof(AttrInfo); + new_info = std::construct_at(reinterpret_cast(buffer), attr.GetLevel(), 1, size); } /* If we failed to make a new attr info, we can't register. */ diff --git a/libraries/libstratosphere/source/fssystem/fssystem_file_system_proxy_api.cpp b/libraries/libstratosphere/source/fssystem/fssystem_file_system_proxy_api.cpp index fdb76c479..955d4f317 100644 --- a/libraries/libstratosphere/source/fssystem/fssystem_file_system_proxy_api.cpp +++ b/libraries/libstratosphere/source/fssystem/fssystem_file_system_proxy_api.cpp @@ -102,8 +102,8 @@ namespace ams::fssystem { InitializeExpHeap(); /* Initialize buffer allocator. */ - new (GetPointer(g_buffer_allocator)) mem::StandardAllocator(g_buffer_pool, BufferPoolSize); - new (GetPointer(g_allocator)) fssrv::MemoryResourceFromStandardAllocator(GetPointer(g_buffer_allocator)); + util::ConstructAt(g_buffer_allocator, g_buffer_pool, BufferPoolSize); + util::ConstructAt(g_allocator, GetPointer(g_buffer_allocator)); /* Set allocators. */ fs::SetAllocator(AllocateForFileSystemProxy, DeallocateForFileSystemProxy); @@ -111,7 +111,7 @@ namespace ams::fssystem { /* Initialize the buffer manager. */ /* TODO FS-REIMPL: os::AllocateMemoryBlock(...); */ - new (GetPointer(g_buffer_manager)) fssystem::FileSystemBufferManager; + util::ConstructAt(g_buffer_manager); GetReference(g_buffer_manager).Initialize(MaxCacheCount, reinterpret_cast(g_buffer_manager_heap), BufferManagerHeapSize, BlockSize); /* TODO FS-REIMPL: Memory Report Creators, fssrv::SetMemoryReportCreator */ @@ -119,9 +119,9 @@ namespace ams::fssystem { /* TODO FS-REIMPL: Create Pooled Threads, fssystem::RegisterThreadPool. */ /* Initialize fs creators. */ - new (GetPointer(g_rom_fs_creator)) fssrv::fscreator::RomFileSystemCreator(GetPointer(g_allocator)); - new (GetPointer(g_partition_fs_creator)) fssrv::fscreator::PartitionFileSystemCreator; - new (GetPointer(g_storage_on_nca_creator)) fssrv::fscreator::StorageOnNcaCreator(GetPointer(g_allocator), *GetNcaCryptoConfiguration(is_prod), is_prod, GetPointer(g_buffer_manager)); + util::ConstructAt(g_rom_fs_creator, GetPointer(g_allocator)); + util::ConstructAt(g_partition_fs_creator); + util::ConstructAt(g_storage_on_nca_creator, GetPointer(g_allocator), *GetNcaCryptoConfiguration(is_prod), is_prod, GetPointer(g_buffer_manager)); /* TODO FS-REIMPL: Initialize other creators. */ diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/gpio_driver_api.cpp b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/gpio_driver_api.cpp index e04a74d29..484235378 100644 --- a/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/gpio_driver_api.cpp +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo/nx/gpio_driver_api.cpp @@ -38,7 +38,7 @@ namespace ams::gpio::driver::board::nintendo::nx { AMS_ABORT_UNLESS(driver_storage != nullptr); /* Construct the new driver. */ - g_driver_impl = new (driver_storage) ams::gpio::driver::board::nintendo::nx::impl::DriverImpl(impl::GpioRegistersPhysicalAddress, impl::GpioRegistersSize); + g_driver_impl = std::construct_at(driver_storage, impl::GpioRegistersPhysicalAddress, impl::GpioRegistersSize); /* Register the driver. */ gpio::driver::RegisterDriver(g_driver_impl); @@ -47,11 +47,11 @@ namespace ams::gpio::driver::board::nintendo::nx { if (enable_interrupt_handlers) { for (size_t i = 0; i < util::size(impl::InterruptNameTable); ++i) { /* Allocate a handler. */ - impl::InterruptEventHandler *handler_storage = static_cast(memory_resource->Allocate(sizeof(impl::InterruptEventHandler))); + void *handler_storage = memory_resource->Allocate(sizeof(impl::InterruptEventHandler)); AMS_ABORT_UNLESS(handler_storage != nullptr); /* Initialize the handler. */ - impl::InterruptEventHandler *handler = new (handler_storage) impl::InterruptEventHandler; + auto *handler = std::construct_at(static_cast(handler_storage)); handler->Initialize(g_driver_impl, impl::InterruptNameTable[i], static_cast(i)); /* Register the handler. */ @@ -62,11 +62,11 @@ namespace ams::gpio::driver::board::nintendo::nx { /* Create and register all pads. */ for (const auto &entry : impl::PadMapCombinationList) { /* Allocate a pad for our device. */ - impl::TegraPad *pad_storage = static_cast(memory_resource->Allocate(sizeof(impl::TegraPad))); + void *pad_storage = memory_resource->Allocate(sizeof(impl::TegraPad)); AMS_ABORT_UNLESS(pad_storage != nullptr); /* Create a pad for our device. */ - impl::TegraPad *pad = new (pad_storage) impl::TegraPad; + auto *pad = std::construct_at(static_cast(pad_storage)); pad->SetParameters(entry.internal_number, impl::PadInfo{entry.wake_event}); /* Register the pad with our driver. */ diff --git a/libraries/libstratosphere/source/gpio/driver/gpio_pad_api.cpp b/libraries/libstratosphere/source/gpio/driver/gpio_pad_api.cpp index b2aaaed84..7be717eb9 100644 --- a/libraries/libstratosphere/source/gpio/driver/gpio_pad_api.cpp +++ b/libraries/libstratosphere/source/gpio/driver/gpio_pad_api.cpp @@ -23,8 +23,8 @@ namespace ams::gpio::driver { Result OpenSessionImpl(GpioPadSession *out, Pad *pad, ddsf::AccessMode access_mode) { /* Construct the session. */ - auto *session = new (std::addressof(impl::GetPadSessionImpl(*out))) impl::PadSessionImpl; - auto session_guard = SCOPE_GUARD { session->~PadSessionImpl(); }; + auto *session = std::construct_at(std::addressof(impl::GetPadSessionImpl(*out))); + auto session_guard = SCOPE_GUARD { std::destroy_at(session); }; /* Open the session. */ R_TRY(session->Open(pad, access_mode)); @@ -59,7 +59,7 @@ namespace ams::gpio::driver { void CloseSession(GpioPadSession *session) { AMS_ASSERT(session != nullptr); - impl::GetOpenPadSessionImpl(*session).~PadSessionImpl(); + std::destroy_at(std::addressof(impl::GetOpenPadSessionImpl(*session))); } Result SetDirection(GpioPadSession *session, gpio::Direction direction) { diff --git a/libraries/libstratosphere/source/i2c/driver/board/nintendo/nx/impl/i2c_i_allocator.hpp b/libraries/libstratosphere/source/i2c/driver/board/nintendo/nx/impl/i2c_i_allocator.hpp index 71e264d90..13cb57d08 100644 --- a/libraries/libstratosphere/source/i2c/driver/board/nintendo/nx/impl/i2c_i_allocator.hpp +++ b/libraries/libstratosphere/source/i2c/driver/board/nintendo/nx/impl/i2c_i_allocator.hpp @@ -38,7 +38,7 @@ namespace ams::i2c::driver::board::nintendo::nx::impl { T *obj = std::addressof(*it); it = this->list.erase(it); - obj->~T(); + std::destroy_at(obj); this->memory_resource->Deallocate(obj, sizeof(T)); } } @@ -52,7 +52,7 @@ namespace ams::i2c::driver::board::nintendo::nx::impl { AMS_ABORT_UNLESS(storage != nullptr); /* Construct the object. */ - T *t = new (static_cast(storage)) T(std::forward(args)...); + T *t = std::construct_at(static_cast(storage), std::forward(args)...); /* Link the object into our list. */ this->list.push_back(*t); diff --git a/libraries/libstratosphere/source/i2c/driver/i2c_driver_bus_api.cpp b/libraries/libstratosphere/source/i2c/driver/i2c_driver_bus_api.cpp index 3b11792ae..0beb65937 100644 --- a/libraries/libstratosphere/source/i2c/driver/i2c_driver_bus_api.cpp +++ b/libraries/libstratosphere/source/i2c/driver/i2c_driver_bus_api.cpp @@ -26,8 +26,8 @@ namespace ams::i2c::driver { Result OpenSessionImpl(I2cSession *out, I2cDeviceProperty *device) { /* Construct the session. */ - auto *session = new (std::addressof(impl::GetI2cSessionImpl(*out))) impl::I2cSessionImpl(DefaultRetryCount, DefaultRetryInterval); - auto session_guard = SCOPE_GUARD { session->~I2cSessionImpl(); }; + auto *session = std::construct_at(std::addressof(impl::GetI2cSessionImpl(*out)), DefaultRetryCount, DefaultRetryInterval); + auto session_guard = SCOPE_GUARD { std::destroy_at(session); }; /* Open the session. */ R_TRY(session->Open(device, ddsf::AccessMode_ReadWrite)); @@ -54,7 +54,7 @@ namespace ams::i2c::driver { } void CloseSession(I2cSession &session) { - impl::GetOpenI2cSessionImpl(session).~I2cSessionImpl(); + std::destroy_at(std::addressof(impl::GetOpenI2cSessionImpl(session))); } Result Send(I2cSession &session, const void *src, size_t src_size, TransactionOption option) { diff --git a/libraries/libstratosphere/source/lmem/impl/lmem_impl_common_heap.cpp b/libraries/libstratosphere/source/lmem/impl/lmem_impl_common_heap.cpp index 7afc2d78c..3e555a31b 100644 --- a/libraries/libstratosphere/source/lmem/impl/lmem_impl_common_heap.cpp +++ b/libraries/libstratosphere/source/lmem/impl/lmem_impl_common_heap.cpp @@ -30,8 +30,8 @@ namespace ams::lmem::impl { void InitializeHeapHead(HeapHead *out, u32 magic, void *start, void *end, u32 option) { /* Call member constructors. */ - new (&out->list_node) util::IntrusiveListNode; - new (&out->child_list) decltype(out->child_list); + std::construct_at(std::addressof(out->list_node)); + std::construct_at(std::addressof(out->child_list)); /* Set fields. */ out->magic = magic; diff --git a/libraries/libstratosphere/source/lmem/impl/lmem_impl_exp_heap.cpp b/libraries/libstratosphere/source/lmem/impl/lmem_impl_exp_heap.cpp index 021a4344f..213fbd5fa 100644 --- a/libraries/libstratosphere/source/lmem/impl/lmem_impl_exp_heap.cpp +++ b/libraries/libstratosphere/source/lmem/impl/lmem_impl_exp_heap.cpp @@ -147,12 +147,11 @@ namespace ams::lmem::impl { } inline ExpHeapMemoryBlockHead *InitializeMemoryBlock(const MemoryRegion ®ion, u16 magic) { - ExpHeapMemoryBlockHead *block = reinterpret_cast(region.start); + /* Construct the block. */ + ExpHeapMemoryBlockHead *block = std::construct_at(reinterpret_cast(region.start)); - /* Ensure all member constructors are called. */ - new (block) ExpHeapMemoryBlockHead; - - block->magic = magic; + /* Initialize all members. */ + block->magic = magic; block->attributes = 0; block->block_size = GetPointerDifference(GetMemoryBlockStart(block), region.end); @@ -175,8 +174,8 @@ namespace ams::lmem::impl { InitializeHeapHead(heap_head, ExpHeapMagic, GetExpHeapMemoryStart(exp_heap_head), end, option); /* Call exp heap member constructors. */ - new (&exp_heap_head->free_list) ExpHeapMemoryBlockList; - new (&exp_heap_head->used_list) ExpHeapMemoryBlockList; + std::construct_at(std::addressof(exp_heap_head->free_list)); + std::construct_at(std::addressof(exp_heap_head->used_list)); /* Set exp heap fields. */ exp_heap_head->group_id = DefaultGroupId; diff --git a/libraries/libstratosphere/source/mem/impl/heap/mem_impl_heap_central_heap.cpp b/libraries/libstratosphere/source/mem/impl/heap/mem_impl_heap_central_heap.cpp index 4685dbf4a..9e2874e85 100644 --- a/libraries/libstratosphere/source/mem/impl/heap/mem_impl_heap_central_heap.cpp +++ b/libraries/libstratosphere/source/mem/impl/heap/mem_impl_heap_central_heap.cpp @@ -42,9 +42,9 @@ namespace ams::mem::impl::heap { this->start = aligned_start; this->end = aligned_end; this->option = option; - this->tls_heap_central = new (this->start) TlsHeapCentral; + this->tls_heap_central = std::construct_at(reinterpret_cast(this->start)); if (auto err = this->tls_heap_central->Initialize(this->start, this->end - this->start, false); err != 0) { - this->tls_heap_central->~TlsHeapCentral(); + std::destroy_at(this->tls_heap_central); this->tls_heap_central = nullptr; AMS_ASSERT(err == 0); return err; @@ -70,9 +70,9 @@ namespace ams::mem::impl::heap { if (auto err = AllocatePhysicalMemory(central, sizeof(TlsHeapCentral)); err != 0) { return err; } - this->tls_heap_central = new (central) TlsHeapCentral; + this->tls_heap_central = std::construct_at(static_cast(central)); if (auto err = this->tls_heap_central->Initialize(central, size, true); err != 0) { - this->tls_heap_central->~TlsHeapCentral(); + std::destroy_at(this->tls_heap_central); this->tls_heap_central = nullptr; AMS_ASSERT(err == 0); return err; @@ -85,7 +85,7 @@ namespace ams::mem::impl::heap { void CentralHeap::Finalize() { if (this->tls_heap_central) { - this->tls_heap_central->~TlsHeapCentral(); + std::destroy_at(this->tls_heap_central); } if (this->use_virtual_memory) { mem::impl::physical_free(util::AlignUp(static_cast(this->start), PageSize), this->end - this->start); @@ -249,7 +249,7 @@ namespace ams::mem::impl::heap { return false; } - new (tls_heap_cache) TlsHeapCache(this->tls_heap_central, this->option); + std::construct_at(static_cast(tls_heap_cache), this->tls_heap_central, this->option); if (this->tls_heap_central->AddThreadCache(reinterpret_cast(tls_heap_cache)) != 0) { this->tls_heap_central->UncacheSmallMemory(tls_heap_cache); return false; diff --git a/libraries/libstratosphere/source/mem/mem_standard_allocator.cpp b/libraries/libstratosphere/source/mem/mem_standard_allocator.cpp index cc7125d40..c76928585 100644 --- a/libraries/libstratosphere/source/mem/mem_standard_allocator.cpp +++ b/libraries/libstratosphere/source/mem/mem_standard_allocator.cpp @@ -68,7 +68,7 @@ namespace ams::mem { StandardAllocator::StandardAllocator() : initialized(false), enable_thread_cache(false), unused(0) { static_assert(sizeof(impl::heap::CentralHeap) <= sizeof(this->central_heap_storage)); - new (std::addressof(this->central_heap_storage)) impl::heap::CentralHeap; + std::construct_at(GetCentral(this->central_heap_storage)); } StandardAllocator::StandardAllocator(void *mem, size_t size) : StandardAllocator() { diff --git a/libraries/libstratosphere/source/os/impl/os_inter_process_event.cpp b/libraries/libstratosphere/source/os/impl/os_inter_process_event.cpp index 8ae899184..2de8a5a31 100644 --- a/libraries/libstratosphere/source/os/impl/os_inter_process_event.cpp +++ b/libraries/libstratosphere/source/os/impl/os_inter_process_event.cpp @@ -33,7 +33,7 @@ namespace ams::os::impl { event->auto_clear = (clear_mode == EventClearMode_AutoClear); /* Create the waitlist node. */ - new (GetPointer(event->waitable_object_list_storage)) impl::WaitableObjectList; + util::ConstructAt(event->waitable_object_list_storage); /* Set state. */ event->state = InterProcessEventType::State_Initialized; @@ -71,7 +71,7 @@ namespace ams::os::impl { } /* Destroy the waitlist. */ - GetReference(event->waitable_object_list_storage).~WaitableObjectList(); + util::DestroyAt(event->waitable_object_list_storage); } void AttachInterProcessEvent(InterProcessEventType *event, Handle read_handle, bool read_handle_managed, Handle write_handle, bool write_handle_managed, EventClearMode clear_mode) { diff --git a/libraries/libstratosphere/source/os/impl/os_resource_manager.hpp b/libraries/libstratosphere/source/os/impl/os_resource_manager.hpp index 0845ff3f8..4b8472002 100644 --- a/libraries/libstratosphere/source/os/impl/os_resource_manager.hpp +++ b/libraries/libstratosphere/source/os/impl/os_resource_manager.hpp @@ -48,7 +48,7 @@ namespace ams::os::impl { public: static ALWAYS_INLINE void InitializeResourceManagerInstance() { /* Construct the resource manager instance. */ - new (GetPointer(s_resource_manager_storage)) OsResourceManager; + util::ConstructAt(s_resource_manager_storage); } static ALWAYS_INLINE OsResourceManager &GetResourceManagerInstance() { diff --git a/libraries/libstratosphere/source/os/impl/os_thread_manager.cpp b/libraries/libstratosphere/source/os/impl/os_thread_manager.cpp index 2c7c8a1a2..b55db71d4 100644 --- a/libraries/libstratosphere/source/os/impl/os_thread_manager.cpp +++ b/libraries/libstratosphere/source/os/impl/os_thread_manager.cpp @@ -24,11 +24,11 @@ namespace ams::os::impl { void SetupThreadObjectUnsafe(ThreadType *thread, ThreadImpl *thread_impl, ThreadFunction function, void *arg, void *stack, size_t stack_size, s32 priority) { /* Setup objects. */ - new (GetPointer(thread->cs_thread)) impl::InternalCriticalSection; - new (GetPointer(thread->cv_thread)) impl::InternalConditionVariable; + util::ConstructAt(thread->cs_thread); + util::ConstructAt(thread->cv_thread); - new (GetPointer(thread->all_threads_node)) util::IntrusiveListNode; - new (GetPointer(thread->waitlist)) WaitableObjectList; + util::ConstructAt(thread->all_threads_node); + util::ConstructAt(thread->waitlist); /* Set member variables. */ thread->thread_impl = (thread_impl != nullptr) ? thread_impl : std::addressof(thread->thread_impl_storage); @@ -131,7 +131,7 @@ namespace ams::os::impl { thread->state = ThreadType::State_NotInitialized; - GetReference(thread->waitlist).~WaitableObjectList(); + util::DestroyAt(thread->waitlist); thread->name_buffer[0] = '\x00'; diff --git a/libraries/libstratosphere/source/os/os_condition_variable.cpp b/libraries/libstratosphere/source/os/os_condition_variable.cpp index 4feec65c7..6a830128c 100644 --- a/libraries/libstratosphere/source/os/os_condition_variable.cpp +++ b/libraries/libstratosphere/source/os/os_condition_variable.cpp @@ -22,7 +22,7 @@ namespace ams::os { void InitializeConditionVariable(ConditionVariableType *cv) { /* Construct object. */ - new (GetPointer(cv->_storage)) impl::InternalConditionVariable; + util::ConstructAt(cv->_storage); /* Mark initialized. */ cv->state = ConditionVariableType::State_Initialized; @@ -35,7 +35,7 @@ namespace ams::os { cv->state = ConditionVariableType::State_NotInitialized; /* Destroy objects. */ - GetReference(cv->_storage).~InternalConditionVariable(); + util::DestroyAt(cv->_storage); } void SignalConditionVariable(ConditionVariableType *cv) { diff --git a/libraries/libstratosphere/source/os/os_event.cpp b/libraries/libstratosphere/source/os/os_event.cpp index 2350b8ae0..11328190a 100644 --- a/libraries/libstratosphere/source/os/os_event.cpp +++ b/libraries/libstratosphere/source/os/os_event.cpp @@ -37,11 +37,11 @@ namespace ams::os { void InitializeEvent(EventType *event, bool signaled, EventClearMode clear_mode) { /* Initialize internal variables. */ - new (GetPointer(event->cs_event)) impl::InternalCriticalSection; - new (GetPointer(event->cv_signaled)) impl::InternalConditionVariable; + util::ConstructAt(event->cs_event); + util::ConstructAt(event->cv_signaled); /* Initialize the waitable object list. */ - new (GetPointer(event->waitable_object_list_storage)) impl::WaitableObjectList(); + util::ConstructAt(event->waitable_object_list_storage); /* Initialize member variables. */ event->signaled = signaled; @@ -61,9 +61,9 @@ namespace ams::os { event->state = EventType::State_NotInitialized; /* Destroy objects. */ - GetReference(event->waitable_object_list_storage).~WaitableObjectList(); - GetReference(event->cv_signaled).~InternalConditionVariable(); - GetReference(event->cs_event).~InternalCriticalSection(); + util::DestroyAt(event->waitable_object_list_storage); + util::DestroyAt(event->cv_signaled); + util::DestroyAt(event->cs_event); } void SignalEvent(EventType *event) { @@ -163,7 +163,7 @@ namespace ams::os { void InitializeWaitableHolder(WaitableHolderType *waitable_holder, EventType *event) { AMS_ASSERT(event->state == EventType::State_Initialized); - new (GetPointer(waitable_holder->impl_storage)) impl::WaitableHolderOfEvent(event); + util::ConstructAt(GetReference(waitable_holder->impl_storage).holder_of_event_storage, event); waitable_holder->user_data = 0; } diff --git a/libraries/libstratosphere/source/os/os_interrupt_event.cpp b/libraries/libstratosphere/source/os/os_interrupt_event.cpp index 54de8d18b..509ea39b8 100644 --- a/libraries/libstratosphere/source/os/os_interrupt_event.cpp +++ b/libraries/libstratosphere/source/os/os_interrupt_event.cpp @@ -25,7 +25,7 @@ namespace ams::os { event->clear_mode = static_cast(clear_mode); /* Initialize implementation. */ - new (GetPointer(event->impl)) impl::InterruptEventImpl(name, clear_mode); + util::ConstructAt(event->impl, name, clear_mode); /* Mark initialized. */ event->state = InterruptEventType::State_Initialized; @@ -38,7 +38,7 @@ namespace ams::os { event->state = InterruptEventType::State_NotInitialized; /* Destroy objects. */ - GetReference(event->impl).~InterruptEventImpl(); + util::DestroyAt(event->impl); } void WaitInterruptEvent(InterruptEventType *event) { @@ -65,7 +65,7 @@ namespace ams::os { void InitializeWaitableHolder(WaitableHolderType *waitable_holder, InterruptEventType *event) { AMS_ASSERT(event->state == InterruptEventType::State_Initialized); - new (GetPointer(waitable_holder->impl_storage)) impl::WaitableHolderOfInterruptEvent(event); + util::ConstructAt(GetReference(waitable_holder->impl_storage).holder_of_interrupt_event_storage, event); waitable_holder->user_data = 0; } diff --git a/libraries/libstratosphere/source/os/os_message_queue.cpp b/libraries/libstratosphere/source/os/os_message_queue.cpp index b9bdb0255..6e15ffdb2 100644 --- a/libraries/libstratosphere/source/os/os_message_queue.cpp +++ b/libraries/libstratosphere/source/os/os_message_queue.cpp @@ -112,13 +112,13 @@ namespace ams::os { AMS_ASSERT(count >= 1); /* Setup objects. */ - new (GetPointer(mq->cs_queue)) impl::InternalCriticalSection; - new (GetPointer(mq->cv_not_full)) impl::InternalConditionVariable; - new (GetPointer(mq->cv_not_empty)) impl::InternalConditionVariable; + util::ConstructAt(mq->cs_queue); + util::ConstructAt(mq->cv_not_full); + util::ConstructAt(mq->cv_not_empty); /* Setup wait lists. */ - new (GetPointer(mq->waitlist_not_empty)) impl::WaitableObjectList; - new (GetPointer(mq->waitlist_not_full)) impl::WaitableObjectList; + util::ConstructAt(mq->waitlist_not_empty); + util::ConstructAt(mq->waitlist_not_full); /* Set member variables. */ mq->buffer = buffer; @@ -140,13 +140,13 @@ namespace ams::os { mq->state = MessageQueueType::State_NotInitialized; /* Destroy wait lists. */ - GetReference(mq->waitlist_not_empty).~WaitableObjectList(); - GetReference(mq->waitlist_not_full).~WaitableObjectList(); + util::DestroyAt(mq->waitlist_not_empty); + util::DestroyAt(mq->waitlist_not_full); /* Destroy objects. */ - GetReference(mq->cv_not_empty).~InternalConditionVariable(); - GetReference(mq->cv_not_full).~InternalConditionVariable(); - GetReference(mq->cs_queue).~InternalCriticalSection(); + util::DestroyAt(mq->cv_not_empty); + util::DestroyAt(mq->cv_not_full); + util::DestroyAt(mq->cs_queue); } /* Sending (FIFO functionality) */ diff --git a/libraries/libstratosphere/source/os/os_mutex.cpp b/libraries/libstratosphere/source/os/os_mutex.cpp index 5b33bda12..bd2f70fe1 100644 --- a/libraries/libstratosphere/source/os/os_mutex.cpp +++ b/libraries/libstratosphere/source/os/os_mutex.cpp @@ -72,7 +72,7 @@ namespace ams::os { AMS_ASSERT((lock_level == 0) || (MutexLockLevelMin <= lock_level && lock_level <= MutexLockLevelMax)); /* Create object. */ - new (GetPointer(mutex->_storage)) impl::InternalCriticalSection; + util::ConstructAt(mutex->_storage); /* Set member variables. */ mutex->is_recursive = recursive; @@ -91,7 +91,7 @@ namespace ams::os { mutex->state = MutexType::State_NotInitialized; /* Destroy object. */ - GetReference(mutex->_storage).~InternalCriticalSection(); + util::DestroyAt(mutex->_storage); } void LockMutex(MutexType *mutex) { diff --git a/libraries/libstratosphere/source/os/os_rw_lock.cpp b/libraries/libstratosphere/source/os/os_rw_lock.cpp index fc2738c96..ca6782e1b 100644 --- a/libraries/libstratosphere/source/os/os_rw_lock.cpp +++ b/libraries/libstratosphere/source/os/os_rw_lock.cpp @@ -21,9 +21,9 @@ namespace ams::os { void InitalizeReadWriteLock(ReadWriteLockType *rw_lock) { /* Create objects. */ - new (GetPointer(impl::GetLockCount(rw_lock).cs_storage)) impl::InternalCriticalSection; - new (GetPointer(rw_lock->cv_read_lock._storage)) impl::InternalConditionVariable; - new (GetPointer(rw_lock->cv_write_lock._storage)) impl::InternalConditionVariable; + util::ConstructAt(impl::GetLockCount(rw_lock).cs_storage); + util::ConstructAt(rw_lock->cv_read_lock._storage); + util::ConstructAt(rw_lock->cv_write_lock._storage); /* Set member variables. */ impl::ClearReadLockCount(impl::GetLockCount(rw_lock)); @@ -48,9 +48,9 @@ namespace ams::os { rw_lock->state = ReadWriteLockType::State_NotInitialized; /* Destroy objects. */ - GetReference(rw_lock->cv_write_lock._storage).~InternalConditionVariable(); - GetReference(rw_lock->cv_read_lock._storage).~InternalConditionVariable(); - GetReference(impl::GetLockCount(rw_lock).cs_storage).~InternalCriticalSection(); + util::DestroyAt(rw_lock->cv_write_lock._storage); + util::DestroyAt(rw_lock->cv_read_lock._storage); + util::DestroyAt(impl::GetLockCount(rw_lock).cs_storage); } void AcquireReadLock(ReadWriteLockType *rw_lock) { diff --git a/libraries/libstratosphere/source/os/os_semaphore.cpp b/libraries/libstratosphere/source/os/os_semaphore.cpp index 62fe78d24..3234c3e2e 100644 --- a/libraries/libstratosphere/source/os/os_semaphore.cpp +++ b/libraries/libstratosphere/source/os/os_semaphore.cpp @@ -24,11 +24,11 @@ namespace ams::os { AMS_ASSERT(count >= 0); /* Setup objects. */ - new (GetPointer(sema->cs_sema)) impl::InternalCriticalSection; - new (GetPointer(sema->cv_not_zero)) impl::InternalConditionVariable; + util::ConstructAt(sema->cs_sema); + util::ConstructAt(sema->cv_not_zero); /* Setup wait lists. */ - new (GetPointer(sema->waitlist)) impl::WaitableObjectList; + util::ConstructAt(sema->waitlist); /* Set member variables. */ sema->count = count; @@ -47,11 +47,11 @@ namespace ams::os { sema->state = SemaphoreType::State_NotInitialized; /* Destroy wait lists. */ - GetReference(sema->waitlist).~WaitableObjectList(); + util::DestroyAt(sema->waitlist); /* Destroy objects. */ - GetReference(sema->cv_not_zero).~InternalConditionVariable(); - GetReference(sema->cs_sema).~InternalCriticalSection(); + util::DestroyAt(sema->cv_not_zero); + util::DestroyAt(sema->cs_sema); } void AcquireSemaphore(SemaphoreType *sema) { diff --git a/libraries/libstratosphere/source/os/os_system_event.cpp b/libraries/libstratosphere/source/os/os_system_event.cpp index 829b98d7c..e828356e3 100644 --- a/libraries/libstratosphere/source/os/os_system_event.cpp +++ b/libraries/libstratosphere/source/os/os_system_event.cpp @@ -122,10 +122,10 @@ namespace ams::os { void InitializeWaitableHolder(WaitableHolderType *waitable_holder, SystemEventType *event) { switch (event->state) { case SystemEventType::State_InitializedAsInterProcessEvent: - new (GetPointer(waitable_holder->impl_storage)) impl::WaitableHolderOfInterProcessEvent(std::addressof(event->inter_process_event)); + util::ConstructAt(GetReference(waitable_holder->impl_storage).holder_of_inter_process_event_storage, std::addressof(event->inter_process_event)); break; case SystemEventType::State_InitializedAsEvent: - new (GetPointer(waitable_holder->impl_storage)) impl::WaitableHolderOfEvent(std::addressof(event->event)); + util::ConstructAt(GetReference(waitable_holder->impl_storage).holder_of_event_storage, std::addressof(event->event)); break; AMS_UNREACHABLE_DEFAULT_CASE(); } diff --git a/libraries/libstratosphere/source/os/os_timer_event.cpp b/libraries/libstratosphere/source/os/os_timer_event.cpp index ec1b120e4..0a632de1b 100644 --- a/libraries/libstratosphere/source/os/os_timer_event.cpp +++ b/libraries/libstratosphere/source/os/os_timer_event.cpp @@ -56,11 +56,11 @@ namespace ams::os { void InitializeTimerEvent(TimerEventType *event, EventClearMode clear_mode) { /* Initialize internal variables. */ - new (GetPointer(event->cs_timer_event)) impl::InternalCriticalSection; - new (GetPointer(event->cv_signaled)) impl::InternalConditionVariable; + util::ConstructAt(event->cs_timer_event); + util::ConstructAt(event->cv_signaled); /* Initialize the waitable object list. */ - new (GetPointer(event->waitable_object_list_storage)) impl::WaitableObjectList(); + util::ConstructAt(event->waitable_object_list_storage); /* Initialize member variables. */ event->clear_mode = static_cast(clear_mode); @@ -83,9 +83,9 @@ namespace ams::os { event->state = TimerEventType::State_NotInitialized; /* Destroy objects. */ - GetReference(event->waitable_object_list_storage).~WaitableObjectList(); - GetReference(event->cv_signaled).~InternalConditionVariable(); - GetReference(event->cs_timer_event).~InternalCriticalSection(); + util::DestroyAt(event->waitable_object_list_storage); + util::DestroyAt(event->cv_signaled); + util::DestroyAt(event->cs_timer_event); } void StartOneShotTimerEvent(TimerEventType *event, TimeSpan first_time) { @@ -255,7 +255,7 @@ namespace ams::os { void InitializeWaitableHolder(WaitableHolderType *waitable_holder, TimerEventType *event) { AMS_ASSERT(event->state == EventType::State_Initialized); - new (GetPointer(waitable_holder->impl_storage)) impl::WaitableHolderOfTimerEvent(event); + util::ConstructAt(GetReference(waitable_holder->impl_storage).holder_of_timer_event_storage, event); waitable_holder->user_data = 0; } diff --git a/libraries/libstratosphere/source/os/os_transfer_memory_api.cpp b/libraries/libstratosphere/source/os/os_transfer_memory_api.cpp index c6b23be45..e6f3a4548 100644 --- a/libraries/libstratosphere/source/os/os_transfer_memory_api.cpp +++ b/libraries/libstratosphere/source/os/os_transfer_memory_api.cpp @@ -46,7 +46,7 @@ namespace ams::os { tmem->handle_managed = managed; /* Create the critical section. */ - new (GetPointer(tmem->cs_transfer_memory)) impl::InternalCriticalSection; + util::ConstructAt(tmem->cs_transfer_memory); } } @@ -118,7 +118,7 @@ namespace ams::os { tmem->handle = svc::InvalidHandle; /* Destroy the critical section. */ - GetReference(tmem->cs_transfer_memory).~InternalCriticalSection(); + util::DestroyAt(tmem->cs_transfer_memory); } Result MapTransferMemory(void **out, TransferMemoryType *tmem, MemoryPermission owner_perm) { diff --git a/libraries/libstratosphere/source/os/os_waitable.cpp b/libraries/libstratosphere/source/os/os_waitable.cpp index 5653eaf2c..3aed7da6f 100644 --- a/libraries/libstratosphere/source/os/os_waitable.cpp +++ b/libraries/libstratosphere/source/os/os_waitable.cpp @@ -34,7 +34,7 @@ namespace ams::os { void InitializeWaitableManager(WaitableManagerType *manager) { /* Initialize storage. */ - new (std::addressof(GetWaitableManagerImpl(manager))) impl::WaitableManagerImpl; + util::ConstructAt(manager->impl_storage); /* Mark initialized. */ manager->state = WaitableManagerType::State_Initialized; @@ -50,7 +50,7 @@ namespace ams::os { manager->state = WaitableManagerType::State_NotInitialized; /* Destroy. */ - impl.~WaitableManagerImpl(); + util::DestroyAt(manager->impl_storage); } WaitableHolderType *WaitAny(WaitableManagerType *manager) { @@ -90,7 +90,7 @@ namespace ams::os { AMS_ASSERT(!holder_base->IsLinkedToManager()); - holder_base->~WaitableHolderBase(); + std::destroy_at(holder_base); } void LinkWaitableHolder(WaitableManagerType *manager, WaitableHolderType *holder) { @@ -143,7 +143,7 @@ namespace ams::os { void InitializeWaitableHolder(WaitableHolderType *holder, Handle handle) { AMS_ASSERT(handle != svc::InvalidHandle); - new (GetPointer(holder->impl_storage)) impl::WaitableHolderOfHandle(handle); + util::ConstructAt(GetReference(holder->impl_storage).holder_of_handle_storage, handle); holder->user_data = 0; } diff --git a/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell_event_observer.cpp b/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell_event_observer.cpp index 5b7bacec9..ad78718ab 100644 --- a/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell_event_observer.cpp +++ b/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell_event_observer.cpp @@ -22,13 +22,12 @@ namespace ams::pgl::srv { ShellEventObserver::ShellEventObserver() : message_queue(queue_buffer, QueueCapacity), event(os::EventClearMode_AutoClear, true) { this->heap_handle = lmem::CreateUnitHeap(this->event_info_data, sizeof(this->event_info_data), sizeof(this->event_info_data[0]), lmem::CreateOption_ThreadSafe, 8, GetPointer(this->heap_head)); - new (GetPointer(this->holder)) ShellEventObserverHolder(this); - RegisterShellEventObserver(GetPointer(this->holder)); + RegisterShellEventObserver(util::ConstructAt(this->holder, this)); } ShellEventObserver::~ShellEventObserver() { UnregisterShellEventObserver(GetPointer(this->holder)); - GetReference(this->holder).~ShellEventObserverHolder(); + util::DestroyAt(this->holder); } Result ShellEventObserver::PopEventInfo(pm::ProcessEventInfo *out) { diff --git a/libraries/libstratosphere/source/powctl/powctl_session_api.cpp b/libraries/libstratosphere/source/powctl/powctl_session_api.cpp index 3b46ab0b6..9b7d0afe0 100644 --- a/libraries/libstratosphere/source/powctl/powctl_session_api.cpp +++ b/libraries/libstratosphere/source/powctl/powctl_session_api.cpp @@ -38,7 +38,7 @@ namespace ams::powctl { } void DestroySession(Session &session) { - GetSessionImpl(session).~SessionImpl(); + std::destroy_at(std::addressof(GetSessionImpl(session))); session.has_session = false; } @@ -69,11 +69,11 @@ namespace ams::powctl { DestroySessionIfNecessary(*out); /* Construct the session. */ - new (std::addressof(GetSessionImpl(*out))) impl::SessionImpl; + auto *session = std::construct_at(std::addressof(GetSessionImpl(*out))); auto guard = SCOPE_GUARD { DestroySessionIfNecessary(*out); }; /* Try to open the session. */ - R_TRY(ddsf::OpenSession(device, std::addressof(GetSessionImpl(*out)), access_mode)); + R_TRY(ddsf::OpenSession(device, session, access_mode)); /* We opened the session! */ guard.Cancel(); diff --git a/libraries/libstratosphere/source/pwm/driver/board/nintendo/nx/impl/pwm_impl_pwm_driver_api.cpp b/libraries/libstratosphere/source/pwm/driver/board/nintendo/nx/impl/pwm_impl_pwm_driver_api.cpp index 9cc52cf61..3d402bae0 100644 --- a/libraries/libstratosphere/source/pwm/driver/board/nintendo/nx/impl/pwm_impl_pwm_driver_api.cpp +++ b/libraries/libstratosphere/source/pwm/driver/board/nintendo/nx/impl/pwm_impl_pwm_driver_api.cpp @@ -40,7 +40,7 @@ namespace ams::pwm::driver::board::nintendo::nx::impl { AMS_ABORT_UNLESS(driver_storage != nullptr); /* Create our driver. */ - auto *driver = new (static_cast(driver_storage)) PwmDriverImpl(PwmRegistersPhysicalAddress, PwmRegistersSize, SupportedChannels, util::size(SupportedChannels)); + auto *driver = std::construct_at(static_cast(driver_storage), PwmRegistersPhysicalAddress, PwmRegistersSize, SupportedChannels, util::size(SupportedChannels)); /* Register our driver. */ pwm::driver::RegisterDriver(driver); @@ -51,7 +51,7 @@ namespace ams::pwm::driver::board::nintendo::nx::impl { AMS_ABORT_UNLESS(device_storage != nullptr); /* Create our driver. */ - auto *device = new (static_cast(device_storage)) PwmDeviceImpl(entry.channel_id); + auto *device = std::construct_at(static_cast(device_storage), entry.channel_id); /* Register the device with our driver. */ driver->RegisterDevice(device); diff --git a/libraries/libstratosphere/source/pwm/driver/pwm_driver_channel_api.cpp b/libraries/libstratosphere/source/pwm/driver/pwm_driver_channel_api.cpp index 27ab69454..567ffa0a2 100644 --- a/libraries/libstratosphere/source/pwm/driver/pwm_driver_channel_api.cpp +++ b/libraries/libstratosphere/source/pwm/driver/pwm_driver_channel_api.cpp @@ -24,8 +24,8 @@ namespace ams::pwm::driver { Result OpenSessionImpl(ChannelSession *out, IPwmDevice *device) { /* Construct the session. */ - auto *session = new (std::addressof(impl::GetChannelSessionImpl(*out))) impl::ChannelSessionImpl; - auto session_guard = SCOPE_GUARD { session->~ChannelSessionImpl(); }; + auto *session = std::construct_at(std::addressof(impl::GetChannelSessionImpl(*out))); + auto session_guard = SCOPE_GUARD { std::destroy_at(session); }; /* Open the session. */ R_TRY(session->Open(device, ddsf::AccessMode_ReadWrite)); @@ -52,7 +52,7 @@ namespace ams::pwm::driver { } void CloseSession(ChannelSession &session) { - impl::GetOpenChannelSessionImpl(session).~ChannelSessionImpl(); + std::destroy_at(std::addressof(impl::GetOpenChannelSessionImpl(session))); } void SetPeriod(ChannelSession &session, TimeSpan period) { diff --git a/libraries/libstratosphere/source/sf/cmif/sf_cmif_domain_manager.cpp b/libraries/libstratosphere/source/sf/cmif/sf_cmif_domain_manager.cpp index 01e119586..7c16a9624 100644 --- a/libraries/libstratosphere/source/sf/cmif/sf_cmif_domain_manager.cpp +++ b/libraries/libstratosphere/source/sf/cmif/sf_cmif_domain_manager.cpp @@ -33,7 +33,7 @@ namespace ams::sf::cmif { void ServerDomainManager::Domain::DisposeImpl() { ServerDomainManager *manager = this->manager; - this->~Domain(); + std::destroy_at(this); manager->FreeDomain(this); } @@ -110,14 +110,13 @@ namespace ams::sf::cmif { this->entries = reinterpret_cast(entry_storage); this->num_entries = entry_count; for (size_t i = 0; i < this->num_entries; i++) { - Entry *entry = new (this->entries + i) Entry(); - this->free_list.push_back(*entry); + this->free_list.push_back(*std::construct_at(this->entries + i)); } } ServerDomainManager::EntryManager::~EntryManager() { for (size_t i = 0; i < this->num_entries; i++) { - this->entries[i].~Entry(); + std::destroy_at(this->entries + i); } } diff --git a/libraries/libstratosphere/source/sf/hipc/sf_hipc_mitm_query_api.cpp b/libraries/libstratosphere/source/sf/hipc/sf_hipc_mitm_query_api.cpp index c641c6b5c..01ec229c9 100644 --- a/libraries/libstratosphere/source/sf/hipc/sf_hipc_mitm_query_api.cpp +++ b/libraries/libstratosphere/source/sf/hipc/sf_hipc_mitm_query_api.cpp @@ -58,12 +58,12 @@ namespace ams::sf::hipc::impl { std::scoped_lock lk(g_query_server_lock); if (AMS_UNLIKELY(!g_constructed_server)) { - new (GetPointer(g_query_server_storage)) sf::hipc::ServerManager(); + util::ConstructAt(g_query_server_storage); g_constructed_server = true; } /* TODO: Better object factory? */ - R_ABORT_UNLESS(GetPointer(g_query_server_storage)->RegisterSession(query_handle, cmif::ServiceObjectHolder(sf::CreateSharedObjectEmplaced(query_func)))); + R_ABORT_UNLESS(GetReference(g_query_server_storage).RegisterSession(query_handle, cmif::ServiceObjectHolder(sf::CreateSharedObjectEmplaced(query_func)))); if (AMS_UNLIKELY(!g_registered_any)) { R_ABORT_UNLESS(os::CreateThread(std::addressof(g_query_server_process_thread), &QueryServerProcessThreadMain, GetPointer(g_query_server_storage), g_server_process_thread_stack, sizeof(g_server_process_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(mitm_sf, QueryServerProcessThread))); diff --git a/libraries/libstratosphere/source/sf/hipc/sf_hipc_server_session_manager.cpp b/libraries/libstratosphere/source/sf/hipc/sf_hipc_server_session_manager.cpp index 0e19a97df..0693625e8 100644 --- a/libraries/libstratosphere/source/sf/hipc/sf_hipc_server_session_manager.cpp +++ b/libraries/libstratosphere/source/sf/hipc/sf_hipc_server_session_manager.cpp @@ -70,7 +70,8 @@ namespace ams::sf::hipc { void ServerSessionManager::DestroySession(ServerSession *session) { /* Destroy object. */ - session->~ServerSession(); + std::destroy_at(session); + /* Free object memory. */ this->FreeSession(session); } @@ -84,10 +85,12 @@ namespace ams::sf::hipc { Result ServerSessionManager::RegisterSessionImpl(ServerSession *session_memory, Handle session_handle, cmif::ServiceObjectHolder &&obj) { /* Create session object. */ - new (session_memory) ServerSession(session_handle, std::forward(obj)); + std::construct_at(session_memory, session_handle, std::forward(obj)); + /* Assign session resources. */ session_memory->pointer_buffer = this->GetSessionPointerBuffer(session_memory); session_memory->saved_message = this->GetSessionSavedMessageBuffer(session_memory); + /* Register to wait list. */ this->RegisterSessionToWaitList(session_memory); return ResultSuccess(); @@ -97,27 +100,28 @@ namespace ams::sf::hipc { /* Create session handle. */ Handle session_handle; R_TRY(svcAcceptSession(&session_handle, port_handle)); - bool succeeded = false; - ON_SCOPE_EXIT { - if (!succeeded) { - R_ABORT_UNLESS(svcCloseHandle(session_handle)); - } - }; + + auto session_guard = SCOPE_GUARD { R_ABORT_UNLESS(svc::CloseHandle(session_handle)); }; + /* Register session. */ R_TRY(this->RegisterSessionImpl(session_memory, session_handle, std::forward(obj))); - succeeded = true; + + session_guard.Cancel(); return ResultSuccess(); } Result ServerSessionManager::RegisterMitmSessionImpl(ServerSession *session_memory, Handle mitm_session_handle, cmif::ServiceObjectHolder &&obj, std::shared_ptr<::Service> &&fsrv) { /* Create session object. */ - new (session_memory) ServerSession(mitm_session_handle, std::forward(obj), std::forward>(fsrv)); + std::construct_at(session_memory, mitm_session_handle, std::forward(obj), std::forward>(fsrv)); + /* Assign session resources. */ session_memory->pointer_buffer = this->GetSessionPointerBuffer(session_memory); session_memory->saved_message = this->GetSessionSavedMessageBuffer(session_memory); + /* Validate session pointer buffer. */ AMS_ABORT_UNLESS(session_memory->pointer_buffer.GetSize() >= session_memory->forward_service->pointer_buffer_size); session_memory->pointer_buffer = cmif::PointerAndSize(session_memory->pointer_buffer.GetAddress(), session_memory->forward_service->pointer_buffer_size); + /* Register to wait list. */ this->RegisterSessionToWaitList(session_memory); return ResultSuccess(); @@ -127,15 +131,13 @@ namespace ams::sf::hipc { /* Create session handle. */ Handle mitm_session_handle; R_TRY(svcAcceptSession(&mitm_session_handle, mitm_port_handle)); - bool succeeded = false; - ON_SCOPE_EXIT { - if (!succeeded) { - R_ABORT_UNLESS(svcCloseHandle(mitm_session_handle)); - } - }; + + auto session_guard = SCOPE_GUARD { R_ABORT_UNLESS(svc::CloseHandle(mitm_session_handle)); }; + /* Register session. */ R_TRY(this->RegisterMitmSessionImpl(session_memory, mitm_session_handle, std::forward(obj), std::forward>(fsrv))); - succeeded = true; + + session_guard.Cancel(); return ResultSuccess(); } diff --git a/libraries/libvapours/include/vapours/util/util_bounded_map.hpp b/libraries/libvapours/include/vapours/util/util_bounded_map.hpp index 5d0ab6ee5..0bb290ea0 100644 --- a/libraries/libvapours/include/vapours/util/util_bounded_map.hpp +++ b/libraries/libvapours/include/vapours/util/util_bounded_map.hpp @@ -29,7 +29,7 @@ namespace ams::util { private: ALWAYS_INLINE void FreeEntry(size_t i) { this->keys[i].reset(); - GetReference(this->values[i]).~Value(); + DestroyAt(this->values[i]); } public: constexpr BoundedMap() : keys(), values() { /* ... */ } @@ -78,7 +78,7 @@ namespace ams::util { for (size_t i = 0; i < N; i++) { if (!this->keys[i]) { this->keys[i] = key; - new (GetPointer(this->values[i])) Value(std::move(value)); + ConstructAt(this->values[i], std::forward(value)); return true; } } @@ -90,7 +90,7 @@ namespace ams::util { /* Try to find and assign an existing value. */ for (size_t i = 0; i < N; i++) { if (this->keys[i] && this->keys[i].value() == key) { - GetReference(this->values[i]) = std::move(value); + GetReference(this->values[i]) = std::forward(value); return true; } } @@ -99,7 +99,7 @@ namespace ams::util { for (size_t i = 0; i < N; i++) { if (!this->keys[i]) { this->keys[i] = key; - new (GetPointer(this->values[i])) Value(std::move(value)); + ConstructAt(this->values[i], std::move(value)); return true; } } @@ -118,7 +118,7 @@ namespace ams::util { for (size_t i = 0; i < N; i++) { if (!this->keys[i]) { this->keys[i] = key; - new (GetPointer(this->values[i])) Value(std::forward(args)...); + ConstructAt(this->values[i], std::forward(args)...); return true; } } diff --git a/libraries/libvapours/include/vapours/util/util_scope_guard.hpp b/libraries/libvapours/include/vapours/util/util_scope_guard.hpp index 60b9808f8..203f43d55 100644 --- a/libraries/libvapours/include/vapours/util/util_scope_guard.hpp +++ b/libraries/libvapours/include/vapours/util/util_scope_guard.hpp @@ -36,6 +36,8 @@ namespace ams::util { ALWAYS_INLINE ScopeGuard(ScopeGuard&& rhs) : f(std::move(rhs.f)), active(rhs.active) { rhs.Cancel(); } + + ScopeGuard &operator=(ScopeGuard&& rhs) = delete; }; template diff --git a/libraries/libvapours/include/vapours/util/util_typed_storage.hpp b/libraries/libvapours/include/vapours/util/util_typed_storage.hpp index cfbb9505d..9def8bfbe 100644 --- a/libraries/libvapours/include/vapours/util/util_typed_storage.hpp +++ b/libraries/libvapours/include/vapours/util/util_typed_storage.hpp @@ -45,4 +45,46 @@ namespace ams::util { return *GetPointer(ts); } + template + static constexpr ALWAYS_INLINE T *ConstructAt(TypedStorage &ts, Args &&... args) { + return std::construct_at(GetPointer(ts), std::forward(args)...); + } + + template + static constexpr ALWAYS_INLINE void DestroyAt(TypedStorage &ts) { + return std::destroy_at(GetPointer(ts)); + } + + namespace impl { + + template + class TypedStorageGuard { + NON_COPYABLE(TypedStorageGuard); + private: + TypedStorage &m_ts; + bool m_active; + public: + template + constexpr ALWAYS_INLINE TypedStorageGuard(TypedStorage &ts, Args &&... args) : m_ts(ts), m_active(true) { + ConstructAt(m_ts, std::forward(args)...); + } + + ALWAYS_INLINE ~TypedStorageGuard() { if (m_active) { DestroyAt(m_ts); } } + + ALWAYS_INLINE void Cancel() { m_active = false; } + + ALWAYS_INLINE TypedStorageGuard(TypedStorageGuard&& rhs) : m_ts(rhs.m_ts), m_active(rhs.m_active) { + rhs.Cancel(); + } + + TypedStorageGuard &operator=(TypedStorageGuard&& rhs) = delete; + }; + + } + + template + static constexpr ALWAYS_INLINE impl::TypedStorageGuard ConstructAtGuarded(TypedStorage &ts, Args &&... args) { + return impl::TypedStorageGuard(ts, std::forward(args)...); + } + } diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_sdmmc_controller.board.nintendo_nx.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_sdmmc_controller.board.nintendo_nx.cpp index 3b16008af..bffc5d764 100644 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_sdmmc_controller.board.nintendo_nx.cpp +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_sdmmc_controller.board.nintendo_nx.cpp @@ -1085,7 +1085,7 @@ namespace ams::sdmmc::impl { /* This initializes a lot of globals in pcv, most of which we don't care about. */ /* However, we do care about the Sdmmc1PowerController. */ AMS_ABORT_UNLESS(this->power_controller == nullptr); - this->power_controller = new (GetPointer(this->power_controller_storage)) PowerController; + this->power_controller = util::ConstructAt(this->power_controller_storage); /* Perform base initialization. */ SdmmcController::Initialize(); @@ -1099,8 +1099,8 @@ namespace ams::sdmmc::impl { /* As with initialize, we mostly don't care about the globals this touches. */ /* However, we do want to finalize the Sdmmc1PowerController. */ AMS_ABORT_UNLESS(this->power_controller != nullptr); - this->power_controller->~PowerController(); this->power_controller = nullptr; + util::DestroyAt(this->power_controller_storage); /* pinmux::CloseSession(std::addressof(this->pinmux_session)); */ /* This does nothing. */ diff --git a/stratosphere/creport/source/creport_crash_report.cpp b/stratosphere/creport/source/creport_crash_report.cpp index edb0074bb..df446498d 100644 --- a/stratosphere/creport/source/creport_crash_report.cpp +++ b/stratosphere/creport/source/creport_crash_report.cpp @@ -85,8 +85,8 @@ namespace ams::creport { this->heap_handle = lmem::CreateExpHeap(this->heap_storage, sizeof(this->heap_storage), lmem::CreateOption_None); /* Allocate members. */ - this->module_list = new (lmem::AllocateFromExpHeap(this->heap_handle, sizeof(ModuleList))) ModuleList; - this->thread_list = new (lmem::AllocateFromExpHeap(this->heap_handle, sizeof(ThreadList))) ThreadList; + this->module_list = std::construct_at(static_cast(lmem::AllocateFromExpHeap(this->heap_handle, sizeof(ModuleList)))); + this->thread_list = std::construct_at(static_cast(lmem::AllocateFromExpHeap(this->heap_handle, sizeof(ThreadList)))); this->dying_message = static_cast(lmem::AllocateFromExpHeap(this->heap_handle, DyingMessageSizeMax)); if (this->dying_message != nullptr) { std::memset(this->dying_message, 0, DyingMessageSizeMax); @@ -321,8 +321,8 @@ namespace ams::creport { } /* Finalize our heap. */ - this->module_list->~ModuleList(); - this->thread_list->~ThreadList(); + std::destroy_at(this->module_list); + std::destroy_at(this->thread_list); lmem::FreeToExpHeap(this->heap_handle, this->module_list); lmem::FreeToExpHeap(this->heap_handle, this->thread_list); if (this->dying_message != nullptr) { diff --git a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp index 5fda294ae..21f027328 100644 --- a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp +++ b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp @@ -66,13 +66,13 @@ namespace ams::dmnt::cheat::impl { FrozenAddressMapEntry *AllocateFrozenAddress(u64 address, FrozenAddressValue value) { FrozenAddressMapEntry *entry = static_cast(lmem::AllocateFromUnitHeap(g_frozen_address_map_heap)); if (entry != nullptr) { - new (entry) FrozenAddressMapEntry(address, value); + std::construct_at(entry, address, value); } return entry; } void DeallocateFrozenAddress(FrozenAddressMapEntry *entry) { - entry->~FrozenAddressMapEntry(); + std::destroy_at(entry); lmem::FreeToUnitHeap(g_frozen_address_map_heap, entry); } @@ -1160,7 +1160,7 @@ namespace ams::dmnt::cheat::impl { g_frozen_address_map_heap = lmem::CreateUnitHeap(g_frozen_address_map_memory, sizeof(g_frozen_address_map_memory), sizeof(FrozenAddressMapEntry), lmem::CreateOption_ThreadSafe); /* Create the cheat process manager (spawning its threads). */ - new (GetPointer(g_cheat_process_manager)) CheatProcessManager; + util::ConstructAt(g_cheat_process_manager); } bool GetHasActiveCheatProcess() { diff --git a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_debug_events_manager.cpp b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_debug_events_manager.cpp index 07df7f143..bf4be5ef2 100644 --- a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_debug_events_manager.cpp +++ b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_debug_events_manager.cpp @@ -148,7 +148,7 @@ namespace ams::dmnt::cheat::impl { } void InitializeDebugEventsManager() { - new (GetPointer(g_events_manager)) DebugEventsManager; + util::ConstructAt(g_events_manager); } Result ContinueCheatProcess(Handle cheat_dbg_hnd) { diff --git a/stratosphere/pm/source/impl/pm_process_manager.cpp b/stratosphere/pm/source/impl/pm_process_manager.cpp index 5cc2c09c4..9e9c7650a 100644 --- a/stratosphere/pm/source/impl/pm_process_manager.cpp +++ b/stratosphere/pm/source/impl/pm_process_manager.cpp @@ -91,15 +91,20 @@ namespace ams::pm::impl { std::memset(this->process_info_allocated, 0, sizeof(this->process_info_allocated)); } - void *AllocateProcessInfoStorage() { + template + ProcessInfo *AllocateProcessInfo(Args &&... args) { std::scoped_lock lk(this->lock); + for (size_t i = 0; i < MaxProcessInfos; i++) { if (!this->process_info_allocated[i]) { this->process_info_allocated[i] = true; - std::memset(&this->process_info_storages[i], 0, sizeof(this->process_info_storages[i])); - return GetPointer(this->process_info_storages[i]); + + std::memset(this->process_info_storages + i, 0, sizeof(this->process_info_storages[i])); + + return util::ConstructAt(this->process_info_storages[i], std::forward(args)...); } } + return nullptr; } @@ -110,7 +115,7 @@ namespace ams::pm::impl { AMS_ABORT_UNLESS(index < MaxProcessInfos); AMS_ABORT_UNLESS(this->process_info_allocated[index]); - process_info->~ProcessInfo(); + util::DestroyAt(this->process_info_storages[index]); this->process_info_allocated[index] = false; } }; @@ -251,9 +256,8 @@ namespace ams::pm::impl { os::ProcessId process_id = os::GetProcessId(process_handle); /* Make new process info. */ - void *process_info_storage = g_process_info_allocator.AllocateProcessInfoStorage(); - AMS_ABORT_UNLESS(process_info_storage != nullptr); - ProcessInfo *process_info = new (process_info_storage) ProcessInfo(process_handle, process_id, pin_id, location, override_status); + ProcessInfo *process_info = g_process_info_allocator.AllocateProcessInfo(process_handle, process_id, pin_id, location, override_status); + AMS_ABORT_UNLESS(process_info != nullptr); /* Link new process info. */ { From c99ce36d7ddf7731a9a086b76fd6acebaf2f8ce9 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 21 Mar 2021 20:36:49 -0700 Subject: [PATCH 110/280] ams: convert to util::ConstructAt where appropriate --- libraries/libstratosphere/source/fs/fs_code.cpp | 6 ++---- .../source/htc/server/htc_htcmisc_hipc_server.cpp | 7 ++----- libraries/libstratosphere/source/htcfs/htcfs_client.cpp | 4 ++-- .../source/htclow/mux/htclow_mux_channel_impl_map.cpp | 2 +- 4 files changed, 7 insertions(+), 12 deletions(-) diff --git a/libraries/libstratosphere/source/fs/fs_code.cpp b/libraries/libstratosphere/source/fs/fs_code.cpp index e58acb3e3..eb4fa2956 100644 --- a/libraries/libstratosphere/source/fs/fs_code.cpp +++ b/libraries/libstratosphere/source/fs/fs_code.cpp @@ -40,12 +40,10 @@ namespace ams::fs { /* Setup the storage. */ /* NOTE: This owns the file, and so on failure it will be closed appropriately. */ - std::construct_at(GetPointer(g_stratosphere_romfs_storage), stratosphere_romfs_file, true); - auto storage_guard = SCOPE_GUARD { std::destroy_at(GetPointer(g_stratosphere_romfs_storage)); }; + auto storage_guard = util::ConstructAtGuarded(g_stratosphere_romfs_storage, stratosphere_romfs_file, true); /* Create the filesystem. */ - std::construct_at(GetPointer(g_stratosphere_romfs_fs)); - auto fs_guard = SCOPE_GUARD { std::destroy_at(GetPointer(g_stratosphere_romfs_fs)); }; + auto fs_guard = util::ConstructAtGuarded(g_stratosphere_romfs_fs); /* Initialize the filesystem. */ R_TRY(GetReference(g_stratosphere_romfs_fs).Initialize(GetPointer(g_stratosphere_romfs_storage), nullptr, 0, false)); diff --git a/libraries/libstratosphere/source/htc/server/htc_htcmisc_hipc_server.cpp b/libraries/libstratosphere/source/htc/server/htc_htcmisc_hipc_server.cpp index 6ee4fda59..08694ab63 100644 --- a/libraries/libstratosphere/source/htc/server/htc_htcmisc_hipc_server.cpp +++ b/libraries/libstratosphere/source/htc/server/htc_htcmisc_hipc_server.cpp @@ -39,11 +39,8 @@ namespace ams::htc::server { /* Check that we haven't already initialized. */ AMS_ASSERT(g_server_manager == nullptr); - /* Create the server manager. */ - std::construct_at(GetPointer(g_server_manager_storage)); - - /* Set the server manager pointer. */ - g_server_manager = GetPointer(g_server_manager_storage); + /* Create/Set the server manager pointer. */ + g_server_manager = util::ConstructAt(g_server_manager_storage); /* Create and register the htc manager object. */ HtcServiceObject *service_object; diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client.cpp b/libraries/libstratosphere/source/htcfs/htcfs_client.cpp index 37a4dc93f..d1920972e 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client.cpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client.cpp @@ -28,13 +28,13 @@ namespace ams::htcfs { void InitializeClient(htclow::HtclowManager *manager) { AMS_ASSERT(!g_initialized); - std::construct_at(GetPointer(g_client_storage), manager); + util::ConstructAt(g_client_storage, manager); } void FinalizeClient() { AMS_ASSERT(g_initialized); - std::destroy_at(GetPointer(g_client_storage)); + util::DestroyAt(g_client_storage); } Client &GetClient() { diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.cpp index 8cc55f7c3..1ab38b1f6 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.cpp @@ -56,7 +56,7 @@ namespace ams::htclow::mux { R_UNLESS(idx < MaxChannelCount, htclow::ResultOutOfChannel()); /* Create the channel impl. */ - std::construct_at(GetPointer(m_channel_storage[idx]), channel, m_packet_factory, m_state_machine, m_task_manager, m_event); + util::ConstructAt(m_channel_storage[idx], channel, m_packet_factory, m_state_machine, m_task_manager, m_event); /* Mark the storage valid. */ m_storage_valid[idx] = true; From 5666c596577df2eddc095529d1ce802ef9f842ed Mon Sep 17 00:00:00 2001 From: Adubbz Date: Sat, 20 Mar 2021 20:54:44 +1100 Subject: [PATCH 111/280] ncm: Updated ListContentId for 11.0.0 --- .../source/ncm/ncm_content_storage_impl.cpp | 202 +++++++++++++++--- .../source/ncm/ncm_content_storage_impl.hpp | 29 +++ 2 files changed, 200 insertions(+), 31 deletions(-) diff --git a/libraries/libstratosphere/source/ncm/ncm_content_storage_impl.cpp b/libraries/libstratosphere/source/ncm/ncm_content_storage_impl.cpp index bb18337b9..2787fe0e8 100644 --- a/libraries/libstratosphere/source/ncm/ncm_content_storage_impl.cpp +++ b/libraries/libstratosphere/source/ncm/ncm_content_storage_impl.cpp @@ -166,6 +166,134 @@ namespace ams::ncm { } + ContentStorageImpl::ContentIterator::~ContentIterator() { + for (size_t i = 0; i < this->depth; i++) { + fs::CloseDirectory(this->handles[i]); + } + } + + Result ContentStorageImpl::ContentIterator::Initialize(const char *root_path, size_t max_depth) { + /* Initialize tracking variables. */ + this->depth = 0; + this->max_depth = max_depth; + this->entry_count = 0; + + /* Create the base content directory path. */ + MakeBaseContentDirectoryPath(std::addressof(this->path), root_path); + + /* Open the base directory. */ + R_TRY(this->OpenCurrentDirectory()); + + return ResultSuccess(); + } + + Result ContentStorageImpl::ContentIterator::OpenCurrentDirectory() { + /* Determine valid directory mode (prior to 2.0.0, NotRequireFileSize was not valid). */ + const auto open_mode = hos::GetVersion() >= hos::Version_2_0_0 ? (fs::OpenDirectoryMode_All | fs::OpenDirectoryMode_NotRequireFileSize) : (fs::OpenDirectoryMode_All); + + /* Open the directory for our current path. */ + R_TRY(fs::OpenDirectory(std::addressof(this->handles[this->depth]), this->path, open_mode)); + + /* Increase our depth. */ + ++this->depth; + + return ResultSuccess(); + } + + Result ContentStorageImpl::ContentIterator::OpenDirectory(const char *dir) { + /* Set our current path. */ + this->path.Set(dir); + + /* Open the directory. */ + return this->OpenCurrentDirectory(); + } + + Result ContentStorageImpl::ContentIterator::GetNext(std::optional *out) { + /* Iterate until we get the next entry. */ + while (true) { + /* Ensure that we have entries loaded. */ + R_TRY(this->LoadEntries()); + + /* If we failed to load any entries, there's nothing to get. */ + if (this->entry_count <= 0) { + *out = std::nullopt; + return ResultSuccess(); + } + + /* Get the next entry. */ + const auto &entry = this->entries[--this->entry_count]; + + /* Process the current entry. */ + switch (entry.type) { + case fs::DirectoryEntryType_Directory: + /* If the entry if a directory, we want to recurse into it if we can. */ + if (this->depth < this->max_depth) { + /* Construct the full path for the subdirectory. */ + PathString entry_path; + entry_path.SetFormat("%s/%s", this->path.Get(), entry.name); + + /* Open the subdirectory. */ + R_TRY(this->OpenDirectory(entry_path.Get())); + + } + break; + case fs::DirectoryEntryType_File: + /* Otherwise, if the entry is a file, return it. */ + *out = entry; + return ResultSuccess(); + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + return ResultSuccess(); + } + + Result ContentStorageImpl::ContentIterator::LoadEntries() { + /* If we already have entries loaded, we don't need to do anything. */ + R_SUCCEED_IF(this->entry_count != 0); + + /* If we have no directories open, there's nothing for us to load. */ + if (this->depth == 0) { + this->entry_count = 0; + return ResultSuccess(); + } + + /* Determine the maximum entries that we can load. */ + const s64 max_entries = this->depth == this->max_depth ? MaxDirectoryEntries : 1; + + /* Read entries from the current directory. */ + s64 num_entries; + R_TRY(fs::ReadDirectory(std::addressof(num_entries), this->entries, this->handles[this->depth - 1], max_entries)); + + /* If we successfully read entries, load them. */ + if (num_entries > 0) { + /* Reverse the order of the loaded entries, for our future convenience. */ + for (fs::DirectoryEntry *start_entry = this->entries, *end_entry = this->entries + num_entries - 1; start_entry < end_entry; ++start_entry, --end_entry) { + std::swap(*start_entry, *end_entry); + } + + /* Set our entry count. */ + this->entry_count = num_entries; + + return ResultSuccess(); + } + + /* We didn't read any entries, so we need to advance to the next directory. */ + fs::CloseDirectory(this->handles[--this->depth]); + + /* Find the index of the parent directory's substring. */ + size_t i = this->path.GetLength() - 1; + while (this->path.Get()[i--] != '/') { + AMS_ABORT_UNLESS(i > 0); + } + + /* Set the path to the parent directory. */ + this->path.Set(this->path.GetSubstring(0, i)); + + /* Try to load again from the parent directory. */ + return this->LoadEntries(); + } + ContentStorageImpl::~ContentStorageImpl() { this->InvalidateFileCache(); } @@ -225,6 +353,7 @@ namespace ams::ncm { fs::CloseFile(this->cached_file_handle); this->cached_content_id = InvalidContentId; } + this->content_iterator = std::nullopt; } Result ContentStorageImpl::OpenContentIdFile(ContentId content_id) { @@ -445,48 +574,59 @@ namespace ams::ncm { return ResultSuccess(); } - Result ContentStorageImpl::ListContentId(sf::Out out_count, const sf::OutArray &out_buf, s32 offset) { + Result ContentStorageImpl::ListContentId(sf::Out out_count, const sf::OutArray &out, s32 offset) { R_UNLESS(offset >= 0, ncm::ResultInvalidOffset()); R_TRY(this->EnsureEnabled()); - /* Obtain the content base directory path. */ - PathString path; - MakeBaseContentDirectoryPath(std::addressof(path), this->root_path); + if (!this->content_iterator.has_value() || !this->last_content_offset.has_value() || this->last_content_offset != offset) { + /* Create and initialize the content cache. */ + this->content_iterator.emplace(); + R_TRY(this->content_iterator->Initialize(this->root_path, GetHierarchicalContentDirectoryDepth(this->make_content_path_func))); - const auto depth = GetHierarchicalContentDirectoryDepth(this->make_content_path_func); - size_t entry_count = 0; + /* Advance to the desired offset. */ + for (auto current_offset = 0; current_offset < offset; /* ... */) { + /* Get the next directory entry. */ + std::optional dir_entry; + R_TRY(this->content_iterator->GetNext(std::addressof(dir_entry))); - /* Traverse the content base directory finding all valid content. */ - R_TRY(TraverseDirectory(path, depth, [&](bool *should_continue, bool *should_retry_dir_read, const char *current_path, const fs::DirectoryEntry &entry) { - *should_retry_dir_read = false; - *should_continue = true; + /* If we run out of entries before reaching the desired offset, we're done. */ + if (!dir_entry) { + out_count.SetValue(0); + return ResultSuccess(); + } - /* We have nothing to do if not working with a file. */ - if (entry.type != fs::DirectoryEntryType_File) { - return ResultSuccess(); + /* If the current entry is a valid content id, advance. */ + if (GetContentIdFromString(dir_entry->name, std::strlen(dir_entry->name))) { + ++current_offset; + } + } + } + + /* Iterate, reading as many entries as we can. */ + s32 count = 0; + while (count < static_cast(out.GetSize())) { + /* Get the next directory entry. */ + std::optional dir_entry; + R_TRY(this->content_iterator->GetNext(std::addressof(dir_entry))); + + /* Don't continue if the directory entry is absent. */ + if (!dir_entry) { + break; } - /* Skip entries until we reach the start offset. */ - if (offset > 0) { - --offset; - return ResultSuccess(); + /* Process the entry, if it's a valid content id. */ + if (auto content_id = GetContentIdFromString(dir_entry->name, std::strlen(dir_entry->name)); content_id.has_value()) { + /* Output the content id. */ + out[count++] = *content_id; + + /* Update our last content offset. */ + this->last_content_offset = offset + count; } + } - /* We don't necessarily expect to be able to completely fill the output buffer. */ - if (entry_count >= out_buf.GetSize()) { - *should_continue = false; - return ResultSuccess(); - } + /* Set the output count. */ + *out_count = count; - auto content_id = GetContentIdFromString(entry.name, std::strlen(entry.name)); - if (content_id) { - out_buf[entry_count++] = *content_id; - } - - return ResultSuccess(); - })); - - out_count.SetValue(static_cast(entry_count)); return ResultSuccess(); } diff --git a/libraries/libstratosphere/source/ncm/ncm_content_storage_impl.hpp b/libraries/libstratosphere/source/ncm/ncm_content_storage_impl.hpp index f68e22fb4..8d8d5fc1a 100644 --- a/libraries/libstratosphere/source/ncm/ncm_content_storage_impl.hpp +++ b/libraries/libstratosphere/source/ncm/ncm_content_storage_impl.hpp @@ -22,16 +22,45 @@ namespace ams::ncm { class ContentStorageImpl : public ContentStorageImplBase { + private: + class ContentIterator { + NON_COPYABLE(ContentIterator); + NON_MOVEABLE(ContentIterator); + + static constexpr size_t MaxDirectoryHandles = 0x8; + static constexpr size_t MaxDirectoryEntries = 0x10; + + public: + fs::DirectoryHandle handles[MaxDirectoryHandles]{}; + size_t depth{}; + size_t max_depth{}; + PathString path{}; + fs::DirectoryEntry entries[MaxDirectoryEntries]{}; + s64 entry_count{}; + public: + constexpr ContentIterator() = default; + ~ContentIterator(); + + Result Initialize(const char *root_path, size_t max_depth); + Result GetNext(std::optional *out); + private: + Result OpenCurrentDirectory(); + Result OpenDirectory(const char *dir); + Result LoadEntries(); + }; protected: PlaceHolderAccessor placeholder_accessor; ContentId cached_content_id; fs::FileHandle cached_file_handle; RightsIdCache *rights_id_cache; + std::optional content_iterator; + std::optional last_content_offset; public: static Result InitializeBase(const char *root_path); static Result CleanupBase(const char *root_path); static Result VerifyBase(const char *root_path); public: + ContentStorageImpl() : rights_id_cache(nullptr), content_iterator(std::nullopt), last_content_offset(std::nullopt) { /* ... */ } ~ContentStorageImpl(); Result Initialize(const char *root_path, MakeContentPathFunction content_path_func, MakePlaceHolderPathFunction placeholder_path_func, bool delay_flush, RightsIdCache *rights_id_cache); From 75a2052144274166006ff05229447f071f983e2d Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 24 Mar 2021 07:16:55 -0700 Subject: [PATCH 112/280] ncm: fix GameCardStorageRoot mount point (closes #1404) --- .../libstratosphere/source/ncm/ncm_content_manager_impl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/libstratosphere/source/ncm/ncm_content_manager_impl.cpp b/libraries/libstratosphere/source/ncm/ncm_content_manager_impl.cpp index 60f349888..b4fa0bad3 100644 --- a/libraries/libstratosphere/source/ncm/ncm_content_manager_impl.cpp +++ b/libraries/libstratosphere/source/ncm/ncm_content_manager_impl.cpp @@ -214,7 +214,7 @@ namespace ams::ncm { /* Create a new mount name and copy it to out. */ std::strcpy(out->mount_name, impl::CreateUniqueMountName().str); - util::SNPrintf(out->path, sizeof(out->path), "%s:/", out->mount_name); + util::SNPrintf(out->path, sizeof(out->path), "%s:", out->mount_name); return ResultSuccess(); } From e3e3679cfe56869eccaab05af3e549b53c5855d7 Mon Sep 17 00:00:00 2001 From: Easy World Date: Thu, 8 Apr 2021 09:37:43 +0800 Subject: [PATCH 113/280] Update dmnt_cheat_vm.cpp * fix case fallthrough in function "LogOpcode" * use same attribute name in one opcode case --- stratosphere/dmnt/source/cheat/impl/dmnt_cheat_vm.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_vm.cpp b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_vm.cpp index 1e670cae7..0b521ceea 100644 --- a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_vm.cpp +++ b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_vm.cpp @@ -284,6 +284,7 @@ namespace ams::dmnt::cheat::impl { this->LogToDebugFile("O Reg Idx: %x\n", opcode->debug_log.ofs_reg_index); break; } + break; default: this->LogToDebugFile("Unknown opcode: %x\n", opcode->opcode); break; @@ -381,7 +382,7 @@ namespace ams::dmnt::cheat::impl { opcode.begin_cond.mem_type = (MemoryAccessType)((first_dword >> 20) & 0xF); opcode.begin_cond.cond_type = (ConditionalComparisonType)((first_dword >> 16) & 0xF); opcode.begin_cond.rel_address = ((u64)(first_dword & 0xFF) << 32ul) | ((u64)second_dword); - opcode.begin_cond.value = GetNextVmInt(opcode.store_static.bit_width); + opcode.begin_cond.value = GetNextVmInt(opcode.begin_cond.bit_width); } break; case CheatVmOpcodeType_EndConditionalBlock: @@ -1287,4 +1288,4 @@ namespace ams::dmnt::cheat::impl { } } -} \ No newline at end of file +} From f89cfe41da03796340ccda8d248f9d13b39eab13 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 5 Apr 2021 23:08:04 -0700 Subject: [PATCH 114/280] emummc: update for 12.0.0 --- emummc/source/FS/FS_offsets.c | 8 ++++ emummc/source/FS/FS_versions.h | 3 ++ emummc/source/FS/offsets/1200.h | 59 +++++++++++++++++++++++++++ emummc/source/FS/offsets/1200_exfat.h | 59 +++++++++++++++++++++++++++ 4 files changed, 129 insertions(+) create mode 100644 emummc/source/FS/offsets/1200.h create mode 100644 emummc/source/FS/offsets/1200_exfat.h diff --git a/emummc/source/FS/FS_offsets.c b/emummc/source/FS/FS_offsets.c index 86dc12d6c..e36f6ea82 100644 --- a/emummc/source/FS/FS_offsets.c +++ b/emummc/source/FS/FS_offsets.c @@ -51,6 +51,8 @@ #include "offsets/1020_exfat.h" #include "offsets/1100.h" #include "offsets/1100_exfat.h" +#include "offsets/1200.h" +#include "offsets/1200_exfat.h" #include "../utils/fatal.h" #define GET_OFFSET_STRUCT_NAME(vers) g_offsets##vers @@ -113,6 +115,8 @@ DEFINE_OFFSET_STRUCT(_1020); DEFINE_OFFSET_STRUCT(_1020_EXFAT); DEFINE_OFFSET_STRUCT(_1100); DEFINE_OFFSET_STRUCT(_1100_EXFAT); +DEFINE_OFFSET_STRUCT(_1200); +DEFINE_OFFSET_STRUCT(_1200_EXFAT); const fs_offsets_t *get_fs_offsets(enum FS_VER version) { switch (version) { @@ -186,6 +190,10 @@ const fs_offsets_t *get_fs_offsets(enum FS_VER version) { return &(GET_OFFSET_STRUCT_NAME(_1100)); case FS_VER_11_0_0_EXFAT: return &(GET_OFFSET_STRUCT_NAME(_1100_EXFAT)); + case FS_VER_12_0_0: + return &(GET_OFFSET_STRUCT_NAME(_1200)); + case FS_VER_12_0_0_EXFAT: + return &(GET_OFFSET_STRUCT_NAME(_1200_EXFAT)); default: fatal_abort(Fatal_UnknownVersion); } diff --git a/emummc/source/FS/FS_versions.h b/emummc/source/FS/FS_versions.h index 635210dcf..401112530 100644 --- a/emummc/source/FS/FS_versions.h +++ b/emummc/source/FS/FS_versions.h @@ -74,6 +74,9 @@ enum FS_VER FS_VER_11_0_0, FS_VER_11_0_0_EXFAT, + FS_VER_12_0_0, + FS_VER_12_0_0_EXFAT, + FS_VER_MAX, }; diff --git a/emummc/source/FS/offsets/1200.h b/emummc/source/FS/offsets/1200.h new file mode 100644 index 000000000..c18d4340d --- /dev/null +++ b/emummc/source/FS/offsets/1200.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef __FS_1200_H__ +#define __FS_1200_H__ + +// Accessor vtable getters +#define FS_OFFSET_1200_SDMMC_ACCESSOR_GC 0x154FD0 +#define FS_OFFSET_1200_SDMMC_ACCESSOR_SD 0x156DE0 +#define FS_OFFSET_1200_SDMMC_ACCESSOR_NAND 0x155500 + +// Hooks +#define FS_OFFSET_1200_SDMMC_WRAPPER_READ 0x150970 +#define FS_OFFSET_1200_SDMMC_WRAPPER_WRITE 0x150A30 +#define FS_OFFSET_1200_RTLD 0x688 +#define FS_OFFSET_1200_RTLD_DESTINATION ((uintptr_t)(INT64_C(-0x3C))) + +#define FS_OFFSET_1200_CLKRST_SET_MIN_V_CLK_RATE 0x14FCC0 + +// Misc funcs +#define FS_OFFSET_1200_LOCK_MUTEX 0x29350 +#define FS_OFFSET_1200_UNLOCK_MUTEX 0x293A0 + +#define FS_OFFSET_1200_SDMMC_WRAPPER_CONTROLLER_OPEN 0x150850 +#define FS_OFFSET_1200_SDMMC_WRAPPER_CONTROLLER_CLOSE 0x1508E0 + +// Misc Data +#define FS_OFFSET_1200_SD_MUTEX 0xE3D3E8 +#define FS_OFFSET_1200_NAND_MUTEX 0xE38768 +#define FS_OFFSET_1200_ACTIVE_PARTITION 0xE387A8 +#define FS_OFFSET_1200_SDMMC_DAS_HANDLE 0xE20DB0 + +// NOPs +#define FS_OFFSET_1200_SD_DAS_INIT 0x27244 + +// Nintendo Paths +#define FS_OFFSET_1200_NINTENDO_PATHS \ +{ \ + {.opcode_reg = 3, .adrp_offset = 0x0006E810, .add_rel_offset = 0x00000004}, \ + {.opcode_reg = 3, .adrp_offset = 0x0007AEC0, .add_rel_offset = 0x00000004}, \ + {.opcode_reg = 4, .adrp_offset = 0x00081254, .add_rel_offset = 0x00000004}, \ + {.opcode_reg = 4, .adrp_offset = 0x00092850, .add_rel_offset = 0x00000004}, \ + {.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \ +} + +#endif // __FS_1200_H__ diff --git a/emummc/source/FS/offsets/1200_exfat.h b/emummc/source/FS/offsets/1200_exfat.h new file mode 100644 index 000000000..b6fb7ef93 --- /dev/null +++ b/emummc/source/FS/offsets/1200_exfat.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef __FS_1200_EXFAT_H__ +#define __FS_1200_EXFAT_H__ + +// Accessor vtable getters +#define FS_OFFSET_1200_EXFAT_SDMMC_ACCESSOR_GC 0x154FD0 +#define FS_OFFSET_1200_EXFAT_SDMMC_ACCESSOR_SD 0x156DE0 +#define FS_OFFSET_1200_EXFAT_SDMMC_ACCESSOR_NAND 0x155500 + +// Hooks +#define FS_OFFSET_1200_EXFAT_SDMMC_WRAPPER_READ 0x150970 +#define FS_OFFSET_1200_EXFAT_SDMMC_WRAPPER_WRITE 0x150A30 +#define FS_OFFSET_1200_EXFAT_RTLD 0x688 +#define FS_OFFSET_1200_EXFAT_RTLD_DESTINATION ((uintptr_t)(INT64_C(-0x3C))) + +#define FS_OFFSET_1200_EXFAT_CLKRST_SET_MIN_V_CLK_RATE 0x14FCC0 + +// Misc funcs +#define FS_OFFSET_1200_EXFAT_LOCK_MUTEX 0x29350 +#define FS_OFFSET_1200_EXFAT_UNLOCK_MUTEX 0x293A0 + +#define FS_OFFSET_1200_EXFAT_SDMMC_WRAPPER_CONTROLLER_OPEN 0x150850 +#define FS_OFFSET_1200_EXFAT_SDMMC_WRAPPER_CONTROLLER_CLOSE 0x1508E0 + +// Misc Data +#define FS_OFFSET_1200_EXFAT_SD_MUTEX 0xE4B3E8 +#define FS_OFFSET_1200_EXFAT_NAND_MUTEX 0xE46768 +#define FS_OFFSET_1200_EXFAT_ACTIVE_PARTITION 0xE467A8 +#define FS_OFFSET_1200_EXFAT_SDMMC_DAS_HANDLE 0xE2EDB0 + +// NOPs +#define FS_OFFSET_1200_EXFAT_SD_DAS_INIT 0x27244 + +// Nintendo Paths +#define FS_OFFSET_1200_EXFAT_NINTENDO_PATHS \ +{ \ + {.opcode_reg = 3, .adrp_offset = 0x0006E810, .add_rel_offset = 0x00000004}, \ + {.opcode_reg = 3, .adrp_offset = 0x0007AEC0, .add_rel_offset = 0x00000004}, \ + {.opcode_reg = 4, .adrp_offset = 0x00081254, .add_rel_offset = 0x00000004}, \ + {.opcode_reg = 4, .adrp_offset = 0x00092850, .add_rel_offset = 0x00000004}, \ + {.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \ +} + +#endif // __FS_1200_EXFAT_H__ From 252ea97ca523b9c4584d0a8caf0c0a805cca186c Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 5 Apr 2021 23:15:45 -0700 Subject: [PATCH 115/280] nogc: add patches for 12.0.0 --- ...5826772EEDFC11FD8626146B388E5E59322719B6AF7.ips | Bin 0 -> 27 bytes ...CF196F940597E0B8EBFF2AC701F34F9997CCB531CDE.ips | Bin 0 -> 27 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 config_templates/kip_patches/default_nogc/D5A5BF36640C49EA1D6BC5826772EEDFC11FD8626146B388E5E59322719B6AF7.ips create mode 100644 config_templates/kip_patches/default_nogc/DC2A084996BB3C010035FCF196F940597E0B8EBFF2AC701F34F9997CCB531CDE.ips diff --git a/config_templates/kip_patches/default_nogc/D5A5BF36640C49EA1D6BC5826772EEDFC11FD8626146B388E5E59322719B6AF7.ips b/config_templates/kip_patches/default_nogc/D5A5BF36640C49EA1D6BC5826772EEDFC11FD8626146B388E5E59322719B6AF7.ips new file mode 100644 index 0000000000000000000000000000000000000000..0cc94013da11788a64a29781854c3571b2389074 GIT binary patch literal 27 icmWG=3~}}l4asC+Y!H5}!ocx>SzhY^bNn?|e>VVUI|u9l literal 0 HcmV?d00001 diff --git a/config_templates/kip_patches/default_nogc/DC2A084996BB3C010035FCF196F940597E0B8EBFF2AC701F34F9997CCB531CDE.ips b/config_templates/kip_patches/default_nogc/DC2A084996BB3C010035FCF196F940597E0B8EBFF2AC701F34F9997CCB531CDE.ips new file mode 100644 index 0000000000000000000000000000000000000000..0cc94013da11788a64a29781854c3571b2389074 GIT binary patch literal 27 icmWG=3~}}l4asC+Y!H5}!ocx>SzhY^bNn?|e>VVUI|u9l literal 0 HcmV?d00001 From 279bb863df96992098892c09db7fcf65518473b5 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 5 Apr 2021 23:48:48 -0700 Subject: [PATCH 116/280] fusee: add support for 12.0.0 kernel --- fusee/fusee-secondary/src/kernel_patches.c | 98 ++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/fusee/fusee-secondary/src/kernel_patches.c b/fusee/fusee-secondary/src/kernel_patches.c index 6678f8008..56fbe0882 100644 --- a/fusee/fusee-secondary/src/kernel_patches.c +++ b/fusee/fusee-secondary/src/kernel_patches.c @@ -568,6 +568,7 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(1000, proc_id_recv)[] = {0xA9B */ static const uint8_t MAKE_KERNEL_PATTERN_NAME(1100, proc_id_send)[] = {0xE0, 0x03, 0x15, 0xAA, 0xA8, 0x02, 0x40, 0xF9, 0x08, 0x1D, 0x40, 0xF9, 0x00, 0x01, 0x3F, 0xD6, 0x88, 0x4A, 0x3C, 0x8B, 0x09, 0xFC, 0x60, 0xD3, 0x00, 0x25, 0x00, 0x29}; static const instruction_t MAKE_KERNEL_PATCH_NAME(1100, proc_id_send)[] = {0xA9BF2FEA, 0xF94043EB, 0x5280006A, 0xD37EF54A, 0xF86A696A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000100, 0xA9BF27E8, 0xF94002A8, 0xF9401D08, 0xAA1503E0, 0xD63F0100, 0xA8C127E8, 0xAA0003E8, 0xA8C12FEA, 0xAA0803E0}; + /* stp x10, x11, [sp, #-0x10]! ldr x11, [sp, #0xE0] @@ -596,6 +597,63 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(1100, proc_id_send)[] = {0xA9B static const uint8_t MAKE_KERNEL_PATTERN_NAME(1100, proc_id_recv)[] = {0x08, 0x03, 0x40, 0xF9, 0xE0, 0x03, 0x18, 0xAA, 0x08, 0x1D, 0x40, 0xF9, 0x00, 0x01, 0x3F, 0xD6, 0xE8, 0x7F, 0x40, 0xF9, 0x09, 0xFC, 0x60, 0xD3, 0xEA, 0x5B, 0x40, 0xF9}; static const instruction_t MAKE_KERNEL_PATCH_NAME(1100, proc_id_recv)[] = {0xA9BF2FEA, 0xF94073EB, 0x5280006A, 0xD37EF54A, 0xF86A696A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000100, 0xA9BF27E8, 0xF9400308, 0xF9401D08, 0xAA1803E0, 0xD63F0100, 0xA8C127E8, 0xAA0003E8, 0xA8C12FEA, 0xAA0803E0}; +/* + stp x10, x11, [sp, #-0x10]! + ldr x11, [sp, #0x98] + mov w10, #3 + lsl x10, x10, #2 + ldr x10, [x11, x10] + mov x9, #0x0000ffffffffffff + and x8, x10, x9 + mov x9, #0xffff000000000000 + and x10, x10, x9 + mov x9, #0xfffe000000000000 + cmp x10, x9 + beq #0x20 + + stp x8, x9, [sp, #-0x10]! + ldr x8, [x22] + ldr x8, [x8, #0x38] + mov x0, x22 + blr x8 + ldp x8, x9, [sp],#0x10 + mov x8, x0 + + ldp x10, x11, [sp],#0x10 + mov x0, x8 +*/ +static const uint8_t MAKE_KERNEL_PATTERN_NAME(1200, proc_id_send)[] = {0xE0, 0x03, 0x16, 0xAA, 0xC8, 0x02, 0x40, 0xF9, 0x08, 0x1D, 0x40, 0xF9, 0x00, 0x01, 0x3F, 0xD6, 0xA8, 0x4A, 0x3B, 0x8B, 0x09, 0xFC, 0x60, 0xD3, 0x00, 0x25, 0x00, 0x29}; +static const instruction_t MAKE_KERNEL_PATCH_NAME(1200, proc_id_send)[] = {0xA9BF2FEA, 0xF9404FEB, 0x5280006A, 0xD37EF54A, 0xF86A696A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000100, 0xA9BF27E8, 0xF94002C8, 0xF9401D08, 0xAA1603E0, 0xD63F0100, 0xA8C127E8, 0xAA0003E8, 0xA8C12FEA, 0xAA0803E0}; + +/* + stp x10, x11, [sp, #-0x10]! + ldr x11, [sp, #0xE0] + mov w10, #3 + lsl x10, x10, #2 + ldr x10, [x11, x10] + mov x9, #0x0000ffffffffffff + and x8, x10, x9 + mov x9, #0xffff000000000000 + and x10, x10, x9 + mov x9, #0xfffe000000000000 + cmp x10, x9 + beq #0x20 + + stp x8, x9, [sp, #-0x10]! + ldr x8, [x28] + ldr x8, [x8, #0x38] + mov x0, x28 + blr x8 + ldp x8, x9, [sp],#0x10 + mov x8, x0 + + ldp x10, x11, [sp],#0x10 + mov x0, x8 +*/ +static const uint8_t MAKE_KERNEL_PATTERN_NAME(1200, proc_id_recv)[] = {0x88, 0x03, 0x40, 0xF9, 0xE0, 0x03, 0x1C, 0xAA, 0x08, 0x1D, 0x40, 0xF9, 0x00, 0x01, 0x3F, 0xD6, 0x08, 0x4B, 0x3A, 0x8B, 0x09, 0xFC, 0x60, 0xD3, 0x00, 0x25, 0x00, 0x29}; +static const instruction_t MAKE_KERNEL_PATCH_NAME(1200, proc_id_recv)[] = {0xA9BF2FEA, 0xF94073EB, 0x5280006A, 0xD37EF54A, 0xF86A696A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000100, 0xA9BF27E8, 0xF9400388, 0xF9401D08, 0xAA1C03E0, 0xD63F0100, 0xA8C127E8, 0xAA0003E8, 0xA8C12FEA, 0xAA0803E0}; + + /* svcControlCodeMemory Patches */ /* b.eq -> nop */ static const instruction_t MAKE_KERNEL_PATCH_NAME(500, svc_control_codememory)[] = {MAKE_NOP}; @@ -605,6 +663,7 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(800, svc_control_codememory)[ static const instruction_t MAKE_KERNEL_PATCH_NAME(900, svc_control_codememory)[] = {MAKE_NOP}; static const instruction_t MAKE_KERNEL_PATCH_NAME(1000, svc_control_codememory)[] = {MAKE_NOP}; static const instruction_t MAKE_KERNEL_PATCH_NAME(1100, svc_control_codememory)[] = {MAKE_NOP}; +static const instruction_t MAKE_KERNEL_PATCH_NAME(1200, svc_control_codememory)[] = {MAKE_NOP}; static const instruction_t MAKE_KERNEL_PATCH_NAME(500, system_memory_increase)[] = {0x52A3C008}; /* MOV W8, #0x1E000000 */ static const instruction_t MAKE_KERNEL_PATCH_NAME(600, system_memory_increase)[] = {0x52A3B008}; /* MOV W8, #0x1D800000 */ @@ -613,6 +672,7 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(800, system_memory_increase)[ static const instruction_t MAKE_KERNEL_PATCH_NAME(900, system_memory_increase)[] = {0x52A3B013}; /* MOV W19, #0x1D800000 */ static const instruction_t MAKE_KERNEL_PATCH_NAME(1000, system_memory_increase)[] = {0x52A3B013}; /* MOV W19, #0x1D800000 */ static const instruction_t MAKE_KERNEL_PATCH_NAME(1100, system_memory_increase)[] = {0x52A3B015}; /* MOV W21, #0x1D800000 */ +static const instruction_t MAKE_KERNEL_PATCH_NAME(1200, system_memory_increase)[] = {0x52A3B015}; /* MOV W21, #0x1D800000 */ /* Hook Definitions. */ static const kernel_patch_t g_kernel_patches_100[] = { @@ -935,6 +995,35 @@ static const kernel_patch_t g_kernel_patches_1101[] = { } }; +static const kernel_patch_t g_kernel_patches_1200[] = { + { /* Send Message Process ID Patch. */ + .pattern_size = 0x1C, + .pattern = MAKE_KERNEL_PATTERN_NAME(1200, proc_id_send), + .pattern_hook_offset = 0x0, + .payload_num_instructions = sizeof(MAKE_KERNEL_PATCH_NAME(1200, proc_id_send))/sizeof(instruction_t), + .branch_back_offset = 0x10, + .payload = MAKE_KERNEL_PATCH_NAME(1200, proc_id_send) + }, + { /* Receive Message Process ID Patch. */ + .pattern_size = 0x1C, + .pattern = MAKE_KERNEL_PATTERN_NAME(1200, proc_id_recv), + .pattern_hook_offset = 0x0, + .payload_num_instructions = sizeof(MAKE_KERNEL_PATCH_NAME(1200, proc_id_recv))/sizeof(instruction_t), + .branch_back_offset = 0x10, + .payload = MAKE_KERNEL_PATCH_NAME(1200, proc_id_recv) + }, + { /* svcControlCodeMemory Patch. */ + .payload_num_instructions = sizeof(MAKE_KERNEL_PATCH_NAME(1200, svc_control_codememory))/sizeof(instruction_t), + .payload = MAKE_KERNEL_PATCH_NAME(1200, svc_control_codememory), + .patch_offset = 0x2FCB4, + }, + { /* System Memory Increase Patch. */ + .payload_num_instructions = sizeof(MAKE_KERNEL_PATCH_NAME(1200, system_memory_increase))/sizeof(instruction_t), + .payload = MAKE_KERNEL_PATCH_NAME(1200, system_memory_increase), + .patch_offset = 0x4809C, + } +}; + #define KERNEL_PATCHES(vers) .num_patches = sizeof(g_kernel_patches_##vers)/sizeof(kernel_patch_t), .patches = g_kernel_patches_##vers, /* Kernel Infos. */ @@ -1038,6 +1127,15 @@ static const kernel_info_t g_kernel_infos[] = { .embedded_ini_ptr = 0x180, .free_code_space_offset = 0x49EE8, KERNEL_PATCHES(1101) + }, + { /* 12.0.0. */ + .hash = {0x8D, 0x4A, 0x1E, 0xFC, 0xCC, 0x6C, 0xFE, 0x6C, 0x45, 0x14, 0x13, 0xA1, 0x7F, 0xF6, 0xDF, 0xFD, 0x7E, 0x5D, 0xD1, 0x38, 0xCE, 0x86, 0x11, 0x8B, 0x58, 0x5F, 0x89, 0x67, 0x84, 0x48, 0xA8, 0x17, }, + .hash_offset = 0x1C4, + .hash_size = 0x68000 - 0x1C4, + .embedded_ini_offset = 0x68000, + .embedded_ini_ptr = 0x180, + .free_code_space_offset = 0x48810, + KERNEL_PATCHES(1200) } }; From 46612156f459dc6d628cf3a1f809b8dbe23b3f64 Mon Sep 17 00:00:00 2001 From: hexkyz Date: Tue, 6 Apr 2021 21:37:46 +0100 Subject: [PATCH 117/280] exo: add new dram ID --- exosphere/program/source/smc/secmon_smc_info.cpp | 4 ++-- libraries/libexosphere/include/exosphere/fuse.hpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/exosphere/program/source/smc/secmon_smc_info.cpp b/exosphere/program/source/smc/secmon_smc_info.cpp index de7fbeb72..68efc2990 100644 --- a/exosphere/program/source/smc/secmon_smc_info.cpp +++ b/exosphere/program/source/smc/secmon_smc_info.cpp @@ -47,9 +47,9 @@ namespace ams::secmon::smc { [fuse::DramId_IcosaSamsung4GB] = pkg1::MemorySize_4GB, [fuse::DramId_IcosaHynix4GB] = pkg1::MemorySize_4GB, [fuse::DramId_IcosaMicron4GB] = pkg1::MemorySize_4GB, - [fuse::DramId_AulaHynix1y4GB] = pkg1::MemorySize_4GB, + [fuse::DramId_IowaHynix1y4GB] = pkg1::MemorySize_4GB, [fuse::DramId_IcosaSamsung6GB] = pkg1::MemorySize_6GB, - [fuse::DramId_CopperHynix4GB] = pkg1::MemorySize_4GB, + [fuse::DramId_HoagHynix1y4GB] = pkg1::MemorySize_4GB, [fuse::DramId_CopperMicron4GB] = pkg1::MemorySize_4GB, [fuse::DramId_IowaX1X2Samsung4GB] = pkg1::MemorySize_4GB, [fuse::DramId_IowaSansung4GB] = pkg1::MemorySize_4GB, diff --git a/libraries/libexosphere/include/exosphere/fuse.hpp b/libraries/libexosphere/include/exosphere/fuse.hpp index cf8b54e9d..3c042db84 100644 --- a/libraries/libexosphere/include/exosphere/fuse.hpp +++ b/libraries/libexosphere/include/exosphere/fuse.hpp @@ -51,9 +51,9 @@ namespace ams::fuse { DramId_IcosaSamsung4GB = 0, DramId_IcosaHynix4GB = 1, DramId_IcosaMicron4GB = 2, - DramId_AulaHynix1y4GB = 3, + DramId_IowaHynix1y4GB = 3, DramId_IcosaSamsung6GB = 4, - DramId_CopperHynix4GB = 5, + DramId_HoagHynix1y4GB = 5, DramId_CopperMicron4GB = 6, DramId_IowaX1X2Samsung4GB = 7, DramId_IowaSansung4GB = 8, From 4b9e7c7d27aef7d3ce3ad726aeb92154d2a8374b Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 6 Apr 2021 22:26:52 -0700 Subject: [PATCH 118/280] kern: bump svc limit to 192 from 128 --- .../arch/arm64/kern_assembly_offsets.h | 27 +++++++++++++++++ .../include/mesosphere/kern_common.hpp | 1 + .../include/mesosphere/kern_k_thread.hpp | 21 ++++++++++---- .../mesosphere/kern_select_assembly_offsets.h | 26 +++++++++++++++++ .../mesosphere/svc/kern_svc_prototypes.hpp | 2 +- .../arch/arm64/svc/kern_svc_exception_asm.s | 5 ++-- .../arch/arm64/svc/kern_svc_handlers_asm.s | 29 ++++++++++--------- .../arch/arm64/kern_exception_handlers_asm.s | 9 +++--- .../source/arch/arm64/kern_k_scheduler_asm.s | 5 ++-- .../arch/arm64/kern_k_thread_context_asm.s | 5 ++-- 10 files changed, 100 insertions(+), 30 deletions(-) create mode 100644 libraries/libmesosphere/include/mesosphere/arch/arm64/kern_assembly_offsets.h create mode 100644 libraries/libmesosphere/include/mesosphere/kern_select_assembly_offsets.h diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_assembly_offsets.h b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_assembly_offsets.h new file mode 100644 index 000000000..f756d85c1 --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_assembly_offsets.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#define THREAD_STACK_PARAMETERS_SIZE 0x30 +#define THREAD_STACK_PARAMETERS_SVC_PERMISSION 0x00 +#define THREAD_STACK_PARAMETERS_CONTEXT 0x18 +#define THREAD_STACK_PARAMETERS_CUR_THREAD 0x20 +#define THREAD_STACK_PARAMETERS_DISABLE_COUNT 0x28 +#define THREAD_STACK_PARAMETERS_DPC_FLAGS 0x2A +#define THREAD_STACK_PARAMETERS_CURRENT_SVC_ID 0x2B +#define THREAD_STACK_PARAMETERS_IS_CALLING_SVC 0x2C +#define THREAD_STACK_PARAMETERS_IS_IN_EXCEPTION_HANDLER 0x2D +#define THREAD_STACK_PARAMETERS_IS_PINNED 0x2E \ No newline at end of file diff --git a/libraries/libmesosphere/include/mesosphere/kern_common.hpp b/libraries/libmesosphere/include/mesosphere/kern_common.hpp index 091d4ba68..7e4bd00cd 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_common.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_common.hpp @@ -17,6 +17,7 @@ #include #include #include +#include namespace ams::kern { diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp index 184d55f37..0220b75ff 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp @@ -82,17 +82,28 @@ namespace ams::kern { }; struct StackParameters { - alignas(0x10) u8 svc_permission[0x10]; + alignas(0x10) u8 svc_permission[0x18]; + KThreadContext *context; + KThread *cur_thread; + s16 disable_count; std::atomic dpc_flags; u8 current_svc_id; bool is_calling_svc; bool is_in_exception_handler; bool is_pinned; - s32 disable_count; - KThreadContext *context; - KThread *cur_thread; }; static_assert(alignof(StackParameters) == 0x10); + static_assert(sizeof(StackParameters) == THREAD_STACK_PARAMETERS_SIZE); + + static_assert(__builtin_offsetof(StackParameters, svc_permission) == THREAD_STACK_PARAMETERS_SVC_PERMISSION); + static_assert(__builtin_offsetof(StackParameters, context) == THREAD_STACK_PARAMETERS_CONTEXT); + static_assert(__builtin_offsetof(StackParameters, cur_thread) == THREAD_STACK_PARAMETERS_CUR_THREAD); + static_assert(__builtin_offsetof(StackParameters, disable_count) == THREAD_STACK_PARAMETERS_DISABLE_COUNT); + static_assert(__builtin_offsetof(StackParameters, dpc_flags) == THREAD_STACK_PARAMETERS_DPC_FLAGS); + static_assert(__builtin_offsetof(StackParameters, current_svc_id) == THREAD_STACK_PARAMETERS_CURRENT_SVC_ID); + static_assert(__builtin_offsetof(StackParameters, is_calling_svc) == THREAD_STACK_PARAMETERS_IS_CALLING_SVC); + static_assert(__builtin_offsetof(StackParameters, is_in_exception_handler) == THREAD_STACK_PARAMETERS_IS_IN_EXCEPTION_HANDLER); + static_assert(__builtin_offsetof(StackParameters, is_pinned) == THREAD_STACK_PARAMETERS_IS_PINNED); struct QueueEntry { private: @@ -251,7 +262,7 @@ namespace ams::kern { return *(reinterpret_cast(m_kernel_stack_top) - 1); } public: - ALWAYS_INLINE s32 GetDisableDispatchCount() const { + ALWAYS_INLINE s16 GetDisableDispatchCount() const { MESOSPHERE_ASSERT_THIS(); return this->GetStackParameters().disable_count; } diff --git a/libraries/libmesosphere/include/mesosphere/kern_select_assembly_offsets.h b/libraries/libmesosphere/include/mesosphere/kern_select_assembly_offsets.h new file mode 100644 index 000000000..30d6e2c07 --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/kern_select_assembly_offsets.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#ifdef ATMOSPHERE_ARCH_ARM64 + + #include + +#else + + #error "Unknown architecture for CPU" + +#endif diff --git a/libraries/libmesosphere/include/mesosphere/svc/kern_svc_prototypes.hpp b/libraries/libmesosphere/include/mesosphere/svc/kern_svc_prototypes.hpp index dddff3139..078b2cd2b 100644 --- a/libraries/libmesosphere/include/mesosphere/svc/kern_svc_prototypes.hpp +++ b/libraries/libmesosphere/include/mesosphere/svc/kern_svc_prototypes.hpp @@ -20,7 +20,7 @@ namespace ams::kern::svc { - static constexpr size_t NumSupervisorCalls = 0x80; + static constexpr size_t NumSupervisorCalls = 0xC0; #define AMS_KERN_SVC_DECLARE_ENUM_ID(ID, RETURN_TYPE, NAME, ...) \ SvcId_##NAME = ID, diff --git a/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_exception_asm.s b/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_exception_asm.s index c3e2a74d5..e3f72e410 100644 --- a/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_exception_asm.s +++ b/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_exception_asm.s @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#include /* ams::kern::svc::CallReturnFromException64(Result result) */ .section .text._ZN3ams4kern3svc25CallReturnFromException64Ev, "ax", %progbits @@ -62,7 +63,7 @@ _ZN3ams4kern3svc14RestoreContextEm: 0: /* We should handle DPC. */ /* Check the dpc flags. */ - ldrb w8, [sp, #(0x120 + 0x10)] + ldrb w8, [sp, #(0x120 + THREAD_STACK_PARAMETERS_DPC_FLAGS)] cbz w8, 1f /* We have DPC to do! */ @@ -82,7 +83,7 @@ _ZN3ams4kern3svc14RestoreContextEm: 1: /* We're done with DPC, and should return from the svc. */ /* Clear our in-SVC note. */ - strb wzr, [sp, #(0x120 + 0x12)] + strb wzr, [sp, #(0x120 + THREAD_STACK_PARAMETERS_IS_CALLING_SVC)] /* Restore registers. */ ldp x30, x8, [sp, #(8 * 30)] diff --git a/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_handlers_asm.s b/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_handlers_asm.s index 3de6e3c88..eaffea9cd 100644 --- a/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_handlers_asm.s +++ b/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_handlers_asm.s @@ -14,6 +14,7 @@ * along with this program. If not, see . */ #include +#include /* ams::kern::arch::arm64::SvcHandler64() */ .section .text._ZN3ams4kern4arch5arm6412SvcHandler64Ev, "ax", %progbits @@ -32,7 +33,7 @@ _ZN3ams4kern4arch5arm6412SvcHandler64Ev: mrs x9, elr_el1 mrs x10, spsr_el1 mrs x11, tpidr_el0 - ldr x18, [sp, #(0x120 + 0x28)] + ldr x18, [sp, #(0x120 + THREAD_STACK_PARAMETERS_CUR_THREAD)] /* Save callee-saved registers. */ stp x19, x20, [sp, #(8 * 19)] @@ -59,7 +60,7 @@ _ZN3ams4kern4arch5arm6412SvcHandler64Ev: /* Check the specific SVC permission bit for allowal. */ mov x9, sp add x9, x9, x8, lsr#3 - ldrb w9, [x9, #0x120] + ldrb w9, [x9, #(0x120 + THREAD_STACK_PARAMETERS_SVC_PERMISSION)] and x10, x8, #0x7 lsr x10, x9, x10 tst x10, #1 @@ -71,7 +72,7 @@ _ZN3ams4kern4arch5arm6412SvcHandler64Ev: cbz w10, 1f /* It might not, so check the stack params to see if we must not allow the SVC. */ - ldrb w10, [sp, #(0x120 + 0x14)] + ldrb w10, [sp, #(0x120 + THREAD_STACK_PARAMETERS_IS_PINNED)] cbz w10, 3f 1: /* We can call the SVC. */ @@ -81,8 +82,8 @@ _ZN3ams4kern4arch5arm6412SvcHandler64Ev: /* Note that we're calling the SVC. */ mov w10, #1 - strb w10, [sp, #(0x120 + 0x12)] - strb w8, [sp, #(0x120 + 0x11)] + strb w10, [sp, #(0x120 + THREAD_STACK_PARAMETERS_IS_CALLING_SVC)] + strb w8, [sp, #(0x120 + THREAD_STACK_PARAMETERS_CURRENT_SVC_ID)] /* If we should, trace the svc entry. */ #if defined(MESOSPHERE_BUILD_FOR_TRACING) @@ -109,7 +110,7 @@ _ZN3ams4kern4arch5arm6412SvcHandler64Ev: 2: /* We completed the SVC, and we should handle DPC. */ /* Check the dpc flags. */ - ldrb w8, [sp, #(0x120 + 0x10)] + ldrb w8, [sp, #(0x120 + THREAD_STACK_PARAMETERS_DPC_FLAGS)] cbz w8, 4f /* We have DPC to do! */ @@ -179,7 +180,7 @@ _ZN3ams4kern4arch5arm6412SvcHandler64Ev: 4: /* Return from SVC. */ /* Clear our in-SVC note. */ - strb wzr, [sp, #(0x120 + 0x12)] + strb wzr, [sp, #(0x120 + THREAD_STACK_PARAMETERS_IS_CALLING_SVC)] /* If we should, trace the svc exit. */ #if defined(MESOSPHERE_BUILD_FOR_TRACING) @@ -245,7 +246,7 @@ _ZN3ams4kern4arch5arm6412SvcHandler32Ev: mrs x17, elr_el1 mrs x20, spsr_el1 mrs x19, tpidr_el0 - ldr x18, [sp, #(0x120 + 0x28)] + ldr x18, [sp, #(0x120 + THREAD_STACK_PARAMETERS_CUR_THREAD)] stp x17, x20, [sp, #(8 * 32)] str x19, [sp, #(8 * 34)] @@ -268,7 +269,7 @@ _ZN3ams4kern4arch5arm6412SvcHandler32Ev: /* Check the specific SVC permission bit for allowal. */ mov x20, sp add x20, x20, x16, lsr#3 - ldrb w20, [x20, #0x120] + ldrb w20, [x20, #(0x120 + THREAD_STACK_PARAMETERS_SVC_PERMISSION)] and x17, x16, #0x7 lsr x17, x20, x17 tst x17, #1 @@ -280,7 +281,7 @@ _ZN3ams4kern4arch5arm6412SvcHandler32Ev: cbz w15, 1f /* It might not, so check the stack params to see if we must not allow the SVC. */ - ldrb w15, [sp, #(0x120 + 0x14)] + ldrb w15, [sp, #(0x120 + THREAD_STACK_PARAMETERS_IS_PINNED)] cbz w15, 3f 1: /* We can call the SVC. */ @@ -290,8 +291,8 @@ _ZN3ams4kern4arch5arm6412SvcHandler32Ev: /* Note that we're calling the SVC. */ mov w15, #1 - strb w15, [sp, #(0x120 + 0x12)] - strb w16, [sp, #(0x120 + 0x11)] + strb w15, [sp, #(0x120 + THREAD_STACK_PARAMETERS_IS_CALLING_SVC)] + strb w16, [sp, #(0x120 + THREAD_STACK_PARAMETERS_CURRENT_SVC_ID)] /* If we should, trace the svc entry. */ #if defined(MESOSPHERE_BUILD_FOR_TRACING) @@ -318,7 +319,7 @@ _ZN3ams4kern4arch5arm6412SvcHandler32Ev: 2: /* We completed the SVC, and we should handle DPC. */ /* Check the dpc flags. */ - ldrb w16, [sp, #(0x120 + 0x10)] + ldrb w16, [sp, #(0x120 + THREAD_STACK_PARAMETERS_DPC_FLAGS)] cbz w16, 4f /* We have DPC to do! */ @@ -376,7 +377,7 @@ _ZN3ams4kern4arch5arm6412SvcHandler32Ev: 4: /* Return from SVC. */ /* Clear our in-SVC note. */ - strb wzr, [sp, #(0x120 + 0x12)] + strb wzr, [sp, #(0x120 + THREAD_STACK_PARAMETERS_IS_CALLING_SVC)] /* If we should, trace the svc exit. */ #if defined(MESOSPHERE_BUILD_FOR_TRACING) diff --git a/mesosphere/kernel/source/arch/arm64/kern_exception_handlers_asm.s b/mesosphere/kernel/source/arch/arm64/kern_exception_handlers_asm.s index 6b5ab1675..f7645ee1e 100644 --- a/mesosphere/kernel/source/arch/arm64/kern_exception_handlers_asm.s +++ b/mesosphere/kernel/source/arch/arm64/kern_exception_handlers_asm.s @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#include /* ams::kern::arch::arm64::EL1IrqExceptionHandler() */ .section .text._ZN3ams4kern4arch5arm6422EL1IrqExceptionHandlerEv, "ax", %progbits @@ -99,7 +100,7 @@ _ZN3ams4kern4arch5arm6422EL0IrqExceptionHandlerEv: str x23, [sp, #(8 * 34)] /* Invoke KInterruptManager::HandleInterrupt(bool user_mode). */ - ldr x18, [sp, #(0x120 + 0x28)] + ldr x18, [sp, #(0x120 + THREAD_STACK_PARAMETERS_CUR_THREAD)] mov x0, #1 bl _ZN3ams4kern4arch5arm6417KInterruptManager15HandleInterruptEb @@ -196,7 +197,7 @@ _ZN3ams4kern4arch5arm6430EL0SynchronousExceptionHandlerEv: str x23, [sp, #(8 * 34)] /* Call ams::kern::arch::arm64::HandleException(ams::kern::arch::arm64::KExceptionContext *) */ - ldr x18, [sp, #(0x120 + 0x28)] + ldr x18, [sp, #(0x120 + THREAD_STACK_PARAMETERS_CUR_THREAD)] mov x0, sp bl _ZN3ams4kern4arch5arm6415HandleExceptionEPNS2_17KExceptionContextE @@ -440,7 +441,7 @@ _ZN3ams4kern4arch5arm6425FpuAccessExceptionHandlerEv: stp x20, x21, [sp, #(8 * 32)] /* Invoke the FPU context switch handler. */ - ldr x18, [sp, #(0x120 + 0x28)] + ldr x18, [sp, #(0x120 + THREAD_STACK_PARAMETERS_CUR_THREAD)] bl _ZN3ams4kern4arch5arm6423FpuContextSwitchHandlerEv /* Restore registers that we saved. */ @@ -554,7 +555,7 @@ _ZN3ams4kern4arch5arm6421EL0SystemErrorHandlerEv: str x23, [sp, #(8 * 34)] /* Invoke ams::kern::arch::arm64::HandleException(ams::kern::arch::arm64::KExceptionContext *). */ - ldr x18, [sp, #(0x120 + 0x28)] + ldr x18, [sp, #(0x120 + THREAD_STACK_PARAMETERS_CUR_THREAD)] mov x0, sp bl _ZN3ams4kern4arch5arm6415HandleExceptionEPNS2_17KExceptionContextE diff --git a/mesosphere/kernel/source/arch/arm64/kern_k_scheduler_asm.s b/mesosphere/kernel/source/arch/arm64/kern_k_scheduler_asm.s index 64e27a656..5ac0af70c 100644 --- a/mesosphere/kernel/source/arch/arm64/kern_k_scheduler_asm.s +++ b/mesosphere/kernel/source/arch/arm64/kern_k_scheduler_asm.s @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#include #define SAVE_THREAD_CONTEXT(ctx, tmp0, tmp1, done_label) \ /* Save the callee save registers + SP and cpacr. */ \ @@ -148,11 +149,11 @@ _ZN3ams4kern10KScheduler12ScheduleImplEv: and x2, x2, #~(0x1000-1) /* Check if the thread has terminated. We can do this by checking the DPC flags for DpcFlag_Terminated. */ - ldurb w3, [x2, #-0x20] + ldurb w3, [x2, #-(THREAD_STACK_PARAMETERS_SIZE - THREAD_STACK_PARAMETERS_DPC_FLAGS)] tbnz w3, #1, 3f /* The current thread hasn't terminated, so we want to save its context. */ - ldur x2, [x2, #-0x10] + ldur x2, [x2, #-(THREAD_STACK_PARAMETERS_SIZE - THREAD_STACK_PARAMETERS_CONTEXT)] SAVE_THREAD_CONTEXT(x2, x4, x5, 2f) 2: /* We're done saving this thread's context, so we need to unlock it. */ diff --git a/mesosphere/kernel/source/arch/arm64/kern_k_thread_context_asm.s b/mesosphere/kernel/source/arch/arm64/kern_k_thread_context_asm.s index c50e28dac..0e914eb60 100644 --- a/mesosphere/kernel/source/arch/arm64/kern_k_thread_context_asm.s +++ b/mesosphere/kernel/source/arch/arm64/kern_k_thread_context_asm.s @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#include /* ams::kern::arch::arm64::UserModeThreadStarter() */ .section .text._ZN3ams4kern4arch5arm6421UserModeThreadStarterEv, "ax", %progbits @@ -26,7 +27,7 @@ _ZN3ams4kern4arch5arm6421UserModeThreadStarterEv: /* | KExceptionContext (size 0x120) | KThread::StackParameters (size 0x30) | */ /* Clear the disable count for this thread's stack parameters. */ - str wzr, [sp, #(0x120 + 0x18)] + strh wzr, [sp, #(0x120 + THREAD_STACK_PARAMETERS_DISABLE_COUNT)] /* Call ams::kern::arch::arm64::OnThreadStart() */ bl _ZN3ams4kern4arch5arm6413OnThreadStartEv @@ -78,7 +79,7 @@ _ZN3ams4kern4arch5arm6427SupervisorModeThreadStarterEv: ldp x0, x1, [sp], #0x10 /* Clear the disable count for this thread's stack parameters. */ - str wzr, [sp, #(0x18)] + strh wzr, [sp, #(THREAD_STACK_PARAMETERS_DISABLE_COUNT)] /* Mask I bit in DAIF */ msr daifclr, #2 From b3bd4436366e86c0adbcadb74dadf1c83b2116d0 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 6 Apr 2021 23:07:58 -0700 Subject: [PATCH 119/280] svc: sanitize booleans in autogenerated abi stubs --- .../impl/svc_codegen_impl_code_generator.hpp | 5 +++ .../svc_codegen_impl_kernel_svc_wrapper.hpp | 35 +++++++++++++++++++ .../codegen/impl/svc_codegen_impl_layout.hpp | 25 ++++++------- .../impl/svc_codegen_impl_meta_code.hpp | 9 +++++ .../impl/svc_codegen_impl_parameter.hpp | 12 +++++-- 5 files changed, 71 insertions(+), 15 deletions(-) diff --git a/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_code_generator.hpp b/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_code_generator.hpp index 88866a051..d0233ef7d 100644 --- a/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_code_generator.hpp +++ b/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_code_generator.hpp @@ -189,6 +189,11 @@ namespace ams::svc::codegen::impl { __asm__ __volatile__("mov x%c[dst], x%c[src]" :: [dst]"i"(Dst), [src]"i"(Src) : "memory"); } + template + static ALWAYS_INLINE void ConvertToBoolean() { + __asm__ __volatile__("and x%c[reg], x%c[reg], #1" :: [reg]"i"(Reg) : "memory"); + } + template static ALWAYS_INLINE void LoadFromStack() { if constexpr (Size == 4) { diff --git a/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_kernel_svc_wrapper.hpp b/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_kernel_svc_wrapper.hpp index e553ae080..49af7dc43 100644 --- a/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_kernel_svc_wrapper.hpp +++ b/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_kernel_svc_wrapper.hpp @@ -317,6 +317,38 @@ namespace ams::svc::codegen::impl { return true; } + template + static constexpr void SanitizeInputBooleans(MetaCodeGenerator &mcg) { + /* Get the input layout. */ + constexpr auto InputLayout = Conversion::LayoutForSvc.GetInputLayout(); + + /* Check if we're done. */ + if constexpr (ParameterIndex < InputLayout.GetNumParameters()) { + /* Get the relevant parameter. */ + constexpr auto Param = InputLayout.GetParameter(ParameterIndex); + + /* Handle the case where the parameter is a boolean. */ + if constexpr (Param.IsBoolean()) { + /* Boolean parameters should have one location. */ + static_assert(Param.GetNumLocations() == 1); + + /* Get the location. */ + constexpr auto Loc = Param.GetLocation(0); + + /* TODO: Support boolean parameters passed-by-stack. */ + static_assert(Loc.GetStorage() == Storage::Register); + + /* Convert the input to boolean. */ + mcg.template ConvertToBoolean(); + } + + /* Handle the next parameter. */ + if constexpr (ParameterIndex + 1 < InputLayout.GetNumParameters()) { + SanitizeInputBooleans(mcg); + } + } + } + template struct TypeIndexFilter { @@ -436,6 +468,9 @@ namespace ams::svc::codegen::impl { mcg.template AllocateStackSpace(); } + /* Sanitize all input booleans. */ + SanitizeInputBooleans(mcg); + /* Generate code for before operations. */ if constexpr (Conversion::NumBeforeOperations > 0) { allocator = GenerateBeforeOperations(mcg, typename Conversion::BeforeOperations{}); diff --git a/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_layout.hpp b/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_layout.hpp index 4a6adadd8..ad8d5748c 100644 --- a/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_layout.hpp +++ b/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_layout.hpp @@ -36,21 +36,21 @@ namespace ams::svc::codegen::impl { : abi(a), num_parameters(0), parameters() { /* ... */ } - constexpr void AddSingle(Parameter::Identifier id, ArgumentType type, size_t ts, size_t ps, bool p, Storage s, size_t idx) { + constexpr void AddSingle(Parameter::Identifier id, ArgumentType type, size_t ts, size_t ps, bool p, bool b, Storage s, size_t idx) { for (size_t i = 0; i < this->num_parameters; i++) { if (this->parameters[i].Is(id)) { this->parameters[i].AddLocation(Location(s, idx)); return; } } - this->parameters[this->num_parameters++] = Parameter(id, type, ts, ps, p, Location(s, idx)); + this->parameters[this->num_parameters++] = Parameter(id, type, ts, ps, p, b, Location(s, idx)); } - constexpr size_t Add(Parameter::Identifier id, ArgumentType type, size_t ts, size_t ps, bool p, Storage s, size_t i) { + constexpr size_t Add(Parameter::Identifier id, ArgumentType type, size_t ts, size_t ps, bool p, bool b, Storage s, size_t i) { size_t required_registers = 0; while (required_registers * this->abi.register_size < ps) { - this->AddSingle(id, type, ts, ps, p, s, i++); + this->AddSingle(id, type, ts, ps, p, b, s, i++); required_registers++; } @@ -115,6 +115,7 @@ namespace ams::svc::codegen::impl { constexpr size_t ArgumentTypeSize = AbiType::template Size; constexpr bool PassedByPointer = IsPassedByPointer; + constexpr bool IsBoolean = std::same_as; constexpr size_t ArgumentPassSize = PassedByPointer ? AbiType::PointerSize : ArgumentTypeSize; /* TODO: Is there ever a case where this is not the correct alignment? */ @@ -135,19 +136,19 @@ namespace ams::svc::codegen::impl { const size_t registers_available = AbiType::RegisterCount - NGRN; if constexpr (!PassedByPointer && IsIntegralOrUserPointer && ArgumentTypeSize > AbiType::RegisterSize) { if (registers_available >= 2) { - this->input.Add(id, Type, ArgumentTypeSize, ArgumentPassSize, PassedByPointer, Storage::Register, NGRN); + this->input.Add(id, Type, ArgumentTypeSize, ArgumentPassSize, PassedByPointer, IsBoolean, Storage::Register, NGRN); NGRN += 2; } else { /* Argument went on stack, so stop allocating arguments in registers. */ NGRN = AbiType::RegisterCount; NSAA += (NSAA & 1); - this->input.Add(id, Type, ArgumentTypeSize, ArgumentPassSize, PassedByPointer, Storage::Stack, NSAA); + this->input.Add(id, Type, ArgumentTypeSize, ArgumentPassSize, PassedByPointer, IsBoolean, Storage::Stack, NSAA); NSAA += 2; } } else { if (ArgumentPassSize <= AbiType::RegisterSize * registers_available) { - NGRN += this->input.Add(id, Type, ArgumentTypeSize, ArgumentPassSize, PassedByPointer, Storage::Register, NGRN); + NGRN += this->input.Add(id, Type, ArgumentTypeSize, ArgumentPassSize, PassedByPointer, IsBoolean, Storage::Register, NGRN); } else { /* Argument went on stack, so stop allocating arguments in registers. */ NGRN = AbiType::RegisterCount; @@ -155,7 +156,7 @@ namespace ams::svc::codegen::impl { /* TODO: Stack pointer alignment is only ensured for aapcs64. */ /* What should we do here? */ - NSAA += this->input.Add(id, Type, ArgumentTypeSize, ArgumentPassSize, PassedByPointer, Storage::Stack, NSAA); + NSAA += this->input.Add(id, Type, ArgumentTypeSize, ArgumentPassSize, PassedByPointer, IsBoolean, Storage::Stack, NSAA); } } } @@ -176,7 +177,7 @@ namespace ams::svc::codegen::impl { /* TODO: It's unclear how to handle the non-integral and too-large case. */ if constexpr (!std::is_same::value) { constexpr size_t ReturnTypeSize = AbiType::template Size; - layout.output.Add(Parameter::Identifier("ReturnType"), ArgumentType::Invalid, ReturnTypeSize, ReturnTypeSize, false, Storage::Register, 0); + layout.output.Add(Parameter::Identifier("ReturnType"), ArgumentType::Invalid, ReturnTypeSize, ReturnTypeSize, false, false /* TODO */, Storage::Register, 0); static_assert(IsIntegral || ReturnTypeSize <= AbiType::RegisterSize); } @@ -270,7 +271,7 @@ namespace ams::svc::codegen::impl { const auto location = param.GetLocation(i); if (location.GetStorage() == Storage::Register) { reg_allocator.Allocate(location.GetIndex()); - dst_layout.AddSingle(param.GetIdentifier(), param.GetArgumentType(), param.GetTypeSize(), param.GetPassedSize(), param.IsPassedByPointer(), Storage::Register, location.GetIndex()); + dst_layout.AddSingle(param.GetIdentifier(), param.GetArgumentType(), param.GetTypeSize(), param.GetPassedSize(), param.IsPassedByPointer(), param.IsBoolean(), Storage::Register, location.GetIndex()); } } } @@ -281,7 +282,7 @@ namespace ams::svc::codegen::impl { const auto location = param.GetLocation(i); if (location.GetStorage() == Storage::Stack) { const size_t free_reg = reg_allocator.AllocateFirstFree(); - dst_layout.AddSingle(param.GetIdentifier(), param.GetArgumentType(), param.GetTypeSize(), param.GetPassedSize(), param.IsPassedByPointer(), Storage::Register, free_reg); + dst_layout.AddSingle(param.GetIdentifier(), param.GetArgumentType(), param.GetTypeSize(), param.GetPassedSize(), param.IsPassedByPointer(), param.IsBoolean(), Storage::Register, free_reg); } } } @@ -291,7 +292,7 @@ namespace ams::svc::codegen::impl { const size_t type_size = param.GetTypeSize(); for (size_t sz = 0; sz < type_size; sz += AbiType::RegisterSize) { const size_t free_reg = reg_allocator.AllocateFirstFree(); - dst_layout.AddSingle(param.GetIdentifier(), param.GetArgumentType(), type_size, type_size, false, Storage::Register, free_reg); + dst_layout.AddSingle(param.GetIdentifier(), param.GetArgumentType(), type_size, type_size, false, param.IsBoolean(), Storage::Register, free_reg); } } public: diff --git a/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_meta_code.hpp b/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_meta_code.hpp index 8d84a6182..5b5d0bc8f 100644 --- a/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_meta_code.hpp +++ b/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_meta_code.hpp @@ -29,6 +29,7 @@ namespace ams::svc::codegen::impl { AllocateStackSpace, FreeStackSpace, MoveRegister, + ConvertToBoolean, LoadFromStack, LoadPairFromStack, StoreToStack, @@ -47,6 +48,7 @@ namespace ams::svc::codegen::impl { META_CODE_OPERATION_KIND_ENUM_CASE(AllocateStackSpace); META_CODE_OPERATION_KIND_ENUM_CASE(FreeStackSpace); META_CODE_OPERATION_KIND_ENUM_CASE(MoveRegister); + META_CODE_OPERATION_KIND_ENUM_CASE(ConvertToBoolean); META_CODE_OPERATION_KIND_ENUM_CASE(LoadFromStack); META_CODE_OPERATION_KIND_ENUM_CASE(LoadPairFromStack); META_CODE_OPERATION_KIND_ENUM_CASE(StoreToStack); @@ -117,6 +119,7 @@ namespace ams::svc::codegen::impl { META_CODE_OPERATION_KIND_GENERATE_CODE(AllocateStackSpace) META_CODE_OPERATION_KIND_GENERATE_CODE(FreeStackSpace) META_CODE_OPERATION_KIND_GENERATE_CODE(MoveRegister) + META_CODE_OPERATION_KIND_GENERATE_CODE(ConvertToBoolean) META_CODE_OPERATION_KIND_GENERATE_CODE(LoadFromStack) META_CODE_OPERATION_KIND_GENERATE_CODE(LoadPairFromStack) META_CODE_OPERATION_KIND_GENERATE_CODE(StoreToStack) @@ -185,6 +188,12 @@ namespace ams::svc::codegen::impl { this->meta_code.AddOperation(op); } + template + constexpr void ConvertToBoolean() { + constexpr auto op = MetaCode::MakeOperation(); + this->meta_code.AddOperation(op); + } + template constexpr void LoadFromStack() { constexpr auto op = MetaCode::MakeOperation(); diff --git a/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_parameter.hpp b/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_parameter.hpp index f077e30ba..e7b502720 100644 --- a/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_parameter.hpp +++ b/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_parameter.hpp @@ -105,15 +105,16 @@ namespace ams::svc::codegen::impl { size_t type_size; size_t passed_size; bool passed_by_pointer; + bool is_boolean; size_t num_locations; Location locations[MaxLocations]; public: constexpr explicit Parameter() - : identifier(), type(ArgumentType::Invalid), type_size(0), passed_size(0), passed_by_pointer(0), num_locations(0), locations() + : identifier(), type(ArgumentType::Invalid), type_size(0), passed_size(0), passed_by_pointer(0), is_boolean(0), num_locations(0), locations() { /* ... */ } - constexpr explicit Parameter(Identifier id, ArgumentType t, size_t ts, size_t ps, bool p, Location l) - : identifier(id), type(t), type_size(ts), passed_size(ps), passed_by_pointer(p), num_locations(1), locations() + constexpr explicit Parameter(Identifier id, ArgumentType t, size_t ts, size_t ps, bool p, bool b, Location l) + : identifier(id), type(t), type_size(ts), passed_size(ps), passed_by_pointer(p), is_boolean(b), num_locations(1), locations() { this->locations[0] = l; } @@ -142,6 +143,10 @@ namespace ams::svc::codegen::impl { return this->passed_by_pointer; } + constexpr bool IsBoolean() const { + return this->is_boolean; + } + constexpr size_t GetNumLocations() const { return this->num_locations; } @@ -169,6 +174,7 @@ namespace ams::svc::codegen::impl { this->type_size == rhs.type_size && this->passed_size == rhs.passed_size && this->passed_by_pointer == rhs.passed_by_pointer && + this->is_boolean == rhs.is_boolean && this->num_locations == rhs.num_locations)) { return false; From 962cf97150fa95eaece7840e35abdb310905c51a Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 6 Apr 2021 23:33:33 -0700 Subject: [PATCH 120/280] kern: KLinkedList no longer exists --- .../mesosphere/kern_k_condition_variable.hpp | 2 +- .../include/mesosphere/kern_k_linked_list.hpp | 236 ------------------ .../kern_k_synchronization_object.hpp | 1 - .../source/init/kern_init_slab_setup.cpp | 3 +- .../source/kern_k_condition_variable.cpp | 32 +-- .../source/kern_k_dump_object.cpp | 1 - 6 files changed, 5 insertions(+), 270 deletions(-) delete mode 100644 libraries/libmesosphere/include/mesosphere/kern_k_linked_list.hpp diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_condition_variable.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_condition_variable.hpp index d967ea430..5a5eaaf81 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_condition_variable.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_condition_variable.hpp @@ -36,7 +36,7 @@ namespace ams::kern { void Signal(uintptr_t cv_key, s32 count); Result Wait(KProcessAddress addr, uintptr_t key, u32 value, s64 timeout); private: - KThread *SignalImpl(KThread *thread); + void SignalImpl(KThread *thread); }; ALWAYS_INLINE void BeforeUpdatePriority(KConditionVariable::ThreadTree *tree, KThread *thread) { diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_linked_list.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_linked_list.hpp deleted file mode 100644 index 2ed4cb900..000000000 --- a/libraries/libmesosphere/include/mesosphere/kern_k_linked_list.hpp +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#pragma once -#include -#include -#include - -namespace ams::kern { - - class KLinkedListNode : public util::IntrusiveListBaseNode, public KSlabAllocated { - private: - void *m_item; - public: - constexpr KLinkedListNode() : util::IntrusiveListBaseNode(), m_item(nullptr) { MESOSPHERE_ASSERT_THIS(); } - - constexpr void Initialize(void *it) { - MESOSPHERE_ASSERT_THIS(); - m_item = it; - } - - constexpr void *GetItem() const { - return m_item; - } - }; - static_assert(sizeof(KLinkedListNode) == sizeof(util::IntrusiveListNode) + sizeof(void *)); - - template - class KLinkedList : private util::IntrusiveListBaseTraits::ListType { - private: - using BaseList = util::IntrusiveListBaseTraits::ListType; - public: - template - class Iterator; - - using value_type = T; - using size_type = size_t; - using difference_type = ptrdiff_t; - using pointer = value_type *; - using const_pointer = const value_type *; - using reference = value_type &; - using const_reference = const value_type &; - using iterator = Iterator; - using const_iterator = Iterator; - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; - - template - class Iterator { - private: - using BaseIterator = BaseList::Iterator; - friend class KLinkedList; - public: - using iterator_category = std::bidirectional_iterator_tag; - using value_type = typename KLinkedList::value_type; - using difference_type = typename KLinkedList::difference_type; - using pointer = typename std::conditional::type; - using reference = typename std::conditional::type; - private: - BaseIterator m_base_it; - public: - explicit Iterator(BaseIterator it) : m_base_it(it) { /* ... */ } - - pointer GetItem() const { - return static_cast(m_base_it->GetItem()); - } - - bool operator==(const Iterator &rhs) const { - return m_base_it == rhs.m_base_it; - } - - bool operator!=(const Iterator &rhs) const { - return !(*this == rhs); - } - - pointer operator->() const { - return this->GetItem(); - } - - reference operator*() const { - return *this->GetItem(); - } - - Iterator &operator++() { - ++m_base_it; - return *this; - } - - Iterator &operator--() { - --m_base_it; - return *this; - } - - Iterator operator++(int) { - const Iterator it{*this}; - ++(*this); - return it; - } - - Iterator operator--(int) { - const Iterator it{*this}; - --(*this); - return it; - } - - operator Iterator() const { - return Iterator(m_base_it); - } - }; - public: - constexpr KLinkedList() : BaseList() { /* ... */ } - - ~KLinkedList() { - /* Erase all elements. */ - for (auto it = this->begin(); it != this->end(); it = this->erase(it)) { - /* ... */ - } - - /* Ensure we succeeded. */ - MESOSPHERE_ASSERT(this->empty()); - } - - /* Iterator accessors. */ - iterator begin() { - return iterator(BaseList::begin()); - } - - const_iterator begin() const { - return const_iterator(BaseList::begin()); - } - - iterator end() { - return iterator(BaseList::end()); - } - - const_iterator end() const { - return const_iterator(BaseList::end()); - } - - const_iterator cbegin() const { - return this->begin(); - } - - const_iterator cend() const { - return this->end(); - } - - reverse_iterator rbegin() { - return reverse_iterator(this->end()); - } - - const_reverse_iterator rbegin() const { - return const_reverse_iterator(this->end()); - } - - reverse_iterator rend() { - return reverse_iterator(this->begin()); - } - - const_reverse_iterator rend() const { - return const_reverse_iterator(this->begin()); - } - - const_reverse_iterator crbegin() const { - return this->rbegin(); - } - - const_reverse_iterator crend() const { - return this->rend(); - } - - /* Content management. */ - using BaseList::empty; - using BaseList::size; - - reference back() { - return *(--this->end()); - } - - const_reference back() const { - return *(--this->end()); - } - - reference front() { - return *this->begin(); - } - - const_reference front() const { - return *this->begin(); - } - - iterator insert(const_iterator pos, reference ref) { - KLinkedListNode *node = KLinkedListNode::Allocate(); - MESOSPHERE_ABORT_UNLESS(node != nullptr); - node->Initialize(std::addressof(ref)); - return iterator(BaseList::insert(pos.m_base_it, *node)); - } - - void push_back(reference ref) { - this->insert(this->end(), ref); - } - - void push_front(reference ref) { - this->insert(this->begin(), ref); - } - - void pop_back() { - this->erase(--this->end()); - } - - void pop_front() { - this->erase(this->begin()); - } - - iterator erase(const iterator pos) { - KLinkedListNode *freed_node = std::addressof(*pos.m_base_it); - iterator ret = iterator(BaseList::erase(pos.m_base_it)); - KLinkedListNode::Free(freed_node); - - return ret; - } - }; - -} diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_synchronization_object.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_synchronization_object.hpp index f1bf59197..ea340a155 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_synchronization_object.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_synchronization_object.hpp @@ -16,7 +16,6 @@ #pragma once #include #include -#include namespace ams::kern { diff --git a/libraries/libmesosphere/source/init/kern_init_slab_setup.cpp b/libraries/libmesosphere/source/init/kern_init_slab_setup.cpp index dcff4c205..5fab99370 100644 --- a/libraries/libmesosphere/source/init/kern_init_slab_setup.cpp +++ b/libraries/libmesosphere/source/init/kern_init_slab_setup.cpp @@ -22,7 +22,6 @@ namespace ams::kern::init { #define FOREACH_SLAB_TYPE(HANDLER, ...) \ HANDLER(KProcess, (SLAB_COUNT(KProcess)), ## __VA_ARGS__) \ HANDLER(KThread, (SLAB_COUNT(KThread)), ## __VA_ARGS__) \ - HANDLER(KLinkedListNode, (SLAB_COUNT(KThread)), ## __VA_ARGS__) \ HANDLER(KEvent, (SLAB_COUNT(KEvent)), ## __VA_ARGS__) \ HANDLER(KInterruptEvent, (SLAB_COUNT(KInterruptEvent)), ## __VA_ARGS__) \ HANDLER(KInterruptEventTask, (SLAB_COUNT(KInterruptEvent)), ## __VA_ARGS__) \ @@ -77,7 +76,7 @@ namespace ams::kern::init { namespace test { - constexpr size_t RequiredSizeForExtraThreadCount = SlabCountExtraKThread * (sizeof(KThread) + sizeof(KLinkedListNode) + (sizeof(KThreadLocalPage) / 8) + sizeof(KEventInfo)); + constexpr size_t RequiredSizeForExtraThreadCount = SlabCountExtraKThread * (sizeof(KThread) + (sizeof(KThreadLocalPage) / 8) + sizeof(KEventInfo)); static_assert(RequiredSizeForExtraThreadCount <= KernelSlabHeapAdditionalSize); } diff --git a/libraries/libmesosphere/source/kern_k_condition_variable.cpp b/libraries/libmesosphere/source/kern_k_condition_variable.cpp index 3234b87d9..ba25f35c4 100644 --- a/libraries/libmesosphere/source/kern_k_condition_variable.cpp +++ b/libraries/libmesosphere/source/kern_k_condition_variable.cpp @@ -118,7 +118,7 @@ namespace ams::kern { return cur_thread->GetWaitResult(std::addressof(dummy)); } - KThread *KConditionVariable::SignalImpl(KThread *thread) { + void KConditionVariable::SignalImpl(KThread *thread) { /* Check pre-conditions. */ MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread()); @@ -137,7 +137,6 @@ namespace ams::kern { } } - KThread *thread_to_close = nullptr; if (AMS_LIKELY(can_access)) { if (prev_tag == ams::svc::InvalidHandle) { /* If nobody held the lock previously, we're all good. */ @@ -150,7 +149,7 @@ namespace ams::kern { if (AMS_LIKELY(owner_thread != nullptr)) { /* Add the thread as a waiter on the owner. */ owner_thread->AddWaiter(thread); - thread_to_close = owner_thread; + owner_thread->Close(); } else { /* The lock was tagged with a thread that doesn't exist. */ thread->SetSyncedObject(nullptr, svc::ResultInvalidState()); @@ -162,17 +161,9 @@ namespace ams::kern { thread->SetSyncedObject(nullptr, svc::ResultInvalidCurrentMemory()); thread->Wakeup(); } - - return thread_to_close; } void KConditionVariable::Signal(uintptr_t cv_key, s32 count) { - /* Prepare for signaling. */ - constexpr int MaxThreads = 16; - KLinkedList thread_list; - KThread *thread_array[MaxThreads]; - int num_to_close = 0; - /* Perform signaling. */ int num_waiters = 0; { @@ -182,14 +173,7 @@ namespace ams::kern { while ((it != m_tree.end()) && (count <= 0 || num_waiters < count) && (it->GetConditionVariableKey() == cv_key)) { KThread *target_thread = std::addressof(*it); - if (KThread *thread = this->SignalImpl(target_thread); thread != nullptr) { - if (num_to_close < MaxThreads) { - thread_array[num_to_close++] = thread; - } else { - thread_list.push_back(*thread); - } - } - + this->SignalImpl(target_thread); it = m_tree.erase(it); target_thread->ClearConditionVariable(); ++num_waiters; @@ -201,16 +185,6 @@ namespace ams::kern { WriteToUser(cv_key, std::addressof(has_waiter_flag)); } } - - /* Close threads in the array. */ - for (auto i = 0; i < num_to_close; ++i) { - thread_array[i]->Close(); - } - - /* Close threads in the list. */ - for (auto it = thread_list.begin(); it != thread_list.end(); it = thread_list.erase(it)) { - (*it).Close(); - } } Result KConditionVariable::Wait(KProcessAddress addr, uintptr_t key, u32 value, s64 timeout) { diff --git a/libraries/libmesosphere/source/kern_k_dump_object.cpp b/libraries/libmesosphere/source/kern_k_dump_object.cpp index 0a9a3f86a..1c8f3cb12 100644 --- a/libraries/libmesosphere/source/kern_k_dump_object.cpp +++ b/libraries/libmesosphere/source/kern_k_dump_object.cpp @@ -345,7 +345,6 @@ namespace ams::kern::KDumpObject { DUMP_KSLABOBJ(KDebug); DUMP_KSLABOBJ(KSession); DUMP_KSLABOBJ(KLightSession); - DUMP_KSLABOBJ(KLinkedListNode); DUMP_KSLABOBJ(KThreadLocalPage); DUMP_KSLABOBJ(KObjectName); DUMP_KSLABOBJ(KEventInfo); From 2fb258ca7e4a16898fe7c20a83465fd271efcf62 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 00:09:29 -0700 Subject: [PATCH 121/280] kern: update KInitialPageTable/KInitialPageAllocator --- .../arm64/init/kern_k_init_page_table.hpp | 241 ++++++++++++++---- .../arch/arm64/kern_cpu_system_registers.hpp | 5 + .../source/arch/arm64/init/kern_init_core.cpp | 6 +- .../kernel_ldr/source/kern_init_loader.cpp | 52 ++-- 4 files changed, 225 insertions(+), 79 deletions(-) diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/init/kern_k_init_page_table.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/init/kern_k_init_page_table.hpp index 21fbb28b5..ea53fca94 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/init/kern_k_init_page_table.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/init/kern_k_init_page_table.hpp @@ -38,37 +38,67 @@ namespace ams::kern::arch::arm64::init { public: class IPageAllocator { public: - virtual KPhysicalAddress Allocate() { return Null; } - virtual void Free(KPhysicalAddress phys_addr) { /* Nothing to do here. */ (void)(phys_addr); } + virtual KPhysicalAddress Allocate(size_t size) = 0; + virtual void Free(KPhysicalAddress phys_addr, size_t size) = 0; }; - - struct NoClear{}; private: - KPhysicalAddress m_l1_table; + KPhysicalAddress m_l1_tables[2]; + u32 m_num_entries[2]; + public: - constexpr ALWAYS_INLINE KInitialPageTable(KPhysicalAddress l1, NoClear) : m_l1_table(l1) { /* ... */ } + KInitialPageTable(KVirtualAddress start_address, KVirtualAddress end_address, IPageAllocator &allocator) { + /* Set tables. */ + m_l1_tables[0] = AllocateNewPageTable(allocator); + m_l1_tables[1] = AllocateNewPageTable(allocator); - constexpr ALWAYS_INLINE KInitialPageTable(KPhysicalAddress l1) : KInitialPageTable(l1, NoClear{}) { - ClearNewPageTable(m_l1_table); + /* Set counts. */ + m_num_entries[0] = MaxPageTableEntries; + m_num_entries[1] = ((end_address / L1BlockSize) & (MaxPageTableEntries - 1)) - ((start_address / L1BlockSize) & (MaxPageTableEntries - 1)) + 1; } - constexpr ALWAYS_INLINE uintptr_t GetL1TableAddress() const { - return GetInteger(m_l1_table); + KInitialPageTable() { + /* Set tables. */ + m_l1_tables[0] = util::AlignDown(cpu::GetTtbr0El1(), PageSize); + m_l1_tables[1] = util::AlignDown(cpu::GetTtbr1El1(), PageSize); + + /* Set counts. */ + cpu::TranslationControlRegisterAccessor tcr; + m_num_entries[0] = tcr.GetT0Size() / L1BlockSize; + m_num_entries[1] = tcr.GetT1Size() / L1BlockSize; + + /* Check counts. */ + MESOSPHERE_INIT_ABORT_UNLESS(0 < m_num_entries[0] && m_num_entries[0] <= MaxPageTableEntries); + MESOSPHERE_INIT_ABORT_UNLESS(0 < m_num_entries[1] && m_num_entries[1] <= MaxPageTableEntries); + } + + constexpr ALWAYS_INLINE uintptr_t GetTtbr0L1TableAddress() const { + return GetInteger(m_l1_tables[0]); + } + + constexpr ALWAYS_INLINE uintptr_t GetTtbr1L1TableAddress() const { + return GetInteger(m_l1_tables[1]); } private: - static constexpr ALWAYS_INLINE L1PageTableEntry *GetL1Entry(KPhysicalAddress _l1_table, KVirtualAddress address) { - L1PageTableEntry *l1_table = reinterpret_cast(GetInteger(_l1_table)); - return l1_table + ((GetInteger(address) >> 30) & (MaxPageTableEntries - 1)); + constexpr ALWAYS_INLINE L1PageTableEntry *GetL1Entry(KVirtualAddress address) const { + const size_t index = (GetInteger(address) >> (BITSIZEOF(address) - 1)) & 1; + L1PageTableEntry *l1_table = reinterpret_cast(GetInteger(m_l1_tables[index])); + return l1_table + ((GetInteger(address) / L1BlockSize) & (m_num_entries[index] - 1)); } static constexpr ALWAYS_INLINE L2PageTableEntry *GetL2Entry(const L1PageTableEntry *entry, KVirtualAddress address) { L2PageTableEntry *l2_table = reinterpret_cast(GetInteger(entry->GetTable())); - return l2_table + ((GetInteger(address) >> 21) & (MaxPageTableEntries - 1)); + return l2_table + ((GetInteger(address) / L2BlockSize) & (MaxPageTableEntries - 1)); } static constexpr ALWAYS_INLINE L3PageTableEntry *GetL3Entry(const L2PageTableEntry *entry, KVirtualAddress address) { L3PageTableEntry *l3_table = reinterpret_cast(GetInteger(entry->GetTable())); - return l3_table + ((GetInteger(address) >> 12) & (MaxPageTableEntries - 1)); + return l3_table + ((GetInteger(address) / L3BlockSize) & (MaxPageTableEntries - 1)); + } + + static ALWAYS_INLINE KPhysicalAddress AllocateNewPageTable(IPageAllocator &allocator) { + auto address = allocator.Allocate(PageSize); + ClearNewPageTable(address); + return address; } static ALWAYS_INLINE void ClearNewPageTable(KPhysicalAddress address) { @@ -83,7 +113,7 @@ namespace ams::kern::arch::arm64::init { const KVirtualAddress end_virt_addr = virt_addr + size; size_t count = 0; while (virt_addr < end_virt_addr) { - L1PageTableEntry *l1_entry = GetL1Entry(m_l1_table, virt_addr); + L1PageTableEntry *l1_entry = this->GetL1Entry(virt_addr); /* If an L1 block is mapped or we're empty, advance by L1BlockSize. */ if (l1_entry->IsBlock() || l1_entry->IsEmpty()) { @@ -137,7 +167,7 @@ namespace ams::kern::arch::arm64::init { const KVirtualAddress end_virt_addr = virt_addr + size; size_t count = 0; while (virt_addr < end_virt_addr) { - L1PageTableEntry *l1_entry = GetL1Entry(m_l1_table, virt_addr); + L1PageTableEntry *l1_entry = this->GetL1Entry(virt_addr); /* If an L1 block is mapped or we're empty, advance by L1BlockSize. */ if (l1_entry->IsBlock() || l1_entry->IsEmpty()) { @@ -194,7 +224,7 @@ namespace ams::kern::arch::arm64::init { } PageTableEntry *GetMappingEntry(KVirtualAddress virt_addr, size_t block_size) { - L1PageTableEntry *l1_entry = GetL1Entry(m_l1_table, virt_addr); + L1PageTableEntry *l1_entry = this->GetL1Entry(virt_addr); if (l1_entry->IsBlock()) { MESOSPHERE_INIT_ABORT_UNLESS(block_size == L1BlockSize); @@ -301,7 +331,7 @@ namespace ams::kern::arch::arm64::init { /* Iteratively map pages until the requested region is mapped. */ while (size > 0) { - L1PageTableEntry *l1_entry = GetL1Entry(m_l1_table, virt_addr); + L1PageTableEntry *l1_entry = this->GetL1Entry(virt_addr); /* Can we make an L1 block? */ if (util::IsAligned(GetInteger(virt_addr), L1BlockSize) && util::IsAligned(GetInteger(phys_addr), L1BlockSize) && size >= L1BlockSize) { @@ -316,7 +346,7 @@ namespace ams::kern::arch::arm64::init { /* If we don't already have an L2 table, we need to make a new one. */ if (!l1_entry->IsTable()) { - KPhysicalAddress new_table = allocator.Allocate(); + KPhysicalAddress new_table = AllocateNewPageTable(allocator); ClearNewPageTable(new_table); *l1_entry = L1PageTableEntry(PageTableEntry::TableTag{}, new_table, attr.IsPrivilegedExecuteNever()); cpu::DataSynchronizationBarrierInnerShareable(); @@ -350,7 +380,7 @@ namespace ams::kern::arch::arm64::init { /* If we don't already have an L3 table, we need to make a new one. */ if (!l2_entry->IsTable()) { - KPhysicalAddress new_table = allocator.Allocate(); + KPhysicalAddress new_table = AllocateNewPageTable(allocator); ClearNewPageTable(new_table); *l2_entry = L2PageTableEntry(PageTableEntry::TableTag{}, new_table, attr.IsPrivilegedExecuteNever()); cpu::DataSynchronizationBarrierInnerShareable(); @@ -382,7 +412,7 @@ namespace ams::kern::arch::arm64::init { KPhysicalAddress GetPhysicalAddress(KVirtualAddress virt_addr) const { /* Get the L1 entry. */ - const L1PageTableEntry *l1_entry = GetL1Entry(m_l1_table, virt_addr); + const L1PageTableEntry *l1_entry = this->GetL1Entry(virt_addr); if (l1_entry->IsBlock()) { return l1_entry->GetBlock() + (GetInteger(virt_addr) & (L1BlockSize - 1)); @@ -444,7 +474,7 @@ namespace ams::kern::arch::arm64::init { }; while (virt_addr < end_virt_addr) { - L1PageTableEntry *l1_entry = GetL1Entry(m_l1_table, virt_addr); + L1PageTableEntry *l1_entry = this->GetL1Entry(virt_addr); /* If an L1 block is mapped, update. */ if (l1_entry->IsBlock()) { @@ -485,7 +515,7 @@ namespace ams::kern::arch::arm64::init { const KVirtualAddress end_virt_addr = virt_addr + size; while (virt_addr < end_virt_addr) { - L1PageTableEntry *l1_entry = GetL1Entry(m_l1_table, virt_addr); + L1PageTableEntry *l1_entry = this->GetL1Entry(virt_addr); /* If an L1 block is mapped, the address isn't free. */ if (l1_entry->IsBlock()) { @@ -534,7 +564,7 @@ namespace ams::kern::arch::arm64::init { /* Iteratively reprotect pages until the requested region is reprotected. */ while (size > 0) { - L1PageTableEntry *l1_entry = GetL1Entry(m_l1_table, virt_addr); + L1PageTableEntry *l1_entry = this->GetL1Entry(virt_addr); /* Check if an L1 block is present. */ if (l1_entry->IsBlock()) { @@ -673,11 +703,18 @@ namespace ams::kern::arch::arm64::init { }; - class KInitialPageAllocator : public KInitialPageTable::IPageAllocator { + class KInitialPageAllocator final : public KInitialPageTable::IPageAllocator { + private: + static constexpr inline size_t FreeUnitSize = BITSIZEOF(u64) * PageSize; + struct FreeListEntry { + FreeListEntry *next; + size_t size; + }; public: struct State { - uintptr_t next_address; - uintptr_t free_bitmap; + uintptr_t start_address; + uintptr_t end_address; + FreeListEntry *free_head; }; private: State m_state; @@ -685,8 +722,8 @@ namespace ams::kern::arch::arm64::init { constexpr ALWAYS_INLINE KInitialPageAllocator() : m_state{} { /* ... */ } ALWAYS_INLINE void Initialize(uintptr_t address) { - m_state.next_address = address + BITSIZEOF(m_state.free_bitmap) * PageSize; - m_state.free_bitmap = ~uintptr_t(); + m_state.start_address = address; + m_state.end_address = address; } ALWAYS_INLINE void InitializeFromState(uintptr_t state_val) { @@ -697,28 +734,134 @@ namespace ams::kern::arch::arm64::init { *out = m_state; m_state = {}; } - public: - virtual KPhysicalAddress Allocate() override { - MESOSPHERE_INIT_ABORT_UNLESS(m_state.next_address != Null); - uintptr_t allocated = m_state.next_address; - if (m_state.free_bitmap != 0) { - u64 index; - uintptr_t mask; - do { - index = KSystemControl::Init::GenerateRandomRange(0, BITSIZEOF(m_state.free_bitmap) - 1); - mask = (static_cast(1) << index); - } while ((m_state.free_bitmap & mask) == 0); - m_state.free_bitmap &= ~mask; - allocated = m_state.next_address - ((BITSIZEOF(m_state.free_bitmap) - index) * PageSize); - } else { - m_state.next_address += PageSize; + private: + bool CanAllocate(size_t align, size_t size) const { + for (auto *cur = m_state.free_head; cur != nullptr; cur = cur->next) { + const uintptr_t cur_last = reinterpret_cast(cur) + cur->size - 1; + const uintptr_t alloc_last = util::AlignUp(reinterpret_cast(cur), align) + size - 1; + if (alloc_last <= cur_last) { + return true; + } } - - ClearPhysicalMemory(allocated, PageSize); - return allocated; + return false; } - /* No need to override free. The default does nothing, and so would we. */ + bool TryAllocate(uintptr_t address, size_t size) { + /* Try to allocate the region. */ + auto **prev_next = std::addressof(m_state.free_head); + for (auto *cur = m_state.free_head; cur != nullptr; prev_next = std::addressof(cur->next), cur = cur->next) { + const uintptr_t cur_start = reinterpret_cast(cur); + const uintptr_t cur_last = cur_start + cur->size - 1; + if (cur_start <= address && address + size - 1 <= cur_last) { + auto *alloc = reinterpret_cast(address); + + /* Perform fragmentation at front. */ + if (cur != alloc) { + prev_next = std::addressof(cur->next); + *alloc = { + .next = cur->next, + .size = cur_start + cur->size - address, + }; + *cur = { + .next = alloc, + .size = address - cur_start, + }; + } + + /* Perform fragmentation at tail. */ + if (alloc->size != size) { + auto *next = reinterpret_cast(address + size); + *next = { + .next = alloc->next, + .size = alloc->size - size, + }; + *alloc = { + .next = next, + .size = size, + }; + } + + *prev_next = alloc->next; + return true; + } + } + + return false; + } + public: + KPhysicalAddress Allocate(size_t align, size_t size) { + /* Ensure that the free list is non-empty. */ + while (!this->CanAllocate(align, size)) { + this->Free(m_state.end_address, FreeUnitSize); + m_state.end_address += FreeUnitSize; + } + + /* Allocate a random address. */ + const uintptr_t aligned_start = util::AlignUp(m_state.start_address, align); + const uintptr_t aligned_end = util::AlignDown(m_state.end_address, align); + const size_t ind_max = ((aligned_end - aligned_start) / align) - 1; + while (true) { + if (const uintptr_t random_address = aligned_start + (KSystemControl::Init::GenerateRandomRange(0, ind_max) * align); this->TryAllocate(random_address, size)) { + return random_address; + } + } + } + + virtual KPhysicalAddress Allocate(size_t size) override { + return this->Allocate(size, size); + } + + virtual void Free(KPhysicalAddress phys_addr, size_t size) override { + auto **prev_next = std::addressof(m_state.free_head); + auto *new_chunk = reinterpret_cast(GetInteger(phys_addr)); + if (auto *cur = m_state.free_head; cur != nullptr) { + const uintptr_t new_start = reinterpret_cast(new_chunk); + const uintptr_t new_end = GetInteger(phys_addr) + size; + while (true) { + /* Attempt coalescing. */ + const uintptr_t cur_start = reinterpret_cast(cur); + const uintptr_t cur_end = cur_start + cur->size; + if (new_start < new_end) { + if (new_end < cur_start) { + *new_chunk = { + .next = cur, + .size = size, + }; + break; + } else if (new_end == cur_start) { + *new_chunk = { + .next = cur->next, + .size = cur->size + size, + }; + break; + } + } else if (cur_end == new_start) { + cur->size += size; + return; + } + + prev_next = std::addressof(cur->next); + if (cur->next != nullptr) { + cur = cur->next; + } else { + *new_chunk = { + .next = nullptr, + .size = size, + }; + cur->next = new_chunk; + return; + } + } + + } else { + *new_chunk = { + .next = nullptr, + .size = size, + }; + } + + *prev_next = new_chunk; + } }; } diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_cpu_system_registers.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_cpu_system_registers.hpp index 2d4410f84..7a5fe499c 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_cpu_system_registers.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_cpu_system_registers.hpp @@ -197,6 +197,11 @@ namespace ams::kern::arch::arm64::cpu { public: MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(TranslationControl, tcr_el1) + constexpr ALWAYS_INLINE size_t GetT0Size() const { + const size_t shift_value = this->GetBits(0, 6); + return size_t(1) << (size_t(64) - shift_value); + } + constexpr ALWAYS_INLINE size_t GetT1Size() const { const size_t shift_value = this->GetBits(16, 6); return size_t(1) << (size_t(64) - shift_value); diff --git a/mesosphere/kernel/source/arch/arm64/init/kern_init_core.cpp b/mesosphere/kernel/source/arch/arm64/init/kern_init_core.cpp index 6332eb27a..98cd02ed8 100644 --- a/mesosphere/kernel/source/arch/arm64/init/kern_init_core.cpp +++ b/mesosphere/kernel/source/arch/arm64/init/kern_init_core.cpp @@ -50,7 +50,7 @@ namespace ams::kern::init { constexpr size_t StackSize = PageSize; constexpr size_t StackAlign = PageSize; const KVirtualAddress stack_start_virt = KMemoryLayout::GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard(StackSize, StackAlign, KMemoryRegionType_KernelMisc, PageSize); - const KPhysicalAddress stack_start_phys = g_initial_page_allocator.Allocate(); + const KPhysicalAddress stack_start_phys = g_initial_page_allocator.Allocate(PageSize); MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetVirtualMemoryRegionTree().Insert(GetInteger(stack_start_virt), StackSize, type, core_id)); page_table.Map(stack_start_virt, StackSize, stack_start_phys, KernelRwDataAttribute, g_initial_page_allocator); @@ -135,7 +135,7 @@ namespace ams::kern::init { MESOSPHERE_INIT_ABORT_UNLESS((cpu::TranslationControlRegisterAccessor().GetT1Size() / arch::arm64::L1BlockSize) == arch::arm64::MaxPageTableEntries); /* Create page table object for use during initialization. */ - KInitialPageTable ttbr1_table(util::AlignDown(cpu::GetTtbr1El1(), PageSize), KInitialPageTable::NoClear{}); + KInitialPageTable ttbr1_table; /* Initialize the slab allocator counts. */ InitializeSlabResourceCounts(); @@ -382,7 +382,7 @@ namespace ams::kern::init { /* Finalize the page allocator, we're done allocating at this point. */ KInitialPageAllocator::State final_init_page_table_state; g_initial_page_allocator.GetFinalState(std::addressof(final_init_page_table_state)); - const KPhysicalAddress final_init_page_table_end_address = final_init_page_table_state.next_address; + const KPhysicalAddress final_init_page_table_end_address = final_init_page_table_state.end_address; const size_t init_page_table_region_size = GetInteger(final_init_page_table_end_address) - GetInteger(resource_end_phys_addr); /* Insert regions for the initial page table region. */ diff --git a/mesosphere/kernel_ldr/source/kern_init_loader.cpp b/mesosphere/kernel_ldr/source/kern_init_loader.cpp index 0f26fae46..6be53d77b 100644 --- a/mesosphere/kernel_ldr/source/kern_init_loader.cpp +++ b/mesosphere/kernel_ldr/source/kern_init_loader.cpp @@ -28,6 +28,14 @@ namespace ams::kern::init::loader { namespace { + constexpr uintptr_t KernelBaseAlignment = 0x200000; + constexpr uintptr_t KernelBaseRangeStart = 0xFFFFFF8000000000; + constexpr uintptr_t KernelBaseRangeEnd = 0xFFFFFFFFFFE00000; + constexpr uintptr_t KernelBaseRangeLast = KernelBaseRangeEnd - 1; + static_assert(util::IsAligned(KernelBaseRangeStart, KernelBaseAlignment)); + static_assert(util::IsAligned(KernelBaseRangeEnd, KernelBaseAlignment)); + static_assert(KernelBaseRangeStart <= KernelBaseRangeLast); + static_assert(InitialProcessBinarySizeMax <= KernelResourceSize); constexpr size_t InitialPageTableRegionSizeMax = 2_MB; @@ -71,27 +79,24 @@ namespace ams::kern::init::loader { cpu::InvalidateEntireTlb(); } - void SetupInitialIdentityMapping(KInitialPageTable &ttbr1_table, uintptr_t base_address, uintptr_t kernel_size, uintptr_t page_table_region, size_t page_table_region_size, KInitialPageTable::IPageAllocator &allocator) { - /* Make a new page table for TTBR0_EL1. */ - KInitialPageTable ttbr0_table(allocator.Allocate()); - + void SetupInitialIdentityMapping(KInitialPageTable &init_pt, uintptr_t base_address, uintptr_t kernel_size, uintptr_t page_table_region, size_t page_table_region_size, KInitialPageTable::IPageAllocator &allocator) { /* Map in an RWX identity mapping for the kernel. */ constexpr PageTableEntry KernelRWXIdentityAttribute(PageTableEntry::Permission_KernelRWX, PageTableEntry::PageAttribute_NormalMemory, PageTableEntry::Shareable_InnerShareable, PageTableEntry::MappingFlag_Mapped); - ttbr0_table.Map(base_address, kernel_size, base_address, KernelRWXIdentityAttribute, allocator); + init_pt.Map(base_address, kernel_size, base_address, KernelRWXIdentityAttribute, allocator); /* Map in an RWX identity mapping for ourselves. */ constexpr PageTableEntry KernelLdrRWXIdentityAttribute(PageTableEntry::Permission_KernelRWX, PageTableEntry::PageAttribute_NormalMemory, PageTableEntry::Shareable_InnerShareable, PageTableEntry::MappingFlag_Mapped); const uintptr_t kernel_ldr_base = util::AlignDown(reinterpret_cast(__start__), PageSize); const uintptr_t kernel_ldr_size = util::AlignUp(reinterpret_cast(__end__), PageSize) - kernel_ldr_base; - ttbr0_table.Map(kernel_ldr_base, kernel_ldr_size, kernel_ldr_base, KernelRWXIdentityAttribute, allocator); + init_pt.Map(kernel_ldr_base, kernel_ldr_size, kernel_ldr_base, KernelRWXIdentityAttribute, allocator); /* Map in the page table region as RW- for ourselves. */ constexpr PageTableEntry PageTableRegionRWAttribute(PageTableEntry::Permission_KernelRW, PageTableEntry::PageAttribute_NormalMemory, PageTableEntry::Shareable_InnerShareable, PageTableEntry::MappingFlag_Mapped); - ttbr0_table.Map(page_table_region, page_table_region_size, page_table_region, KernelRWXIdentityAttribute, allocator); + init_pt.Map(page_table_region, page_table_region_size, page_table_region, KernelRWXIdentityAttribute, allocator); /* Place the L1 table addresses in the relevant system registers. */ - cpu::SetTtbr0El1(ttbr0_table.GetL1TableAddress()); - cpu::SetTtbr1El1(ttbr1_table.GetL1TableAddress()); + cpu::SetTtbr0El1(init_pt.GetTtbr0L1TableAddress()); + cpu::SetTtbr1El1(init_pt.GetTtbr1L1TableAddress()); /* Setup MAIR_EL1, TCR_EL1. */ /* TODO: Define these bits properly elsewhere, document exactly what each bit set is doing .*/ @@ -115,19 +120,12 @@ namespace ams::kern::init::loader { KVirtualAddress GetRandomKernelBaseAddress(KInitialPageTable &page_table, KPhysicalAddress phys_base_address, size_t kernel_size) { /* Define useful values for random generation. */ - constexpr uintptr_t KernelBaseAlignment = 0x200000; - constexpr uintptr_t KernelBaseRangeMin = 0xFFFFFF8000000000; - constexpr uintptr_t KernelBaseRangeMax = 0xFFFFFFFFFFE00000; - constexpr uintptr_t KernelBaseRangeEnd = KernelBaseRangeMax - 1; - static_assert(util::IsAligned(KernelBaseRangeMin, KernelBaseAlignment)); - static_assert(util::IsAligned(KernelBaseRangeMax, KernelBaseAlignment)); - static_assert(KernelBaseRangeMin <= KernelBaseRangeEnd); const uintptr_t kernel_offset = GetInteger(phys_base_address) % KernelBaseAlignment; /* Repeatedly generate a random virtual address until we get one that's unmapped in the destination page table. */ while (true) { - const uintptr_t random_kaslr_slide = KSystemControl::Init::GenerateRandomRange(KernelBaseRangeMin / KernelBaseAlignment, KernelBaseRangeEnd / KernelBaseAlignment); + const uintptr_t random_kaslr_slide = KSystemControl::Init::GenerateRandomRange(KernelBaseRangeStart / KernelBaseAlignment, KernelBaseRangeLast / KernelBaseAlignment); const KVirtualAddress kernel_region_start = random_kaslr_slide * KernelBaseAlignment; const KVirtualAddress kernel_region_end = kernel_region_start + util::AlignUp(kernel_offset + kernel_size, KernelBaseAlignment); const size_t kernel_region_size = GetInteger(kernel_region_end) - GetInteger(kernel_region_start); @@ -138,7 +136,7 @@ namespace ams::kern::init::loader { } /* Make sure that the region stays within our intended bounds. */ - if (kernel_region_end > KernelBaseRangeMax) { + if (kernel_region_end > KernelBaseRangeEnd) { continue; } @@ -201,28 +199,28 @@ namespace ams::kern::init::loader { g_initial_page_allocator.Initialize(ini_end_address); /* Make a new page table for TTBR1_EL1. */ - KInitialPageTable ttbr1_table(g_initial_page_allocator.Allocate()); + KInitialPageTable init_pt(KernelBaseRangeStart, KernelBaseRangeLast, g_initial_page_allocator); /* Setup initial identity mapping. TTBR1 table passed by reference. */ - SetupInitialIdentityMapping(ttbr1_table, base_address, bss_end_offset, ini_end_address, InitialPageTableRegionSizeMax, g_initial_page_allocator); + SetupInitialIdentityMapping(init_pt, base_address, bss_end_offset, ini_end_address, InitialPageTableRegionSizeMax, g_initial_page_allocator); /* Generate a random slide for the kernel's base address. */ - const KVirtualAddress virtual_base_address = GetRandomKernelBaseAddress(ttbr1_table, base_address, bss_end_offset); + const KVirtualAddress virtual_base_address = GetRandomKernelBaseAddress(init_pt, base_address, bss_end_offset); /* Map kernel .text as R-X. */ constexpr PageTableEntry KernelTextAttribute(PageTableEntry::Permission_KernelRX, PageTableEntry::PageAttribute_NormalMemory, PageTableEntry::Shareable_InnerShareable, PageTableEntry::MappingFlag_Mapped); - ttbr1_table.Map(virtual_base_address + rx_offset, rx_end_offset - rx_offset, base_address + rx_offset, KernelTextAttribute, g_initial_page_allocator); + init_pt.Map(virtual_base_address + rx_offset, rx_end_offset - rx_offset, base_address + rx_offset, KernelTextAttribute, g_initial_page_allocator); /* Map kernel .rodata and .rwdata as RW-. */ /* Note that we will later reprotect .rodata as R-- */ constexpr PageTableEntry KernelRoDataAttribute(PageTableEntry::Permission_KernelR, PageTableEntry::PageAttribute_NormalMemory, PageTableEntry::Shareable_InnerShareable, PageTableEntry::MappingFlag_Mapped); constexpr PageTableEntry KernelRwDataAttribute(PageTableEntry::Permission_KernelRW, PageTableEntry::PageAttribute_NormalMemory, PageTableEntry::Shareable_InnerShareable, PageTableEntry::MappingFlag_Mapped); - ttbr1_table.Map(virtual_base_address + ro_offset, ro_end_offset - ro_offset, base_address + ro_offset, KernelRwDataAttribute, g_initial_page_allocator); - ttbr1_table.Map(virtual_base_address + rw_offset, bss_end_offset - rw_offset, base_address + rw_offset, KernelRwDataAttribute, g_initial_page_allocator); + init_pt.Map(virtual_base_address + ro_offset, ro_end_offset - ro_offset, base_address + ro_offset, KernelRwDataAttribute, g_initial_page_allocator); + init_pt.Map(virtual_base_address + rw_offset, bss_end_offset - rw_offset, base_address + rw_offset, KernelRwDataAttribute, g_initial_page_allocator); /* Physically randomize the kernel region. */ /* NOTE: Nintendo does this only on 10.0.0+ */ - ttbr1_table.PhysicallyRandomize(virtual_base_address + rx_offset, bss_end_offset - rx_offset, true); + init_pt.PhysicallyRandomize(virtual_base_address + rx_offset, bss_end_offset - rx_offset, true); /* Clear kernel .bss. */ std::memset(GetVoidPointer(virtual_base_address + bss_offset), 0, bss_end_offset - bss_offset); @@ -237,14 +235,14 @@ namespace ams::kern::init::loader { Elf::CallInitArrayFuncs(GetInteger(virtual_base_address) + init_array_offset, GetInteger(virtual_base_address) + init_array_end_offset); /* Reprotect .rodata as R-- */ - ttbr1_table.Reprotect(virtual_base_address + ro_offset, ro_end_offset - ro_offset, KernelRwDataAttribute, KernelRoDataAttribute); + init_pt.Reprotect(virtual_base_address + ro_offset, ro_end_offset - ro_offset, KernelRwDataAttribute, KernelRoDataAttribute); /* Return the difference between the random virtual base and the physical base. */ return GetInteger(virtual_base_address) - base_address; } KPhysicalAddress AllocateKernelInitStack() { - return g_initial_page_allocator.Allocate() + PageSize; + return g_initial_page_allocator.Allocate(PageSize) + PageSize; } uintptr_t GetFinalPageAllocatorState() { From 1b2cf173b366d3b40b6205c130a890a1264952fb Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 01:11:17 -0700 Subject: [PATCH 122/280] kern: add new checks to SetThreadPriority/CoreMask --- .../libmesosphere/source/kern_k_thread.cpp | 10 +++++---- .../source/svc/kern_svc_thread.cpp | 22 ++++++++++++------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/libraries/libmesosphere/source/kern_k_thread.cpp b/libraries/libmesosphere/source/kern_k_thread.cpp index d2d5581dd..fd6ed1637 100644 --- a/libraries/libmesosphere/source/kern_k_thread.cpp +++ b/libraries/libmesosphere/source/kern_k_thread.cpp @@ -599,14 +599,16 @@ namespace ams::kern { KScopedSchedulerLock sl; MESOSPHERE_ASSERT(m_num_core_migration_disables >= 0); - /* If the core id is no-update magic, preserve the ideal core id. */ - if (core_id == ams::svc::IdealCoreNoUpdate) { + /* If we're updating, set our ideal virtual core. */ + if (core_id != ams::svc::IdealCoreNoUpdate) { + m_virtual_ideal_core_id = core_id; + } else { + /* Preserve our ideal core id. */ core_id = m_virtual_ideal_core_id; R_UNLESS(((1ul << core_id) & v_affinity_mask) != 0, svc::ResultInvalidCombination()); } - /* Set the virtual core/affinity mask. */ - m_virtual_ideal_core_id = core_id; + /* Set our affinity mask. */ m_virtual_affinity_mask = v_affinity_mask; /* Translate the virtual core to a physical core. */ diff --git a/libraries/libmesosphere/source/svc/kern_svc_thread.cpp b/libraries/libmesosphere/source/svc/kern_svc_thread.cpp index c887c5f56..3c511008b 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_thread.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_thread.cpp @@ -132,14 +132,17 @@ namespace ams::kern::svc { /* Get the current process. */ KProcess &process = GetCurrentProcess(); - /* Validate the priority. */ - R_UNLESS(ams::svc::HighestThreadPriority <= priority && priority <= ams::svc::LowestThreadPriority, svc::ResultInvalidPriority()); - R_UNLESS(process.CheckThreadPriority(priority), svc::ResultInvalidPriority()); - /* Get the thread from its handle. */ KScopedAutoObject thread = process.GetHandleTable().GetObject(thread_handle); R_UNLESS(thread.IsNotNull(), svc::ResultInvalidHandle()); + /* Validate the thread is owned by the current process. */ + R_UNLESS(thread->GetOwnerProcess() == GetCurrentProcessPointer(), svc::ResultInvalidHandle()); + + /* Validate the priority. */ + R_UNLESS(ams::svc::HighestThreadPriority <= priority && priority <= ams::svc::LowestThreadPriority, svc::ResultInvalidPriority()); + R_UNLESS(process.CheckThreadPriority(priority), svc::ResultInvalidPriority()); + /* Set the thread priority. */ thread->SetBasePriority(priority); return ResultSuccess(); @@ -157,6 +160,13 @@ namespace ams::kern::svc { } Result SetThreadCoreMask(ams::svc::Handle thread_handle, int32_t core_id, uint64_t affinity_mask) { + /* Get the thread from its handle. */ + KScopedAutoObject thread = GetCurrentProcess().GetHandleTable().GetObject(thread_handle); + R_UNLESS(thread.IsNotNull(), svc::ResultInvalidHandle()); + + /* Validate the thread is owned by the current process. */ + R_UNLESS(thread->GetOwnerProcess() == GetCurrentProcessPointer(), svc::ResultInvalidHandle()); + /* Determine the core id/affinity mask. */ if (core_id == ams::svc::IdealCoreUseProcessValue) { core_id = GetCurrentProcess().GetIdealCoreId(); @@ -175,10 +185,6 @@ namespace ams::kern::svc { } } - /* Get the thread from its handle. */ - KScopedAutoObject thread = GetCurrentProcess().GetHandleTable().GetObject(thread_handle); - R_UNLESS(thread.IsNotNull(), svc::ResultInvalidHandle()); - /* Set the core mask. */ R_TRY(thread->SetCoreMask(core_id, affinity_mask)); From e64fef109cf46bb2ecac4c25e91fac0773776c1b Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 01:18:26 -0700 Subject: [PATCH 123/280] kern: update pinned thread priority rules --- .../include/mesosphere/kern_k_thread.hpp | 3 ++ .../libmesosphere/source/kern_k_thread.cpp | 42 ++++++++++++++++++- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp index 0220b75ff..715bd0deb 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp @@ -209,6 +209,7 @@ namespace ams::kern { Result m_wait_result; Result m_debug_exception_result; s32 m_base_priority{}; + s32 m_base_priority_on_unpin{}; s32 m_physical_ideal_core_id{}; s32 m_virtual_ideal_core_id{}; s32 m_num_kernel_waiters{}; @@ -346,6 +347,8 @@ namespace ams::kern { void StartTermination(); void FinishTermination(); + + void IncreaseBasePriority(s32 priority); public: constexpr u64 GetThreadId() const { return m_thread_id; } diff --git a/libraries/libmesosphere/source/kern_k_thread.cpp b/libraries/libmesosphere/source/kern_k_thread.cpp index fd6ed1637..00220923b 100644 --- a/libraries/libmesosphere/source/kern_k_thread.cpp +++ b/libraries/libmesosphere/source/kern_k_thread.cpp @@ -19,6 +19,8 @@ namespace ams::kern { namespace { + constexpr inline s32 TerminatingThreadPriority = ams::svc::SystemThreadPriorityHighest - 1; + constexpr bool IsKernelAddressKey(KProcessAddress key) { const uintptr_t key_uptr = GetInteger(key); return KernelVirtualAddressSpaceBase <= key_uptr && key_uptr <= KernelVirtualAddressSpaceLast; @@ -426,6 +428,14 @@ namespace ams::kern { if (active_core != current_core || m_physical_affinity_mask.GetAffinityMask() != m_original_physical_affinity_mask.GetAffinityMask()) { KScheduler::OnThreadAffinityMaskChanged(this, m_original_physical_affinity_mask, active_core); } + + /* Set base priority-on-unpin. */ + const s32 old_base_priority = m_base_priority; + m_base_priority_on_unpin = old_base_priority; + + /* Set base priority to higher than any possible process priority. */ + m_base_priority = std::min(old_base_priority, __builtin_ctzll(this->GetOwnerProcess()->GetPriorityMask())); + RestorePriority(this); } /* Disallow performing thread suspension. */ @@ -476,6 +486,9 @@ namespace ams::kern { } KScheduler::OnThreadAffinityMaskChanged(this, old_mask, active_core); } + + m_base_priority = m_base_priority_on_unpin; + RestorePriority(this); } /* Allow performing thread suspension (if termination hasn't been requested). */ @@ -710,13 +723,38 @@ namespace ams::kern { KScopedSchedulerLock sl; + /* Determine the priority value to use. */ + const s32 target_priority = m_termination_requested.load() && priority >= TerminatingThreadPriority ? TerminatingThreadPriority : priority; + /* Change our base priority. */ - m_base_priority = priority; + if (this->GetStackParameters().is_pinned) { + m_base_priority_on_unpin = target_priority; + } else { + m_base_priority = target_priority; + } /* Perform a priority restoration. */ RestorePriority(this); } + void KThread::IncreaseBasePriority(s32 priority) { + MESOSPHERE_ASSERT_THIS(); + MESOSPHERE_ASSERT(ams::svc::HighestThreadPriority <= priority && priority <= ams::svc::LowestThreadPriority); + + /* Set our unpin base priority, if we're pinned. */ + if (this->GetStackParameters().is_pinned && m_base_priority_on_unpin > priority) { + m_base_priority_on_unpin = priority; + } + + /* Set our base priority. */ + if (m_base_priority > priority) { + m_base_priority = priority; + + /* Perform a priority restoration. */ + RestorePriority(this); + } + } + Result KThread::SetPriorityToIdle() { MESOSPHERE_ASSERT_THIS(); @@ -1187,7 +1225,7 @@ namespace ams::kern { /* Change the thread's priority to be higher than any system thread's. */ if (this->GetBasePriority() >= ams::svc::SystemThreadPriorityHighest) { - this->SetBasePriority(ams::svc::SystemThreadPriorityHighest - 1); + this->SetBasePriority(TerminatingThreadPriority); } /* If the thread is runnable, send a termination interrupt to other cores. */ From f67d1b702676834c43306c14e19c894d19821942 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 01:25:42 -0700 Subject: [PATCH 124/280] kern: update KInterruptEvent to store core id --- .../arch/arm64/kern_k_interrupt_manager.hpp | 1 - .../mesosphere/kern_k_interrupt_event.hpp | 7 +++--- .../arch/arm64/kern_k_interrupt_manager.cpp | 8 ------ .../source/kern_k_interrupt_event.cpp | 25 +++++++++++-------- 4 files changed, 18 insertions(+), 23 deletions(-) diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_interrupt_manager.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_interrupt_manager.hpp index 235ae00f2..204efd447 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_interrupt_manager.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_interrupt_manager.hpp @@ -85,7 +85,6 @@ namespace ams::kern::arch::arm64 { NOINLINE Result BindHandler(KInterruptHandler *handler, s32 irq, s32 core_id, s32 priority, bool manual_clear, bool level); NOINLINE Result UnbindHandler(s32 irq, s32 core); - NOINLINE Result ClearInterrupt(s32 irq); NOINLINE Result ClearInterrupt(s32 irq, s32 core_id); ALWAYS_INLINE void SendInterProcessorInterrupt(s32 irq, u64 core_mask) { diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_interrupt_event.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_interrupt_event.hpp index acc72dc0d..08808ab94 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_interrupt_event.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_interrupt_event.hpp @@ -28,9 +28,10 @@ namespace ams::kern { MESOSPHERE_AUTOOBJECT_TRAITS(KInterruptEvent, KReadableEvent); private: s32 m_interrupt_id; + s32 m_core_id; bool m_is_initialized; public: - constexpr KInterruptEvent() : m_interrupt_id(-1), m_is_initialized(false) { /* ... */ } + constexpr KInterruptEvent() : m_interrupt_id(-1), m_core_id(-1), m_is_initialized(false) { /* ... */ } virtual ~KInterruptEvent() { /* ... */ } Result Initialize(int32_t interrupt_name, ams::svc::InterruptType type); @@ -58,9 +59,9 @@ namespace ams::kern { virtual KInterruptTask *OnInterrupt(s32 interrupt_id) override; virtual void DoTask() override; - void Unregister(s32 interrupt_id); + void Unregister(s32 interrupt_id, s32 core_id); public: - static Result Register(s32 interrupt_id, bool level, KInterruptEvent *event); + static Result Register(s32 interrupt_id, s32 core_id, bool level, KInterruptEvent *event); }; } diff --git a/libraries/libmesosphere/source/arch/arm64/kern_k_interrupt_manager.cpp b/libraries/libmesosphere/source/arch/arm64/kern_k_interrupt_manager.cpp index a554dcf3c..1a1a64a5b 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_k_interrupt_manager.cpp +++ b/libraries/libmesosphere/source/arch/arm64/kern_k_interrupt_manager.cpp @@ -239,14 +239,6 @@ namespace ams::kern::arch::arm64 { } } - Result KInterruptManager::ClearInterrupt(s32 irq) { - R_UNLESS(KInterruptController::IsGlobal(irq), svc::ResultOutOfRange()); - - KScopedInterruptDisable di; - KScopedSpinLock lk(this->GetGlobalInterruptLock()); - return this->ClearGlobal(irq); - } - Result KInterruptManager::ClearInterrupt(s32 irq, s32 core_id) { MESOSPHERE_UNUSED(core_id); diff --git a/libraries/libmesosphere/source/kern_k_interrupt_event.cpp b/libraries/libmesosphere/source/kern_k_interrupt_event.cpp index 08c46dd71..bb88a7d60 100644 --- a/libraries/libmesosphere/source/kern_k_interrupt_event.cpp +++ b/libraries/libmesosphere/source/kern_k_interrupt_event.cpp @@ -27,14 +27,21 @@ namespace ams::kern { Result KInterruptEvent::Initialize(int32_t interrupt_name, ams::svc::InterruptType type) { MESOSPHERE_ASSERT_THIS(); + /* Verify the interrupt is defined and global. */ + R_UNLESS(Kernel::GetInterruptManager().IsInterruptDefined(interrupt_name), svc::ResultOutOfRange()); + R_UNLESS(Kernel::GetInterruptManager().IsGlobal(interrupt_name), svc::ResultOutOfRange()); + /* Set interrupt id. */ m_interrupt_id = interrupt_name; + /* Set core id. */ + m_core_id = GetCurrentCoreId(); + /* Initialize readable event base. */ KReadableEvent::Initialize(nullptr); /* Try to register the task. */ - R_TRY(KInterruptEventTask::Register(m_interrupt_id, type == ams::svc::InterruptType_Level, this)); + R_TRY(KInterruptEventTask::Register(m_interrupt_id, m_core_id, type == ams::svc::InterruptType_Level, this)); /* Mark initialized. */ m_is_initialized = true; @@ -44,7 +51,7 @@ namespace ams::kern { void KInterruptEvent::Finalize() { MESOSPHERE_ASSERT_THIS(); - g_interrupt_event_task_table[m_interrupt_id]->Unregister(m_interrupt_id); + g_interrupt_event_task_table[m_interrupt_id]->Unregister(m_interrupt_id, m_core_id); /* Perform inherited finalization. */ KAutoObjectWithSlabHeapAndContainer::Finalize(); @@ -60,16 +67,12 @@ namespace ams::kern { R_TRY(KReadableEvent::Reset()); /* Clear the interrupt. */ - Kernel::GetInterruptManager().ClearInterrupt(m_interrupt_id); + Kernel::GetInterruptManager().ClearInterrupt(m_interrupt_id, m_core_id); return ResultSuccess(); } - Result KInterruptEventTask::Register(s32 interrupt_id, bool level, KInterruptEvent *event) { - /* Verify the interrupt id is defined and global. */ - R_UNLESS(Kernel::GetInterruptManager().IsInterruptDefined(interrupt_id), svc::ResultOutOfRange()); - R_UNLESS(Kernel::GetInterruptManager().IsGlobal(interrupt_id), svc::ResultOutOfRange()); - + Result KInterruptEventTask::Register(s32 interrupt_id, s32 core_id, bool level, KInterruptEvent *event) { /* Lock the task table. */ KScopedLightLock lk(g_interrupt_event_lock); @@ -96,7 +99,7 @@ namespace ams::kern { KScopedLightLock tlk(task->m_lock); /* Bind the interrupt handler. */ - R_TRY(Kernel::GetInterruptManager().BindHandler(task, interrupt_id, GetCurrentCoreId(), KInterruptController::PriorityLevel_High, true, level)); + R_TRY(Kernel::GetInterruptManager().BindHandler(task, interrupt_id, core_id, KInterruptController::PriorityLevel_High, true, level)); /* Set the event. */ task->m_event = event; @@ -112,7 +115,7 @@ namespace ams::kern { return ResultSuccess(); } - void KInterruptEventTask::Unregister(s32 interrupt_id) { + void KInterruptEventTask::Unregister(s32 interrupt_id, s32 core_id) { MESOSPHERE_ASSERT_THIS(); /* Lock the task table. */ @@ -127,7 +130,7 @@ namespace ams::kern { /* Unbind the interrupt. */ m_event = nullptr; - Kernel::GetInterruptManager().UnbindHandler(interrupt_id, GetCurrentCoreId()); + Kernel::GetInterruptManager().UnbindHandler(interrupt_id, core_id); } KInterruptTask *KInterruptEventTask::OnInterrupt(s32 interrupt_id) { From 3356eddcbaf152cfd5b849d523db704d8368300e Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 01:30:36 -0700 Subject: [PATCH 125/280] kern: update kernel waiter management rules --- libraries/libmesosphere/source/kern_k_process.cpp | 2 +- libraries/libmesosphere/source/kern_k_thread.cpp | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/libmesosphere/source/kern_k_process.cpp b/libraries/libmesosphere/source/kern_k_process.cpp index 9b888f56e..79e2acbd9 100644 --- a/libraries/libmesosphere/source/kern_k_process.cpp +++ b/libraries/libmesosphere/source/kern_k_process.cpp @@ -744,7 +744,7 @@ namespace ams::kern { } /* Otherwise, wait for us to not have an exception thread. */ - cur_thread->SetAddressKey(address_key); + cur_thread->SetAddressKey(address_key | 1); m_exception_thread->AddWaiter(cur_thread); if (cur_thread->GetState() == KThread::ThreadState_Runnable) { cur_thread->SetState(KThread::ThreadState_Waiting); diff --git a/libraries/libmesosphere/source/kern_k_thread.cpp b/libraries/libmesosphere/source/kern_k_thread.cpp index 00220923b..c25b9b121 100644 --- a/libraries/libmesosphere/source/kern_k_thread.cpp +++ b/libraries/libmesosphere/source/kern_k_thread.cpp @@ -23,7 +23,7 @@ namespace ams::kern { constexpr bool IsKernelAddressKey(KProcessAddress key) { const uintptr_t key_uptr = GetInteger(key); - return KernelVirtualAddressSpaceBase <= key_uptr && key_uptr <= KernelVirtualAddressSpaceLast; + return KernelVirtualAddressSpaceBase <= key_uptr && key_uptr <= KernelVirtualAddressSpaceLast && (key_uptr & 1) == 0; } void InitializeKernelStack(uintptr_t stack_top) { @@ -996,6 +996,7 @@ namespace ams::kern { /* Keep track of how many kernel waiters we have. */ if (IsKernelAddressKey(thread->GetAddressKey())) { MESOSPHERE_ABORT_UNLESS((m_num_kernel_waiters++) >= 0); + KScheduler::SetSchedulerUpdateNeeded(); } /* Insert the waiter. */ @@ -1010,6 +1011,7 @@ namespace ams::kern { /* Keep track of how many kernel waiters we have. */ if (IsKernelAddressKey(thread->GetAddressKey())) { MESOSPHERE_ABORT_UNLESS((m_num_kernel_waiters--) > 0); + KScheduler::SetSchedulerUpdateNeeded(); } /* Remove the waiter. */ @@ -1088,6 +1090,7 @@ namespace ams::kern { /* Keep track of how many kernel waiters we have. */ if (IsKernelAddressKey(thread->GetAddressKey())) { MESOSPHERE_ABORT_UNLESS((m_num_kernel_waiters--) > 0); + KScheduler::SetSchedulerUpdateNeeded(); } it = m_waiter_list.erase(it); From ec1d9c4c497d61c8f9cdb3ac4e89a73d6eb95076 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 01:44:27 -0700 Subject: [PATCH 126/280] kern: unconditionally set thread state when appropriate --- .../source/kern_k_light_lock.cpp | 19 ++++----------- .../libmesosphere/source/kern_k_process.cpp | 24 +++++++------------ .../libmesosphere/source/kern_k_scheduler.cpp | 4 +--- 3 files changed, 15 insertions(+), 32 deletions(-) diff --git a/libraries/libmesosphere/source/kern_k_light_lock.cpp b/libraries/libmesosphere/source/kern_k_light_lock.cpp index 06f73ea5b..74cb31aa5 100644 --- a/libraries/libmesosphere/source/kern_k_light_lock.cpp +++ b/libraries/libmesosphere/source/kern_k_light_lock.cpp @@ -35,11 +35,7 @@ namespace ams::kern { owner_thread->AddWaiter(cur_thread); /* Set thread states. */ - if (AMS_LIKELY(cur_thread->GetState() == KThread::ThreadState_Runnable)) { - cur_thread->SetState(KThread::ThreadState_Waiting); - } else { - KScheduler::SetSchedulerUpdateNeeded(); - } + cur_thread->SetState(KThread::ThreadState_Waiting); if (owner_thread->IsSuspended()) { owner_thread->ContinueIfHasKernelWaiters(); @@ -49,10 +45,9 @@ namespace ams::kern { /* We're no longer waiting on the lock owner. */ { KScopedSchedulerLock sl; - KThread *owner_thread = cur_thread->GetLockOwner(); - if (AMS_UNLIKELY(owner_thread)) { + + if (KThread *owner_thread = cur_thread->GetLockOwner(); AMS_UNLIKELY(owner_thread != nullptr)) { owner_thread->RemoveWaiter(cur_thread); - KScheduler::SetSchedulerUpdateNeeded(); } } } @@ -70,17 +65,13 @@ namespace ams::kern { /* Pass the lock to the next owner. */ uintptr_t next_tag = 0; - if (next_owner) { + if (next_owner != nullptr) { next_tag = reinterpret_cast(next_owner); if (num_waiters > 1) { next_tag |= 0x1; } - if (AMS_LIKELY(next_owner->GetState() == KThread::ThreadState_Waiting)) { - next_owner->SetState(KThread::ThreadState_Runnable); - } else { - KScheduler::SetSchedulerUpdateNeeded(); - } + next_owner->SetState(KThread::ThreadState_Runnable); if (next_owner->IsSuspended()) { next_owner->ContinueIfHasKernelWaiters(); diff --git a/libraries/libmesosphere/source/kern_k_process.cpp b/libraries/libmesosphere/source/kern_k_process.cpp index 79e2acbd9..f8a0ad354 100644 --- a/libraries/libmesosphere/source/kern_k_process.cpp +++ b/libraries/libmesosphere/source/kern_k_process.cpp @@ -740,25 +740,22 @@ namespace ams::kern { /* If we have no exception thread, we succeeded. */ if (m_exception_thread == nullptr) { m_exception_thread = cur_thread; + KScheduler::SetSchedulerUpdateNeeded(); return true; } /* Otherwise, wait for us to not have an exception thread. */ cur_thread->SetAddressKey(address_key | 1); m_exception_thread->AddWaiter(cur_thread); - if (cur_thread->GetState() == KThread::ThreadState_Runnable) { - cur_thread->SetState(KThread::ThreadState_Waiting); - } else { - KScheduler::SetSchedulerUpdateNeeded(); - } + cur_thread->SetState(KThread::ThreadState_Waiting); } + /* Remove the thread as a waiter from the lock owner. */ { KScopedSchedulerLock sl; - KThread *owner_thread = cur_thread->GetLockOwner(); - if (owner_thread != nullptr) { + + if (KThread *owner_thread = cur_thread->GetLockOwner(); owner_thread != nullptr) { owner_thread->RemoveWaiter(cur_thread); - KScheduler::SetSchedulerUpdateNeeded(); } } } @@ -779,15 +776,12 @@ namespace ams::kern { /* Remove waiter thread. */ s32 num_waiters; - KThread *next = thread->RemoveWaiterByKey(std::addressof(num_waiters), reinterpret_cast(std::addressof(m_exception_thread))); - if (next != nullptr) { - if (next->GetState() == KThread::ThreadState_Waiting) { - next->SetState(KThread::ThreadState_Runnable); - } else { - KScheduler::SetSchedulerUpdateNeeded(); - } + if (KThread *next = thread->RemoveWaiterByKey(std::addressof(num_waiters), reinterpret_cast(std::addressof(m_exception_thread))); next != nullptr) { + next->SetState(KThread::ThreadState_Runnable); } + KScheduler::SetSchedulerUpdateNeeded(); + return true; } else { return false; diff --git a/libraries/libmesosphere/source/kern_k_scheduler.cpp b/libraries/libmesosphere/source/kern_k_scheduler.cpp index 9534a6e95..1773a8a0e 100644 --- a/libraries/libmesosphere/source/kern_k_scheduler.cpp +++ b/libraries/libmesosphere/source/kern_k_scheduler.cpp @@ -218,9 +218,7 @@ namespace ams::kern { KThread *task_thread = Kernel::GetInterruptTaskManager().GetThread(); { KScopedSchedulerLock sl; - if (AMS_LIKELY(task_thread->GetState() == KThread::ThreadState_Waiting)) { - task_thread->SetState(KThread::ThreadState_Runnable); - } + task_thread->SetState(KThread::ThreadState_Runnable); } } From 256eb92f4cd4cb4c807b6c91652e6a659902d538 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 08:17:15 -0700 Subject: [PATCH 127/280] kern: update process/thread for new running/termination semantics --- .../include/mesosphere/kern_k_process.hpp | 10 ++- .../include/mesosphere/kern_k_thread.hpp | 4 +- .../nintendo/nx/kern_k_device_page_table.cpp | 2 +- .../source/kern_initial_process.cpp | 1 + .../libmesosphere/source/kern_k_process.cpp | 68 +++++++++++-------- .../libmesosphere/source/kern_k_thread.cpp | 60 ++++++++-------- .../source/svc/kern_svc_process.cpp | 7 +- .../source/svc/kern_svc_thread.cpp | 6 +- 8 files changed, 76 insertions(+), 82 deletions(-) diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp index 9e5dd82f7..c2d50acdf 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp @@ -77,8 +77,7 @@ namespace ams::kern { bool m_is_initialized{}; bool m_is_application{}; char m_name[13]{}; - std::atomic m_num_threads{}; - u16 m_peak_num_threads{}; + std::atomic m_num_running_threads{}; u32 m_flags{}; KMemoryManager::Pool m_memory_pool{}; s64 m_schedule_count{}; @@ -108,7 +107,6 @@ namespace ams::kern { KThread *m_running_threads[cpu::NumCores]{}; u64 m_running_thread_idle_counts[cpu::NumCores]{}; KThread *m_pinned_threads[cpu::NumCores]{}; - std::atomic m_num_created_threads{}; std::atomic m_cpu_time{}; std::atomic m_num_process_switches{}; std::atomic m_num_thread_switches{}; @@ -124,7 +122,7 @@ namespace ams::kern { private: Result Initialize(const ams::svc::CreateProcessParameter ¶ms); - void StartTermination(); + Result StartTermination(); void FinishTermination(); void PinThread(s32 core_id, KThread *thread) { @@ -285,8 +283,8 @@ namespace ams::kern { constexpr s64 GetScheduledCount() const { return m_schedule_count; } void IncrementScheduledCount() { ++m_schedule_count; } - void IncrementThreadCount(); - void DecrementThreadCount(); + void IncrementRunningThreadCount(); + void DecrementRunningThreadCount(); size_t GetTotalSystemResourceSize() const { return m_system_resource_num_pages * PageSize; } size_t GetUsedSystemResourceSize() const { diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp index 715bd0deb..ff8dfadea 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp @@ -340,7 +340,7 @@ namespace ams::kern { return this->GetDpc() != 0; } private: - void Suspend(); + void UpdateState(); ALWAYS_INLINE void AddWaiterImpl(KThread *thread); ALWAYS_INLINE void RemoveWaiterImpl(KThread *thread); ALWAYS_INLINE static void RestorePriority(KThread *thread); @@ -535,7 +535,7 @@ namespace ams::kern { Result Run(); void Exit(); - void Terminate(); + Result Terminate(); ThreadState RequestTerminate(); Result Sleep(s64 timeout); diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_device_page_table.cpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_device_page_table.cpp index 76410a1fc..582df3f7f 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_device_page_table.cpp +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_device_page_table.cpp @@ -526,7 +526,7 @@ namespace ams::kern::board::nintendo::nx { #if defined(MESOSPHERE_ENABLE_MEMORY_CONTROLLER_INTERRUPT) { /* Clear the interrupt when we're done. */ - ON_SCOPE_EXIT { Kernel::GetInterruptManager().ClearInterrupt(KInterruptName_MemoryController); }; + ON_SCOPE_EXIT { Kernel::GetInterruptManager().ClearInterrupt(KInterruptName_MemoryController, GetCurrentCoreId()); }; /* Get and clear the interrupt status. */ u32 int_status, err_status, err_adr; diff --git a/libraries/libmesosphere/source/kern_initial_process.cpp b/libraries/libmesosphere/source/kern_initial_process.cpp index 1b526ca6d..be962d668 100644 --- a/libraries/libmesosphere/source/kern_initial_process.cpp +++ b/libraries/libmesosphere/source/kern_initial_process.cpp @@ -210,6 +210,7 @@ namespace ams::kern { /* Run the processes. */ for (size_t i = 0; i < g_initial_process_binary_header.num_processes; i++) { MESOSPHERE_R_ABORT_UNLESS(infos[i].process->Run(infos[i].priority, infos[i].stack_size)); + infos[i].process->Close(); } } diff --git a/libraries/libmesosphere/source/kern_k_process.cpp b/libraries/libmesosphere/source/kern_k_process.cpp index f8a0ad354..6aaaefa8f 100644 --- a/libraries/libmesosphere/source/kern_k_process.cpp +++ b/libraries/libmesosphere/source/kern_k_process.cpp @@ -28,7 +28,7 @@ namespace ams::kern { std::atomic g_initial_process_id = InitialProcessIdMin; std::atomic g_process_id = ProcessIdMin; - void TerminateChildren(KProcess *process, const KThread *thread_to_not_terminate) { + Result TerminateChildren(KProcess *process, const KThread *thread_to_not_terminate) { /* Request that all children threads terminate. */ { KScopedLightLock proc_lk(process->GetListLock()); @@ -70,9 +70,14 @@ namespace ams::kern { } /* Terminate and close the thread. */ - cur_child->Terminate(); - cur_child->Close(); + ON_SCOPE_EXIT { cur_child->Close(); }; + + if (Result terminate_result = cur_child->Terminate(); svc::ResultTerminationRequested::Includes(terminate_result)) { + return terminate_result; + } } + + return ResultSuccess(); } } @@ -206,9 +211,7 @@ namespace ams::kern { KSystemControl::GenerateRandomBytes(m_entropy, sizeof(m_entropy)); /* Clear remaining fields. */ - m_num_threads = 0; - m_peak_num_threads = 0; - m_num_created_threads = 0; + m_num_running_threads = 0; m_num_process_switches = 0; m_num_thread_switches = 0; m_num_fpu_switches = 0; @@ -402,12 +405,14 @@ namespace ams::kern { this->FinishTermination(); } - void KProcess::StartTermination() { - /* Terminate child threads other than the current one. */ - TerminateChildren(this, GetCurrentThreadPointer()); + Result KProcess::StartTermination() { + /* Finalize the handle table, when we're done. */ + ON_SCOPE_EXIT { + m_handle_table.Finalize(); + }; - /* Finalize the handle tahble. */ - m_handle_table.Finalize(); + /* Terminate child threads other than the current one. */ + return TerminateChildren(this, GetCurrentThreadPointer()); } void KProcess::FinishTermination() { @@ -485,16 +490,22 @@ namespace ams::kern { /* If we need to terminate, do so. */ if (needs_terminate) { /* Start termination. */ - this->StartTermination(); + if (R_SUCCEEDED(this->StartTermination())) { + /* Note for debug that we're terminating the process. */ + MESOSPHERE_LOG("KProcess::Terminate() OK pid=%ld name=%-12s\n", m_process_id, m_name); - /* Note for debug that we're terminating the process. */ - MESOSPHERE_LOG("KProcess::Terminate() pid=%ld name=%-12s\n", m_process_id, m_name); + /* Call the debug callback. */ + KDebug::OnTerminateProcess(this); - /* Call the debug callback. */ - KDebug::OnTerminateProcess(this); + /* Finish termination. */ + this->FinishTermination(); + } else { + /* Note for debug that we're terminating the process. */ + MESOSPHERE_LOG("KProcess::Terminate() FAIL pid=%ld name=%-12s\n", m_process_id, m_name); - /* Finish termination. */ - this->FinishTermination(); + /* Register the process as a work task. */ + KWorkerTaskManager::AddTask(KWorkerTaskManager::WorkerType_Exit, this); + } } return ResultSuccess(); @@ -703,19 +714,16 @@ namespace ams::kern { } } - void KProcess::IncrementThreadCount() { - MESOSPHERE_ASSERT(m_num_threads >= 0); - ++m_num_created_threads; + void KProcess::IncrementRunningThreadCount() { + MESOSPHERE_ASSERT(m_num_running_threads.load() >= 0); - if (const auto count = ++m_num_threads; count > m_peak_num_threads) { - m_peak_num_threads = count; - } + m_num_running_threads.fetch_add(1); } - void KProcess::DecrementThreadCount() { - MESOSPHERE_ASSERT(m_num_threads > 0); + void KProcess::DecrementRunningThreadCount() { + MESOSPHERE_ASSERT(m_num_running_threads.load() > 0); - if (const auto count = --m_num_threads; count == 0) { + if (m_num_running_threads.fetch_sub(1) == 1) { this->Terminate(); } } @@ -896,7 +904,7 @@ namespace ams::kern { /* Create a new thread for the process. */ KThread *main_thread = KThread::Create(); R_UNLESS(main_thread != nullptr, svc::ResultOutOfResource()); - auto thread_guard = SCOPE_GUARD { main_thread->Close(); }; + ON_SCOPE_EXIT { main_thread->Close(); }; /* Initialize the thread. */ R_TRY(KThread::InitializeUserThread(main_thread, reinterpret_cast(GetVoidPointer(this->GetEntryPoint())), 0, stack_top, priority, m_ideal_core_id, this)); @@ -919,9 +927,11 @@ namespace ams::kern { /* Run our thread. */ R_TRY(main_thread->Run()); + /* Open a reference to represent that we're running. */ + this->Open(); + /* We succeeded! Cancel our guards. */ state_guard.Cancel(); - thread_guard.Cancel(); ht_guard.Cancel(); stack_guard.Cancel(); mem_reservation.Commit(); diff --git a/libraries/libmesosphere/source/kern_k_thread.cpp b/libraries/libmesosphere/source/kern_k_thread.cpp index c25b9b121..972e4220d 100644 --- a/libraries/libmesosphere/source/kern_k_thread.cpp +++ b/libraries/libmesosphere/source/kern_k_thread.cpp @@ -184,7 +184,6 @@ namespace ams::kern { if (owner != nullptr) { m_parent = owner; m_parent->Open(); - m_parent->IncrementThreadCount(); } /* Initialize thread context. */ @@ -312,11 +311,6 @@ namespace ams::kern { CleanupKernelStack(reinterpret_cast(m_kernel_stack_top)); } - /* Decrement the parent process's thread count. */ - if (m_parent != nullptr) { - m_parent->DecrementThreadCount(); - } - /* Perform inherited finalization. */ KAutoObjectWithSlabHeapAndContainer::Finalize(); } @@ -444,11 +438,7 @@ namespace ams::kern { m_suspend_allowed_flags &= ~(1 << (SuspendType_Thread + ThreadState_SuspendShift)); /* Update our state. */ - const ThreadState old_state = m_thread_state; - m_thread_state = static_cast(this->GetSuspendFlags() | (old_state & ThreadState_Mask)); - if (m_thread_state != old_state) { - KScheduler::OnThreadStateChanged(this, old_state); - } + this->UpdateState(); } /* Update our SVC access permissions. */ @@ -499,11 +489,7 @@ namespace ams::kern { } /* Update our state. */ - const ThreadState old_state = m_thread_state; - m_thread_state = static_cast(this->GetSuspendFlags() | (old_state & ThreadState_Mask)); - if (m_thread_state != old_state) { - KScheduler::OnThreadStateChanged(this, old_state); - } + this->UpdateState(); } /* Update our SVC access permissions. */ @@ -790,11 +776,7 @@ namespace ams::kern { m_suspend_request_flags &= ~(1u << (ThreadState_SuspendShift + type)); /* Update our state. */ - const ThreadState old_state = m_thread_state; - m_thread_state = static_cast(this->GetSuspendFlags() | (old_state & ThreadState_Mask)); - if (m_thread_state != old_state) { - KScheduler::OnThreadStateChanged(this, old_state); - } + this->UpdateState(); } void KThread::WaitCancel() { @@ -830,20 +812,22 @@ namespace ams::kern { MESOSPHERE_ABORT_UNLESS(this->GetNumKernelWaiters() == 0); /* Perform the suspend. */ - this->Suspend(); + this->UpdateState(); } - void KThread::Suspend() { + void KThread::UpdateState() { MESOSPHERE_ASSERT_THIS(); MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread()); - MESOSPHERE_ASSERT(this->IsSuspendRequested()); /* Set our suspend flags in state. */ const auto old_state = m_thread_state; - m_thread_state = static_cast(this->GetSuspendFlags() | (old_state & ThreadState_Mask)); + const auto new_state = static_cast(this->GetSuspendFlags() | (old_state & ThreadState_Mask)); + m_thread_state = new_state; /* Note the state change in scheduler. */ - KScheduler::OnThreadStateChanged(this, old_state); + if (new_state != old_state) { + KScheduler::OnThreadStateChanged(this, old_state); + } } void KThread::Continue() { @@ -1137,15 +1121,21 @@ namespace ams::kern { /* If the current thread has been asked to suspend, suspend it and retry. */ if (GetCurrentThread().IsSuspended()) { - GetCurrentThread().Suspend(); + GetCurrentThread().UpdateState(); continue; } /* If we're not a kernel thread and we've been asked to suspend, suspend ourselves. */ - if (this->IsUserThread() && this->IsSuspended()) { - this->Suspend(); + if (KProcess *parent = this->GetOwnerProcess(); parent != nullptr) { + if (this->IsSuspended()) { + this->UpdateState(); + } + parent->IncrementRunningThreadCount(); } + /* Open a reference, now that we're running. */ + this->Open(); + /* Set our state and finish. */ this->SetState(KThread::ThreadState_Runnable); return ResultSuccess(); @@ -1160,10 +1150,11 @@ namespace ams::kern { /* Call the debug callback. */ KDebug::OnExitThread(this); - /* Release the thread resource hint from parent. */ + /* Release the thread resource hint, running thread count from parent. */ if (m_parent != nullptr) { m_parent->ReleaseResource(ams::svc::LimitableResource_ThreadCountMax, 0, 1); m_resource_limit_release_hint = true; + m_parent->DecrementRunningThreadCount(); } /* Perform termination. */ @@ -1172,6 +1163,7 @@ namespace ams::kern { /* Disallow all suspension. */ m_suspend_allowed_flags = 0; + this->UpdateState(); /* Start termination. */ this->StartTermination(); @@ -1183,7 +1175,7 @@ namespace ams::kern { MESOSPHERE_PANIC("KThread::Exit() would return"); } - void KThread::Terminate() { + Result KThread::Terminate() { MESOSPHERE_ASSERT_THIS(); MESOSPHERE_ASSERT(this != GetCurrentThreadPointer()); @@ -1192,7 +1184,9 @@ namespace ams::kern { /* If the thread isn't terminated, wait for it to terminate. */ s32 index; KSynchronizationObject *objects[] = { this }; - KSynchronizationObject::Wait(std::addressof(index), objects, 1, ams::svc::WaitInfinite); + return KSynchronizationObject::Wait(std::addressof(index), objects, 1, ams::svc::WaitInfinite); + } else { + return ResultSuccess(); } } @@ -1223,7 +1217,7 @@ namespace ams::kern { /* If the thread is suspended, continue it. */ if (this->IsSuspended()) { m_suspend_allowed_flags = 0; - this->Continue(); + this->UpdateState(); } /* Change the thread's priority to be higher than any system thread's. */ diff --git a/libraries/libmesosphere/source/svc/kern_svc_process.cpp b/libraries/libmesosphere/source/svc/kern_svc_process.cpp index 7625cbf3b..6d12f2bfd 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_process.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_process.cpp @@ -286,12 +286,7 @@ namespace ams::kern::svc { process->SetIdealCoreId(core_id); /* Run the process. */ - R_TRY(process->Run(priority, static_cast(main_thread_stack_size))); - - /* Open a reference to the process, since it's now running. */ - process->Open(); - - return ResultSuccess(); + return process->Run(priority, static_cast(main_thread_stack_size)); } Result TerminateProcess(ams::svc::Handle process_handle) { diff --git a/libraries/libmesosphere/source/svc/kern_svc_thread.cpp b/libraries/libmesosphere/source/svc/kern_svc_thread.cpp index 3c511008b..e8277a22b 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_thread.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_thread.cpp @@ -75,11 +75,7 @@ namespace ams::kern::svc { R_UNLESS(thread.IsNotNull(), svc::ResultInvalidHandle()); /* Try to start the thread. */ - R_TRY(thread->Run()); - - /* If we succeeded, persist a reference to the thread. */ - thread->Open(); - return ResultSuccess(); + return thread->Run(); } void ExitThread() { From 2f930c2d5fc017c3afcb15f9f5c1c25f6bd1656b Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 08:23:21 -0700 Subject: [PATCH 128/280] kern: support immortal processes --- .../kern_k_initial_process_reader.hpp | 2 + .../include/mesosphere/kern_k_process.hpp | 3 +- .../source/kern_initial_process.cpp | 2 +- .../libmesosphere/source/kern_k_process.cpp | 37 +++++++++++-------- 4 files changed, 27 insertions(+), 17 deletions(-) diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_initial_process_reader.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_initial_process_reader.hpp index db3d58e6b..ce534533f 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_initial_process_reader.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_initial_process_reader.hpp @@ -70,6 +70,7 @@ namespace ams::kern { constexpr bool Is64Bit() const { return (m_flags & (1 << 3)); } constexpr bool Is64BitAddressSpace() const { return (m_flags & (1 << 4)); } constexpr bool UsesSecureMemory() const { return (m_flags & (1 << 5)); } + constexpr bool IsImmortal() const { return (m_flags & (1 << 6)); } constexpr u32 GetRxAddress() const { return m_rx_address; } constexpr u32 GetRxSize() const { return m_rx_size; } @@ -117,6 +118,7 @@ namespace ams::kern { constexpr bool Is64Bit() const { return m_kip_header->Is64Bit(); } constexpr bool Is64BitAddressSpace() const { return m_kip_header->Is64BitAddressSpace(); } constexpr bool UsesSecureMemory() const { return m_kip_header->UsesSecureMemory(); } + constexpr bool IsImmortal() const { return m_kip_header->IsImmortal(); } bool Attach(u8 *bin) { if (KInitialProcessHeader *header = reinterpret_cast(bin); header->IsValid()) { diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp index c2d50acdf..1706a0721 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp @@ -98,6 +98,7 @@ namespace ams::kern { SharedMemoryInfoList m_shared_memory_list{}; BetaList m_beta_list{}; bool m_is_suspended{}; + bool m_is_immortal{}; bool m_is_jit_debug{}; ams::svc::DebugEvent m_jit_debug_event_type{}; ams::svc::DebugException m_jit_debug_exception_type{}; @@ -143,7 +144,7 @@ namespace ams::kern { KProcess() { /* ... */ } virtual ~KProcess() { /* ... */ } - Result Initialize(const ams::svc::CreateProcessParameter ¶ms, const KPageGroup &pg, const u32 *caps, s32 num_caps, KResourceLimit *res_limit, KMemoryManager::Pool pool); + Result Initialize(const ams::svc::CreateProcessParameter ¶ms, const KPageGroup &pg, const u32 *caps, s32 num_caps, KResourceLimit *res_limit, KMemoryManager::Pool pool, bool immortal); Result Initialize(const ams::svc::CreateProcessParameter ¶ms, svc::KUserPointer caps, s32 num_caps, KResourceLimit *res_limit, KMemoryManager::Pool pool); void Exit(); diff --git a/libraries/libmesosphere/source/kern_initial_process.cpp b/libraries/libmesosphere/source/kern_initial_process.cpp index be962d668..24616fbb6 100644 --- a/libraries/libmesosphere/source/kern_initial_process.cpp +++ b/libraries/libmesosphere/source/kern_initial_process.cpp @@ -118,7 +118,7 @@ namespace ams::kern { MESOSPHERE_ABORT_UNLESS(new_process != nullptr); /* Initialize the process. */ - MESOSPHERE_R_ABORT_UNLESS(new_process->Initialize(params, pg, reader.GetCapabilities(), reader.GetNumCapabilities(), std::addressof(Kernel::GetSystemResourceLimit()), pool)); + MESOSPHERE_R_ABORT_UNLESS(new_process->Initialize(params, pg, reader.GetCapabilities(), reader.GetNumCapabilities(), std::addressof(Kernel::GetSystemResourceLimit()), pool, reader.IsImmortal())); } } diff --git a/libraries/libmesosphere/source/kern_k_process.cpp b/libraries/libmesosphere/source/kern_k_process.cpp index 6aaaefa8f..474f08c7f 100644 --- a/libraries/libmesosphere/source/kern_k_process.cpp +++ b/libraries/libmesosphere/source/kern_k_process.cpp @@ -231,7 +231,7 @@ namespace ams::kern { return ResultSuccess(); } - Result KProcess::Initialize(const ams::svc::CreateProcessParameter ¶ms, const KPageGroup &pg, const u32 *caps, s32 num_caps, KResourceLimit *res_limit, KMemoryManager::Pool pool) { + Result KProcess::Initialize(const ams::svc::CreateProcessParameter ¶ms, const KPageGroup &pg, const u32 *caps, s32 num_caps, KResourceLimit *res_limit, KMemoryManager::Pool pool, bool immortal) { MESOSPHERE_ASSERT_THIS(); MESOSPHERE_ASSERT(res_limit != nullptr); MESOSPHERE_ABORT_UNLESS((params.code_num_pages * PageSize) / PageSize == static_cast(params.code_num_pages)); @@ -241,6 +241,7 @@ namespace ams::kern { m_resource_limit = res_limit; m_system_resource_address = Null; m_system_resource_num_pages = 0; + m_is_immortal = immortal; /* Setup page table. */ /* NOTE: Nintendo passes process ID despite not having set it yet. */ @@ -289,6 +290,7 @@ namespace ams::kern { /* Set pool and resource limit. */ m_memory_pool = pool; m_resource_limit = res_limit; + m_is_immortal = false; /* Get the memory sizes. */ const size_t code_num_pages = params.code_num_pages; @@ -406,9 +408,11 @@ namespace ams::kern { } Result KProcess::StartTermination() { - /* Finalize the handle table, when we're done. */ + /* Finalize the handle table when we're done, if the process isn't immortal. */ ON_SCOPE_EXIT { - m_handle_table.Finalize(); + if (!m_is_immortal) { + m_handle_table.Finalize(); + } }; /* Terminate child threads other than the current one. */ @@ -416,20 +420,23 @@ namespace ams::kern { } void KProcess::FinishTermination() { - /* Release resource limit hint. */ - if (m_resource_limit != nullptr) { - m_memory_release_hint = this->GetUsedUserPhysicalMemorySize(); - m_resource_limit->Release(ams::svc::LimitableResource_PhysicalMemoryMax, 0, m_memory_release_hint); - } + /* Only allow termination to occur if the process isn't immortal. */ + if (!m_is_immortal) { + /* Release resource limit hint. */ + if (m_resource_limit != nullptr) { + m_memory_release_hint = this->GetUsedUserPhysicalMemorySize(); + m_resource_limit->Release(ams::svc::LimitableResource_PhysicalMemoryMax, 0, m_memory_release_hint); + } - /* Change state. */ - { - KScopedSchedulerLock sl; - this->ChangeState(State_Terminated); - } + /* Change state. */ + { + KScopedSchedulerLock sl; + this->ChangeState(State_Terminated); + } - /* Close. */ - this->Close(); + /* Close. */ + this->Close(); + } } void KProcess::Exit() { From c216f92a91ca6bc91a0306ff586585f242abbabe Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 08:36:03 -0700 Subject: [PATCH 129/280] kern: swap tpidr_el1/cntv_cval_el0 as scratch vs exception stack --- .../mesosphere/arch/arm64/kern_cpu.hpp | 2 +- .../arch/arm64/kern_cpu_system_registers.hpp | 1 + .../nintendo/nx/kern_k_sleep_manager_asm.s | 12 ++++--- .../source/arch/arm64/init/kern_init_core.cpp | 4 +-- .../kernel/source/arch/arm64/init/start.s | 4 +-- .../arch/arm64/kern_exception_handlers_asm.s | 36 ++++++++++--------- .../kernel_ldr/source/arch/arm64/start.s | 3 +- 7 files changed, 34 insertions(+), 28 deletions(-) diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_cpu.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_cpu.hpp index c3c915a23..e9ca675ec 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_cpu.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_cpu.hpp @@ -232,7 +232,7 @@ namespace ams::kern::arch::arm64::cpu { } ALWAYS_INLINE void SetExceptionThreadStackTop(uintptr_t top) { - SetTpidrEl1(top); + cpu::SetCntvCvalEl0(top); } ALWAYS_INLINE void SwitchThreadLocalRegion(uintptr_t tlr) { diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_cpu_system_registers.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_cpu_system_registers.hpp index 7a5fe499c..d8c936e25 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_cpu_system_registers.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_cpu_system_registers.hpp @@ -74,6 +74,7 @@ namespace ams::kern::arch::arm64::cpu { MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(CntkCtlEl1, cntkctl_el1) MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(CntpCtlEl0, cntp_ctl_el0) MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(CntpCvalEl0, cntp_cval_el0) + MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(CntvCvalEl0, cntv_cval_el0) MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(Daif, daif) diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_sleep_manager_asm.s b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_sleep_manager_asm.s index bdc23c714..db198a294 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_sleep_manager_asm.s +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_sleep_manager_asm.s @@ -95,9 +95,10 @@ _ZN3ams4kern5board8nintendo2nx13KSleepManager15CpuSleepHandlerEmm: mrs x2, tpidr_el1 stp x1, x2, [x0], #0x10 - /* Save the virtual resumption entrypoint. */ + /* Save the virtual resumption entrypoint and cntv_cval_el0. */ adr x1, 77f - stp x1, xzr, [x0], #0x10 + mrs x2, cntv_cval_el0 + stp x1, x2, [x0], #0x10 /* Get the current core id. */ mrs x0, mpidr_el1 @@ -245,12 +246,13 @@ _ZN3ams4kern5board8nintendo2nx13KSleepManager11ResumeEntryEm: msr tcr_el1, x1 msr mair_el1, x2 - /* Get sctlr, tpidr, and the entrypoint. */ - ldp x1, x2, [x0], #0x10 - ldp x3, xzr, [x0], #0x10 + /* Get sctlr, tpidr, the entrypoint, and cntv_cval_el0. */ + ldp x1, x2, [x0], #0x10 + ldp x3, x4, [x0], #0x10 /* Set the global context back into x18/tpidr. */ msr tpidr_el1, x2 + msr cntv_cval_el0, x4 dsb sy isb diff --git a/mesosphere/kernel/source/arch/arm64/init/kern_init_core.cpp b/mesosphere/kernel/source/arch/arm64/init/kern_init_core.cpp index 98cd02ed8..6ca985e31 100644 --- a/mesosphere/kernel/source/arch/arm64/init/kern_init_core.cpp +++ b/mesosphere/kernel/source/arch/arm64/init/kern_init_core.cpp @@ -125,9 +125,6 @@ namespace ams::kern::init { /* Ensure our first argument is page aligned (as we will map it if it is non-zero). */ MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(misc_unk_debug_phys_addr, PageSize)); - /* Clear TPIDR_EL1 to zero. */ - cpu::ThreadIdRegisterAccessor(0).Store(); - /* Restore the page allocator state setup by kernel loader. */ g_initial_page_allocator.InitializeFromState(initial_page_allocator_state); @@ -476,6 +473,7 @@ namespace ams::kern::init { void InitializeExceptionVectors() { cpu::SetVbarEl1(reinterpret_cast(::ams::kern::ExceptionVectors)); + cpu::SetTpidrEl1(0); cpu::SetExceptionThreadStackTop(0); cpu::EnsureInstructionConsistency(); } diff --git a/mesosphere/kernel/source/arch/arm64/init/start.s b/mesosphere/kernel/source/arch/arm64/init/start.s index e6c8288af..398440c7d 100644 --- a/mesosphere/kernel/source/arch/arm64/init/start.s +++ b/mesosphere/kernel/source/arch/arm64/init/start.s @@ -227,9 +227,9 @@ _ZN3ams4kern4init16InvokeEntrypointEPKNS1_14KInitArgumentsE: /* Ensure that the exception vectors are setup. */ bl _ZN3ams4kern4init26InitializeExceptionVectorsEv - /* Setup the exception stack in tpidr_el1. */ + /* Setup the exception stack in cntv_cval_el0. */ ldr x1, [x20, #0x58] - msr tpidr_el1, x1 + msr cntv_cval_el0, x1 /* Jump to the entrypoint. */ ldr x1, [x20, #0x40] diff --git a/mesosphere/kernel/source/arch/arm64/kern_exception_handlers_asm.s b/mesosphere/kernel/source/arch/arm64/kern_exception_handlers_asm.s index f7645ee1e..2c8f1936d 100644 --- a/mesosphere/kernel/source/arch/arm64/kern_exception_handlers_asm.s +++ b/mesosphere/kernel/source/arch/arm64/kern_exception_handlers_asm.s @@ -281,8 +281,8 @@ _ZN3ams4kern4arch5arm6430EL0SynchronousExceptionHandlerEv: .global _ZN3ams4kern4arch5arm6430EL1SynchronousExceptionHandlerEv .type _ZN3ams4kern4arch5arm6430EL1SynchronousExceptionHandlerEv, %function _ZN3ams4kern4arch5arm6430EL1SynchronousExceptionHandlerEv: - /* Nintendo uses the "unused" virtual timer compare value as a scratch register. */ - msr cntv_cval_el0, x0 + /* Nintendo uses tpidr_el1 as a scratch register. */ + msr tpidr_el1, x0 /* Get and parse the exception syndrome register. */ mrs x0, esr_el1 @@ -297,18 +297,21 @@ _ZN3ams4kern4arch5arm6430EL1SynchronousExceptionHandlerEv: b.eq 5f 1: /* The exception is not a data abort or instruction abort caused by a TLB conflict. */ - /* Load the exception stack top from tpidr_el1. */ - mrs x0, tpidr_el1 + /* Load the exception stack top from otherwise "unused" virtual timer compare value. */ + mrs x0, cntv_cval_el0 /* Setup the stack for a generic exception handle */ + lsl x0, x0, #8 + asr x0, x0, #8 sub x0, x0, #0x20 - str x1, [x0, #16] + str x1, [x0, #8] mov x1, sp str x1, [x0] mov sp, x0 - ldr x1, [x0, #16] - mrs x0, cntv_cval_el0 + ldr x1, [x0, #8] + mrs x0, tpidr_el1 str x0, [sp, #8] + str x1, [sp, #16] /* Check again if this is a data abort from EL1. */ mrs x0, esr_el1 @@ -406,7 +409,7 @@ _ZN3ams4kern4arch5arm6430EL1SynchronousExceptionHandlerEv: isb /* Restore x0 from scratch. */ - mrs x0, cntv_cval_el0 + mrs x0, tpidr_el1 /* Return from the exception. */ eret @@ -474,21 +477,22 @@ _ZN3ams4kern4arch5arm6425FpuAccessExceptionHandlerEv: .global _ZN3ams4kern4arch5arm6421EL1SystemErrorHandlerEv .type _ZN3ams4kern4arch5arm6421EL1SystemErrorHandlerEv, %function _ZN3ams4kern4arch5arm6421EL1SystemErrorHandlerEv: - /* Nintendo uses the "unused" virtual timer compare value as a scratch register. */ - msr cntv_cval_el0, x0 + /* Nintendo uses tpidr_el1 as a scratch register. */ + msr tpidr_el1, x0 - /* Load the exception stack top from tpidr_el1. */ - mrs x0, tpidr_el1 + /* Load the exception stack top from otherwise "unused" virtual timer compare value. */ + mrs x0, cntv_cval_el0 /* Setup the stack for a generic exception handle */ + lsl x0, x0, #8 + asr x0, x0, #8 sub x0, x0, #0x20 - str x1, [x0, #16] + str x1, [x0, #8] mov x1, sp str x1, [x0] mov sp, x0 - ldr x1, [x0, #16] - mrs x0, cntv_cval_el0 - str x0, [sp, #8] + ldr x1, [x0, #8] + mrs x0, tpidr_el1 /* Create a KExceptionContext to pass to HandleException. */ sub sp, sp, #0x120 diff --git a/mesosphere/kernel_ldr/source/arch/arm64/start.s b/mesosphere/kernel_ldr/source/arch/arm64/start.s index f8610ae72..9254d065f 100644 --- a/mesosphere/kernel_ldr/source/arch/arm64/start.s +++ b/mesosphere/kernel_ldr/source/arch/arm64/start.s @@ -89,7 +89,8 @@ _main: bl _ZN3ams4kern4init3Elf18CallInitArrayFuncsEmm /* Setup system registers, for detection of errors during init later. */ - msr tpidr_el1, xzr /* Clear TPIDR_EL1 */ + msr tpidr_el1, xzr + msr cntv_cval_el0, xzr adr x0, __external_references adr x1, _start ldr x0, [x0, #0x30] From 1fce7b08b1bf1c0fbc664af61116237969e9ad09 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 08:46:06 -0700 Subject: [PATCH 130/280] kern: update KMemoryBlockManagerUpdaterAllocator for new ctor/init semantics --- .../kern_k_memory_block_manager.hpp | 26 +-- .../source/kern_k_page_table_base.cpp | 182 +++++++++++------- 2 files changed, 122 insertions(+), 86 deletions(-) diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_memory_block_manager.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_memory_block_manager.hpp index 605711891..e25a0dcc2 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_memory_block_manager.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_memory_block_manager.hpp @@ -27,18 +27,8 @@ namespace ams::kern { KMemoryBlock *m_blocks[MaxBlocks]; size_t m_index; KMemoryBlockSlabManager *m_slab_manager; - public: - constexpr explicit KMemoryBlockManagerUpdateAllocator(KMemoryBlockSlabManager *sm) : m_blocks(), m_index(MaxBlocks), m_slab_manager(sm) { /* ... */ } - - ~KMemoryBlockManagerUpdateAllocator() { - for (const auto &block : m_blocks) { - if (block != nullptr) { - m_slab_manager->Free(block); - } - } - } - - Result Initialize(size_t num_blocks) { + private: + ALWAYS_INLINE Result Initialize(size_t num_blocks) { /* Check num blocks. */ MESOSPHERE_ASSERT(num_blocks <= MaxBlocks); @@ -53,6 +43,18 @@ namespace ams::kern { return ResultSuccess(); } + public: + KMemoryBlockManagerUpdateAllocator(Result *out_result, KMemoryBlockSlabManager *sm, size_t num_blocks = MaxBlocks) : m_blocks(), m_index(MaxBlocks), m_slab_manager(sm) { + *out_result = this->Initialize(num_blocks); + } + + ~KMemoryBlockManagerUpdateAllocator() { + for (const auto &block : m_blocks) { + if (block != nullptr) { + m_slab_manager->Free(block); + } + } + } KMemoryBlock *Allocate() { MESOSPHERE_ABORT_UNLESS(m_index < MaxBlocks); diff --git a/libraries/libmesosphere/source/kern_k_page_table_base.cpp b/libraries/libmesosphere/source/kern_k_page_table_base.cpp index 28a2a0386..dc44eecbc 100644 --- a/libraries/libmesosphere/source/kern_k_page_table_base.cpp +++ b/libraries/libmesosphere/source/kern_k_page_table_base.cpp @@ -529,8 +529,9 @@ namespace ams::kern { } /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* Decide on new perm and attr. */ new_perm = (new_perm != KMemoryPermission_None) ? new_perm : old_perm; @@ -585,10 +586,9 @@ namespace ams::kern { KMemoryAttribute new_attr = static_cast(old_attr & ~lock_attr); /* Create an update allocator. */ - /* NOTE: Nintendo does not initialize the allocator with any blocks. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(0)); - MESOSPHERE_UNUSED(num_allocator_blocks); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* Update permission, if we need to. */ if (new_perm != old_perm) { @@ -702,12 +702,14 @@ namespace ams::kern { R_TRY(this->CheckMemoryState(std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState_All, KMemoryState_Free, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryAttribute_None)); /* Create an update allocator for the source. */ - KMemoryBlockManagerUpdateAllocator src_allocator(m_memory_block_slab_manager); - R_TRY(src_allocator.Initialize(num_src_allocator_blocks)); + Result src_allocator_result; + KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), m_memory_block_slab_manager, num_src_allocator_blocks); + R_TRY(src_allocator_result); /* Create an update allocator for the destination. */ - KMemoryBlockManagerUpdateAllocator dst_allocator(m_memory_block_slab_manager); - R_TRY(dst_allocator.Initialize(num_dst_allocator_blocks)); + Result dst_allocator_result; + KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), m_memory_block_slab_manager, num_dst_allocator_blocks); + R_TRY(dst_allocator_result); /* Map the memory. */ { @@ -765,12 +767,14 @@ namespace ams::kern { R_TRY(this->CheckMemoryState(nullptr, std::addressof(dst_perm), nullptr, std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState_All, KMemoryState_Stack, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_All, KMemoryAttribute_None)); /* Create an update allocator for the source. */ - KMemoryBlockManagerUpdateAllocator src_allocator(m_memory_block_slab_manager); - R_TRY(src_allocator.Initialize(num_src_allocator_blocks)); + Result src_allocator_result; + KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), m_memory_block_slab_manager, num_src_allocator_blocks); + R_TRY(src_allocator_result); /* Create an update allocator for the destination. */ - KMemoryBlockManagerUpdateAllocator dst_allocator(m_memory_block_slab_manager); - R_TRY(dst_allocator.Initialize(num_dst_allocator_blocks)); + Result dst_allocator_result; + KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), m_memory_block_slab_manager, num_dst_allocator_blocks); + R_TRY(dst_allocator_result); /* Unmap the memory. */ { @@ -831,12 +835,14 @@ namespace ams::kern { R_TRY(this->CheckMemoryState(std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState_All, KMemoryState_Free, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryAttribute_None)); /* Create an update allocator for the source. */ - KMemoryBlockManagerUpdateAllocator src_allocator(m_memory_block_slab_manager); - R_TRY(src_allocator.Initialize(num_src_allocator_blocks)); + Result src_allocator_result; + KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), m_memory_block_slab_manager, num_src_allocator_blocks); + R_TRY(src_allocator_result); /* Create an update allocator for the destination. */ - KMemoryBlockManagerUpdateAllocator dst_allocator(m_memory_block_slab_manager); - R_TRY(dst_allocator.Initialize(num_dst_allocator_blocks)); + Result dst_allocator_result; + KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), m_memory_block_slab_manager, num_dst_allocator_blocks); + R_TRY(dst_allocator_result); /* Map the code memory. */ { @@ -940,12 +946,14 @@ namespace ams::kern { R_UNLESS(this->IsValidPageGroup(pg, src_address, num_pages), svc::ResultInvalidMemoryRegion()); /* Create an update allocator for the source. */ - KMemoryBlockManagerUpdateAllocator src_allocator(m_memory_block_slab_manager); - R_TRY(src_allocator.Initialize(num_src_allocator_blocks)); + Result src_allocator_result; + KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), m_memory_block_slab_manager, num_src_allocator_blocks); + R_TRY(src_allocator_result); /* Create an update allocator for the destination. */ - KMemoryBlockManagerUpdateAllocator dst_allocator(m_memory_block_slab_manager); - R_TRY(dst_allocator.Initialize(num_dst_allocator_blocks)); + Result dst_allocator_result; + KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), m_memory_block_slab_manager, num_dst_allocator_blocks); + R_TRY(dst_allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -1335,8 +1343,9 @@ namespace ams::kern { R_SUCCEED_IF(old_perm == new_perm); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -1391,8 +1400,9 @@ namespace ams::kern { R_SUCCEED_IF(old_perm == new_perm && old_state == new_state); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -1436,8 +1446,9 @@ namespace ams::kern { AttributeTestMask, KMemoryAttribute_None, ~AttributeTestMask)); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -1483,8 +1494,9 @@ namespace ams::kern { KMemoryAttribute_All, KMemoryAttribute_None)); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -1547,8 +1559,9 @@ namespace ams::kern { R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), m_current_heap_end, allocation_size, KMemoryState_All, KMemoryState_Free, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryAttribute_None)); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -1751,8 +1764,9 @@ namespace ams::kern { MESOSPHERE_R_ASSERT(this->CheckMemoryState(addr, size, KMemoryState_All, KMemoryState_Free, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryAttribute_None)); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(KMemoryBlockManagerUpdateAllocator::MaxBlocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -1818,8 +1832,9 @@ namespace ams::kern { MESOSPHERE_R_ASSERT(this->CheckMemoryState(addr, size, KMemoryState_All, KMemoryState_Free, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryAttribute_None)); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(KMemoryBlockManagerUpdateAllocator::MaxBlocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -1869,8 +1884,9 @@ namespace ams::kern { MESOSPHERE_R_ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState_All, KMemoryState_Free, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryAttribute_None)); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(KMemoryBlockManagerUpdateAllocator::MaxBlocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -1904,8 +1920,9 @@ namespace ams::kern { R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, KMemoryState_All, KMemoryState_Free, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryAttribute_None)); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -1932,8 +1949,9 @@ namespace ams::kern { R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, KMemoryState_All, state, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_All, KMemoryAttribute_None)); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -1966,8 +1984,9 @@ namespace ams::kern { MESOSPHERE_R_ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState_All, KMemoryState_Free, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryAttribute_None)); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(KMemoryBlockManagerUpdateAllocator::MaxBlocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -2000,8 +2019,9 @@ namespace ams::kern { R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), addr, size, KMemoryState_All, KMemoryState_Free, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryAttribute_None)); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -2036,8 +2056,9 @@ namespace ams::kern { R_UNLESS(this->IsValidPageGroup(pg, address, num_pages), svc::ResultInvalidCurrentMemory()); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -2331,8 +2352,9 @@ namespace ams::kern { } /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* Update the memory blocks. */ m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages, &KMemoryBlock::ShareToDevice, KMemoryPermission_None); @@ -2362,8 +2384,9 @@ namespace ams::kern { KMemoryAttribute_DeviceShared | KMemoryAttribute_Locked, KMemoryAttribute_DeviceShared)); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* Update the memory blocks. */ m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages, &KMemoryBlock::UnshareToDevice, KMemoryPermission_None); @@ -2388,8 +2411,9 @@ namespace ams::kern { KMemoryAttribute_DeviceShared | KMemoryAttribute_Locked, KMemoryAttribute_DeviceShared)); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* Make the page group. */ R_TRY(this->MakePageGroup(*out, address, num_pages)); @@ -2440,12 +2464,14 @@ namespace ams::kern { } /* Create an update allocator for the region. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(allocator_num_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, allocator_num_blocks); + R_TRY(allocator_result); /* Create an update allocator for the unmapped region. */ - KMemoryBlockManagerUpdateAllocator unmapped_allocator(m_memory_block_slab_manager); - R_TRY(unmapped_allocator.Initialize(unmapped_allocator_num_blocks)); + Result unmapped_allocator_result; + KMemoryBlockManagerUpdateAllocator unmapped_allocator(std::addressof(unmapped_allocator_result), m_memory_block_slab_manager, unmapped_allocator_num_blocks); + R_TRY(unmapped_allocator_result); /* Determine parameters for the update lock call. */ KMemoryBlockManagerUpdateAllocator *lock_allocator; @@ -3229,8 +3255,9 @@ namespace ams::kern { MESOSPHERE_ASSERT(this->CanContain(dst_addr, aligned_src_size, dst_state)); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(KMemoryBlockManagerUpdateAllocator::MaxBlocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -3429,8 +3456,9 @@ namespace ams::kern { R_TRY(src_page_table.SetupForIpcClient(updater.GetPageList(), std::addressof(num_allocator_blocks), src_addr, size, test_perm, dst_state)); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(src_page_table.m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), src_page_table.m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* Get the mapped extents. */ const KProcessAddress src_map_start = util::AlignUp(GetInteger(src_addr), PageSize); @@ -3472,8 +3500,9 @@ namespace ams::kern { R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, KMemoryState_All, dst_state, KMemoryPermission_UserRead, KMemoryPermission_UserRead, KMemoryAttribute_All, KMemoryAttribute_None)); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -3675,8 +3704,9 @@ namespace ams::kern { /* Create an update allocator. */ /* NOTE: Guaranteed zero blocks needed here. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(0)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, 0); + R_TRY(allocator_result); /* Unlock the pages. */ m_memory_block_manager.UpdateLock(std::addressof(allocator), mapping_start, mapping_size / PageSize, &KMemoryBlock::UnlockForIpc, KMemoryPermission_None); @@ -3869,8 +3899,9 @@ namespace ams::kern { /* Create an update allocator. */ MESOSPHERE_ASSERT(num_allocator_blocks <= KMemoryBlockManagerUpdateAllocator::MaxBlocks); - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -4106,8 +4137,9 @@ namespace ams::kern { /* Create an update allocator. */ MESOSPHERE_ASSERT(num_allocator_blocks <= KMemoryBlockManagerUpdateAllocator::MaxBlocks); - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -4249,8 +4281,9 @@ namespace ams::kern { R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, KMemoryState_All, KMemoryState_Free, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryAttribute_None)); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); @@ -4283,8 +4316,9 @@ namespace ams::kern { R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, KMemoryState_All, KMemoryState_Normal, KMemoryPermission_All, KMemoryPermission_UserReadWrite, KMemoryAttribute_All, KMemoryAttribute_None)); /* Create an update allocator. */ - KMemoryBlockManagerUpdateAllocator allocator(m_memory_block_slab_manager); - R_TRY(allocator.Initialize(num_allocator_blocks)); + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); From 561a16a3487243a5eb2999ba3a2582e4cca42470 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 08:50:44 -0700 Subject: [PATCH 131/280] kern: flush memory before reading in KPageTableBase::ReadDebugMemory --- libraries/libmesosphere/source/kern_k_page_table_base.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/libraries/libmesosphere/source/kern_k_page_table_base.cpp b/libraries/libmesosphere/source/kern_k_page_table_base.cpp index dc44eecbc..5e5ff59d1 100644 --- a/libraries/libmesosphere/source/kern_k_page_table_base.cpp +++ b/libraries/libmesosphere/source/kern_k_page_table_base.cpp @@ -2198,7 +2198,9 @@ namespace ams::kern { /* Copy as much aligned data as we can. */ if (cur_size >= sizeof(u32)) { const size_t copy_size = util::AlignDown(cur_size, sizeof(u32)); - R_UNLESS(UserspaceAccess::CopyMemoryToUserAligned32Bit(buffer, GetVoidPointer(GetLinearMappedVirtualAddress(cur_addr)), copy_size), svc::ResultInvalidPointer()); + const void * copy_src = GetVoidPointer(GetLinearMappedVirtualAddress(cur_addr)); + cpu::FlushDataCache(copy_src, copy_size); + R_UNLESS(UserspaceAccess::CopyMemoryToUserAligned32Bit(buffer, copy_src, copy_size), svc::ResultInvalidPointer()); buffer = reinterpret_cast(reinterpret_cast(buffer) + copy_size); cur_addr += copy_size; cur_size -= copy_size; @@ -2206,7 +2208,9 @@ namespace ams::kern { /* Copy remaining data. */ if (cur_size > 0) { - R_UNLESS(UserspaceAccess::CopyMemoryToUser(buffer, GetVoidPointer(GetLinearMappedVirtualAddress(cur_addr)), cur_size), svc::ResultInvalidPointer()); + const void * copy_src = GetVoidPointer(GetLinearMappedVirtualAddress(cur_addr)); + cpu::FlushDataCache(copy_src, cur_size); + R_UNLESS(UserspaceAccess::CopyMemoryToUser(buffer, copy_src, cur_size), svc::ResultInvalidPointer()); } return ResultSuccess(); From 53e7aa0a20317886600377b79d80d601cbb34f09 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 09:25:19 -0700 Subject: [PATCH 132/280] kern: add KPageTableBase::Read/WriteDebugIoMemory --- .../arch/arm64/kern_k_process_page_table.hpp | 12 + .../mesosphere/kern_k_page_table_base.hpp | 7 + .../source/kern_k_debug_base.cpp | 92 +------ .../source/kern_k_page_table_base.cpp | 230 ++++++++++++++++-- 4 files changed, 234 insertions(+), 107 deletions(-) diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp index 3bf4d6cee..9ca342116 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp @@ -136,10 +136,18 @@ namespace ams::kern::arch::arm64 { return m_page_table.ReadDebugMemory(buffer, address, size); } + Result ReadDebugIoMemory(void *buffer, KProcessAddress address, size_t size) { + return m_page_table.ReadDebugIoMemory(buffer, address, size); + } + Result WriteDebugMemory(KProcessAddress address, const void *buffer, size_t size) { return m_page_table.WriteDebugMemory(address, buffer, size); } + Result WriteDebugIoMemory(KProcessAddress address, const void *buffer, size_t size) { + return m_page_table.WriteDebugIoMemory(address, buffer, size); + } + Result LockForDeviceAddressSpace(KPageGroup *out, KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned) { return m_page_table.LockForDeviceAddressSpace(out, address, size, perm, is_aligned); } @@ -290,6 +298,10 @@ namespace ams::kern::arch::arm64 { KBlockInfoManager *GetBlockInfoManager() { return m_page_table.GetBlockInfoManager(); } + + KPageTableBase &GetBasePageTable() { + return m_page_table; + } }; } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp index 2eafc4c49..b63a9a4ee 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp @@ -288,6 +288,10 @@ namespace ams::kern { NOINLINE Result MapPages(KProcessAddress *out_addr, size_t num_pages, size_t alignment, KPhysicalAddress phys_addr, bool is_pa_valid, KProcessAddress region_start, size_t region_num_pages, KMemoryState state, KMemoryPermission perm); + Result MapIoImpl(KProcessAddress *out, PageLinkedList *page_list, KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm); + Result ReadIoMemoryImpl(void *buffer, KPhysicalAddress phys_addr, size_t size); + Result WriteIoMemoryImpl(KPhysicalAddress phys_addr, const void *buffer, size_t size); + Result SetupForIpcClient(PageLinkedList *page_list, size_t *out_blocks_needed, KProcessAddress address, size_t size, KMemoryPermission test_perm, KMemoryState dst_state); Result SetupForIpcServer(KProcessAddress *out_addr, size_t size, KProcessAddress src_addr, KMemoryPermission test_perm, KMemoryState dst_state, KPageTableBase &src_page_table, bool send); void CleanupForIpcClientOnServerSetupFailure(PageLinkedList *page_list, KProcessAddress address, size_t size, KMemoryPermission prot_perm); @@ -341,7 +345,10 @@ namespace ams::kern { Result InvalidateProcessDataCache(KProcessAddress address, size_t size); Result ReadDebugMemory(void *buffer, KProcessAddress address, size_t size); + Result ReadDebugIoMemory(void *buffer, KProcessAddress address, size_t size); + Result WriteDebugMemory(KProcessAddress address, const void *buffer, size_t size); + Result WriteDebugIoMemory(KProcessAddress address, const void *buffer, size_t size); Result LockForDeviceAddressSpace(KPageGroup *out, KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned); Result UnlockForDeviceAddressSpace(KProcessAddress address, size_t size); diff --git a/libraries/libmesosphere/source/kern_k_debug_base.cpp b/libraries/libmesosphere/source/kern_k_debug_base.cpp index 7e9d61557..bd49cab2c 100644 --- a/libraries/libmesosphere/source/kern_k_debug_base.cpp +++ b/libraries/libmesosphere/source/kern_k_debug_base.cpp @@ -92,51 +92,7 @@ namespace ams::kern { R_TRY(target_pt.ReadDebugMemory(GetVoidPointer(buffer), cur_address, cur_size)); } else { /* The memory is IO memory. */ - - /* Verify that the memory is readable. */ - R_UNLESS((info.GetPermission() & KMemoryPermission_UserRead) == KMemoryPermission_UserRead, svc::ResultInvalidAddress()); - - /* Get the physical address of the memory. */ - /* NOTE: Nintendo does not verify the result of this call. */ - KPhysicalAddress phys_addr; - target_pt.GetPhysicalAddress(std::addressof(phys_addr), cur_address); - - /* Map the address as IO in the current process. */ - R_TRY(debugger_pt.MapIo(util::AlignDown(GetInteger(phys_addr), PageSize), PageSize, KMemoryPermission_UserRead)); - - /* Get the address of the newly mapped IO. */ - KProcessAddress io_address; - Result query_result = debugger_pt.QueryIoMapping(std::addressof(io_address), util::AlignDown(GetInteger(phys_addr), PageSize), PageSize); - MESOSPHERE_R_ASSERT(query_result); - R_TRY(query_result); - - /* Ensure we clean up the new mapping on scope exit. */ - ON_SCOPE_EXIT { MESOSPHERE_R_ABORT_UNLESS(debugger_pt.UnmapPages(util::AlignDown(GetInteger(io_address), PageSize), 1, KMemoryState_Io)); }; - - /* Adjust the io address for alignment. */ - io_address += (GetInteger(cur_address) & (PageSize - 1)); - - /* Get the readable size. */ - const size_t readable_size = std::min(cur_size, util::AlignDown(GetInteger(cur_address) + PageSize, PageSize) - GetInteger(cur_address)); - - /* Read the memory. */ - switch ((GetInteger(cur_address) | readable_size) & 3) { - case 0: - { - R_UNLESS(UserspaceAccess::ReadIoMemory32Bit(GetVoidPointer(buffer), GetVoidPointer(io_address), readable_size), svc::ResultInvalidPointer()); - } - break; - case 2: - { - R_UNLESS(UserspaceAccess::ReadIoMemory16Bit(GetVoidPointer(buffer), GetVoidPointer(io_address), readable_size), svc::ResultInvalidPointer()); - } - break; - default: - { - R_UNLESS(UserspaceAccess::ReadIoMemory8Bit(GetVoidPointer(buffer), GetVoidPointer(io_address), readable_size), svc::ResultInvalidPointer()); - } - break; - } + R_TRY(target_pt.ReadDebugIoMemory(GetVoidPointer(buffer), cur_address, cur_size)); } /* Advance. */ @@ -185,51 +141,7 @@ namespace ams::kern { R_TRY(target_pt.WriteDebugMemory(cur_address, GetVoidPointer(buffer), cur_size)); } else { /* The memory is IO memory. */ - - /* Verify that the memory is writable. */ - R_UNLESS((info.GetPermission() & KMemoryPermission_UserReadWrite) == KMemoryPermission_UserReadWrite, svc::ResultInvalidAddress()); - - /* Get the physical address of the memory. */ - /* NOTE: Nintendo does not verify the result of this call. */ - KPhysicalAddress phys_addr; - target_pt.GetPhysicalAddress(std::addressof(phys_addr), cur_address); - - /* Map the address as IO in the current process. */ - R_TRY(debugger_pt.MapIo(util::AlignDown(GetInteger(phys_addr), PageSize), PageSize, KMemoryPermission_UserReadWrite)); - - /* Get the address of the newly mapped IO. */ - KProcessAddress io_address; - Result query_result = debugger_pt.QueryIoMapping(std::addressof(io_address), util::AlignDown(GetInteger(phys_addr), PageSize), PageSize); - MESOSPHERE_R_ASSERT(query_result); - R_TRY(query_result); - - /* Ensure we clean up the new mapping on scope exit. */ - ON_SCOPE_EXIT { MESOSPHERE_R_ABORT_UNLESS(debugger_pt.UnmapPages(util::AlignDown(GetInteger(io_address), PageSize), 1, KMemoryState_Io)); }; - - /* Adjust the io address for alignment. */ - io_address += (GetInteger(cur_address) & (PageSize - 1)); - - /* Get the readable size. */ - const size_t readable_size = std::min(cur_size, util::AlignDown(GetInteger(cur_address) + PageSize, PageSize) - GetInteger(cur_address)); - - /* Read the memory. */ - switch ((GetInteger(cur_address) | readable_size) & 3) { - case 0: - { - R_UNLESS(UserspaceAccess::WriteIoMemory32Bit(GetVoidPointer(io_address), GetVoidPointer(buffer), readable_size), svc::ResultInvalidPointer()); - } - break; - case 2: - { - R_UNLESS(UserspaceAccess::WriteIoMemory16Bit(GetVoidPointer(io_address), GetVoidPointer(buffer), readable_size), svc::ResultInvalidPointer()); - } - break; - default: - { - R_UNLESS(UserspaceAccess::WriteIoMemory8Bit(GetVoidPointer(io_address), GetVoidPointer(buffer), readable_size), svc::ResultInvalidPointer()); - } - break; - } + R_TRY(target_pt.WriteDebugIoMemory(cur_address, GetVoidPointer(buffer), cur_size)); } /* Advance. */ diff --git a/libraries/libmesosphere/source/kern_k_page_table_base.cpp b/libraries/libmesosphere/source/kern_k_page_table_base.cpp index 5e5ff59d1..e503a3a37 100644 --- a/libraries/libmesosphere/source/kern_k_page_table_base.cpp +++ b/libraries/libmesosphere/source/kern_k_page_table_base.cpp @@ -997,7 +997,7 @@ namespace ams::kern { KMemoryInfo info; ams::svc::PageInfo page_info; - MESOSPHERE_R_ABORT_UNLESS(this->QueryInfoImpl(&info, &page_info, candidate)); + MESOSPHERE_R_ABORT_UNLESS(this->QueryInfoImpl(std::addressof(info), std::addressof(page_info), candidate)); if (info.m_state != KMemoryState_Free) { continue; } if (!(region_start <= candidate)) { continue; } @@ -1355,7 +1355,7 @@ namespace ams::kern { R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, Null, false, properties, OperationType_ChangePermissions, false)); /* Update the blocks. */ - m_memory_block_manager.Update(&allocator, addr, num_pages, old_state, new_perm, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_None); + m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_None); return ResultSuccess(); } @@ -1413,7 +1413,7 @@ namespace ams::kern { R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, Null, false, properties, operation, false)); /* Update the blocks. */ - m_memory_block_manager.Update(&allocator, addr, num_pages, new_state, new_perm, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_None); + m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, new_state, new_perm, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_None); /* Ensure cache coherency, if we're setting pages as executable. */ if (is_x) { @@ -1461,7 +1461,7 @@ namespace ams::kern { R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, Null, false, properties, OperationType_ChangePermissionsAndRefresh, false)); /* Update the blocks. */ - m_memory_block_manager.Update(&allocator, addr, num_pages, old_state, old_perm, new_attr, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_None); + m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, old_perm, new_attr, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_None); return ResultSuccess(); } @@ -1701,10 +1701,13 @@ namespace ams::kern { return ResultSuccess(); } - Result KPageTableBase::MapIo(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) { + Result KPageTableBase::MapIoImpl(KProcessAddress *out, PageLinkedList *page_list, KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) { + /* Check pre-conditions. */ + MESOSPHERE_ASSERT(this->IsLockedByCurrentThread()); MESOSPHERE_ASSERT(util::IsAligned(GetInteger(phys_addr), PageSize)); MESOSPHERE_ASSERT(util::IsAligned(size, PageSize)); MESOSPHERE_ASSERT(size > 0); + R_UNLESS(phys_addr < phys_addr + size, svc::ResultInvalidAddress()); const size_t num_pages = size / PageSize; const KPhysicalAddress last = phys_addr + size - 1; @@ -1740,9 +1743,6 @@ namespace ams::kern { region = region->GetNext(); }; - /* Lock the table. */ - KScopedLightLock lk(m_general_lock); - /* Select an address to map at. */ KProcessAddress addr = Null; const size_t phys_alignment = std::min(std::min(GetInteger(phys_addr) & -GetInteger(phys_addr), size & -size), MaxPhysicalMapAlignment); @@ -1763,6 +1763,20 @@ namespace ams::kern { MESOSPHERE_ASSERT(this->CanContain(addr, size, KMemoryState_Io)); MESOSPHERE_R_ASSERT(this->CheckMemoryState(addr, size, KMemoryState_All, KMemoryState_Free, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryAttribute_None)); + /* Perform mapping operation. */ + const KPageProperties properties = { perm, true, false, DisableMergeAttribute_DisableHead }; + R_TRY(this->Operate(page_list, addr, num_pages, phys_addr, true, properties, OperationType_Map, false)); + + /* Set the output address. */ + *out = addr; + + return ResultSuccess(); + } + + Result KPageTableBase::MapIo(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) { + /* Lock the table. */ + KScopedLightLock lk(m_general_lock); + /* Create an update allocator. */ Result allocator_result; KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager); @@ -1771,12 +1785,12 @@ namespace ams::kern { /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); - /* Perform mapping operation. */ - const KPageProperties properties = { perm, true, false, DisableMergeAttribute_DisableHead }; - R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, phys_addr, true, properties, OperationType_Map, false)); + /* Map the io memory. */ + KProcessAddress addr; + R_TRY(this->MapIoImpl(std::addressof(addr), updater.GetPageList(), phys_addr, size, perm)); /* Update the blocks. */ - m_memory_block_manager.Update(&allocator, addr, num_pages, KMemoryState_Io, perm, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_Normal, KMemoryBlockDisableMergeAttribute_None); + m_memory_block_manager.Update(std::addressof(allocator), addr, size / PageSize, KMemoryState_Io, perm, KMemoryAttribute_Locked, KMemoryBlockDisableMergeAttribute_Normal, KMemoryBlockDisableMergeAttribute_None); /* We successfully mapped the pages. */ return ResultSuccess(); @@ -1844,7 +1858,7 @@ namespace ams::kern { R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, phys_addr, true, properties, OperationType_Map, false)); /* Update the blocks. */ - m_memory_block_manager.Update(&allocator, addr, num_pages, KMemoryState_Static, perm, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_Normal, KMemoryBlockDisableMergeAttribute_None); + m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, KMemoryState_Static, perm, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_Normal, KMemoryBlockDisableMergeAttribute_None); /* We successfully mapped the pages. */ return ResultSuccess(); @@ -1961,7 +1975,7 @@ namespace ams::kern { R_TRY(this->Operate(updater.GetPageList(), address, num_pages, Null, false, unmap_properties, OperationType_Unmap, false)); /* Update the blocks. */ - m_memory_block_manager.Update(&allocator, address, num_pages, KMemoryState_Free, KMemoryPermission_None, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_Normal); + m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState_Free, KMemoryPermission_None, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_Normal); return ResultSuccess(); } @@ -1996,7 +2010,7 @@ namespace ams::kern { R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false)); /* Update the blocks. */ - m_memory_block_manager.Update(&allocator, addr, num_pages, state, perm, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_Normal, KMemoryBlockDisableMergeAttribute_None); + m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_Normal, KMemoryBlockDisableMergeAttribute_None); /* We successfully mapped the pages. */ *out_addr = addr; @@ -2031,7 +2045,7 @@ namespace ams::kern { R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false)); /* Update the blocks. */ - m_memory_block_manager.Update(&allocator, addr, num_pages, state, perm, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_Normal, KMemoryBlockDisableMergeAttribute_None); + m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_Normal, KMemoryBlockDisableMergeAttribute_None); /* We successfully mapped the pages. */ return ResultSuccess(); @@ -2068,7 +2082,7 @@ namespace ams::kern { R_TRY(this->Operate(updater.GetPageList(), address, num_pages, Null, false, properties, OperationType_Unmap, false)); /* Update the blocks. */ - m_memory_block_manager.Update(&allocator, address, num_pages, KMemoryState_Free, KMemoryPermission_None, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_Normal); + m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState_Free, KMemoryPermission_None, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_Normal); return ResultSuccess(); } @@ -2337,6 +2351,188 @@ namespace ams::kern { return ResultSuccess(); } + Result KPageTableBase::ReadIoMemoryImpl(void *buffer, KPhysicalAddress phys_addr, size_t size) { + /* Check pre-conditions. */ + MESOSPHERE_ASSERT(this->IsLockedByCurrentThread()); + + /* Determine the mapping extents. */ + const KPhysicalAddress map_start = util::AlignDown(GetInteger(phys_addr), PageSize); + const KPhysicalAddress map_end = util::AlignUp(GetInteger(phys_addr) + size, PageSize); + const size_t map_size = map_end - map_start; + + /* We're going to perform an update, so create a helper. */ + KScopedPageTableUpdater updater(this); + + /* Temporarily map the io memory. */ + KProcessAddress io_addr; + R_TRY(this->MapIoImpl(std::addressof(io_addr), updater.GetPageList(), map_start, map_size, KMemoryPermission_UserRead)); + + /* Ensure we unmap the io memory when we're done with it. */ + ON_SCOPE_EXIT { + const KPageProperties unmap_properties = { KMemoryPermission_None, false, false, DisableMergeAttribute_None }; + MESOSPHERE_R_ABORT_UNLESS(this->Operate(updater.GetPageList(), io_addr, map_size / PageSize, Null, false, unmap_properties, OperationType_Unmap, true)); + }; + + /* Read the memory. */ + const KProcessAddress read_addr = io_addr + (GetInteger(phys_addr) & (PageSize - 1)); + switch ((GetInteger(read_addr) | size) & 3) { + case 0: + { + R_UNLESS(UserspaceAccess::ReadIoMemory32Bit(buffer, GetVoidPointer(read_addr), size), svc::ResultInvalidPointer()); + } + break; + case 2: + { + R_UNLESS(UserspaceAccess::ReadIoMemory16Bit(buffer, GetVoidPointer(read_addr), size), svc::ResultInvalidPointer()); + } + break; + default: + { + R_UNLESS(UserspaceAccess::ReadIoMemory8Bit(buffer, GetVoidPointer(read_addr), size), svc::ResultInvalidPointer()); + } + break; + } + + return ResultSuccess(); + } + + Result KPageTableBase::WriteIoMemoryImpl(KPhysicalAddress phys_addr, const void *buffer, size_t size) { + /* Check pre-conditions. */ + MESOSPHERE_ASSERT(this->IsLockedByCurrentThread()); + + /* Determine the mapping extents. */ + const KPhysicalAddress map_start = util::AlignDown(GetInteger(phys_addr), PageSize); + const KPhysicalAddress map_end = util::AlignUp(GetInteger(phys_addr) + size, PageSize); + const size_t map_size = map_end - map_start; + + /* We're going to perform an update, so create a helper. */ + KScopedPageTableUpdater updater(this); + + /* Temporarily map the io memory. */ + KProcessAddress io_addr; + R_TRY(this->MapIoImpl(std::addressof(io_addr), updater.GetPageList(), map_start, map_size, KMemoryPermission_UserReadWrite)); + + /* Ensure we unmap the io memory when we're done with it. */ + ON_SCOPE_EXIT { + const KPageProperties unmap_properties = { KMemoryPermission_None, false, false, DisableMergeAttribute_None }; + MESOSPHERE_R_ABORT_UNLESS(this->Operate(updater.GetPageList(), io_addr, map_size / PageSize, Null, false, unmap_properties, OperationType_Unmap, true)); + }; + + /* Read the memory. */ + const KProcessAddress write_addr = io_addr + (GetInteger(phys_addr) & (PageSize - 1)); + switch ((GetInteger(write_addr) | size) & 3) { + case 0: + { + R_UNLESS(UserspaceAccess::WriteIoMemory32Bit(GetVoidPointer(write_addr), buffer, size), svc::ResultInvalidPointer()); + } + break; + case 2: + { + R_UNLESS(UserspaceAccess::WriteIoMemory16Bit(GetVoidPointer(write_addr), buffer, size), svc::ResultInvalidPointer()); + } + break; + default: + { + R_UNLESS(UserspaceAccess::WriteIoMemory8Bit(GetVoidPointer(write_addr), buffer, size), svc::ResultInvalidPointer()); + } + break; + } + + return ResultSuccess(); + } + + Result KPageTableBase::ReadDebugIoMemory(void *buffer, KProcessAddress address, size_t size) { + /* Lightly validate the range before doing anything else. */ + R_UNLESS(this->Contains(address, size), svc::ResultInvalidCurrentMemory()); + + /* We need to lock both this table, and the current process's table, so set up some aliases. */ + KPageTableBase &src_page_table = *this; + KPageTableBase &dst_page_table = GetCurrentProcess().GetPageTable().GetBasePageTable(); + + /* Get the table locks. */ + KLightLock &lock_0 = (reinterpret_cast(std::addressof(src_page_table)) <= reinterpret_cast(std::addressof(dst_page_table))) ? src_page_table.m_general_lock : dst_page_table.m_general_lock; + KLightLock &lock_1 = (reinterpret_cast(std::addressof(src_page_table)) <= reinterpret_cast(std::addressof(dst_page_table))) ? dst_page_table.m_general_lock : src_page_table.m_general_lock; + + /* Lock the first lock. */ + KScopedLightLock lk0(lock_0); + + /* If necessary, lock the second lock. */ + std::optional lk1; + if (std::addressof(lock_0) != std::addressof(lock_1)) { + lk1.emplace(lock_1); + } + + /* Check that the desired range is readable io memory. */ + R_TRY(this->CheckMemoryStateContiguous(address, size, KMemoryState_All, KMemoryState_Io, KMemoryPermission_UserRead, KMemoryPermission_UserRead, KMemoryAttribute_None, KMemoryAttribute_None)); + + /* Read the memory. */ + u8 *dst = static_cast(buffer); + const KProcessAddress last_address = address + size - 1; + while (address <= last_address) { + /* Get the current physical address. */ + KPhysicalAddress phys_addr; + MESOSPHERE_ABORT_UNLESS(src_page_table.GetPhysicalAddress(std::addressof(phys_addr), address)); + + /* Determine the current read size. */ + const size_t cur_size = std::min(last_address - address + 1, util::AlignDown(GetInteger(address) + PageSize, PageSize) - GetInteger(address)); + + /* Read. */ + R_TRY(dst_page_table.ReadIoMemoryImpl(dst, phys_addr, cur_size)); + + /* Advance. */ + address += cur_size; + dst += cur_size; + } + + return ResultSuccess(); + } + + Result KPageTableBase::WriteDebugIoMemory(KProcessAddress address, const void *buffer, size_t size) { + /* Lightly validate the range before doing anything else. */ + R_UNLESS(this->Contains(address, size), svc::ResultInvalidCurrentMemory()); + + /* We need to lock both this table, and the current process's table, so set up some aliases. */ + KPageTableBase &src_page_table = *this; + KPageTableBase &dst_page_table = GetCurrentProcess().GetPageTable().GetBasePageTable(); + + /* Get the table locks. */ + KLightLock &lock_0 = (reinterpret_cast(std::addressof(src_page_table)) <= reinterpret_cast(std::addressof(dst_page_table))) ? src_page_table.m_general_lock : dst_page_table.m_general_lock; + KLightLock &lock_1 = (reinterpret_cast(std::addressof(src_page_table)) <= reinterpret_cast(std::addressof(dst_page_table))) ? dst_page_table.m_general_lock : src_page_table.m_general_lock; + + /* Lock the first lock. */ + KScopedLightLock lk0(lock_0); + + /* If necessary, lock the second lock. */ + std::optional lk1; + if (std::addressof(lock_0) != std::addressof(lock_1)) { + lk1.emplace(lock_1); + } + + /* Check that the desired range is writable io memory. */ + R_TRY(this->CheckMemoryStateContiguous(address, size, KMemoryState_All, KMemoryState_Io, KMemoryPermission_UserReadWrite, KMemoryPermission_UserReadWrite, KMemoryAttribute_None, KMemoryAttribute_None)); + + /* Read the memory. */ + const u8 *src = static_cast(buffer); + const KProcessAddress last_address = address + size - 1; + while (address <= last_address) { + /* Get the current physical address. */ + KPhysicalAddress phys_addr; + MESOSPHERE_ABORT_UNLESS(src_page_table.GetPhysicalAddress(std::addressof(phys_addr), address)); + + /* Determine the current read size. */ + const size_t cur_size = std::min(last_address - address + 1, util::AlignDown(GetInteger(address) + PageSize, PageSize) - GetInteger(address)); + + /* Read. */ + R_TRY(dst_page_table.WriteIoMemoryImpl(phys_addr, src, cur_size)); + + /* Advance. */ + address += cur_size; + src += cur_size; + } + + return ResultSuccess(); + } + Result KPageTableBase::LockForDeviceAddressSpace(KPageGroup *out, KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned) { /* Lightly validate the range before doing anything else. */ const size_t num_pages = size / PageSize; From 60b5bd73b7546c38a49b81ac7b4e6f40c0ec2acc Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 09:48:25 -0700 Subject: [PATCH 133/280] kern: track mapped ipc server memory in page table --- .../arch/arm64/kern_k_page_table.hpp | 2 +- .../arch/arm64/kern_k_process_page_table.hpp | 8 ++--- .../mesosphere/kern_k_page_table_base.hpp | 8 +++-- .../source/arch/arm64/kern_k_page_table.cpp | 4 +-- .../source/kern_k_page_table_base.cpp | 35 +++++++++++-------- .../libmesosphere/source/kern_k_process.cpp | 4 +-- .../source/kern_k_server_session.cpp | 8 ++--- 7 files changed, 40 insertions(+), 29 deletions(-) diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_page_table.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_page_table.hpp index 6cd5008b3..a12533207 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_page_table.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_page_table.hpp @@ -176,7 +176,7 @@ namespace ams::kern::arch::arm64 { } NOINLINE Result InitializeForKernel(void *table, KVirtualAddress start, KVirtualAddress end); - NOINLINE Result InitializeForProcess(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager); + NOINLINE Result InitializeForProcess(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager, KResourceLimit *resource_limit); Result Finalize(); private: Result MapL1Blocks(KProcessAddress virt_addr, KPhysicalAddress phys_addr, size_t num_pages, PageTableEntry entry_template, bool disable_head_merge, PageLinkedList *page_list, bool reuse_ll); diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp index 9ca342116..cf603b14f 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp @@ -30,8 +30,8 @@ namespace ams::kern::arch::arm64 { m_page_table.Activate(id); } - Result Initialize(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager) { - return m_page_table.InitializeForProcess(id, as_type, enable_aslr, enable_das_merge, from_back, pool, code_address, code_size, mem_block_slab_manager, block_info_manager, pt_manager); + Result Initialize(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager, KResourceLimit *resource_limit) { + return m_page_table.InitializeForProcess(id, as_type, enable_aslr, enable_das_merge, from_back, pool, code_address, code_size, mem_block_slab_manager, block_info_manager, pt_manager, resource_limit); } void Finalize() { m_page_table.Finalize(); } @@ -216,8 +216,8 @@ namespace ams::kern::arch::arm64 { return m_page_table.SetupForIpc(out_dst_addr, size, src_addr, src_page_table.m_page_table, test_perm, dst_state, send); } - Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state, KProcess *server_process) { - return m_page_table.CleanupForIpcServer(address, size, dst_state, server_process); + Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state) { + return m_page_table.CleanupForIpcServer(address, size, dst_state); } Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state) { diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp index b63a9a4ee..538b98c44 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp @@ -47,6 +47,8 @@ namespace ams::kern { static_assert(std::is_trivial::value); static_assert(sizeof(KPageProperties) == sizeof(u32)); + class KResourceLimit; + class KPageTableBase { NON_COPYABLE(KPageTableBase); NON_MOVEABLE(KPageTableBase); @@ -150,6 +152,7 @@ namespace ams::kern { size_t m_max_heap_size{}; size_t m_mapped_physical_memory_size{}; size_t m_mapped_unsafe_physical_memory{}; + size_t m_mapped_ipc_server_memory{}; mutable KLightLock m_general_lock{}; mutable KLightLock m_map_physical_memory_lock{}; KPageTableImpl m_impl{}; @@ -161,6 +164,7 @@ namespace ams::kern { bool m_enable_device_address_space_merge{}; KMemoryBlockSlabManager *m_memory_block_slab_manager{}; KBlockInfoManager *m_block_info_manager{}; + KResourceLimit *m_resource_limit{}; const KMemoryRegion *m_cached_physical_linear_region{}; const KMemoryRegion *m_cached_physical_heap_region{}; const KMemoryRegion *m_cached_virtual_heap_region{}; @@ -171,7 +175,7 @@ namespace ams::kern { constexpr KPageTableBase() { /* ... */ } NOINLINE Result InitializeForKernel(bool is_64_bit, void *table, KVirtualAddress start, KVirtualAddress end); - NOINLINE Result InitializeForProcess(ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_device_address_space_merge, bool from_back, KMemoryManager::Pool pool, void *table, KProcessAddress start, KProcessAddress end, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager); + NOINLINE Result InitializeForProcess(ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_device_address_space_merge, bool from_back, KMemoryManager::Pool pool, void *table, KProcessAddress start, KProcessAddress end, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KResourceLimit *resource_limit); void Finalize(); @@ -372,7 +376,7 @@ namespace ams::kern { Result CopyMemoryFromHeapToHeapWithoutCheckDestination(KPageTableBase &dst_page_table, KProcessAddress dst_addr, size_t size, u32 dst_state_mask, u32 dst_state, KMemoryPermission dst_test_perm, u32 dst_attr_mask, u32 dst_attr, KProcessAddress src_addr, u32 src_state_mask, u32 src_state, KMemoryPermission src_test_perm, u32 src_attr_mask, u32 src_attr); Result SetupForIpc(KProcessAddress *out_dst_addr, size_t size, KProcessAddress src_addr, KPageTableBase &src_page_table, KMemoryPermission test_perm, KMemoryState dst_state, bool send); - Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state, KProcess *server_process); + Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state); Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state); Result MapPhysicalMemory(KProcessAddress address, size_t size); diff --git a/libraries/libmesosphere/source/arch/arm64/kern_k_page_table.cpp b/libraries/libmesosphere/source/arch/arm64/kern_k_page_table.cpp index 188475ec0..53539e925 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_k_page_table.cpp +++ b/libraries/libmesosphere/source/arch/arm64/kern_k_page_table.cpp @@ -181,7 +181,7 @@ namespace ams::kern::arch::arm64 { return ResultSuccess(); } - Result KPageTable::InitializeForProcess(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager) { + Result KPageTable::InitializeForProcess(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager, KResourceLimit *resource_limit) { /* The input ID isn't actually used. */ MESOSPHERE_UNUSED(id); @@ -202,7 +202,7 @@ namespace ams::kern::arch::arm64 { const size_t as_width = GetAddressSpaceWidth(as_type); const KProcessAddress as_start = 0; const KProcessAddress as_end = (1ul << as_width); - R_TRY(KPageTableBase::InitializeForProcess(as_type, enable_aslr, enable_das_merge, from_back, pool, GetVoidPointer(new_table), as_start, as_end, code_address, code_size, mem_block_slab_manager, block_info_manager)); + R_TRY(KPageTableBase::InitializeForProcess(as_type, enable_aslr, enable_das_merge, from_back, pool, GetVoidPointer(new_table), as_start, as_end, code_address, code_size, mem_block_slab_manager, block_info_manager, resource_limit)); /* We succeeded! */ table_guard.Cancel(); diff --git a/libraries/libmesosphere/source/kern_k_page_table_base.cpp b/libraries/libmesosphere/source/kern_k_page_table_base.cpp index e503a3a37..12e4d196e 100644 --- a/libraries/libmesosphere/source/kern_k_page_table_base.cpp +++ b/libraries/libmesosphere/source/kern_k_page_table_base.cpp @@ -43,9 +43,11 @@ namespace ams::kern { m_max_heap_size = 0; m_mapped_physical_memory_size = 0; m_mapped_unsafe_physical_memory = 0; + m_mapped_ipc_server_memory = 0; m_memory_block_slab_manager = std::addressof(Kernel::GetSystemMemoryBlockManager()); m_block_info_manager = std::addressof(Kernel::GetBlockInfoManager()); + m_resource_limit = std::addressof(Kernel::GetSystemResourceLimit()); m_allocate_option = KMemoryManager::EncodeOption(KMemoryManager::Pool_System, KMemoryManager::Direction_FromFront); m_heap_fill_value = MemoryFillValue_Zero; @@ -65,7 +67,7 @@ namespace ams::kern { return ResultSuccess(); } - Result KPageTableBase::InitializeForProcess(ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, void *table, KProcessAddress start, KProcessAddress end, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager) { + Result KPageTableBase::InitializeForProcess(ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, void *table, KProcessAddress start, KProcessAddress end, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KResourceLimit *resource_limit) { /* Validate the region. */ MESOSPHERE_ABORT_UNLESS(start <= code_address); MESOSPHERE_ABORT_UNLESS(code_address < code_address + code_size); @@ -131,6 +133,7 @@ namespace ams::kern { m_is_kernel = false; m_memory_block_slab_manager = mem_block_slab_manager; m_block_info_manager = block_info_manager; + m_resource_limit = resource_limit; /* Determine the region we can place our undetermineds in. */ KProcessAddress alloc_start; @@ -227,8 +230,9 @@ namespace ams::kern { /* Set heap and fill members. */ m_current_heap_end = m_heap_region_start; m_max_heap_size = 0; - m_mapped_physical_memory_size = 0; + m_mapped_physical_memory_size = 0; m_mapped_unsafe_physical_memory = 0; + m_mapped_ipc_server_memory = 0; const bool fill_memory = KTargetSystem::IsDebugMemoryFillEnabled(); m_heap_fill_value = fill_memory ? MemoryFillValue_Heap : MemoryFillValue_Zero; @@ -283,6 +287,11 @@ namespace ams::kern { Kernel::GetUnsafeMemory().Release(m_mapped_unsafe_physical_memory); } + /* Release any ipc server memory. */ + if (m_mapped_ipc_server_memory) { + m_resource_limit->Release(ams::svc::LimitableResource_PhysicalMemoryMax, m_mapped_ipc_server_memory); + } + /* Invalidate the entire instruction cache. */ cpu::InvalidateEntireInstructionCache(); } @@ -1507,7 +1516,7 @@ namespace ams::kern { R_TRY(this->Operate(updater.GetPageList(), m_heap_region_start + size, num_pages, Null, false, unmap_properties, OperationType_Unmap, false)); /* Release the memory from the resource limit. */ - GetCurrentProcess().ReleaseResource(ams::svc::LimitableResource_PhysicalMemoryMax, num_pages * PageSize); + m_resource_limit->Release(ams::svc::LimitableResource_PhysicalMemoryMax, num_pages * PageSize); /* Apply the memory block update. */ m_memory_block_manager.Update(std::addressof(allocator), m_heap_region_start + size, num_pages, KMemoryState_Free, KMemoryPermission_None, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, size == 0 ? KMemoryBlockDisableMergeAttribute_Normal : KMemoryBlockDisableMergeAttribute_None); @@ -1530,7 +1539,7 @@ namespace ams::kern { } /* Reserve memory for the heap extension. */ - KScopedResourceReservation memory_reservation(GetCurrentProcess().GetResourceLimit(), ams::svc::LimitableResource_PhysicalMemoryMax, allocation_size); + KScopedResourceReservation memory_reservation(m_resource_limit, ams::svc::LimitableResource_PhysicalMemoryMax, allocation_size); R_UNLESS(memory_reservation.Succeeded(), svc::ResultLimitReached()); /* Allocate pages for the heap extension. */ @@ -3464,7 +3473,7 @@ namespace ams::kern { /* Reserve space for any partial pages we allocate. */ const size_t unmapped_size = aligned_src_size - mapping_src_size; - KScopedResourceReservation memory_reservation(GetCurrentProcess().GetResourceLimit(), ams::svc::LimitableResource_PhysicalMemoryMax, unmapped_size); + KScopedResourceReservation memory_reservation(m_resource_limit, ams::svc::LimitableResource_PhysicalMemoryMax, unmapped_size); R_UNLESS(memory_reservation.Succeeded(), svc::ResultLimitReached()); /* Ensure that we manage page references correctly. */ @@ -3688,7 +3697,7 @@ namespace ams::kern { return ResultSuccess(); } - Result KPageTableBase::CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state, KProcess *server_process) { + Result KPageTableBase::CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state) { /* Validate the address. */ R_UNLESS(this->Contains(address, size), svc::ResultInvalidCurrentMemory()); @@ -3721,12 +3730,10 @@ namespace ams::kern { m_memory_block_manager.Update(std::addressof(allocator), aligned_start, aligned_num_pages, KMemoryState_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_Normal); /* Release from the resource limit as relevant. */ - if (auto *resource_limit = server_process->GetResourceLimit(); resource_limit != nullptr) { - const KProcessAddress mapping_start = util::AlignUp(GetInteger(address), PageSize); - const KProcessAddress mapping_end = util::AlignDown(GetInteger(address) + size, PageSize); - const size_t mapping_size = (mapping_start < mapping_end) ? mapping_end - mapping_start : 0; - resource_limit->Release(ams::svc::LimitableResource_PhysicalMemoryMax, aligned_size - mapping_size); - } + const KProcessAddress mapping_start = util::AlignUp(GetInteger(address), PageSize); + const KProcessAddress mapping_end = util::AlignDown(GetInteger(address) + size, PageSize); + const size_t mapping_size = (mapping_start < mapping_end) ? mapping_end - mapping_start : 0; + m_resource_limit->Release(ams::svc::LimitableResource_PhysicalMemoryMax, aligned_size - mapping_size); return ResultSuccess(); } @@ -4031,7 +4038,7 @@ namespace ams::kern { /* Allocate and map the memory. */ { /* Reserve the memory from the process resource limit. */ - KScopedResourceReservation memory_reservation(GetCurrentProcess().GetResourceLimit(), ams::svc::LimitableResource_PhysicalMemoryMax, size - mapped_size); + KScopedResourceReservation memory_reservation(m_resource_limit, ams::svc::LimitableResource_PhysicalMemoryMax, size - mapped_size); R_UNLESS(memory_reservation.Succeeded(), svc::ResultLimitReached()); /* Allocate pages for the new memory. */ @@ -4439,7 +4446,7 @@ namespace ams::kern { /* Release the memory resource. */ m_mapped_physical_memory_size -= mapped_size; - GetCurrentProcess().ReleaseResource(ams::svc::LimitableResource_PhysicalMemoryMax, mapped_size); + m_resource_limit->Release(ams::svc::LimitableResource_PhysicalMemoryMax, mapped_size); /* Update memory blocks. */ m_memory_block_manager.Update(std::addressof(allocator), address, size / PageSize, KMemoryState_Free, KMemoryPermission_None, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_None); diff --git a/libraries/libmesosphere/source/kern_k_process.cpp b/libraries/libmesosphere/source/kern_k_process.cpp index 474f08c7f..b72c84bda 100644 --- a/libraries/libmesosphere/source/kern_k_process.cpp +++ b/libraries/libmesosphere/source/kern_k_process.cpp @@ -254,7 +254,7 @@ namespace ams::kern { auto *mem_block_manager = std::addressof(is_app ? Kernel::GetApplicationMemoryBlockManager() : Kernel::GetSystemMemoryBlockManager()); auto *block_info_manager = std::addressof(Kernel::GetBlockInfoManager()); auto *pt_manager = std::addressof(Kernel::GetPageTableManager()); - R_TRY(m_page_table.Initialize(m_process_id, as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, params.code_address, params.code_num_pages * PageSize, mem_block_manager, block_info_manager, pt_manager)); + R_TRY(m_page_table.Initialize(m_process_id, as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, params.code_address, params.code_num_pages * PageSize, mem_block_manager, block_info_manager, pt_manager, res_limit)); } auto pt_guard = SCOPE_GUARD { m_page_table.Finalize(); }; @@ -359,7 +359,7 @@ namespace ams::kern { const auto as_type = static_cast(params.flags & ams::svc::CreateProcessFlag_AddressSpaceMask); const bool enable_aslr = (params.flags & ams::svc::CreateProcessFlag_EnableAslr) != 0; const bool enable_das_merge = (params.flags & ams::svc::CreateProcessFlag_DisableDeviceAddressSpaceMerge) == 0; - R_TRY(m_page_table.Initialize(m_process_id, as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, params.code_address, code_size, mem_block_manager, block_info_manager, pt_manager)); + R_TRY(m_page_table.Initialize(m_process_id, as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, params.code_address, code_size, mem_block_manager, block_info_manager, pt_manager, res_limit)); } auto pt_guard = SCOPE_GUARD { m_page_table.Finalize(); }; diff --git a/libraries/libmesosphere/source/kern_k_server_session.cpp b/libraries/libmesosphere/source/kern_k_server_session.cpp index e49f78ebb..1cd5a1a8d 100644 --- a/libraries/libmesosphere/source/kern_k_server_session.cpp +++ b/libraries/libmesosphere/source/kern_k_server_session.cpp @@ -398,17 +398,17 @@ namespace ams::kern { /* Cleanup Send mappings. */ for (size_t i = 0; i < request->GetSendCount(); ++i) { - R_TRY(server_page_table.CleanupForIpcServer(request->GetSendServerAddress(i), request->GetSendSize(i), request->GetSendMemoryState(i), server_process)); + R_TRY(server_page_table.CleanupForIpcServer(request->GetSendServerAddress(i), request->GetSendSize(i), request->GetSendMemoryState(i))); } /* Cleanup Receive mappings. */ for (size_t i = 0; i < request->GetReceiveCount(); ++i) { - R_TRY(server_page_table.CleanupForIpcServer(request->GetReceiveServerAddress(i), request->GetReceiveSize(i), request->GetReceiveMemoryState(i), server_process)); + R_TRY(server_page_table.CleanupForIpcServer(request->GetReceiveServerAddress(i), request->GetReceiveSize(i), request->GetReceiveMemoryState(i))); } /* Cleanup Exchange mappings. */ for (size_t i = 0; i < request->GetExchangeCount(); ++i) { - R_TRY(server_page_table.CleanupForIpcServer(request->GetExchangeServerAddress(i), request->GetExchangeSize(i), request->GetExchangeMemoryState(i), server_process)); + R_TRY(server_page_table.CleanupForIpcServer(request->GetExchangeServerAddress(i), request->GetExchangeSize(i), request->GetExchangeMemoryState(i))); } return ResultSuccess(); @@ -470,7 +470,7 @@ namespace ams::kern { /* Ensure that we clean up on failure. */ auto setup_guard = SCOPE_GUARD { - dst_page_table.CleanupForIpcServer(dst_address, size, dst_state, request->GetServerProcess()); + dst_page_table.CleanupForIpcServer(dst_address, size, dst_state); src_page_table.CleanupForIpcClient(src_address, size, dst_state); }; From 85f93551844d2d3968d79882cb3b75ddb375ac4c Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 09:57:32 -0700 Subject: [PATCH 134/280] kern: use KScopedLightLockPair helper for page table pair-locks --- .../mesosphere/kern_k_page_table_base.hpp | 15 +- .../source/kern_k_page_table_base.cpp | 132 +++++++++--------- 2 files changed, 83 insertions(+), 64 deletions(-) diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp index 538b98c44..1a7afe4ef 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp @@ -301,9 +301,22 @@ namespace ams::kern { void CleanupForIpcClientOnServerSetupFailure(PageLinkedList *page_list, KProcessAddress address, size_t size, KMemoryPermission prot_perm); size_t GetSize(KMemoryState state) const; + + ALWAYS_INLINE bool GetPhysicalAddressLocked(KPhysicalAddress *out, KProcessAddress virt_addr) const { + /* Validate pre-conditions. */ + MESOSPHERE_AUDIT(this->IsLockedByCurrentThread()); + + return this->GetImpl().GetPhysicalAddress(out, virt_addr); + } public: bool GetPhysicalAddress(KPhysicalAddress *out, KProcessAddress virt_addr) const { - return this->GetImpl().GetPhysicalAddress(out, virt_addr); + /* Validate pre-conditions. */ + MESOSPHERE_AUDIT(!this->IsLockedByCurrentThread()); + + /* Acquire exclusive access to the table while doing address translation. */ + KScopedLightLock lk(m_general_lock); + + return this->GetPhysicalAddressLocked(out, virt_addr); } KBlockInfoManager *GetBlockInfoManager() const { return m_block_info_manager; } diff --git a/libraries/libmesosphere/source/kern_k_page_table_base.cpp b/libraries/libmesosphere/source/kern_k_page_table_base.cpp index 12e4d196e..ed46bbd23 100644 --- a/libraries/libmesosphere/source/kern_k_page_table_base.cpp +++ b/libraries/libmesosphere/source/kern_k_page_table_base.cpp @@ -18,6 +18,62 @@ namespace ams::kern { + namespace { + + class KScopedLightLockPair { + NON_COPYABLE(KScopedLightLockPair); + NON_MOVEABLE(KScopedLightLockPair); + private: + KLightLock *m_lower; + KLightLock *m_upper; + public: + ALWAYS_INLINE KScopedLightLockPair(KLightLock &lhs, KLightLock &rhs) { + /* Ensure our locks are in a consistent order. */ + if (std::addressof(lhs) <= std::addressof(rhs)) { + m_lower = std::addressof(lhs); + m_upper = std::addressof(rhs); + } else { + m_lower = std::addressof(rhs); + m_upper = std::addressof(lhs); + } + + /* Acquire both locks. */ + m_lower->Lock(); + if (m_lower != m_upper) { + m_upper->Lock(); + } + } + + ~KScopedLightLockPair() { + /* Unlock the upper lock. */ + if (m_upper != nullptr && m_upper != m_lower) { + m_upper->Unlock(); + } + + /* Unlock the lower lock. */ + if (m_lower != nullptr) { + m_lower->Unlock(); + } + } + public: + /* Utility. */ + ALWAYS_INLINE void TryUnlockHalf(KLightLock &lock) { + /* Only allow unlocking if the lock is half the pair. */ + if (m_lower != m_upper) { + /* We want to be sure the lock is one we own. */ + if (m_lower == std::addressof(lock)) { + lock.Unlock(); + m_lower = nullptr; + } else if (m_upper == std::addressof(lock)) { + lock.Unlock(); + m_upper = nullptr; + } + } + } + }; + + } + Result KPageTableBase::InitializeForKernel(bool is_64_bit, void *table, KVirtualAddress start, KVirtualAddress end) { /* Initialize our members. */ m_address_space_width = (is_64_bit) ? BITSIZEOF(u64) : BITSIZEOF(u32); @@ -529,7 +585,7 @@ namespace ams::kern { /* Get the physical address, if we're supposed to. */ if (out_paddr != nullptr) { - MESOSPHERE_ABORT_UNLESS(this->GetPhysicalAddress(out_paddr, addr)); + MESOSPHERE_ABORT_UNLESS(this->GetPhysicalAddressLocked(out_paddr, addr)); } /* Make the page group, if we're supposed to. */ @@ -2458,18 +2514,8 @@ namespace ams::kern { KPageTableBase &src_page_table = *this; KPageTableBase &dst_page_table = GetCurrentProcess().GetPageTable().GetBasePageTable(); - /* Get the table locks. */ - KLightLock &lock_0 = (reinterpret_cast(std::addressof(src_page_table)) <= reinterpret_cast(std::addressof(dst_page_table))) ? src_page_table.m_general_lock : dst_page_table.m_general_lock; - KLightLock &lock_1 = (reinterpret_cast(std::addressof(src_page_table)) <= reinterpret_cast(std::addressof(dst_page_table))) ? dst_page_table.m_general_lock : src_page_table.m_general_lock; - - /* Lock the first lock. */ - KScopedLightLock lk0(lock_0); - - /* If necessary, lock the second lock. */ - std::optional lk1; - if (std::addressof(lock_0) != std::addressof(lock_1)) { - lk1.emplace(lock_1); - } + /* Acquire the table locks. */ + KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock); /* Check that the desired range is readable io memory. */ R_TRY(this->CheckMemoryStateContiguous(address, size, KMemoryState_All, KMemoryState_Io, KMemoryPermission_UserRead, KMemoryPermission_UserRead, KMemoryAttribute_None, KMemoryAttribute_None)); @@ -2480,7 +2526,7 @@ namespace ams::kern { while (address <= last_address) { /* Get the current physical address. */ KPhysicalAddress phys_addr; - MESOSPHERE_ABORT_UNLESS(src_page_table.GetPhysicalAddress(std::addressof(phys_addr), address)); + MESOSPHERE_ABORT_UNLESS(src_page_table.GetPhysicalAddressLocked(std::addressof(phys_addr), address)); /* Determine the current read size. */ const size_t cur_size = std::min(last_address - address + 1, util::AlignDown(GetInteger(address) + PageSize, PageSize) - GetInteger(address)); @@ -2504,18 +2550,8 @@ namespace ams::kern { KPageTableBase &src_page_table = *this; KPageTableBase &dst_page_table = GetCurrentProcess().GetPageTable().GetBasePageTable(); - /* Get the table locks. */ - KLightLock &lock_0 = (reinterpret_cast(std::addressof(src_page_table)) <= reinterpret_cast(std::addressof(dst_page_table))) ? src_page_table.m_general_lock : dst_page_table.m_general_lock; - KLightLock &lock_1 = (reinterpret_cast(std::addressof(src_page_table)) <= reinterpret_cast(std::addressof(dst_page_table))) ? dst_page_table.m_general_lock : src_page_table.m_general_lock; - - /* Lock the first lock. */ - KScopedLightLock lk0(lock_0); - - /* If necessary, lock the second lock. */ - std::optional lk1; - if (std::addressof(lock_0) != std::addressof(lock_1)) { - lk1.emplace(lock_1); - } + /* Acquire the table locks. */ + KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock); /* Check that the desired range is writable io memory. */ R_TRY(this->CheckMemoryStateContiguous(address, size, KMemoryState_All, KMemoryState_Io, KMemoryPermission_UserReadWrite, KMemoryPermission_UserReadWrite, KMemoryAttribute_None, KMemoryAttribute_None)); @@ -2526,7 +2562,7 @@ namespace ams::kern { while (address <= last_address) { /* Get the current physical address. */ KPhysicalAddress phys_addr; - MESOSPHERE_ABORT_UNLESS(src_page_table.GetPhysicalAddress(std::addressof(phys_addr), address)); + MESOSPHERE_ABORT_UNLESS(src_page_table.GetPhysicalAddressLocked(std::addressof(phys_addr), address)); /* Determine the current read size. */ const size_t cur_size = std::min(last_address - address + 1, util::AlignDown(GetInteger(address) + PageSize, PageSize) - GetInteger(address)); @@ -3076,18 +3112,8 @@ namespace ams::kern { /* Copy the memory. */ { - /* Get the table locks. */ - KLightLock &lock_0 = (reinterpret_cast(std::addressof(src_page_table)) <= reinterpret_cast(std::addressof(dst_page_table))) ? src_page_table.m_general_lock : dst_page_table.m_general_lock; - KLightLock &lock_1 = (reinterpret_cast(std::addressof(src_page_table)) <= reinterpret_cast(std::addressof(dst_page_table))) ? dst_page_table.m_general_lock : src_page_table.m_general_lock; - - /* Lock the first lock. */ - KScopedLightLock lk0(lock_0); - - /* If necessary, lock the second lock. */ - std::optional lk1; - if (std::addressof(lock_0) != std::addressof(lock_1)) { - lk1.emplace(lock_1); - } + /* Acquire the table locks. */ + KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock); /* Check memory state. */ R_TRY(src_page_table.CheckMemoryStateContiguous(src_addr, size, src_state_mask, src_state, src_test_perm, src_test_perm, src_attr_mask | KMemoryAttribute_Uncached, src_attr)); @@ -3203,18 +3229,8 @@ namespace ams::kern { /* Copy the memory. */ { - /* Get the table locks. */ - KLightLock &lock_0 = (reinterpret_cast(std::addressof(src_page_table)) <= reinterpret_cast(std::addressof(dst_page_table))) ? src_page_table.m_general_lock : dst_page_table.m_general_lock; - KLightLock &lock_1 = (reinterpret_cast(std::addressof(src_page_table)) <= reinterpret_cast(std::addressof(dst_page_table))) ? dst_page_table.m_general_lock : src_page_table.m_general_lock; - - /* Lock the first lock. */ - KScopedLightLock lk0(lock_0); - - /* If necessary, lock the second lock. */ - std::optional lk1; - if (std::addressof(lock_0) != std::addressof(lock_1)) { - lk1.emplace(lock_1); - } + /* Acquire the table locks. */ + KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock); /* Check memory state for source. */ R_TRY(src_page_table.CheckMemoryStateContiguous(src_addr, size, src_state_mask, src_state, src_test_perm, src_test_perm, src_attr_mask | KMemoryAttribute_Uncached, src_attr)); @@ -3644,18 +3660,8 @@ namespace ams::kern { /* For convenience, alias this. */ KPageTableBase &dst_page_table = *this; - /* Get the table locks. */ - KLightLock &lock_0 = (reinterpret_cast(std::addressof(src_page_table)) <= reinterpret_cast(std::addressof(dst_page_table))) ? src_page_table.m_general_lock : dst_page_table.m_general_lock; - KLightLock &lock_1 = (reinterpret_cast(std::addressof(src_page_table)) <= reinterpret_cast(std::addressof(dst_page_table))) ? dst_page_table.m_general_lock : src_page_table.m_general_lock; - - /* Lock the first lock. */ - KScopedLightLock lk0(lock_0); - - /* If necessary, lock the second lock. */ - std::optional lk1; - if (std::addressof(lock_0) != std::addressof(lock_1)) { - lk1.emplace(lock_1); - } + /* Acquire the table locks. */ + KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock); /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(std::addressof(src_page_table)); From 6e4664ee05efa2ad97d8924b880aae510bb87b86 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 09:59:22 -0700 Subject: [PATCH 135/280] kern: if a page table region is zero-size, nothing overlaps it --- libraries/libmesosphere/source/kern_k_page_table_base.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/libmesosphere/source/kern_k_page_table_base.cpp b/libraries/libmesosphere/source/kern_k_page_table_base.cpp index ed46bbd23..166cce173 100644 --- a/libraries/libmesosphere/source/kern_k_page_table_base.cpp +++ b/libraries/libmesosphere/source/kern_k_page_table_base.cpp @@ -426,8 +426,8 @@ namespace ams::kern { const size_t region_size = this->GetRegionSize(state); const bool is_in_region = region_start <= addr && addr < end && last <= region_start + region_size - 1; - const bool is_in_heap = !(end <= m_heap_region_start || m_heap_region_end <= addr); - const bool is_in_alias = !(end <= m_alias_region_start || m_alias_region_end <= addr); + const bool is_in_heap = !(end <= m_heap_region_start || m_heap_region_end <= addr || m_heap_region_start == m_heap_region_end); + const bool is_in_alias = !(end <= m_alias_region_start || m_alias_region_end <= addr || m_alias_region_start == m_alias_region_end); switch (state) { case KMemoryState_Free: case KMemoryState_Kernel: @@ -1543,9 +1543,9 @@ namespace ams::kern { KScopedLightLock lk(m_general_lock); /* Validate that setting heap size is possible at all. */ - R_UNLESS(!m_is_kernel, svc::ResultOutOfMemory()); + R_UNLESS(!m_is_kernel, svc::ResultOutOfMemory()); R_UNLESS(size <= static_cast(m_heap_region_end - m_heap_region_start), svc::ResultOutOfMemory()); - R_UNLESS(size <= m_max_heap_size, svc::ResultOutOfMemory()); + R_UNLESS(size <= m_max_heap_size, svc::ResultOutOfMemory()); if (size < static_cast(m_current_heap_end - m_heap_region_start)) { /* The size being requested is less than the current size, so we need to free the end of the heap. */ From 44ccbc2a7befb181c701b6ca83fff9aa4b59db02 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 10:05:09 -0700 Subject: [PATCH 136/280] kern: update set/way cache operations for new semantics --- .../source/arch/arm64/kern_cpu.cpp | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/libraries/libmesosphere/source/arch/arm64/kern_cpu.cpp b/libraries/libmesosphere/source/arch/arm64/kern_cpu.cpp index e46c6285d..3da383f02 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_cpu.cpp +++ b/libraries/libmesosphere/source/arch/arm64/kern_cpu.cpp @@ -283,6 +283,16 @@ namespace ams::kern::arch::arm64::cpu { } } + void StoreDataCacheBySetWay(int level) { + PerformCacheOperationBySetWayImpl(level, StoreDataCacheLineBySetWayImpl); + cpu::DataSynchronizationBarrier(); + } + + void FlushDataCacheBySetWay(int level) { + PerformCacheOperationBySetWayImpl(level, FlushDataCacheLineBySetWayImpl); + cpu::DataSynchronizationBarrier(); + } + void KCacheHelperInterruptHandler::ProcessOperation() { switch (m_operation) { case Operation::Idle: @@ -291,12 +301,10 @@ namespace ams::kern::arch::arm64::cpu { InstructionMemoryBarrier(); break; case Operation::StoreDataCache: - PerformCacheOperationBySetWayLocal(StoreDataCacheLineBySetWayImpl); - DataSynchronizationBarrier(); + StoreDataCacheBySetWay(0); break; case Operation::FlushDataCache: - PerformCacheOperationBySetWayLocal(FlushDataCacheLineBySetWayImpl); - DataSynchronizationBarrier(); + FlushDataCacheBySetWay(0); break; } @@ -374,7 +382,20 @@ namespace ams::kern::arch::arm64::cpu { } void FlushEntireDataCache() { - return PerformCacheOperationBySetWayShared(FlushDataCacheLineBySetWayImpl); + KScopedCoreMigrationDisable dm; + + CacheLineIdRegisterAccessor clidr_el1; + const int levels_of_coherency = clidr_el1.GetLevelsOfCoherency(); + + /* Store cache from L2 up to the level of coherence (if there's an L3 cache or greater). */ + for (int level = 2; level < levels_of_coherency; ++level) { + StoreDataCacheBySetWay(level - 1); + } + + /* Flush cache from the level of coherence down to L2. */ + for (int level = levels_of_coherency; level > 1; --level) { + FlushDataCacheBySetWay(level - 1); + } } Result InvalidateDataCache(void *addr, size_t size) { From 01f5c899023c9e1e0259cb185c48cb9bc993185d Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 10:07:24 -0700 Subject: [PATCH 137/280] kern: add bounds checking to KHandleTable::Register/Unreserve --- .../source/kern_k_handle_table.cpp | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/libraries/libmesosphere/source/kern_k_handle_table.cpp b/libraries/libmesosphere/source/kern_k_handle_table.cpp index 7b3733fc4..d40de9f69 100644 --- a/libraries/libmesosphere/source/kern_k_handle_table.cpp +++ b/libraries/libmesosphere/source/kern_k_handle_table.cpp @@ -120,15 +120,16 @@ namespace ams::kern { const auto reserved = handle_pack.Get(); MESOSPHERE_ASSERT(reserved == 0); MESOSPHERE_ASSERT(linear_id != 0); - MESOSPHERE_ASSERT(index < m_table_size); MESOSPHERE_UNUSED(linear_id, reserved); - /* Free the entry. */ - /* NOTE: This code does not check the linear id. */ - Entry *entry = std::addressof(m_table[index]); - MESOSPHERE_ASSERT(entry->GetObject() == nullptr); + if (index < m_table_size) { + /* Free the entry. */ + /* NOTE: This code does not check the linear id. */ + Entry *entry = std::addressof(m_table[index]); + MESOSPHERE_ASSERT(entry->GetObject() == nullptr); - this->FreeEntry(entry); + this->FreeEntry(entry); + } } void KHandleTable::Register(ams::svc::Handle handle, KAutoObject *obj, u16 type) { @@ -143,15 +144,16 @@ namespace ams::kern { const auto reserved = handle_pack.Get(); MESOSPHERE_ASSERT(reserved == 0); MESOSPHERE_ASSERT(linear_id != 0); - MESOSPHERE_ASSERT(index < m_table_size); MESOSPHERE_UNUSED(reserved); - /* Set the entry. */ - Entry *entry = std::addressof(m_table[index]); - MESOSPHERE_ASSERT(entry->GetObject() == nullptr); + if (index < m_table_size) { + /* Set the entry. */ + Entry *entry = std::addressof(m_table[index]); + MESOSPHERE_ASSERT(entry->GetObject() == nullptr); - entry->SetUsed(obj, linear_id, type); - obj->Open(); + entry->SetUsed(obj, linear_id, type); + obj->Open(); + } } } From 19b253fd1777694e2d2f7b0fb9fbc43a2ccf874b Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 10:09:35 -0700 Subject: [PATCH 138/280] kern: trivially optimize userspace io memory write --- .../source/arch/arm64/kern_userspace_memory_access_asm.s | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/libraries/libmesosphere/source/arch/arm64/kern_userspace_memory_access_asm.s b/libraries/libmesosphere/source/arch/arm64/kern_userspace_memory_access_asm.s index ebdec0c46..c9c04f7be 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_userspace_memory_access_asm.s +++ b/libraries/libmesosphere/source/arch/arm64/kern_userspace_memory_access_asm.s @@ -758,7 +758,6 @@ _ZN3ams4kern4arch5arm6415UserspaceAccess18WriteIoMemory32BitEPvPKvm: mov x8, x30 1: /* Read the word from normal memory. */ - mov x30, x8 ldtr w9, [x5] /* Set our return address so that on read failure we continue. */ @@ -769,7 +768,7 @@ _ZN3ams4kern4arch5arm6415UserspaceAccess18WriteIoMemory32BitEPvPKvm: dsb sy 2: /* Continue. */ - nop + mov x30, x8 /* Advance. */ add x4, x4, #4 @@ -801,7 +800,6 @@ _ZN3ams4kern4arch5arm6415UserspaceAccess18WriteIoMemory16BitEPvPKvm: mov x8, x30 1: /* Read the word from normal memory. */ - mov x30, x8 ldtrh w9, [x5] /* Set our return address so that on read failure we continue. */ @@ -812,7 +810,7 @@ _ZN3ams4kern4arch5arm6415UserspaceAccess18WriteIoMemory16BitEPvPKvm: dsb sy 2: /* Continue. */ - nop + mov x30, x8 /* Advance. */ add x4, x4, #2 @@ -844,7 +842,6 @@ _ZN3ams4kern4arch5arm6415UserspaceAccess17WriteIoMemory8BitEPvPKvm: mov x8, x30 1: /* Read the word from normal memory. */ - mov x30, x8 ldtrb w9, [x5] /* Set our return address so that on read failure we continue. */ @@ -855,7 +852,7 @@ _ZN3ams4kern4arch5arm6415UserspaceAccess17WriteIoMemory8BitEPvPKvm: dsb sy 2: /* Continue. */ - nop + mov x30, x8 /* Advance. */ add x4, x4, #1 From 504472af4e0343e557f9127f7863f0d64df2e4fe Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 10:19:35 -0700 Subject: [PATCH 139/280] kern: update KConditionVariable::WaitForAddress/Wait --- .../source/kern_k_condition_variable.cpp | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/libraries/libmesosphere/source/kern_k_condition_variable.cpp b/libraries/libmesosphere/source/kern_k_condition_variable.cpp index ba25f35c4..7e9a4499f 100644 --- a/libraries/libmesosphere/source/kern_k_condition_variable.cpp +++ b/libraries/libmesosphere/source/kern_k_condition_variable.cpp @@ -102,14 +102,13 @@ namespace ams::kern { } } MESOSPHERE_ASSERT(owner_thread.IsNotNull()); - } - /* Remove the thread as a waiter from the lock owner. */ - { - KScopedSchedulerLock sl; - KThread *owner_thread = cur_thread->GetLockOwner(); - if (owner_thread != nullptr) { - owner_thread->RemoveWaiter(cur_thread); + /* Remove the thread as a waiter from the lock owner. */ + { + KScopedSchedulerLock sl; + if (KThread *mutex_owner = cur_thread->GetLockOwner(); mutex_owner != nullptr) { + mutex_owner->RemoveWaiter(cur_thread); + } } } @@ -250,11 +249,6 @@ namespace ams::kern { } } - /* Cancel the timer wait. */ - if (timer != nullptr) { - timer->CancelTask(cur_thread); - } - /* Remove from the condition variable. */ { KScopedSchedulerLock sl; @@ -269,6 +263,11 @@ namespace ams::kern { } } + /* Cancel the timer wait. */ + if (timer != nullptr) { + timer->CancelTask(cur_thread); + } + /* Get the result. */ KSynchronizationObject *dummy; return cur_thread->GetWaitResult(std::addressof(dummy)); From a1e137cc1ca4a995c20e9446b04f58cf176de71b Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 11:25:49 -0700 Subject: [PATCH 140/280] kern: update Initialize0 for new changes --- .../mesosphere/kern_k_memory_region_type.hpp | 9 +- .../source/arch/arm64/kern_k_page_table.cpp | 8 +- .../source/kern_k_page_table_base.cpp | 4 +- .../include/vapours/util/util_alignment.hpp | 5 + .../source/arch/arm64/init/kern_init_core.cpp | 181 +++++++++++++----- 5 files changed, 147 insertions(+), 60 deletions(-) diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_memory_region_type.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_memory_region_type.hpp index 5c77ed363..e80e246ad 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_memory_region_type.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_memory_region_type.hpp @@ -21,7 +21,8 @@ namespace ams::kern { enum KMemoryRegionType : u32 {}; enum KMemoryRegionAttr : typename std::underlying_type::type { - KMemoryRegionAttr_CarveoutProtected = 0x04000000, + KMemoryRegionAttr_CarveoutProtected = 0x02000000, + KMemoryRegionAttr_Uncached = 0x04000000, KMemoryRegionAttr_DidKernelMap = 0x08000000, KMemoryRegionAttr_ShouldKernelMap = 0x10000000, KMemoryRegionAttr_UserReadOnly = 0x20000000, @@ -216,6 +217,10 @@ namespace ams::kern { static_assert(KMemoryRegionType_VirtualDramKernelPtHeap .GetValue() == 0x2A); static_assert(KMemoryRegionType_VirtualDramKernelTraceBuffer.GetValue() == 0x4A); + /* UNUSED: .DeriveSparse(2, 2, 0); */ + constexpr inline const auto KMemoryRegionType_VirtualDramUnknownDebug = KMemoryRegionType_Dram.DeriveSparse(2, 2, 1); + static_assert(KMemoryRegionType_VirtualDramUnknownDebug.GetValue() == (0x52)); + constexpr inline const auto KMemoryRegionType_VirtualDramKernelInitPt = KMemoryRegionType_VirtualDramHeapBase.Derive(3, 0); constexpr inline const auto KMemoryRegionType_VirtualDramPoolManagement = KMemoryRegionType_VirtualDramHeapBase.Derive(3, 1); constexpr inline const auto KMemoryRegionType_VirtualDramUserPool = KMemoryRegionType_VirtualDramHeapBase.Derive(3, 2); @@ -292,6 +297,8 @@ namespace ams::kern { return KMemoryRegionType_VirtualDramKernelTraceBuffer; } else if (KMemoryRegionType_DramKernelPtHeap.IsAncestorOf(type_id)) { return KMemoryRegionType_VirtualDramKernelPtHeap; + } else if ((type_id | KMemoryRegionAttr_ShouldKernelMap) == type_id) { + return KMemoryRegionType_VirtualDramUnknownDebug; } else { return KMemoryRegionType_Dram; } diff --git a/libraries/libmesosphere/source/arch/arm64/kern_k_page_table.cpp b/libraries/libmesosphere/source/arch/arm64/kern_k_page_table.cpp index 53539e925..6df32849b 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_k_page_table.cpp +++ b/libraries/libmesosphere/source/arch/arm64/kern_k_page_table.cpp @@ -556,13 +556,13 @@ namespace ams::kern::arch::arm64 { /* If we're not forcing an unmap, separate pages immediately. */ if (!force) { const size_t size = num_pages * PageSize; - R_TRY(this->SeparatePages(virt_addr, std::min(GetInteger(virt_addr) & -GetInteger(virt_addr), size), page_list, reuse_ll)); + R_TRY(this->SeparatePages(virt_addr, std::min(util::GetAlignment(GetInteger(virt_addr)), size), page_list, reuse_ll)); if (num_pages > 1) { const auto end_page = virt_addr + size; const auto last_page = end_page - PageSize; auto merge_guard = SCOPE_GUARD { this->MergePages(virt_addr, page_list); }; - R_TRY(this->SeparatePages(last_page, std::min(GetInteger(end_page) & -GetInteger(end_page), size), page_list, reuse_ll)); + R_TRY(this->SeparatePages(last_page, std::min(util::GetAlignment(GetInteger(end_page)), size), page_list, reuse_ll)); merge_guard.Cancel(); } } @@ -1194,13 +1194,13 @@ namespace ams::kern::arch::arm64 { /* Separate pages before we change permissions. */ const size_t size = num_pages * PageSize; - R_TRY(this->SeparatePages(virt_addr, std::min(GetInteger(virt_addr) & -GetInteger(virt_addr), size), page_list, reuse_ll)); + R_TRY(this->SeparatePages(virt_addr, std::min(util::GetAlignment(GetInteger(virt_addr)), size), page_list, reuse_ll)); if (num_pages > 1) { const auto end_page = virt_addr + size; const auto last_page = end_page - PageSize; auto merge_guard = SCOPE_GUARD { this->MergePages(virt_addr, page_list); }; - R_TRY(this->SeparatePages(last_page, std::min(GetInteger(end_page) & -GetInteger(end_page), size), page_list, reuse_ll)); + R_TRY(this->SeparatePages(last_page, std::min(util::GetAlignment(GetInteger(end_page)), size), page_list, reuse_ll)); merge_guard.Cancel(); } diff --git a/libraries/libmesosphere/source/kern_k_page_table_base.cpp b/libraries/libmesosphere/source/kern_k_page_table_base.cpp index 166cce173..3f2aff676 100644 --- a/libraries/libmesosphere/source/kern_k_page_table_base.cpp +++ b/libraries/libmesosphere/source/kern_k_page_table_base.cpp @@ -1810,7 +1810,7 @@ namespace ams::kern { /* Select an address to map at. */ KProcessAddress addr = Null; - const size_t phys_alignment = std::min(std::min(GetInteger(phys_addr) & -GetInteger(phys_addr), size & -size), MaxPhysicalMapAlignment); + const size_t phys_alignment = std::min(std::min(util::GetAlignment(GetInteger(phys_addr)), util::GetAlignment(size)), MaxPhysicalMapAlignment); for (s32 block_type = KPageTable::GetMaxBlockType(); block_type >= 0; block_type--) { const size_t alignment = KPageTable::GetBlockSize(static_cast(block_type)); if (alignment > phys_alignment) { @@ -1892,7 +1892,7 @@ namespace ams::kern { /* Select an address to map at. */ KProcessAddress addr = Null; - const size_t phys_alignment = std::min(std::min(GetInteger(phys_addr) & -GetInteger(phys_addr), size & -size), MaxPhysicalMapAlignment); + const size_t phys_alignment = std::min(std::min(util::GetAlignment(GetInteger(phys_addr)), util::GetAlignment(size)), MaxPhysicalMapAlignment); for (s32 block_type = KPageTable::GetMaxBlockType(); block_type >= 0; block_type--) { const size_t alignment = KPageTable::GetBlockSize(static_cast(block_type)); if (alignment > phys_alignment) { diff --git a/libraries/libvapours/include/vapours/util/util_alignment.hpp b/libraries/libvapours/include/vapours/util/util_alignment.hpp index 3fa951504..79e0ffd05 100644 --- a/libraries/libvapours/include/vapours/util/util_alignment.hpp +++ b/libraries/libvapours/include/vapours/util/util_alignment.hpp @@ -43,6 +43,11 @@ namespace ams::util { return (value & invmask) == 0; } + template requires std::unsigned_integral + constexpr ALWAYS_INLINE T GetAlignment(T value) { + return value & -value; + } + template<> constexpr ALWAYS_INLINE void *AlignUp(void *value, size_t alignment) { return reinterpret_cast(AlignUp(reinterpret_cast(value), alignment)); diff --git a/mesosphere/kernel/source/arch/arm64/init/kern_init_core.cpp b/mesosphere/kernel/source/arch/arm64/init/kern_init_core.cpp index 6ca985e31..a6c213344 100644 --- a/mesosphere/kernel/source/arch/arm64/init/kern_init_core.cpp +++ b/mesosphere/kernel/source/arch/arm64/init/kern_init_core.cpp @@ -46,6 +46,8 @@ namespace ams::kern::init { constexpr PageTableEntry KernelRwDataAttribute(PageTableEntry::Permission_KernelRW, PageTableEntry::PageAttribute_NormalMemory, PageTableEntry::Shareable_InnerShareable, PageTableEntry::MappingFlag_Mapped); constexpr PageTableEntry KernelMmioAttribute(PageTableEntry::Permission_KernelRW, PageTableEntry::PageAttribute_Device_nGnRE, PageTableEntry::Shareable_OuterShareable, PageTableEntry::MappingFlag_Mapped); + constexpr PageTableEntry KernelRwDataUncachedAttribute(PageTableEntry::Permission_KernelRW, PageTableEntry::PageAttribute_NormalMemoryNotCacheable, PageTableEntry::Shareable_InnerShareable, PageTableEntry::MappingFlag_Mapped); + void MapStackForCore(KInitialPageTable &page_table, KMemoryRegionType type, u32 core_id) { constexpr size_t StackSize = PageSize; constexpr size_t StackAlign = PageSize; @@ -77,8 +79,8 @@ namespace ams::kern::init { } } - void SetupInitialArguments(KInitialPageTable &ttbr1_table, KInitialPageAllocator &allocator) { - AMS_UNUSED(ttbr1_table, allocator); + void SetupInitialArguments(KInitialPageTable &init_pt, KInitialPageAllocator &allocator) { + AMS_UNUSED(init_pt, allocator); /* Get parameters for initial arguments. */ const u64 ttbr0 = cpu::GetTtbr0El1(); @@ -98,7 +100,7 @@ namespace ams::kern::init { /* if (cpu::GetPhysicalAddressWritable(std::addressof(phys_addr), KVirtualAddress(init_args), true)) { */ /* g_init_arguments_phys_addr[i] = phys_addr; */ /* } */ - g_init_arguments_phys_addr[i] = ttbr1_table.GetPhysicalAddress(KVirtualAddress(init_args)); + g_init_arguments_phys_addr[i] = init_pt.GetPhysicalAddress(KVirtualAddress(init_args)); /* Set the arguments. */ init_args->ttbr0 = ttbr0; @@ -122,7 +124,7 @@ namespace ams::kern::init { } void InitializeCore(uintptr_t misc_unk_debug_phys_addr, uintptr_t initial_page_allocator_state) { - /* Ensure our first argument is page aligned (as we will map it if it is non-zero). */ + /* Ensure our first argument is page aligned. */ MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(misc_unk_debug_phys_addr, PageSize)); /* Restore the page allocator state setup by kernel loader. */ @@ -132,7 +134,7 @@ namespace ams::kern::init { MESOSPHERE_INIT_ABORT_UNLESS((cpu::TranslationControlRegisterAccessor().GetT1Size() / arch::arm64::L1BlockSize) == arch::arm64::MaxPageTableEntries); /* Create page table object for use during initialization. */ - KInitialPageTable ttbr1_table; + KInitialPageTable init_pt; /* Initialize the slab allocator counts. */ InitializeSlabResourceCounts(); @@ -180,7 +182,14 @@ namespace ams::kern::init { MESOSPHERE_INIT_ABORT_UNLESS(region.GetEndAddress() != 0); /* Account for the region. */ - misc_region_needed_size += PageSize + (util::AlignUp(region.GetLastAddress(), PageSize) - util::AlignDown(region.GetAddress(), PageSize)); + const auto aligned_start = util::AlignDown(region.GetAddress(), PageSize); + const auto aligned_end = util::AlignUp(region.GetLastAddress(), PageSize); + const size_t cur_region_size = aligned_end - aligned_start; + misc_region_needed_size += cur_region_size; + + /* Account for alignment requirements. */ + const size_t min_align = std::min(util::GetAlignment(cur_region_size), util::GetAlignment(aligned_start)); + misc_region_needed_size += min_align >= KernelAslrAlignment ? KernelAslrAlignment : PageSize; } } @@ -215,7 +224,7 @@ namespace ams::kern::init { MESOSPHERE_INIT_ABORT_UNLESS(slab_region_size <= resource_region_size); /* Setup the slab region. */ - const KPhysicalAddress code_start_phys_addr = ttbr1_table.GetPhysicalAddressOfRandomizedRange(code_start_virt_addr, code_region_size); + const KPhysicalAddress code_start_phys_addr = init_pt.GetPhysicalAddressOfRandomizedRange(code_start_virt_addr, code_region_size); const KPhysicalAddress code_end_phys_addr = code_start_phys_addr + code_region_size; const KPhysicalAddress slab_start_phys_addr = code_end_phys_addr; const KPhysicalAddress slab_end_phys_addr = slab_start_phys_addr + slab_region_size; @@ -230,52 +239,116 @@ namespace ams::kern::init { const KVirtualAddress temp_region_start = KMemoryLayout::GetVirtualMemoryRegionTree().GetRandomAlignedRegion(TempRegionSize, TempRegionAlign, KMemoryRegionType_Kernel); MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetVirtualMemoryRegionTree().Insert(GetInteger(temp_region_start), TempRegionSize, KMemoryRegionType_KernelTemp)); - /* Setup the Misc Unknown Debug region, if it's not zero. */ - if (misc_unk_debug_phys_addr) { - constexpr size_t MiscUnknownDebugRegionAlign = PageSize; - const size_t misc_unk_debug_size = GetMiscUnknownDebugRegionSize(); - const KVirtualAddress misc_unk_debug_virt_addr = KMemoryLayout::GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard(misc_unk_debug_size, MiscUnknownDebugRegionAlign, KMemoryRegionType_KernelMisc, PageSize); - MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetVirtualMemoryRegionTree().Insert(GetInteger(misc_unk_debug_virt_addr), misc_unk_debug_size, KMemoryRegionType_KernelMiscUnknownDebug)); - ttbr1_table.Map(misc_unk_debug_virt_addr, misc_unk_debug_size, misc_unk_debug_phys_addr, KernelRoDataAttribute, g_initial_page_allocator); - } + /* Automatically map in devices that have auto-map attributes, from largest region to smallest region. */ + { + /* We want to map the regions from largest to smallest. */ + KMemoryRegion *largest; + do { + /* Begin with no knowledge of the largest region. */ + largest = nullptr; - /* Automatically map in devices that have auto-map attributes. */ - for (auto ®ion : KMemoryLayout::GetPhysicalMemoryRegionTree()) { - /* We only care about kernel regions. */ - if (!region.IsDerivedFrom(KMemoryRegionType_Kernel)) { - continue; - } + for (auto ®ion : KMemoryLayout::GetPhysicalMemoryRegionTree()) { + /* We only care about kernel regions. */ + if (!region.IsDerivedFrom(KMemoryRegionType_Kernel)) { + continue; + } - /* Check whether we should map the region. */ - if (!region.HasTypeAttribute(KMemoryRegionAttr_ShouldKernelMap)) { - continue; - } + /* Check whether we should map the region. */ + if (!region.HasTypeAttribute(KMemoryRegionAttr_ShouldKernelMap)) { + continue; + } - /* If this region has already been mapped, no need to consider it. */ - if (region.HasTypeAttribute(KMemoryRegionAttr_DidKernelMap)) { - continue; - } + /* If this region has already been mapped, no need to consider it. */ + if (region.HasTypeAttribute(KMemoryRegionAttr_DidKernelMap)) { + continue; + } - /* Check that the region is valid. */ - MESOSPHERE_INIT_ABORT_UNLESS(region.GetEndAddress() != 0); + /* Check that the region is valid. */ + MESOSPHERE_INIT_ABORT_UNLESS(region.GetEndAddress() != 0); - /* Set the attribute to note we've mapped this region. */ - region.SetTypeAttribute(KMemoryRegionAttr_DidKernelMap); + /* Update the largest region. */ + if (largest == nullptr || largest->GetSize() < region.GetSize()) { + largest = std::addressof(region); + } + } - /* Create a virtual pair region and insert it into the tree. */ - const KPhysicalAddress map_phys_addr = util::AlignDown(region.GetAddress(), PageSize); - const size_t map_size = util::AlignUp(region.GetEndAddress(), PageSize) - GetInteger(map_phys_addr); - const KVirtualAddress map_virt_addr = KMemoryLayout::GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard(map_size, PageSize, KMemoryRegionType_KernelMisc, PageSize); - MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetVirtualMemoryRegionTree().Insert(GetInteger(map_virt_addr), map_size, KMemoryRegionType_KernelMiscMappedDevice)); - region.SetPairAddress(GetInteger(map_virt_addr) + region.GetAddress() - GetInteger(map_phys_addr)); + /* If we found a region, map it. */ + if (largest != nullptr) { + /* Set the attribute to note we've mapped this region. */ + largest->SetTypeAttribute(KMemoryRegionAttr_DidKernelMap); - /* Map the page in to our page table. */ - ttbr1_table.Map(map_virt_addr, map_size, map_phys_addr, KernelMmioAttribute, g_initial_page_allocator); + /* Create a virtual pair region and insert it into the tree. */ + const KPhysicalAddress map_phys_addr = util::AlignDown(largest->GetAddress(), PageSize); + const size_t map_size = util::AlignUp(largest->GetEndAddress(), PageSize) - GetInteger(map_phys_addr); + const size_t min_align = std::min(util::GetAlignment(map_size), util::GetAlignment(GetInteger(map_phys_addr))); + const size_t map_align = min_align >= KernelAslrAlignment ? KernelAslrAlignment : PageSize; + const KVirtualAddress map_virt_addr = KMemoryLayout::GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard(map_size, map_align, KMemoryRegionType_KernelMisc, map_align); + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetVirtualMemoryRegionTree().Insert(GetInteger(map_virt_addr), map_size, KMemoryRegionType_KernelMiscMappedDevice)); + largest->SetPairAddress(GetInteger(map_virt_addr) + largest->GetAddress() - GetInteger(map_phys_addr)); + + /* Map the page in to our page table. */ + init_pt.Map(map_virt_addr, map_size, map_phys_addr, KernelMmioAttribute, g_initial_page_allocator); + } + } while (largest != nullptr); } /* Setup the basic DRAM regions. */ SetupDramPhysicalMemoryRegions(); + /* Automatically map in reserved physical memory that has auto-map attributes. */ + { + /* We want to map the regions from largest to smallest. */ + KMemoryRegion *largest; + do { + /* Begin with no knowledge of the largest region. */ + largest = nullptr; + + for (auto ®ion : KMemoryLayout::GetPhysicalMemoryRegionTree()) { + /* We only care about reserved memory. */ + if (!region.IsDerivedFrom(KMemoryRegionType_DramReservedBase)) { + continue; + } + + /* Check whether we should map the region. */ + if (!region.HasTypeAttribute(KMemoryRegionAttr_ShouldKernelMap)) { + continue; + } + + /* If this region has already been mapped, no need to consider it. */ + if (region.HasTypeAttribute(KMemoryRegionAttr_DidKernelMap)) { + continue; + } + + /* Check that the region is valid. */ + MESOSPHERE_INIT_ABORT_UNLESS(region.GetEndAddress() != 0); + + /* Update the largest region. */ + if (largest == nullptr || largest->GetSize() < region.GetSize()) { + largest = std::addressof(region); + } + } + + /* If we found a region, map it. */ + if (largest != nullptr) { + /* Set the attribute to note we've mapped this region. */ + largest->SetTypeAttribute(KMemoryRegionAttr_DidKernelMap); + + /* Create a virtual pair region and insert it into the tree. */ + const KPhysicalAddress map_phys_addr = util::AlignDown(largest->GetAddress(), PageSize); + const size_t map_size = util::AlignUp(largest->GetEndAddress(), PageSize) - GetInteger(map_phys_addr); + const size_t min_align = std::min(util::GetAlignment(map_size), util::GetAlignment(GetInteger(map_phys_addr))); + const size_t map_align = min_align >= KernelAslrAlignment ? KernelAslrAlignment : PageSize; + const KVirtualAddress map_virt_addr = KMemoryLayout::GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard(map_size, map_align, KMemoryRegionType_KernelMisc, map_align); + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetVirtualMemoryRegionTree().Insert(GetInteger(map_virt_addr), map_size, KMemoryRegionType_KernelMiscUnknownDebug)); + largest->SetPairAddress(GetInteger(map_virt_addr) + largest->GetAddress() - GetInteger(map_phys_addr)); + + /* Map the page in to our page table. */ + const auto attribute = largest->HasTypeAttribute(KMemoryRegionAttr_Uncached) ? KernelRwDataUncachedAttribute : KernelRwDataAttribute; + init_pt.Map(map_virt_addr, map_size, map_phys_addr, attribute, g_initial_page_allocator); + } + } while (largest != nullptr); + } + /* Insert a physical region for the kernel code region. */ MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(GetInteger(code_start_phys_addr), code_region_size, KMemoryRegionType_DramKernelCode)); @@ -283,11 +356,11 @@ namespace ams::kern::init { MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(GetInteger(slab_start_phys_addr), slab_region_size, KMemoryRegionType_DramKernelSlab)); /* Map the slab region. */ - ttbr1_table.Map(slab_region_start, slab_region_size, slab_start_phys_addr, KernelRwDataAttribute, g_initial_page_allocator); + init_pt.Map(slab_region_start, slab_region_size, slab_start_phys_addr, KernelRwDataAttribute, g_initial_page_allocator); /* Physically randomize the slab region. */ /* NOTE: Nintendo does this only on 10.0.0+ */ - ttbr1_table.PhysicallyRandomize(slab_region_start, slab_region_size, false); + init_pt.PhysicallyRandomize(slab_region_start, slab_region_size, false); /* Determine size available for kernel page table heaps, requiring > 8 MB. */ const KPhysicalAddress resource_end_phys_addr = slab_start_phys_addr + resource_region_size; @@ -338,14 +411,16 @@ namespace ams::kern::init { cur_size += region.GetSize(); } else { const uintptr_t cur_virt_addr = cur_phys_addr + linear_region_phys_to_virt_diff; - ttbr1_table.Map(cur_virt_addr, cur_size, cur_phys_addr, KernelRwDataAttribute, g_initial_page_allocator); + init_pt.Map(cur_virt_addr, cur_size, cur_phys_addr, KernelRwDataAttribute, g_initial_page_allocator); cur_phys_addr = region.GetAddress(); cur_size = region.GetSize(); } const uintptr_t region_virt_addr = region.GetAddress() + linear_region_phys_to_virt_diff; + if (!region.HasTypeAttribute(KMemoryRegionAttr_ShouldKernelMap)) { + region.SetPairAddress(region_virt_addr); + } MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetVirtualMemoryRegionTree().Insert(region_virt_addr, region.GetSize(), GetTypeForVirtualLinearMapping(region.GetType()))); - region.SetPairAddress(region_virt_addr); KMemoryRegion *virt_region = KMemoryLayout::GetVirtualMemoryRegionTree().FindModifiable(region_virt_addr); MESOSPHERE_INIT_ABORT_UNLESS(virt_region != nullptr); @@ -355,7 +430,7 @@ namespace ams::kern::init { /* Map the last block, which we may have skipped. */ if (cur_size != 0) { const uintptr_t cur_virt_addr = cur_phys_addr + linear_region_phys_to_virt_diff; - ttbr1_table.Map(cur_virt_addr, cur_size, cur_phys_addr, KernelRwDataAttribute, g_initial_page_allocator); + init_pt.Map(cur_virt_addr, cur_size, cur_phys_addr, KernelRwDataAttribute, g_initial_page_allocator); } } @@ -363,18 +438,18 @@ namespace ams::kern::init { std::memset(GetVoidPointer(slab_region_start), 0, slab_region_size); /* NOTE: Unknown function is called here which is ifdef'd out on retail kernel. */ - /* The unknown function is immediately before the function which gets the unknown debug region size, inside this translation unit. */ - /* It's likely that this is some kind of initializer for the unknown debug region. */ + /* The unknown function is immediately before the function which gets an unknown debug region size, inside this translation unit. */ + /* It's likely that this is some kind of initializer for this unknown debug region. */ /* Create regions for and map all core-specific stacks. */ for (size_t i = 0; i < cpu::NumCores; i++) { - MapStackForCore(ttbr1_table, KMemoryRegionType_KernelMiscMainStack, i); - MapStackForCore(ttbr1_table, KMemoryRegionType_KernelMiscIdleStack, i); - MapStackForCore(ttbr1_table, KMemoryRegionType_KernelMiscExceptionStack, i); + MapStackForCore(init_pt, KMemoryRegionType_KernelMiscMainStack, i); + MapStackForCore(init_pt, KMemoryRegionType_KernelMiscIdleStack, i); + MapStackForCore(init_pt, KMemoryRegionType_KernelMiscExceptionStack, i); } /* Setup the initial arguments. */ - SetupInitialArguments(ttbr1_table, g_initial_page_allocator); + SetupInitialArguments(init_pt, g_initial_page_allocator); /* Finalize the page allocator, we're done allocating at this point. */ KInitialPageAllocator::State final_init_page_table_state; @@ -400,7 +475,7 @@ namespace ams::kern::init { KMemoryLayout::InitializeLinearMemoryRegionTrees(aligned_linear_phys_start, linear_region_start); /* Turn on all other cores. */ - TurnOnAllCores(GetInteger(ttbr1_table.GetPhysicalAddress(reinterpret_cast(::ams::kern::init::StartOtherCore)))); + TurnOnAllCores(GetInteger(init_pt.GetPhysicalAddress(reinterpret_cast(::ams::kern::init::StartOtherCore)))); } KPhysicalAddress GetInitArgumentsAddress(s32 core_id) { From 0f8b7be2d2c5be3f39767d6e12c1db818f6d4270 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 12:25:10 -0700 Subject: [PATCH 141/280] kern: load initial process binary from user pool, rather than from pt heap --- .../nintendo/nx/kern_k_system_control.hpp | 1 + .../mesosphere/init/kern_init_layout.hpp | 2 +- .../mesosphere/kern_initial_process.hpp | 3 +- .../kern_k_initial_process_reader.hpp | 45 +-- .../mesosphere/kern_k_memory_manager.hpp | 10 +- .../include/mesosphere/kern_k_page_heap.hpp | 13 +- .../nintendo/nx/kern_k_system_control.cpp | 7 +- .../source/kern_initial_process.cpp | 318 ++++++++++++------ .../source/kern_k_initial_process_reader.cpp | 79 +++-- .../source/kern_k_memory_manager.cpp | 39 ++- .../source/arch/arm64/init/kern_init_core.cpp | 3 +- .../kernel/source/arch/arm64/init/start.s | 2 +- .../kernel_ldr/source/kern_init_loader.cpp | 12 +- 13 files changed, 350 insertions(+), 184 deletions(-) diff --git a/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_system_control.hpp b/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_system_control.hpp index f9d99aecf..056d5f32b 100644 --- a/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_system_control.hpp +++ b/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_system_control.hpp @@ -25,6 +25,7 @@ namespace ams::kern::board::nintendo::nx { /* Initialization. */ static size_t GetIntendedMemorySize(); static KPhysicalAddress GetKernelPhysicalBaseAddress(uintptr_t base_address); + static KPhysicalAddress GetInitialProcessBinaryPhysicalAddress(); static bool ShouldIncreaseThreadResourceLimit(); static void CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg); static size_t GetApplicationPoolSize(); diff --git a/libraries/libmesosphere/include/mesosphere/init/kern_init_layout.hpp b/libraries/libmesosphere/include/mesosphere/init/kern_init_layout.hpp index 3d379ca03..63197ec1e 100644 --- a/libraries/libmesosphere/include/mesosphere/init/kern_init_layout.hpp +++ b/libraries/libmesosphere/include/mesosphere/init/kern_init_layout.hpp @@ -27,7 +27,7 @@ namespace ams::kern::init { u32 rw_end_offset; u32 bss_offset; u32 bss_end_offset; - u32 ini_load_offset; + u32 resource_offset; u32 dynamic_offset; u32 init_array_offset; u32 init_array_end_offset; diff --git a/libraries/libmesosphere/include/mesosphere/kern_initial_process.hpp b/libraries/libmesosphere/include/mesosphere/kern_initial_process.hpp index bc15b0b88..08dc14cdd 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_initial_process.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_initial_process.hpp @@ -29,11 +29,12 @@ namespace ams::kern { u32 reserved; }; - NOINLINE void CopyInitialProcessBinaryToKernelMemory(); + NOINLINE size_t CopyInitialProcessBinaryToKernelMemory(); NOINLINE void CreateAndRunInitialProcesses(); u64 GetInitialProcessIdMin(); u64 GetInitialProcessIdMax(); + KVirtualAddress GetInitialProcessBinaryAddress(); size_t GetInitialProcessesSecureMemorySize(); } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_initial_process_reader.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_initial_process_reader.hpp index ce534533f..97b7977f9 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_initial_process_reader.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_initial_process_reader.hpp @@ -91,46 +91,49 @@ namespace ams::kern { class KInitialProcessReader { private: - KInitialProcessHeader *m_kip_header; + KInitialProcessHeader m_kip_header; public: constexpr KInitialProcessReader() : m_kip_header() { /* ... */ } - constexpr const u32 *GetCapabilities() const { return m_kip_header->GetCapabilities(); } - constexpr size_t GetNumCapabilities() const { return m_kip_header->GetNumCapabilities(); } + constexpr const u32 *GetCapabilities() const { return m_kip_header.GetCapabilities(); } + constexpr size_t GetNumCapabilities() const { return m_kip_header.GetNumCapabilities(); } constexpr size_t GetBinarySize() const { - return sizeof(*m_kip_header) + m_kip_header->GetRxCompressedSize() + m_kip_header->GetRoCompressedSize() + m_kip_header->GetRwCompressedSize(); + return m_kip_header.GetRxCompressedSize() + m_kip_header.GetRoCompressedSize() + m_kip_header.GetRwCompressedSize(); } constexpr size_t GetSize() const { - if (const size_t bss_size = m_kip_header->GetBssSize(); bss_size != 0) { - return m_kip_header->GetBssAddress() + m_kip_header->GetBssSize(); + if (const size_t bss_size = m_kip_header.GetBssSize(); bss_size != 0) { + return util::AlignUp(m_kip_header.GetBssAddress() + m_kip_header.GetBssSize(), PageSize); } else { - return m_kip_header->GetRwAddress() + m_kip_header->GetRwSize(); + return util::AlignUp(m_kip_header.GetRwAddress() + m_kip_header.GetRwSize(), PageSize); } } - constexpr u8 GetPriority() const { return m_kip_header->GetPriority(); } - constexpr u8 GetIdealCoreId() const { return m_kip_header->GetIdealCoreId(); } - constexpr u32 GetAffinityMask() const { return m_kip_header->GetAffinityMask(); } - constexpr u32 GetStackSize() const { return m_kip_header->GetStackSize(); } + constexpr u8 GetPriority() const { return m_kip_header.GetPriority(); } + constexpr u8 GetIdealCoreId() const { return m_kip_header.GetIdealCoreId(); } + constexpr u32 GetAffinityMask() const { return m_kip_header.GetAffinityMask(); } + constexpr u32 GetStackSize() const { return m_kip_header.GetStackSize(); } - constexpr bool Is64Bit() const { return m_kip_header->Is64Bit(); } - constexpr bool Is64BitAddressSpace() const { return m_kip_header->Is64BitAddressSpace(); } - constexpr bool UsesSecureMemory() const { return m_kip_header->UsesSecureMemory(); } - constexpr bool IsImmortal() const { return m_kip_header->IsImmortal(); } + constexpr bool Is64Bit() const { return m_kip_header.Is64Bit(); } + constexpr bool Is64BitAddressSpace() const { return m_kip_header.Is64BitAddressSpace(); } + constexpr bool UsesSecureMemory() const { return m_kip_header.UsesSecureMemory(); } + constexpr bool IsImmortal() const { return m_kip_header.IsImmortal(); } - bool Attach(u8 *bin) { - if (KInitialProcessHeader *header = reinterpret_cast(bin); header->IsValid()) { - m_kip_header = header; - return true; + KVirtualAddress Attach(KVirtualAddress bin) { + /* Copy the header. */ + m_kip_header = *GetPointer(bin); + + /* Check that it's valid. */ + if (m_kip_header.IsValid()) { + return bin + sizeof(KInitialProcessHeader); } else { - return false; + return Null; } } Result MakeCreateProcessParameter(ams::svc::CreateProcessParameter *out, bool enable_aslr) const; - Result Load(KProcessAddress address, const ams::svc::CreateProcessParameter ¶ms) const; + Result Load(KProcessAddress address, const ams::svc::CreateProcessParameter ¶ms, KProcessAddress src) const; Result SetMemoryPermissions(KProcessPageTable &page_table, const ams::svc::CreateProcessParameter ¶ms) const; }; diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_memory_manager.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_memory_manager.hpp index 5bef180a9..4b17911e4 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_memory_manager.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_memory_manager.hpp @@ -75,7 +75,7 @@ namespace ams::kern { KVirtualAddress AllocateBlock(s32 index, bool random) { return m_heap.AllocateBlock(index, random); } void Free(KVirtualAddress addr, size_t num_pages) { m_heap.Free(addr, num_pages); } - void UpdateUsedHeapSize() { m_heap.UpdateUsedSize(); } + void SetInitialUsedHeapSize(size_t reserved_size) { m_heap.SetInitialUsedSize(reserved_size); } void InitializeOptimizedMemory() { std::memset(GetVoidPointer(m_management_region), 0, CalculateOptimizedProcessOverheadSize(m_heap.GetSize())); } @@ -168,6 +168,10 @@ namespace ams::kern { return m_managers[KMemoryLayout::GetVirtualLinearRegion(address).GetAttributes()]; } + const Impl &GetManager(KVirtualAddress address) const { + return m_managers[KMemoryLayout::GetVirtualLinearRegion(address).GetAttributes()]; + } + constexpr Impl *GetFirstManager(Pool pool, Direction dir) { return dir == Direction_FromBack ? m_pool_managers_tail[pool] : m_pool_managers_head[pool]; } @@ -197,6 +201,10 @@ namespace ams::kern { NOINLINE Result AllocateAndOpen(KPageGroup *out, size_t num_pages, u32 option); NOINLINE Result AllocateAndOpenForProcess(KPageGroup *out, size_t num_pages, u32 option, u64 process_id, u8 fill_pattern); + Pool GetPool(KVirtualAddress address) const { + return this->GetManager(address).GetPool(); + } + void Open(KVirtualAddress address, size_t num_pages) { /* Repeatedly open references until we've done so for all pages. */ while (num_pages) { diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_page_heap.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_page_heap.hpp index e14e7e0df..563bcb3be 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_page_heap.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_page_heap.hpp @@ -125,7 +125,7 @@ namespace ams::kern { private: KVirtualAddress m_heap_address; size_t m_heap_size; - size_t m_used_size; + size_t m_initial_used_size; size_t m_num_blocks; Block m_blocks[NumMemoryBlockPageShifts]; private: @@ -134,7 +134,7 @@ namespace ams::kern { void FreeBlock(KVirtualAddress block, s32 index); public: - KPageHeap() : m_heap_address(), m_heap_size(), m_used_size(), m_num_blocks(), m_blocks() { /* ... */ } + KPageHeap() : m_heap_address(), m_heap_size(), m_initial_used_size(), m_num_blocks(), m_blocks() { /* ... */ } constexpr KVirtualAddress GetAddress() const { return m_heap_address; } constexpr size_t GetSize() const { return m_heap_size; } @@ -149,8 +149,13 @@ namespace ams::kern { size_t GetFreeSize() const { return this->GetNumFreePages() * PageSize; } void DumpFreeList() const; - void UpdateUsedSize() { - m_used_size = m_heap_size - (this->GetNumFreePages() * PageSize); + void SetInitialUsedSize(size_t reserved_size) { + /* Check that the reserved size is valid. */ + const size_t free_size = this->GetNumFreePages() * PageSize; + MESOSPHERE_ABORT_UNLESS(m_heap_size >= free_size + reserved_size); + + /* Set the initial used size. */ + m_initial_used_size = m_heap_size - free_size - reserved_size; } KVirtualAddress AllocateBlock(s32 index, bool random); diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp index 8120957dd..b689d19ea 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp @@ -21,7 +21,8 @@ namespace ams::kern::board::nintendo::nx { namespace { - constexpr size_t SecureAlignment = 128_KB; + constexpr uintptr_t DramPhysicalAddress = 0x80000000; + constexpr size_t SecureAlignment = 128_KB; /* Global variables for panic. */ constinit bool g_call_smc_on_panic; @@ -348,6 +349,10 @@ namespace ams::kern::board::nintendo::nx { } } + KPhysicalAddress KSystemControl::Init::GetInitialProcessBinaryPhysicalAddress() { + return GetKernelPhysicalBaseAddress(DramPhysicalAddress) + GetIntendedMemorySize() - KTraceBufferSize - InitialProcessBinarySizeMax; + } + bool KSystemControl::Init::ShouldIncreaseThreadResourceLimit() { return GetKernelConfigurationForInit().Get(); } diff --git a/libraries/libmesosphere/source/kern_initial_process.cpp b/libraries/libmesosphere/source/kern_initial_process.cpp index 24616fbb6..6656218bd 100644 --- a/libraries/libmesosphere/source/kern_initial_process.cpp +++ b/libraries/libmesosphere/source/kern_initial_process.cpp @@ -25,101 +25,219 @@ namespace ams::kern { s32 priority; }; - KVirtualAddress GetInitialProcessBinaryAddress() { - const uintptr_t end_address = KMemoryLayout::GetPageTableHeapRegion().GetEndAddress(); - MESOSPHERE_ABORT_UNLESS(end_address != 0); - return end_address - InitialProcessBinarySizeMax; - } + constinit KVirtualAddress g_initial_process_binary_address = Null; + constinit InitialProcessBinaryHeader g_initial_process_binary_header = {}; + constinit size_t g_initial_process_secure_memory_size = 0; + constinit u64 g_initial_process_id_min = std::numeric_limits::max(); + constinit u64 g_initial_process_id_max = std::numeric_limits::min(); - void LoadInitialProcessBinaryHeader(InitialProcessBinaryHeader *header) { - if (header->magic != InitialProcessBinaryMagic) { - *header = *GetPointer(GetInitialProcessBinaryAddress()); - } + void LoadInitialProcessBinaryHeader() { + if (g_initial_process_binary_header.magic != InitialProcessBinaryMagic) { + /* Get the virtual address for the image. */ + const KVirtualAddress virt_addr = GetInitialProcessBinaryAddress(); - MESOSPHERE_ABORT_UNLESS(header->magic == InitialProcessBinaryMagic); - MESOSPHERE_ABORT_UNLESS(header->num_processes <= init::GetSlabResourceCounts().num_KProcess); - } + /* Copy and validate the header. */ + g_initial_process_binary_header = *GetPointer(virt_addr); + MESOSPHERE_ABORT_UNLESS(g_initial_process_binary_header.magic == InitialProcessBinaryMagic); + MESOSPHERE_ABORT_UNLESS(g_initial_process_binary_header.num_processes <= init::GetSlabResourceCounts().num_KProcess); - size_t GetProcessesSecureMemorySize(KVirtualAddress binary_address, const InitialProcessBinaryHeader &header) { - u8 *current = GetPointer(binary_address + sizeof(InitialProcessBinaryHeader)); - const u8 * const end = GetPointer(binary_address + header.size - sizeof(KInitialProcessHeader)); + /* Set the image address. */ + g_initial_process_binary_address = virt_addr; - size_t size = 0; - const size_t num_processes = header.num_processes; - for (size_t i = 0; i < num_processes; i++) { - /* Validate that we can read the current KIP. */ - MESOSPHERE_ABORT_UNLESS(current <= end); - KInitialProcessReader reader; - MESOSPHERE_ABORT_UNLESS(reader.Attach(current)); + /* Process/calculate the secure memory size. */ + KVirtualAddress current = g_initial_process_binary_address + sizeof(InitialProcessBinaryHeader); + const KVirtualAddress end = g_initial_process_binary_address + g_initial_process_binary_header.size; + const size_t num_processes = g_initial_process_binary_header.num_processes; + for (size_t i = 0; i < num_processes; ++i) { + /* Validate that we can read the current KIP. */ + MESOSPHERE_ABORT_UNLESS(current <= end - sizeof(KInitialProcessHeader)); - /* If the process uses secure memory, account for that. */ - if (reader.UsesSecureMemory()) { - size += util::AlignUp(reader.GetSize(), PageSize); + /* Attach to the current KIP. */ + KInitialProcessReader reader; + MESOSPHERE_ABORT_UNLESS(reader.Attach(current) != Null); + + /* If the process uses secure memory, account for that. */ + if (reader.UsesSecureMemory()) { + g_initial_process_secure_memory_size += reader.GetSize() + util::AlignUp(reader.GetStackSize(), PageSize); + } } - - /* Advance the reader. */ - current += reader.GetBinarySize(); } - - return size; } - void CreateProcesses(InitialProcessInfo *infos, KVirtualAddress binary_address, const InitialProcessBinaryHeader &header) { - u8 *current = GetPointer(binary_address + sizeof(InitialProcessBinaryHeader)); - const u8 * const end = GetPointer(binary_address + header.size - sizeof(KInitialProcessHeader)); + void CreateProcesses(InitialProcessInfo *infos) { + /* Determine process image extents. */ + KVirtualAddress current = g_initial_process_binary_address + sizeof(InitialProcessBinaryHeader); + KVirtualAddress end = g_initial_process_binary_address + g_initial_process_binary_header.size; /* Decide on pools to use. */ const auto unsafe_pool = static_cast(KSystemControl::GetCreateProcessMemoryPool()); const auto secure_pool = (GetTargetFirmware() >= TargetFirmware_2_0_0) ? KMemoryManager::Pool_Secure : unsafe_pool; - const size_t num_processes = header.num_processes; - for (size_t i = 0; i < num_processes; i++) { - /* Validate that we can read the current KIP. */ - MESOSPHERE_ABORT_UNLESS(current <= end); - KInitialProcessReader reader; - MESOSPHERE_ABORT_UNLESS(reader.Attach(current)); + const size_t num_processes = g_initial_process_binary_header.num_processes; + for (size_t i = 0; i < num_processes; ++i) { + /* Validate that we can read the current KIP header. */ + MESOSPHERE_ABORT_UNLESS(current <= end - sizeof(KInitialProcessHeader)); - /* Parse process parameters and reserve memory. */ + /* Attach to the current kip. */ + KInitialProcessReader reader; + KVirtualAddress data = reader.Attach(current); + MESOSPHERE_ABORT_UNLESS(data != Null); + + /* Ensure that the remainder of our parse is page aligned. */ + if (!util::IsAligned(GetInteger(data), PageSize)) { + const KVirtualAddress aligned_data = util::AlignDown(GetInteger(data), PageSize); + std::memmove(GetVoidPointer(aligned_data), GetVoidPointer(data), end - data); + + data = aligned_data; + end -= (data - aligned_data); + } + + /* If we crossed a page boundary, free the pages we're done using. */ + if (KVirtualAddress aligned_current = util::AlignDown(GetInteger(current), PageSize); aligned_current != data) { + const size_t freed_size = data - aligned_current; + Kernel::GetMemoryManager().Close(aligned_current, freed_size / PageSize); + Kernel::GetSystemResourceLimit().Release(ams::svc::LimitableResource_PhysicalMemoryMax, freed_size); + } + + /* Parse process parameters. */ ams::svc::CreateProcessParameter params; MESOSPHERE_R_ABORT_UNLESS(reader.MakeCreateProcessParameter(std::addressof(params), true)); - MESOSPHERE_ABORT_UNLESS(Kernel::GetSystemResourceLimit().Reserve(ams::svc::LimitableResource_PhysicalMemoryMax, params.code_num_pages * PageSize)); + + /* Get the binary size for the kip. */ + const size_t binary_size = reader.GetBinarySize(); + const size_t binary_pages = binary_size / PageSize; + + /* Get the pool for both the current (compressed) image, and the decompressed process. */ + const auto src_pool = Kernel::GetMemoryManager().GetPool(data); + const auto dst_pool = reader.UsesSecureMemory() ? secure_pool : unsafe_pool; + + /* Determine the process size, and how much memory isn't already reserved. */ + const size_t process_size = params.code_num_pages * PageSize; + const size_t unreserved_size = process_size - (src_pool == dst_pool ? util::AlignDown(binary_size, PageSize) : 0); + + /* Reserve however much memory we need to reserve. */ + MESOSPHERE_ABORT_UNLESS(Kernel::GetSystemResourceLimit().Reserve(ams::svc::LimitableResource_PhysicalMemoryMax, unreserved_size)); /* Create the process. */ KProcess *new_process = nullptr; { - /* Declare page group to use for process memory. */ + /* Make page groups to represent the data. */ KPageGroup pg(std::addressof(Kernel::GetBlockInfoManager())); + KPageGroup workaround_pg(std::addressof(Kernel::GetBlockInfoManager())); - /* Allocate memory for the process. */ - auto &mm = Kernel::GetMemoryManager(); - const auto pool = reader.UsesSecureMemory() ? secure_pool : unsafe_pool; - MESOSPHERE_R_ABORT_UNLESS(mm.AllocateAndOpen(std::addressof(pg), params.code_num_pages, KMemoryManager::EncodeOption(pool, KMemoryManager::Direction_FromFront))); - + /* Populate the page group to represent the data. */ { - /* Ensure that we do not leak pages. */ - ON_SCOPE_EXIT { pg.Close(); }; + /* Allocate the previously unreserved pages. */ + KPageGroup unreserve_pg(std::addressof(Kernel::GetBlockInfoManager())); + MESOSPHERE_R_ABORT_UNLESS(Kernel::GetMemoryManager().AllocateAndOpen(std::addressof(unreserve_pg), unreserved_size / PageSize, KMemoryManager::EncodeOption(dst_pool, KMemoryManager::Direction_FromFront))); - /* Get the temporary region. */ - const auto &temp_region = KMemoryLayout::GetTempRegion(); - MESOSPHERE_ABORT_UNLESS(temp_region.GetEndAddress() != 0); + /* Add the previously reserved pages. */ + if (src_pool == dst_pool && binary_pages != 0) { + /* NOTE: Nintendo does not check the result of this operation. */ + pg.AddBlock(data, binary_pages); + } - /* Map the process's memory into the temporary region. */ - KProcessAddress temp_address = Null; - MESOSPHERE_R_ABORT_UNLESS(Kernel::GetKernelPageTable().MapPageGroup(std::addressof(temp_address), pg, temp_region.GetAddress(), temp_region.GetSize() / PageSize, KMemoryState_Kernel, KMemoryPermission_KernelReadWrite)); - - /* Load the process. */ - MESOSPHERE_R_ABORT_UNLESS(reader.Load(temp_address, params)); - - /* Unmap the temporary mapping. */ - MESOSPHERE_R_ABORT_UNLESS(Kernel::GetKernelPageTable().UnmapPageGroup(temp_address, pg, KMemoryState_Kernel)); - - /* Create a KProcess object. */ - new_process = KProcess::Create(); - MESOSPHERE_ABORT_UNLESS(new_process != nullptr); - - /* Initialize the process. */ - MESOSPHERE_R_ABORT_UNLESS(new_process->Initialize(params, pg, reader.GetCapabilities(), reader.GetNumCapabilities(), std::addressof(Kernel::GetSystemResourceLimit()), pool, reader.IsImmortal())); + /* Add the previously unreserved pages. */ + for (const auto &block : unreserve_pg) { + /* NOTE: Nintendo does not check the result of this operation. */ + pg.AddBlock(block.GetAddress(), block.GetNumPages()); + } } + MESOSPHERE_ABORT_UNLESS(pg.GetNumPages() == static_cast(params.code_num_pages)); + + /* Ensure that we do not leak pages. */ + KPageGroup *process_pg = std::addressof(pg); + ON_SCOPE_EXIT { process_pg->Close(); }; + + /* Get the temporary region. */ + const auto &temp_region = KMemoryLayout::GetTempRegion(); + MESOSPHERE_ABORT_UNLESS(temp_region.GetEndAddress() != 0); + + /* Map the process's memory into the temporary region. */ + KProcessAddress temp_address = Null; + MESOSPHERE_R_ABORT_UNLESS(Kernel::GetKernelPageTable().MapPageGroup(std::addressof(temp_address), pg, temp_region.GetAddress(), temp_region.GetSize() / PageSize, KMemoryState_Kernel, KMemoryPermission_KernelReadWrite)); + + /* Setup the new page group's memory, so that we can load the process. */ + { + /* Copy the unaligned ending of the compressed binary. */ + if (const size_t unaligned_size = binary_size - util::AlignDown(binary_size, PageSize); unaligned_size != 0) { + std::memcpy(GetVoidPointer(temp_address + process_size - unaligned_size), GetVoidPointer(data + binary_size - unaligned_size), unaligned_size); + } + + /* Copy the aligned part of the compressed binary. */ + if (const size_t aligned_size = util::AlignDown(binary_size, PageSize); aligned_size != 0 && src_pool == dst_pool) { + std::memmove(GetVoidPointer(temp_address + process_size - binary_size), GetVoidPointer(temp_address), aligned_size); + } else { + if (src_pool != dst_pool) { + std::memcpy(GetVoidPointer(temp_address + process_size - binary_size), GetVoidPointer(data), aligned_size); + Kernel::GetMemoryManager().Close(data, aligned_size / PageSize); + } + } + + /* Clear the first part of the memory. */ + std::memset(GetVoidPointer(temp_address), 0, process_size - binary_size); + } + + /* Load the process. */ + MESOSPHERE_R_ABORT_UNLESS(reader.Load(temp_address, params, temp_address + process_size - binary_size)); + + /* Unmap the temporary mapping. */ + MESOSPHERE_R_ABORT_UNLESS(Kernel::GetKernelPageTable().UnmapPageGroup(temp_address, pg, KMemoryState_Kernel)); + + /* Create a KProcess object. */ + new_process = KProcess::Create(); + MESOSPHERE_ABORT_UNLESS(new_process != nullptr); + + /* Ensure the page group is usable for the process. */ + /* If the pool is the same, we need to use the workaround page group. */ + if (src_pool == dst_pool) { + /* Allocate a new, usable group for the process. */ + MESOSPHERE_R_ABORT_UNLESS(Kernel::GetMemoryManager().AllocateAndOpen(std::addressof(workaround_pg), static_cast(params.code_num_pages), KMemoryManager::EncodeOption(dst_pool, KMemoryManager::Direction_FromFront))); + + /* Copy data from the working page group to the usable one. */ + auto work_it = pg.begin(); + MESOSPHERE_ABORT_UNLESS(work_it != pg.end()); + { + auto work_address = work_it->GetAddress(); + auto work_remaining = work_it->GetNumPages(); + for (const auto &block : workaround_pg) { + auto block_address = block.GetAddress(); + auto block_remaining = block.GetNumPages(); + while (block_remaining > 0) { + if (work_remaining == 0) { + ++work_it; + work_address = work_it->GetAddress(); + work_remaining = work_it->GetNumPages(); + } + + const size_t cur_pages = std::min(block_remaining, work_remaining); + const size_t cur_size = cur_pages * PageSize; + std::memcpy(GetVoidPointer(block_address), GetVoidPointer(work_address), cur_size); + + block_address += cur_size; + work_address += cur_size; + + block_remaining -= cur_pages; + work_remaining -= cur_pages; + } + } + + ++work_it; + } + MESOSPHERE_ABORT_UNLESS(work_it == pg.end()); + + /* We want to use the new page group. */ + process_pg = std::addressof(workaround_pg); + pg.Close(); + } + + /* Initialize the process. */ + MESOSPHERE_R_ABORT_UNLESS(new_process->Initialize(params, *process_pg, reader.GetCapabilities(), reader.GetNumCapabilities(), std::addressof(Kernel::GetSystemResourceLimit()), dst_pool, reader.IsImmortal())); + } + + /* Release the memory that was previously reserved. */ + if (const size_t aligned_bin_size = util::AlignDown(binary_size, PageSize); aligned_bin_size != 0 && src_pool != dst_pool) { + Kernel::GetSystemResourceLimit().Release(ams::svc::LimitableResource_PhysicalMemoryMax, aligned_bin_size); } /* Set the process's memory permissions. */ @@ -137,15 +255,18 @@ namespace ams::kern { infos[i].priority = reader.GetPriority(); /* Advance the reader. */ - current += reader.GetBinarySize(); + current = data + binary_size; + } + + /* Release remaining memory used by the image. */ + { + const size_t remaining_size = util::AlignUp(GetInteger(g_initial_process_binary_address) + g_initial_process_binary_header.size, PageSize) - util::AlignDown(GetInteger(current), PageSize); + const size_t remaining_pages = remaining_size / PageSize; + Kernel::GetMemoryManager().Close(util::AlignDown(GetInteger(current), PageSize), remaining_pages); + Kernel::GetSystemResourceLimit().Release(ams::svc::LimitableResource_PhysicalMemoryMax, remaining_size); } } - constinit KVirtualAddress g_initial_process_binary_address = Null; - constinit InitialProcessBinaryHeader g_initial_process_binary_header = {}; - constinit u64 g_initial_process_id_min = std::numeric_limits::max(); - constinit u64 g_initial_process_id_max = std::numeric_limits::min(); - } u64 GetInitialProcessIdMin() { @@ -156,32 +277,37 @@ namespace ams::kern { return g_initial_process_id_max; } - size_t GetInitialProcessesSecureMemorySize() { - LoadInitialProcessBinaryHeader(&g_initial_process_binary_header); - - return GetProcessesSecureMemorySize(g_initial_process_binary_address != Null ? g_initial_process_binary_address : GetInitialProcessBinaryAddress(), g_initial_process_binary_header); + KVirtualAddress GetInitialProcessBinaryAddress() { + /* Get, validate the pool region. */ + const auto *pool_region = KMemoryLayout::GetVirtualMemoryRegionTree().FindLastDerived(KMemoryRegionType_VirtualDramUserPool); + MESOSPHERE_INIT_ABORT_UNLESS(pool_region != nullptr); + MESOSPHERE_INIT_ABORT_UNLESS(pool_region->GetEndAddress() != 0); + MESOSPHERE_ABORT_UNLESS(pool_region->GetSize() >= InitialProcessBinarySizeMax); + return pool_region->GetEndAddress() - InitialProcessBinarySizeMax; } - void CopyInitialProcessBinaryToKernelMemory() { - LoadInitialProcessBinaryHeader(&g_initial_process_binary_header); + size_t GetInitialProcessesSecureMemorySize() { + LoadInitialProcessBinaryHeader(); + + return g_initial_process_secure_memory_size; + } + + size_t CopyInitialProcessBinaryToKernelMemory() { + LoadInitialProcessBinaryHeader(); if (g_initial_process_binary_header.num_processes > 0) { /* Reserve pages for the initial process binary from the system resource limit. */ - auto &mm = Kernel::GetMemoryManager(); const size_t total_size = util::AlignUp(g_initial_process_binary_header.size, PageSize); - const size_t num_pages = total_size / PageSize; MESOSPHERE_ABORT_UNLESS(Kernel::GetSystemResourceLimit().Reserve(ams::svc::LimitableResource_PhysicalMemoryMax, total_size)); - /* Allocate memory for the image. */ - const KMemoryManager::Pool pool = static_cast(KSystemControl::GetCreateProcessMemoryPool()); - const auto allocate_option = KMemoryManager::EncodeOption(pool, KMemoryManager::Direction_FromFront); - KVirtualAddress allocated_memory = mm.AllocateAndOpenContinuous(num_pages, 1, allocate_option); - MESOSPHERE_ABORT_UNLESS(allocated_memory != Null); + /* The initial process binary is potentially over-allocated, so free any extra pages. */ + if (total_size < InitialProcessBinarySizeMax) { + Kernel::GetMemoryManager().Close(g_initial_process_binary_address + total_size, (InitialProcessBinarySizeMax - total_size) / PageSize); + } - /* Relocate the image. */ - std::memmove(GetVoidPointer(allocated_memory), GetVoidPointer(GetInitialProcessBinaryAddress()), g_initial_process_binary_header.size); - std::memset(GetVoidPointer(GetInitialProcessBinaryAddress()), 0, g_initial_process_binary_header.size); - g_initial_process_binary_address = allocated_memory; + return total_size; + } else { + return 0; } } @@ -190,15 +316,7 @@ namespace ams::kern { InitialProcessInfo *infos = static_cast(__builtin_alloca(sizeof(InitialProcessInfo) * g_initial_process_binary_header.num_processes)); /* Create the processes. */ - CreateProcesses(infos, g_initial_process_binary_address, g_initial_process_binary_header); - - /* Release the memory used by the image. */ - { - const size_t total_size = util::AlignUp(g_initial_process_binary_header.size, PageSize); - const size_t num_pages = total_size / PageSize; - Kernel::GetMemoryManager().Close(g_initial_process_binary_address, num_pages); - Kernel::GetSystemResourceLimit().Release(ams::svc::LimitableResource_PhysicalMemoryMax, total_size); - } + CreateProcesses(infos); /* Determine the initial process id range. */ for (size_t i = 0; i < g_initial_process_binary_header.num_processes; i++) { diff --git a/libraries/libmesosphere/source/kern_k_initial_process_reader.cpp b/libraries/libmesosphere/source/kern_k_initial_process_reader.cpp index 6665dcdc8..83922182d 100644 --- a/libraries/libmesosphere/source/kern_k_initial_process_reader.cpp +++ b/libraries/libmesosphere/source/kern_k_initial_process_reader.cpp @@ -77,14 +77,14 @@ namespace ams::kern { Result KInitialProcessReader::MakeCreateProcessParameter(ams::svc::CreateProcessParameter *out, bool enable_aslr) const { /* Get and validate addresses/sizes. */ - const uintptr_t rx_address = m_kip_header->GetRxAddress(); - const size_t rx_size = m_kip_header->GetRxSize(); - const uintptr_t ro_address = m_kip_header->GetRoAddress(); - const size_t ro_size = m_kip_header->GetRoSize(); - const uintptr_t rw_address = m_kip_header->GetRwAddress(); - const size_t rw_size = m_kip_header->GetRwSize(); - const uintptr_t bss_address = m_kip_header->GetBssAddress(); - const size_t bss_size = m_kip_header->GetBssSize(); + const uintptr_t rx_address = m_kip_header.GetRxAddress(); + const size_t rx_size = m_kip_header.GetRxSize(); + const uintptr_t ro_address = m_kip_header.GetRoAddress(); + const size_t ro_size = m_kip_header.GetRoSize(); + const uintptr_t rw_address = m_kip_header.GetRwAddress(); + const size_t rw_size = m_kip_header.GetRwSize(); + const uintptr_t bss_address = m_kip_header.GetBssAddress(); + const size_t bss_size = m_kip_header.GetBssSize(); R_UNLESS(util::IsAligned(rx_address, PageSize), svc::ResultInvalidAddress()); R_UNLESS(util::IsAligned(ro_address, PageSize), svc::ResultInvalidAddress()); R_UNLESS(util::IsAligned(rw_address, PageSize), svc::ResultInvalidAddress()); @@ -115,13 +115,13 @@ namespace ams::kern { /* Set fields in parameter. */ out->code_address = map_start + start_address; out->code_num_pages = util::AlignUp(end_address - start_address, PageSize) / PageSize; - out->program_id = m_kip_header->GetProgramId(); - out->version = m_kip_header->GetVersion(); + out->program_id = m_kip_header.GetProgramId(); + out->version = m_kip_header.GetVersion(); out->flags = 0; MESOSPHERE_ABORT_UNLESS((out->code_address / PageSize) + out->code_num_pages <= (map_end / PageSize)); /* Copy name field. */ - m_kip_header->GetName(out->name, sizeof(out->name)); + m_kip_header.GetName(out->name, sizeof(out->name)); /* Apply ASLR, if needed. */ if (enable_aslr) { @@ -146,39 +146,36 @@ namespace ams::kern { return ResultSuccess(); } - Result KInitialProcessReader::Load(KProcessAddress address, const ams::svc::CreateProcessParameter ¶ms) const { - /* Clear memory at the address. */ - std::memset(GetVoidPointer(address), 0, params.code_num_pages * PageSize); - + Result KInitialProcessReader::Load(KProcessAddress address, const ams::svc::CreateProcessParameter ¶ms, KProcessAddress src) const { /* Prepare to layout the data. */ - const KProcessAddress rx_address = address + m_kip_header->GetRxAddress(); - const KProcessAddress ro_address = address + m_kip_header->GetRoAddress(); - const KProcessAddress rw_address = address + m_kip_header->GetRwAddress(); - const u8 *rx_binary = reinterpret_cast(m_kip_header + 1); - const u8 *ro_binary = rx_binary + m_kip_header->GetRxCompressedSize(); - const u8 *rw_binary = ro_binary + m_kip_header->GetRoCompressedSize(); + const KProcessAddress rx_address = address + m_kip_header.GetRxAddress(); + const KProcessAddress ro_address = address + m_kip_header.GetRoAddress(); + const KProcessAddress rw_address = address + m_kip_header.GetRwAddress(); + const u8 *rx_binary = GetPointer(src); + const u8 *ro_binary = rx_binary + m_kip_header.GetRxCompressedSize(); + const u8 *rw_binary = ro_binary + m_kip_header.GetRoCompressedSize(); /* Copy text. */ - if (util::AlignUp(m_kip_header->GetRxSize(), PageSize)) { - std::memcpy(GetVoidPointer(rx_address), rx_binary, m_kip_header->GetRxCompressedSize()); - if (m_kip_header->IsRxCompressed()) { - BlzUncompress(GetVoidPointer(rx_address + m_kip_header->GetRxCompressedSize())); + if (util::AlignUp(m_kip_header.GetRxSize(), PageSize)) { + std::memmove(GetVoidPointer(rx_address), rx_binary, m_kip_header.GetRxCompressedSize()); + if (m_kip_header.IsRxCompressed()) { + BlzUncompress(GetVoidPointer(rx_address + m_kip_header.GetRxCompressedSize())); } } /* Copy rodata. */ - if (util::AlignUp(m_kip_header->GetRoSize(), PageSize)) { - std::memcpy(GetVoidPointer(ro_address), ro_binary, m_kip_header->GetRoCompressedSize()); - if (m_kip_header->IsRoCompressed()) { - BlzUncompress(GetVoidPointer(ro_address + m_kip_header->GetRoCompressedSize())); + if (util::AlignUp(m_kip_header.GetRoSize(), PageSize)) { + std::memmove(GetVoidPointer(ro_address), ro_binary, m_kip_header.GetRoCompressedSize()); + if (m_kip_header.IsRoCompressed()) { + BlzUncompress(GetVoidPointer(ro_address + m_kip_header.GetRoCompressedSize())); } } /* Copy rwdata. */ - if (util::AlignUp(m_kip_header->GetRwSize(), PageSize)) { - std::memcpy(GetVoidPointer(rw_address), rw_binary, m_kip_header->GetRwCompressedSize()); - if (m_kip_header->IsRwCompressed()) { - BlzUncompress(GetVoidPointer(rw_address + m_kip_header->GetRwCompressedSize())); + if (util::AlignUp(m_kip_header.GetRwSize(), PageSize)) { + std::memmove(GetVoidPointer(rw_address), rw_binary, m_kip_header.GetRwCompressedSize()); + if (m_kip_header.IsRwCompressed()) { + BlzUncompress(GetVoidPointer(rw_address + m_kip_header.GetRwCompressedSize())); } } @@ -192,27 +189,27 @@ namespace ams::kern { } Result KInitialProcessReader::SetMemoryPermissions(KProcessPageTable &page_table, const ams::svc::CreateProcessParameter ¶ms) const { - const size_t rx_size = m_kip_header->GetRxSize(); - const size_t ro_size = m_kip_header->GetRoSize(); - const size_t rw_size = m_kip_header->GetRwSize(); - const size_t bss_size = m_kip_header->GetBssSize(); + const size_t rx_size = m_kip_header.GetRxSize(); + const size_t ro_size = m_kip_header.GetRoSize(); + const size_t rw_size = m_kip_header.GetRwSize(); + const size_t bss_size = m_kip_header.GetBssSize(); /* Set R-X pages. */ if (rx_size) { - const uintptr_t start = m_kip_header->GetRxAddress() + params.code_address; + const uintptr_t start = m_kip_header.GetRxAddress() + params.code_address; R_TRY(page_table.SetProcessMemoryPermission(start, util::AlignUp(rx_size, PageSize), ams::svc::MemoryPermission_ReadExecute)); } /* Set R-- pages. */ if (ro_size) { - const uintptr_t start = m_kip_header->GetRoAddress() + params.code_address; + const uintptr_t start = m_kip_header.GetRoAddress() + params.code_address; R_TRY(page_table.SetProcessMemoryPermission(start, util::AlignUp(ro_size, PageSize), ams::svc::MemoryPermission_Read)); } /* Set RW- pages. */ if (rw_size || bss_size) { - const uintptr_t start = (rw_size ? m_kip_header->GetRwAddress() : m_kip_header->GetBssAddress()) + params.code_address; - const uintptr_t end = (bss_size ? m_kip_header->GetBssAddress() + bss_size : m_kip_header->GetRwAddress() + rw_size) + params.code_address; + const uintptr_t start = (rw_size ? m_kip_header.GetRwAddress() : m_kip_header.GetBssAddress()) + params.code_address; + const uintptr_t end = (bss_size ? m_kip_header.GetBssAddress() + bss_size : m_kip_header.GetRwAddress() + rw_size) + params.code_address; R_TRY(page_table.SetProcessMemoryPermission(start, util::AlignUp(end - start, PageSize), ams::svc::MemoryPermission_ReadWrite)); } diff --git a/libraries/libmesosphere/source/kern_k_memory_manager.cpp b/libraries/libmesosphere/source/kern_k_memory_manager.cpp index 7143f062c..36434ee36 100644 --- a/libraries/libmesosphere/source/kern_k_memory_manager.cpp +++ b/libraries/libmesosphere/source/kern_k_memory_manager.cpp @@ -100,19 +100,48 @@ namespace ams::kern { } /* Free each region to its corresponding heap. */ + size_t reserved_sizes[MaxManagerCount] = {}; + const uintptr_t ini_start = GetInteger(GetInitialProcessBinaryAddress()); + const uintptr_t ini_end = ini_start + InitialProcessBinarySizeMax; + const uintptr_t ini_last = ini_end - 1; for (const auto &it : KMemoryLayout::GetVirtualMemoryRegionTree()) { if (it.IsDerivedFrom(KMemoryRegionType_VirtualDramUserPool)) { - /* Check the region. */ - MESOSPHERE_ABORT_UNLESS(it.GetEndAddress() != 0); + /* Get the manager for the region. */ + auto &manager = m_managers[it.GetAttributes()]; - /* Free the memory to the heap. */ - m_managers[it.GetAttributes()].Free(it.GetAddress(), it.GetSize() / PageSize); + if (it.GetAddress() <= ini_start && ini_last <= it.GetLastAddress()) { + /* Free memory before the ini to the heap. */ + if (it.GetAddress() != ini_start) { + manager.Free(it.GetAddress(), (ini_start - it.GetAddress()) / PageSize); + } + + /* Open/reserve the ini memory. */ + manager.OpenFirst(ini_start, InitialProcessBinarySizeMax / PageSize); + reserved_sizes[it.GetAttributes()] += InitialProcessBinarySizeMax; + + /* Free memory after the ini to the heap. */ + if (ini_last != it.GetLastAddress()) { + MESOSPHERE_ABORT_UNLESS(it.GetEndAddress() != 0); + manager.Free(ini_end, it.GetEndAddress() - ini_end); + } + } else { + /* Ensure there's no partial overlap with the ini image. */ + if (it.GetAddress() <= ini_last) { + MESOSPHERE_ABORT_UNLESS(it.GetLastAddress() < ini_start); + } else { + /* Otherwise, check the region for general validity. */ + MESOSPHERE_ABORT_UNLESS(it.GetEndAddress() != 0); + } + + /* Free the memory to the heap. */ + manager.Free(it.GetAddress(), it.GetSize() / PageSize); + } } } /* Update the used size for all managers. */ for (size_t i = 0; i < m_num_managers; ++i) { - m_managers[i].UpdateUsedHeapSize(); + m_managers[i].SetInitialUsedHeapSize(reserved_sizes[i]); } } diff --git a/mesosphere/kernel/source/arch/arm64/init/kern_init_core.cpp b/mesosphere/kernel/source/arch/arm64/init/kern_init_core.cpp index a6c213344..490999d0e 100644 --- a/mesosphere/kernel/source/arch/arm64/init/kern_init_core.cpp +++ b/mesosphere/kernel/source/arch/arm64/init/kern_init_core.cpp @@ -362,10 +362,9 @@ namespace ams::kern::init { /* NOTE: Nintendo does this only on 10.0.0+ */ init_pt.PhysicallyRandomize(slab_region_start, slab_region_size, false); - /* Determine size available for kernel page table heaps, requiring > 8 MB. */ + /* Determine size available for kernel page table heaps. */ const KPhysicalAddress resource_end_phys_addr = slab_start_phys_addr + resource_region_size; const size_t page_table_heap_size = GetInteger(resource_end_phys_addr) - GetInteger(slab_end_phys_addr); - MESOSPHERE_INIT_ABORT_UNLESS(page_table_heap_size / 4_MB > 2); /* Insert a physical region for the kernel page table heap region */ MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(GetInteger(slab_end_phys_addr), page_table_heap_size, KMemoryRegionType_DramKernelPtHeap)); diff --git a/mesosphere/kernel/source/arch/arm64/init/start.s b/mesosphere/kernel/source/arch/arm64/init/start.s index 398440c7d..6300c50bd 100644 --- a/mesosphere/kernel/source/arch/arm64/init/start.s +++ b/mesosphere/kernel/source/arch/arm64/init/start.s @@ -53,7 +53,7 @@ __metadata_kernel_layout: .word __bss_start__ - _start /* rw_end_offset */ .word __bss_start__ - _start /* bss_offset */ .word __bss_end__ - _start /* bss_end_offset */ - .word __end__ - _start /* ini_load_offset */ + .word __end__ - _start /* resource_offset */ .word _DYNAMIC - _start /* dynamic_offset */ .word __init_array_start - _start /* init_array_offset */ .word __init_array_end - _start /* init_array_end_offset */ diff --git a/mesosphere/kernel_ldr/source/kern_init_loader.cpp b/mesosphere/kernel_ldr/source/kern_init_loader.cpp index 6be53d77b..1eae25f16 100644 --- a/mesosphere/kernel_ldr/source/kern_init_loader.cpp +++ b/mesosphere/kernel_ldr/source/kern_init_loader.cpp @@ -172,7 +172,7 @@ namespace ams::kern::init::loader { MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(rw_offset, PageSize)); MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(bss_end_offset, PageSize)); const uintptr_t bss_offset = layout->bss_offset; - const uintptr_t ini_load_offset = layout->ini_load_offset; + const uintptr_t resource_offset = layout->resource_offset; const uintptr_t dynamic_offset = layout->dynamic_offset; const uintptr_t init_array_offset = layout->init_array_offset; const uintptr_t init_array_end_offset = layout->init_array_end_offset; @@ -181,8 +181,8 @@ namespace ams::kern::init::loader { const size_t resource_region_size = KMemoryLayout::GetResourceRegionSizeForInit(); /* Setup the INI1 header in memory for the kernel. */ - const uintptr_t ini_end_address = base_address + ini_load_offset + resource_region_size; - const uintptr_t ini_load_address = ini_end_address - InitialProcessBinarySizeMax; + const uintptr_t resource_end_address = base_address + resource_offset + resource_region_size; + const uintptr_t ini_load_address = GetInteger(KSystemControl::Init::GetInitialProcessBinaryPhysicalAddress()); if (ini_base_address != ini_load_address) { /* The INI is not at the correct address, so we need to relocate it. */ const InitialProcessBinaryHeader *ini_header = reinterpret_cast(ini_base_address); @@ -195,14 +195,14 @@ namespace ams::kern::init::loader { } } - /* We want to start allocating page tables at ini_end_address. */ - g_initial_page_allocator.Initialize(ini_end_address); + /* We want to start allocating page tables at the end of the resource region. */ + g_initial_page_allocator.Initialize(resource_end_address); /* Make a new page table for TTBR1_EL1. */ KInitialPageTable init_pt(KernelBaseRangeStart, KernelBaseRangeLast, g_initial_page_allocator); /* Setup initial identity mapping. TTBR1 table passed by reference. */ - SetupInitialIdentityMapping(init_pt, base_address, bss_end_offset, ini_end_address, InitialPageTableRegionSizeMax, g_initial_page_allocator); + SetupInitialIdentityMapping(init_pt, base_address, bss_end_offset, resource_end_address, InitialPageTableRegionSizeMax, g_initial_page_allocator); /* Generate a random slide for the kernel's base address. */ const KVirtualAddress virtual_base_address = GetRandomKernelBaseAddress(init_pt, base_address, bss_end_offset); From 8e4be9aef92058f75f91b8ad60dfd0f72fce07e7 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 12:42:35 -0700 Subject: [PATCH 142/280] kern: simplify global rng initialization --- .../nintendo/nx/kern_k_system_control.cpp | 38 ++++++++++++------- libraries/libmesosphere/source/kern_main.cpp | 6 +++ 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp index b689d19ea..c10312bde 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp @@ -42,8 +42,8 @@ namespace ams::kern::board::nintendo::nx { /* Nintendo uses std::mt19937_t for randomness. */ /* To save space (and because mt19337_t isn't secure anyway), */ /* We will use TinyMT. */ - bool g_initialized_random_generator; - util::TinyMT g_random_generator; + constinit bool g_initialized_random_generator; + constinit util::TinyMT g_random_generator; constinit KSpinLock g_random_lock; ALWAYS_INLINE size_t GetRealMemorySizeForInit() { @@ -90,13 +90,10 @@ namespace ams::kern::board::nintendo::nx { return value; } - void EnsureRandomGeneratorInitialized() { - if (AMS_UNLIKELY(!g_initialized_random_generator)) { - u64 seed; - smc::GenerateRandomBytes(&seed, sizeof(seed)); - g_random_generator.Initialize(reinterpret_cast(&seed), sizeof(seed) / sizeof(u32)); - g_initialized_random_generator = true; - } + ALWAYS_INLINE u64 GenerateRandomU64FromSmc() { + u64 value; + smc::GenerateRandomBytes(std::addressof(value), sizeof(value)); + return value; } ALWAYS_INLINE u64 GenerateRandomU64FromGenerator() { @@ -439,6 +436,14 @@ namespace ams::kern::board::nintendo::nx { /* System Initialization. */ void KSystemControl::InitializePhase1() { + /* Initialize our random generator. */ + { + u64 seed; + smc::GenerateRandomBytes(std::addressof(seed), sizeof(seed)); + g_random_generator.Initialize(reinterpret_cast(std::addressof(seed)), sizeof(seed) / sizeof(u32)); + g_initialized_random_generator = true; + } + /* Set IsDebugMode. */ { KTargetSystem::SetIsDebugMode(GetConfigBool(smc::ConfigItem::IsDebugMode)); @@ -544,18 +549,23 @@ namespace ams::kern::board::nintendo::nx { KScopedInterruptDisable intr_disable; KScopedSpinLock lk(g_random_lock); - EnsureRandomGeneratorInitialized(); - return GenerateUniformRange(min, max, GenerateRandomU64FromGenerator); + if (AMS_LIKELY(g_initialized_random_generator)) { + return GenerateUniformRange(min, max, GenerateRandomU64FromGenerator); + } else { + return GenerateUniformRange(min, max, GenerateRandomU64FromSmc); + } } u64 KSystemControl::GenerateRandomU64() { KScopedInterruptDisable intr_disable; KScopedSpinLock lk(g_random_lock); - EnsureRandomGeneratorInitialized(); - - return GenerateRandomU64FromGenerator(); + if (AMS_LIKELY(g_initialized_random_generator)) { + return GenerateRandomU64FromGenerator(); + } else { + return GenerateRandomU64FromSmc(); + } } void KSystemControl::SleepSystem() { diff --git a/libraries/libmesosphere/source/kern_main.cpp b/libraries/libmesosphere/source/kern_main.cpp index 5ee230a41..dc351c708 100644 --- a/libraries/libmesosphere/source/kern_main.cpp +++ b/libraries/libmesosphere/source/kern_main.cpp @@ -49,6 +49,9 @@ namespace ams::kern { /* Initialize the carveout and the system resource limit. */ KSystemControl::InitializePhase1(); + /* Synchronize all cores before proceeding, to ensure access to the global rng is consistent. */ + cpu::SynchronizeAllCores(); + /* Initialize the memory manager and the KPageBuffer slabheap. */ { const auto &management_region = KMemoryLayout::GetPoolManagementRegion(); @@ -74,6 +77,9 @@ namespace ams::kern { Kernel::InitializeResourceManagers(pt_heap_region.GetAddress(), pt_heap_region.GetSize()); } + } else { + /* Synchronize all cores before proceeding, to ensure access to the global rng is consistent. */ + cpu::SynchronizeAllCores(); } /* Initialize the supervisor page table for each core. */ From 6a368d3d1a1319e9824ff762278cfd58a34247f2 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 12:43:20 -0700 Subject: [PATCH 143/280] kern: reallocate pool distributions for 8GB units --- .../source/board/nintendo/nx/kern_k_system_control.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp index c10312bde..8768e0fa1 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp @@ -370,7 +370,7 @@ namespace ams::kern::board::nintendo::nx { case smc::MemoryArrangement_6GBForAppletDev: return 3285_MB; case smc::MemoryArrangement_8GB: - return 4916_MB; + return 6964_MB; } }(); @@ -394,7 +394,7 @@ namespace ams::kern::board::nintendo::nx { case smc::MemoryArrangement_6GBForAppletDev: return 2193_MB; case smc::MemoryArrangement_8GB: - return 2193_MB; + return 562_MB; } }(); From 15956fcf9a724974938a59aab928626d2a1eafcc Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 12:48:20 -0700 Subject: [PATCH 144/280] kern: update for new slab resource counts/extents --- .../include/mesosphere/kern_k_memory_layout.hpp | 5 ++--- .../libmesosphere/source/init/kern_init_slab_setup.cpp | 10 ++++++---- .../libmesosphere/source/kern_k_memory_layout.cpp | 8 +------- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp index 574c48749..dce0af090 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp @@ -44,9 +44,8 @@ namespace ams::kern { constexpr size_t KernelInitialPageHeapSize = 128_KB; constexpr size_t KernelSlabHeapDataSize = 5_MB; - constexpr size_t KernelSlabHeapGapsSize = 2_MB - 64_KB; - constexpr size_t KernelSlabHeapGapsSizeDeprecated = 2_MB; - constexpr size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSize; + constexpr size_t KernelSlabHeapGapsSizeMax = 2_MB - 64_KB; + constexpr size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSizeMax; /* NOTE: This is calculated from KThread slab counts, assuming KThread size <= 0x860. */ constexpr size_t KernelSlabHeapAdditionalSize = 0x68000; diff --git a/libraries/libmesosphere/source/init/kern_init_slab_setup.cpp b/libraries/libmesosphere/source/init/kern_init_slab_setup.cpp index 5fab99370..05fc227a3 100644 --- a/libraries/libmesosphere/source/init/kern_init_slab_setup.cpp +++ b/libraries/libmesosphere/source/init/kern_init_slab_setup.cpp @@ -57,14 +57,14 @@ namespace ams::kern::init { /* Constexpr counts. */ constexpr size_t SlabCountKProcess = 80; constexpr size_t SlabCountKThread = 800; - constexpr size_t SlabCountKEvent = 700; + constexpr size_t SlabCountKEvent = 900; constexpr size_t SlabCountKInterruptEvent = 100; constexpr size_t SlabCountKPort = 256 + 0x20 /* Extra 0x20 ports over Nintendo for homebrew. */; constexpr size_t SlabCountKSharedMemory = 80; constexpr size_t SlabCountKTransferMemory = 200; constexpr size_t SlabCountKCodeMemory = 10; constexpr size_t SlabCountKDeviceAddressSpace = 300; - constexpr size_t SlabCountKSession = 933; + constexpr size_t SlabCountKSession = 1133; constexpr size_t SlabCountKLightSession = 100; constexpr size_t SlabCountKObjectName = 7; constexpr size_t SlabCountKResourceLimit = 5; @@ -82,7 +82,7 @@ namespace ams::kern::init { } /* Global to hold our resource counts. */ - KSlabResourceCounts g_slab_resource_counts = { + constinit KSlabResourceCounts g_slab_resource_counts = { .num_KProcess = SlabCountKProcess, .num_KThread = SlabCountKThread, .num_KEvent = SlabCountKEvent, @@ -131,7 +131,9 @@ namespace ams::kern::init { } size_t CalculateSlabHeapGapSize() { - return (kern::GetTargetFirmware() >= TargetFirmware_10_0_0) ? KernelSlabHeapGapsSize : KernelSlabHeapGapsSizeDeprecated; + constexpr size_t KernelSlabHeapGapSize = 2_MB - 296_KB; + static_assert(KernelSlabHeapGapSize <= KernelSlabHeapGapsSizeMax); + return KernelSlabHeapGapSize; } size_t CalculateTotalSlabHeapSize() { diff --git a/libraries/libmesosphere/source/kern_k_memory_layout.cpp b/libraries/libmesosphere/source/kern_k_memory_layout.cpp index cdc56fb63..8aecb49ca 100644 --- a/libraries/libmesosphere/source/kern_k_memory_layout.cpp +++ b/libraries/libmesosphere/source/kern_k_memory_layout.cpp @@ -170,14 +170,8 @@ namespace ams::kern { size_t KMemoryLayout::GetResourceRegionSizeForInit() { /* Calculate resource region size based on whether we allow extra threads. */ const bool use_extra_resources = KSystemControl::Init::ShouldIncreaseThreadResourceLimit(); - size_t resource_region_size = KernelResourceSize + (use_extra_resources ? KernelSlabHeapAdditionalSize : 0); - /* 10.0.0 reduced the slab heap gaps by 64K. */ - if (kern::GetTargetFirmware() < ams::TargetFirmware_10_0_0) { - resource_region_size += (KernelSlabHeapGapsSizeDeprecated - KernelSlabHeapGapsSize); - } - - return resource_region_size; + return KernelResourceSize + (use_extra_resources ? KernelSlabHeapAdditionalSize : 0); } } From 4407237f5bc6ce241e1f4006b0511443b98fe67b Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 13:38:51 -0700 Subject: [PATCH 145/280] kern: KAutoObject destruction is now scheduled for next dpc-time --- .../include/mesosphere/kern_k_auto_object.hpp | 21 +++++-- .../include/mesosphere/kern_k_thread.hpp | 55 +++++++++++++++++-- .../source/kern_k_dpc_manager.cpp | 19 +++++-- .../source/kern_k_interrupt_task_manager.cpp | 3 + .../libmesosphere/source/kern_k_thread.cpp | 6 +- .../source/kern_k_worker_task_manager.cpp | 3 + .../libmesosphere/source/svc/kern_svc_ipc.cpp | 5 ++ 7 files changed, 94 insertions(+), 18 deletions(-) diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_auto_object.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_auto_object.hpp index f798ca806..ad08ee659 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_auto_object.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_auto_object.hpp @@ -69,11 +69,12 @@ namespace ams::kern { private: MESOSPHERE_AUTOOBJECT_TRAITS(KAutoObject, KAutoObject); private: + KAutoObject *m_next_closed_object; std::atomic m_ref_count; public: static KAutoObject *Create(KAutoObject *ptr); public: - constexpr ALWAYS_INLINE explicit KAutoObject() : m_ref_count(0) { MESOSPHERE_ASSERT_THIS(); } + constexpr ALWAYS_INLINE explicit KAutoObject() : m_next_closed_object(nullptr), m_ref_count(0) { MESOSPHERE_ASSERT_THIS(); } virtual ~KAutoObject() { MESOSPHERE_ASSERT_THIS(); } /* Destroy is responsible for destroying the auto object's resources when ref_count hits zero. */ @@ -120,7 +121,7 @@ namespace ams::kern { } } - ALWAYS_INLINE bool Open() { + NOINLINE bool Open() { MESOSPHERE_ASSERT_THIS(); /* Atomically increment the reference count, only if it's positive. */ @@ -136,7 +137,7 @@ namespace ams::kern { return true; } - ALWAYS_INLINE void Close() { + NOINLINE void Close() { MESOSPHERE_ASSERT_THIS(); /* Atomically decrement the reference count, not allowing it to become negative. */ @@ -145,11 +146,19 @@ namespace ams::kern { MESOSPHERE_ABORT_UNLESS(cur_ref_count > 0); } while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count - 1, std::memory_order_relaxed)); - /* If ref count hits zero, destroy the object. */ + /* If ref count hits zero, schedule the object for destruction. */ if (cur_ref_count - 1 == 0) { - this->Destroy(); + this->ScheduleDestruction(); } } + private: + /* NOTE: This has to be defined *after* KThread is defined. */ + /* Nintendo seems to handle this by defining Open/Close() in a cpp, but we'd like them to remain in headers. */ + /* Implementation for this will be inside kern_k_thread.hpp, so it can be ALWAYS_INLINE. */ + void ScheduleDestruction(); + public: + /* Getter, for KThread. */ + ALWAYS_INLINE KAutoObject *GetNextClosedObject() { return m_next_closed_object; } }; class KAutoObjectWithListContainer; @@ -198,7 +207,7 @@ namespace ams::kern { } } - ~KScopedAutoObject() { + ALWAYS_INLINE ~KScopedAutoObject() { if (m_obj != nullptr) { m_obj->Close(); } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp index ff8dfadea..b8478f5b5 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp @@ -77,8 +77,9 @@ namespace ams::kern { }; enum DpcFlag : u32 { - DpcFlag_Terminating = (1 << 0), - DpcFlag_Terminated = (1 << 1), + DpcFlag_Terminating = (1 << 0), + DpcFlag_Terminated = (1 << 1), + DpcFlag_PerformDestruction = (1 << 2), }; struct StackParameters { @@ -203,6 +204,7 @@ namespace ams::kern { WaiterList m_pinned_waiter_list{}; KThread *m_lock_owner{}; uintptr_t m_debug_params[3]{}; + KAutoObject *m_closed_object{}; u32 m_address_key_value{}; u32 m_suspend_request_flags{}; u32 m_suspend_allowed_flags{}; @@ -324,15 +326,15 @@ namespace ams::kern { } ALWAYS_INLINE void RegisterDpc(DpcFlag flag) { - this->GetStackParameters().dpc_flags |= flag; + this->GetStackParameters().dpc_flags.fetch_or(flag); } ALWAYS_INLINE void ClearDpc(DpcFlag flag) { - this->GetStackParameters().dpc_flags &= ~flag; + this->GetStackParameters().dpc_flags.fetch_and(~flag);; } ALWAYS_INLINE u8 GetDpc() const { - return this->GetStackParameters().dpc_flags; + return this->GetStackParameters().dpc_flags.load(); } ALWAYS_INLINE bool HasDpc() const { @@ -491,6 +493,39 @@ namespace ams::kern { void SetInterruptFlag() const { static_cast(m_tls_heap_address)->interrupt_flag = 1; } void ClearInterruptFlag() const { static_cast(m_tls_heap_address)->interrupt_flag = 0; } + ALWAYS_INLINE KAutoObject *GetClosedObject() { return m_closed_object; } + + ALWAYS_INLINE void SetClosedObject(KAutoObject *object) { + MESOSPHERE_ASSERT(object != nullptr); + + /* Set the object to destroy. */ + m_closed_object = object; + + /* Schedule destruction DPC. */ + if ((this->GetStackParameters().dpc_flags.load(std::memory_order_relaxed) & DpcFlag_PerformDestruction) == 0) { + this->RegisterDpc(DpcFlag_PerformDestruction); + } + } + + ALWAYS_INLINE void DestroyClosedObjects() { + /* Destroy all objects that have been closed. */ + if (KAutoObject *cur = m_closed_object; cur != nullptr) { + do { + /* Set our closed object as the next to close. */ + m_closed_object = cur->GetNextClosedObject(); + + /* Destroy the current object. */ + cur->Destroy(); + + /* Advance. */ + cur = m_closed_object; + } while (cur != nullptr); + + /* Clear the pending DPC. */ + this->ClearDpc(DpcFlag_PerformDestruction); + } + } + constexpr void SetDebugAttached() { m_debug_attached = true; } constexpr bool IsAttachedToDebugger() const { return m_debug_attached; } @@ -603,4 +638,14 @@ namespace ams::kern { return GetCurrentThread().GetCurrentCore(); } + ALWAYS_INLINE void KAutoObject::ScheduleDestruction() { + MESOSPHERE_ASSERT_THIS(); + + /* Set our object to destroy. */ + m_next_closed_object = GetCurrentThread().GetClosedObject(); + + /* Set ourselves as the thread's next object to destroy. */ + GetCurrentThread().SetClosedObject(this); + } + } diff --git a/libraries/libmesosphere/source/kern_k_dpc_manager.cpp b/libraries/libmesosphere/source/kern_k_dpc_manager.cpp index 62ae50a6e..a55c30a2b 100644 --- a/libraries/libmesosphere/source/kern_k_dpc_manager.cpp +++ b/libraries/libmesosphere/source/kern_k_dpc_manager.cpp @@ -174,13 +174,20 @@ namespace ams::kern { MESOSPHERE_ASSERT(!KInterruptManager::AreInterruptsEnabled()); MESOSPHERE_ASSERT(!KScheduler::IsSchedulerLockedByCurrentThread()); - /* The only deferred procedure supported by Horizon is thread termination. */ - /* Check if we need to terminate the current thread. */ - KThread *cur_thread = GetCurrentThreadPointer(); - if (cur_thread->IsTerminationRequested()) { - KScopedInterruptEnable ei; - cur_thread->Exit(); + /* Get reference to the current thread. */ + KThread &cur_thread = GetCurrentThread(); + + /* Enable interrupts, temporarily. */ + KScopedInterruptEnable ei; + + /* If the thread is scheduled for termination, exit the thread. */ + if (cur_thread.IsTerminationRequested()) { + cur_thread.Exit(); + __builtin_unreachable(); } + + /* We may also need to destroy any closed objects. */ + cur_thread.DestroyClosedObjects(); } void KDpcManager::Sync() { diff --git a/libraries/libmesosphere/source/kern_k_interrupt_task_manager.cpp b/libraries/libmesosphere/source/kern_k_interrupt_task_manager.cpp index 4c318ca88..1fe3a6492 100644 --- a/libraries/libmesosphere/source/kern_k_interrupt_task_manager.cpp +++ b/libraries/libmesosphere/source/kern_k_interrupt_task_manager.cpp @@ -83,6 +83,9 @@ namespace ams::kern { /* Do the task. */ task->DoTask(); + + /* Destroy any objects we may need to close. */ + m_thread->DestroyClosedObjects(); } } diff --git a/libraries/libmesosphere/source/kern_k_thread.cpp b/libraries/libmesosphere/source/kern_k_thread.cpp index 972e4220d..fbc565eaa 100644 --- a/libraries/libmesosphere/source/kern_k_thread.cpp +++ b/libraries/libmesosphere/source/kern_k_thread.cpp @@ -155,8 +155,9 @@ namespace ams::kern { m_lock_owner = nullptr; m_num_core_migration_disables = 0; - /* We have no waiters, but we do have an entrypoint. */ + /* We have no waiters, and no closed objects. */ m_num_kernel_waiters = 0; + m_closed_object = nullptr; /* Set our current core id. */ m_current_core_id = phys_core; @@ -1157,6 +1158,9 @@ namespace ams::kern { m_parent->DecrementRunningThreadCount(); } + /* Destroy any dependent objects. */ + this->DestroyClosedObjects(); + /* Perform termination. */ { KScopedSchedulerLock sl; diff --git a/libraries/libmesosphere/source/kern_k_worker_task_manager.cpp b/libraries/libmesosphere/source/kern_k_worker_task_manager.cpp index f2ddc9953..f525acd28 100644 --- a/libraries/libmesosphere/source/kern_k_worker_task_manager.cpp +++ b/libraries/libmesosphere/source/kern_k_worker_task_manager.cpp @@ -68,6 +68,9 @@ namespace ams::kern { /* Do the task. */ task->DoWorkerTask(); + + /* Destroy any objects we may need to close. */ + m_thread->DestroyClosedObjects(); } } diff --git a/libraries/libmesosphere/source/svc/kern_svc_ipc.cpp b/libraries/libmesosphere/source/svc/kern_svc_ipc.cpp index 7454237bc..673dfe702 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_ipc.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_ipc.cpp @@ -74,12 +74,17 @@ namespace ams::kern::svc { /* Wait for a message. */ while (true) { + /* Close any pending objects before we wait. */ + GetCurrentThread().DestroyClosedObjects(); + + /* Wait for an object. */ s32 index; Result result = KSynchronizationObject::Wait(std::addressof(index), objs, num_objects, timeout); if (svc::ResultTimedOut::Includes(result)) { return result; } + /* Receive the request. */ if (R_SUCCEEDED(result)) { KServerSession *session = objs[index]->DynamicCast(); if (session != nullptr) { From b4498734e4061441c7947912262ee1266d7188f8 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 14:07:10 -0700 Subject: [PATCH 146/280] kern: optimize KHandleTable to use indices instead of pointers --- .../mesosphere/kern_k_handle_table.hpp | 139 ++++++++---------- .../source/kern_k_handle_table.cpp | 50 +++---- 2 files changed, 83 insertions(+), 106 deletions(-) diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_handle_table.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_handle_table.hpp index d62b2c85e..25a2d7901 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_handle_table.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_handle_table.hpp @@ -53,46 +53,29 @@ namespace ams::kern { return pack.Get(); } - class Entry { - private: - union { - struct { - u16 linear_id; - u16 type; - } info; - Entry *next_free_entry; - } m_meta; - KAutoObject *m_object; - public: - constexpr Entry() : m_meta(), m_object(nullptr) { /* ... */ } + union EntryInfo { + struct { + u16 linear_id; + u16 type; + } info; + s32 next_free_index; - constexpr ALWAYS_INLINE void SetFree(Entry *next) { - m_object = nullptr; - m_meta.next_free_entry = next; - } - - constexpr ALWAYS_INLINE void SetUsed(KAutoObject *obj, u16 linear_id, u16 type) { - m_object = obj; - m_meta.info = { linear_id, type }; - } - - constexpr ALWAYS_INLINE KAutoObject *GetObject() const { return m_object; } - constexpr ALWAYS_INLINE Entry *GetNextFreeEntry() const { return m_meta.next_free_entry; } - constexpr ALWAYS_INLINE u16 GetLinearId() const { return m_meta.info.linear_id; } - constexpr ALWAYS_INLINE u16 GetType() const { return m_meta.info.type; } + constexpr ALWAYS_INLINE u16 GetLinearId() const { return info.linear_id; } + constexpr ALWAYS_INLINE u16 GetType() const { return info.type; } + constexpr ALWAYS_INLINE s32 GetNextFreeIndex() const { return next_free_index; } }; private: - mutable KSpinLock m_lock; - Entry *m_table; - Entry *m_free_head; - Entry m_entries[MaxTableSize]; + EntryInfo m_entry_infos[MaxTableSize]; + KAutoObject *m_objects[MaxTableSize]; + s32 m_free_head_index; u16 m_table_size; u16 m_max_count; u16 m_next_linear_id; u16 m_count; + mutable KSpinLock m_lock; public: constexpr KHandleTable() : - m_lock(), m_table(nullptr), m_free_head(nullptr), m_entries(), m_table_size(0), m_max_count(0), m_next_linear_id(MinLinearId), m_count(0) + m_entry_infos(), m_objects(), m_free_head_index(-1), m_table_size(0), m_max_count(0), m_next_linear_id(MinLinearId), m_count(0), m_lock() { MESOSPHERE_ASSERT_THIS(); } constexpr NOINLINE Result Initialize(s32 size) { @@ -101,19 +84,18 @@ namespace ams::kern { R_UNLESS(size <= static_cast(MaxTableSize), svc::ResultOutOfMemory()); /* Initialize all fields. */ - m_table = m_entries; - m_table_size = (size <= 0) ? MaxTableSize : size; - m_next_linear_id = MinLinearId; - m_count = 0; - m_max_count = 0; + m_max_count = 0; + m_table_size = (size <= 0) ? MaxTableSize : size; + m_next_linear_id = MinLinearId; + m_count = 0; + m_free_head_index = -1; /* Free all entries. */ - for (size_t i = 0; i < static_cast(m_table_size - 1); i++) { - m_entries[i].SetFree(std::addressof(m_entries[i + 1])); + for (s32 i = 0; i < static_cast(m_table_size); ++i) { + m_objects[i] = nullptr; + m_entry_infos[i].next_free_index = i - 1; + m_free_head_index = i; } - m_entries[m_table_size - 1].SetFree(nullptr); - - m_free_head = std::addressof(m_entries[0]); return ResultSuccess(); } @@ -134,7 +116,7 @@ namespace ams::kern { if constexpr (std::is_same::value) { return this->GetObjectImpl(handle); } else { - if (auto *obj = this->GetObjectImpl(handle); obj != nullptr) { + if (auto *obj = this->GetObjectImpl(handle); AMS_LIKELY(obj != nullptr)) { return obj->DynamicCast(); } else { return nullptr; @@ -256,27 +238,29 @@ namespace ams::kern { NOINLINE Result Add(ams::svc::Handle *out_handle, KAutoObject *obj, u16 type); NOINLINE void Register(ams::svc::Handle handle, KAutoObject *obj, u16 type); - constexpr ALWAYS_INLINE Entry *AllocateEntry() { + constexpr ALWAYS_INLINE s32 AllocateEntry() { MESOSPHERE_ASSERT_THIS(); MESOSPHERE_ASSERT(m_count < m_table_size); - Entry *entry = m_free_head; - m_free_head = entry->GetNextFreeEntry(); + const auto index = m_free_head_index; - m_count++; - m_max_count = std::max(m_max_count, m_count); + m_free_head_index = m_entry_infos[index].GetNextFreeIndex(); - return entry; + m_max_count = std::max(m_max_count, ++m_count); + + return index; } - constexpr ALWAYS_INLINE void FreeEntry(Entry *entry) { + constexpr ALWAYS_INLINE void FreeEntry(s32 index) { MESOSPHERE_ASSERT_THIS(); MESOSPHERE_ASSERT(m_count > 0); - entry->SetFree(m_free_head); - m_free_head = entry; + m_objects[index] = nullptr; + m_entry_infos[index].next_free_index = m_free_head_index; - m_count--; + m_free_head_index = index; + + --m_count; } constexpr ALWAYS_INLINE u16 AllocateLinearId() { @@ -287,13 +271,7 @@ namespace ams::kern { return id; } - constexpr ALWAYS_INLINE size_t GetEntryIndex(Entry *entry) { - const size_t index = entry - m_table; - MESOSPHERE_ASSERT(index < m_table_size); - return index; - } - - constexpr ALWAYS_INLINE Entry *FindEntry(ams::svc::Handle handle) const { + constexpr ALWAYS_INLINE bool IsValidHandle(ams::svc::Handle handle) const { MESOSPHERE_ASSERT_THIS(); /* Unpack the handle. */ @@ -306,38 +284,38 @@ namespace ams::kern { MESOSPHERE_UNUSED(reserved); /* Validate our indexing information. */ - if (raw_value == 0) { - return nullptr; + if (AMS_UNLIKELY(raw_value == 0)) { + return false; } - if (linear_id == 0) { - return nullptr; + if (AMS_UNLIKELY(linear_id == 0)) { + return false; } - if (index >= m_table_size) { - return nullptr; + if (AMS_UNLIKELY(index >= m_table_size)) { + return false; } - /* Get the entry, and ensure our serial id is correct. */ - Entry *entry = std::addressof(m_table[index]); - if (entry->GetObject() == nullptr) { - return nullptr; + /* Check that there's an object, and our serial id is correct. */ + if (AMS_UNLIKELY(m_objects[index] == nullptr)) { + return false; } - if (entry->GetLinearId() != linear_id) { - return nullptr; + if (AMS_UNLIKELY(m_entry_infos[index].GetLinearId() != linear_id)) { + return false; } - return entry; + return true; } constexpr ALWAYS_INLINE KAutoObject *GetObjectImpl(ams::svc::Handle handle) const { MESOSPHERE_ASSERT_THIS(); /* Handles must not have reserved bits set. */ - if (GetHandleBitPack(handle).Get() != 0) { + const auto handle_pack = GetHandleBitPack(handle); + if (AMS_UNLIKELY(handle_pack.Get() != 0)) { return nullptr; } - if (Entry *entry = this->FindEntry(handle); entry != nullptr) { - return entry->GetObject(); + if (AMS_LIKELY(this->IsValidHandle(handle))) { + return m_objects[handle_pack.Get()]; } else { return nullptr; } @@ -347,18 +325,17 @@ namespace ams::kern { MESOSPHERE_ASSERT_THIS(); /* Index must be in bounds. */ - if (index >= m_table_size || m_table == nullptr) { + if (AMS_UNLIKELY(index >= m_table_size)) { return nullptr; } /* Ensure entry has an object. */ - Entry *entry = std::addressof(m_table[index]); - if (entry->GetObject() == nullptr) { + if (KAutoObject *obj = m_objects[index]; obj != nullptr) { + *out_handle = EncodeHandle(index, m_entry_infos[index].GetLinearId()); + return obj; + } else { return nullptr; } - - *out_handle = EncodeHandle(index, entry->GetLinearId()); - return entry->GetObject(); } }; diff --git a/libraries/libmesosphere/source/kern_k_handle_table.cpp b/libraries/libmesosphere/source/kern_k_handle_table.cpp index d40de9f69..8a78013d2 100644 --- a/libraries/libmesosphere/source/kern_k_handle_table.cpp +++ b/libraries/libmesosphere/source/kern_k_handle_table.cpp @@ -21,23 +21,18 @@ namespace ams::kern { MESOSPHERE_ASSERT_THIS(); /* Get the table and clear our record of it. */ - Entry *saved_table = nullptr; u16 saved_table_size = 0; { KScopedDisableDispatch dd; KScopedSpinLock lk(m_lock); - std::swap(m_table, saved_table); std::swap(m_table_size, saved_table_size); } /* Close and free all entries. */ for (size_t i = 0; i < saved_table_size; i++) { - Entry *entry = std::addressof(saved_table[i]); - - if (KAutoObject *obj = entry->GetObject(); obj != nullptr) { + if (KAutoObject *obj = m_objects[i]; obj != nullptr) { obj->Close(); - this->FreeEntry(entry); } } @@ -48,12 +43,13 @@ namespace ams::kern { MESOSPHERE_ASSERT_THIS(); /* Don't allow removal of a pseudo-handle. */ - if (ams::svc::IsPseudoHandle(handle)) { + if (AMS_UNLIKELY(ams::svc::IsPseudoHandle(handle))) { return false; } /* Handles must not have reserved bits set. */ - if (GetHandleBitPack(handle).Get() != 0) { + const auto handle_pack = GetHandleBitPack(handle); + if (AMS_UNLIKELY(handle_pack.Get() != 0)) { return false; } @@ -63,9 +59,11 @@ namespace ams::kern { KScopedDisableDispatch dd; KScopedSpinLock lk(m_lock); - if (Entry *entry = this->FindEntry(handle); entry != nullptr) { - obj = entry->GetObject(); - this->FreeEntry(entry); + if (AMS_LIKELY(this->IsValidHandle(handle))) { + const auto index = handle_pack.Get(); + + obj = m_objects[index]; + this->FreeEntry(index); } else { return false; } @@ -87,10 +85,14 @@ namespace ams::kern { /* Allocate entry, set output handle. */ { const auto linear_id = this->AllocateLinearId(); - Entry *entry = this->AllocateEntry(); - entry->SetUsed(obj, linear_id, type); + const auto index = this->AllocateEntry(); + + m_entry_infos[index].info = { .linear_id = linear_id, .type = type }; + m_objects[index] = obj; + obj->Open(); - *out_handle = EncodeHandle(this->GetEntryIndex(entry), linear_id); + + *out_handle = EncodeHandle(index, linear_id); } return ResultSuccess(); @@ -104,7 +106,7 @@ namespace ams::kern { /* Never exceed our capacity. */ R_UNLESS(m_count < m_table_size, svc::ResultOutOfHandles()); - *out_handle = EncodeHandle(this->GetEntryIndex(this->AllocateEntry()), this->AllocateLinearId()); + *out_handle = EncodeHandle(this->AllocateEntry(), this->AllocateLinearId()); return ResultSuccess(); } @@ -122,13 +124,10 @@ namespace ams::kern { MESOSPHERE_ASSERT(linear_id != 0); MESOSPHERE_UNUSED(linear_id, reserved); - if (index < m_table_size) { - /* Free the entry. */ + if (AMS_LIKELY(index < m_table_size)) { /* NOTE: This code does not check the linear id. */ - Entry *entry = std::addressof(m_table[index]); - MESOSPHERE_ASSERT(entry->GetObject() == nullptr); - - this->FreeEntry(entry); + MESOSPHERE_ASSERT(m_objects[index] == nullptr); + this->FreeEntry(index); } } @@ -146,12 +145,13 @@ namespace ams::kern { MESOSPHERE_ASSERT(linear_id != 0); MESOSPHERE_UNUSED(reserved); - if (index < m_table_size) { + if (AMS_LIKELY(index < m_table_size)) { /* Set the entry. */ - Entry *entry = std::addressof(m_table[index]); - MESOSPHERE_ASSERT(entry->GetObject() == nullptr); + MESOSPHERE_ASSERT(m_objects[index] == nullptr); + + m_entry_infos[index].info = { .linear_id = linear_id, .type = type }; + m_objects[index] = obj; - entry->SetUsed(obj, linear_id, type); obj->Open(); } } From c62a7381f8a99242ef1643355eec9fdc5b5fd585 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 14:35:26 -0700 Subject: [PATCH 147/280] kern: update KLightConditionVariable --- .../kern_k_light_condition_variable.hpp | 39 ++++++++++++++----- .../nintendo/nx/kern_k_sleep_manager.cpp | 2 +- .../source/kern_k_resource_limit.cpp | 6 ++- 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_light_condition_variable.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_light_condition_variable.hpp index 5fdc7ac85..425714815 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_light_condition_variable.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_light_condition_variable.hpp @@ -24,40 +24,59 @@ namespace ams::kern { class KLightConditionVariable { private: - KThreadQueue m_thread_queue; + KThread::WaiterList m_wait_list; public: - constexpr ALWAYS_INLINE KLightConditionVariable() : m_thread_queue() { /* ... */ } + constexpr ALWAYS_INLINE KLightConditionVariable() : m_wait_list() { /* ... */ } private: - void WaitImpl(KLightLock *lock, s64 timeout) { + void WaitImpl(KLightLock *lock, s64 timeout, bool allow_terminating_thread) { KThread *owner = GetCurrentThreadPointer(); KHardwareTimer *timer; /* Sleep the thread. */ { KScopedSchedulerLockAndSleep lk(&timer, owner, timeout); - lock->Unlock(); - if (!m_thread_queue.SleepThread(owner)) { + if (!allow_terminating_thread && owner->IsTerminationRequested()) { lk.CancelSleep(); return; } + + lock->Unlock(); + + + /* Set the thread as waiting. */ + GetCurrentThread().SetState(KThread::ThreadState_Waiting); + + /* Add the thread to the queue. */ + m_wait_list.push_back(GetCurrentThread()); + } + + /* Remove the thread from the wait list. */ + { + KScopedSchedulerLock sl; + + m_wait_list.erase(m_wait_list.iterator_to(GetCurrentThread())); } /* Cancel the task that the sleep setup. */ if (timer != nullptr) { timer->CancelTask(owner); } + + /* Re-acquire the lock. */ + lock->Lock(); } public: - void Wait(KLightLock *lock, s64 timeout = -1ll) { - this->WaitImpl(lock, timeout); - lock->Lock(); + void Wait(KLightLock *lock, s64 timeout = -1ll, bool allow_terminating_thread = true) { + this->WaitImpl(lock, timeout, allow_terminating_thread); } void Broadcast() { KScopedSchedulerLock lk; - while (m_thread_queue.WakeupFrontThread() != nullptr) { - /* We want to signal all threads, and so should continue waking up until there's nothing to wake. */ + + /* Signal all threads. */ + for (auto &thread : m_wait_list) { + thread.SetState(KThread::ThreadState_Runnable); } } diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_sleep_manager.cpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_sleep_manager.cpp index e6860023d..3ad3abb90 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_sleep_manager.cpp +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_sleep_manager.cpp @@ -492,7 +492,7 @@ namespace ams::kern::board::nintendo::nx { /* Wait for a request. */ { KScopedLightLock lk(g_cv_lock); - while (!(g_sleep_target_cores & target_core_mask)) { + while ((g_sleep_target_cores & target_core_mask) == 0) { g_cv.Wait(std::addressof(g_cv_lock)); } } diff --git a/libraries/libmesosphere/source/kern_k_resource_limit.cpp b/libraries/libmesosphere/source/kern_k_resource_limit.cpp index 04cf4873b..9b4941084 100644 --- a/libraries/libmesosphere/source/kern_k_resource_limit.cpp +++ b/libraries/libmesosphere/source/kern_k_resource_limit.cpp @@ -146,8 +146,12 @@ namespace ams::kern { if (m_current_hints[which] + value <= m_limit_values[which] && (timeout < 0 || KHardwareTimer::GetTick() < timeout)) { m_waiter_count++; - m_cond_var.Wait(&m_lock, timeout); + m_cond_var.Wait(&m_lock, timeout, false); m_waiter_count--; + + if (GetCurrentThread().IsTerminationRequested()) { + return false; + } } else { break; } From cbdf33260e6d40339858867076bf75af19dd7410 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 14:45:38 -0700 Subject: [PATCH 148/280] kern: update port/session state semantics --- .../include/mesosphere/kern_k_client_port.hpp | 1 + .../include/mesosphere/kern_k_object_name.hpp | 3 +++ .../include/mesosphere/kern_k_port.hpp | 5 +++++ .../include/mesosphere/kern_k_session.hpp | 16 ++++++++++++---- .../libmesosphere/source/kern_k_client_port.cpp | 4 ++++ .../libmesosphere/source/kern_k_server_port.cpp | 4 ++-- .../libmesosphere/source/kern_k_session.cpp | 10 +++++----- 7 files changed, 32 insertions(+), 11 deletions(-) diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_client_port.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_client_port.hpp index d2462d0fc..a66006a9a 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_client_port.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_client_port.hpp @@ -47,6 +47,7 @@ namespace ams::kern { ALWAYS_INLINE s32 GetMaxSessions() const { return m_max_sessions; } bool IsLight() const; + bool IsServerClosed() const; /* Overridden virtual functions. */ virtual void Destroy() override; diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_object_name.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_object_name.hpp index f97a16d4b..f0bf6b8a0 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_object_name.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_object_name.hpp @@ -47,6 +47,9 @@ namespace ams::kern { Derived *derived = obj->DynamicCast(); R_UNLESS(derived != nullptr, svc::ResultNotFound()); + /* Check that the object is closed. */ + R_UNLESS(derived->IsServerClosed(), svc::ResultInvalidState()); + return Delete(obj.GetPointerUnsafe(), name); } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_port.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_port.hpp index 6182ab537..2929fad62 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_port.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_port.hpp @@ -53,6 +53,11 @@ namespace ams::kern { uintptr_t GetName() const { return m_name; } bool IsLight() const { return m_is_light; } + bool IsServerClosed() const { + KScopedSchedulerLock sl; + return m_state == State::ServerClosed; + } + Result EnqueueSession(KServerSession *session); Result EnqueueSession(KLightServerSession *session); diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_session.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_session.hpp index c00b9dd63..2173c3964 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_session.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_session.hpp @@ -37,14 +37,22 @@ namespace ams::kern { private: KServerSession m_server; KClientSession m_client; - State m_state; + std::atomic::type> m_atomic_state; KClientPort *m_port; uintptr_t m_name; KProcess *m_process; bool m_initialized; + private: + ALWAYS_INLINE void SetState(State state) { + m_atomic_state = static_cast(state); + } + + ALWAYS_INLINE State GetState() const { + return static_cast(m_atomic_state.load()); + } public: constexpr KSession() - : m_server(), m_client(), m_state(State::Invalid), m_port(), m_name(), m_process(), m_initialized() + : m_server(), m_client(), m_atomic_state(static_cast::type>(State::Invalid)), m_port(), m_name(), m_process(), m_initialized() { /* ... */ } @@ -62,8 +70,8 @@ namespace ams::kern { void OnServerClosed(); void OnClientClosed(); - bool IsServerClosed() const { return m_state != State::Normal; } - bool IsClientClosed() const { return m_state != State::Normal; } + bool IsServerClosed() const { return this->GetState() != State::Normal; } + bool IsClientClosed() const { return this->GetState() != State::Normal; } Result OnRequest(KSessionRequest *request) { return m_server.OnRequest(request); } diff --git a/libraries/libmesosphere/source/kern_k_client_port.cpp b/libraries/libmesosphere/source/kern_k_client_port.cpp index 963a0bafd..76ad3a373 100644 --- a/libraries/libmesosphere/source/kern_k_client_port.cpp +++ b/libraries/libmesosphere/source/kern_k_client_port.cpp @@ -42,6 +42,10 @@ namespace ams::kern { return this->GetParent()->IsLight(); } + bool KClientPort::IsServerClosed() const { + return this->GetParent()->IsServerClosed(); + } + void KClientPort::Destroy() { /* Note with our parent that we're closed. */ m_parent->OnClientClosed(); diff --git a/libraries/libmesosphere/source/kern_k_server_port.cpp b/libraries/libmesosphere/source/kern_k_server_port.cpp index e581a257b..1e2909240 100644 --- a/libraries/libmesosphere/source/kern_k_server_port.cpp +++ b/libraries/libmesosphere/source/kern_k_server_port.cpp @@ -40,7 +40,7 @@ namespace ams::kern { KServerSession *session = nullptr; { KScopedSchedulerLock sl; - while (!m_session_list.empty()) { + if (!m_session_list.empty()) { session = std::addressof(m_session_list.front()); m_session_list.pop_front(); } @@ -60,7 +60,7 @@ namespace ams::kern { KLightServerSession *session = nullptr; { KScopedSchedulerLock sl; - while (!m_light_session_list.empty()) { + if (!m_light_session_list.empty()) { session = std::addressof(m_light_session_list.front()); m_light_session_list.pop_front(); } diff --git a/libraries/libmesosphere/source/kern_k_session.cpp b/libraries/libmesosphere/source/kern_k_session.cpp index 88906469f..801be4afc 100644 --- a/libraries/libmesosphere/source/kern_k_session.cpp +++ b/libraries/libmesosphere/source/kern_k_session.cpp @@ -35,7 +35,7 @@ namespace ams::kern { m_client.Initialize(this); /* Set state and name. */ - m_state = State::Normal; + this->SetState(State::Normal); m_name = name; /* Set our owner process. */ @@ -62,8 +62,8 @@ namespace ams::kern { void KSession::OnServerClosed() { MESOSPHERE_ASSERT_THIS(); - if (m_state == State::Normal) { - m_state = State::ServerClosed; + if (this->GetState() == State::Normal) { + this->SetState(State::ServerClosed); m_client.OnServerClosed(); } } @@ -71,8 +71,8 @@ namespace ams::kern { void KSession::OnClientClosed() { MESOSPHERE_ASSERT_THIS(); - if (m_state == State::Normal) { - m_state = State::ClientClosed; + if (this->GetState() == State::Normal) { + this->SetState(State::ClientClosed); m_server.OnClientClosed(); } } From ee91063bbb49be5805f5bd725fc3b0b189a5e716 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 14:53:12 -0700 Subject: [PATCH 149/280] kern: update kdebug process management semantics --- .../libmesosphere/source/kern_k_debug_base.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/libraries/libmesosphere/source/kern_k_debug_base.cpp b/libraries/libmesosphere/source/kern_k_debug_base.cpp index bd49cab2c..cdd2e9b70 100644 --- a/libraries/libmesosphere/source/kern_k_debug_base.cpp +++ b/libraries/libmesosphere/source/kern_k_debug_base.cpp @@ -355,6 +355,15 @@ namespace ams::kern { } else if (state == KProcess::State_DebugBreak) { /* If the process is debug breaked, transition it accordingly. */ new_state = KProcess::State_Crashed; + + /* Suspend all the threads in the process. */ + { + auto end = target->GetThreadList().end(); + for (auto it = target->GetThreadList().begin(); it != end; ++it) { + /* Request that we suspend the thread. */ + it->RequestSuspend(KThread::SuspendType_Debug); + } + } } else { /* Otherwise, don't transition. */ new_state = state; @@ -840,9 +849,6 @@ namespace ams::kern { /* If the process isn't null, detach. */ if (process.IsNotNull()) { - /* When we're done detaching, clear the reference we opened when we attached. */ - ON_SCOPE_EXIT { process->Close(); }; - /* Detach. */ { /* Lock both ourselves and the target process. */ @@ -877,6 +883,9 @@ namespace ams::kern { /* Clear our process. */ m_process = nullptr; } + + /* We're done detaching, so clear the reference we opened when we attached. */ + process->Close(); } } } From 911e431d652f8a20a20c3b302283385242c8a7e4 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 15:03:26 -0700 Subject: [PATCH 150/280] kern: simplify handle table registration for port/session --- .../source/svc/kern_svc_port.cpp | 40 +++++++++---------- .../source/svc/kern_svc_session.cpp | 2 +- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/libraries/libmesosphere/source/svc/kern_svc_port.cpp b/libraries/libmesosphere/source/svc/kern_svc_port.cpp index 1d1171889..1e94b552a 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_port.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_port.cpp @@ -38,28 +38,27 @@ namespace ams::kern::svc { KPort *port = KPort::Create(); R_UNLESS(port != nullptr, svc::ResultOutOfResource()); - /* Reserve a handle for the server port. */ - R_TRY(handle_table.Reserve(out_server_handle)); - auto reserve_guard = SCOPE_GUARD { handle_table.Unreserve(*out_server_handle); }; - /* Initialize the new port. */ port->Initialize(max_sessions, false, 0); /* Register the port. */ KPort::Register(port); + /* Ensure that our only reference to the port is in the handle table when we're done. */ + ON_SCOPE_EXIT { + port->GetClientPort().Close(); + port->GetServerPort().Close(); + }; + /* Register the handle in the table. */ - handle_table.Register(*out_server_handle, std::addressof(port->GetServerPort())); - reserve_guard.Cancel(); - auto register_guard = SCOPE_GUARD { handle_table.Remove(*out_server_handle); }; + R_TRY(handle_table.Add(out_server_handle, std::addressof(port->GetServerPort()))); + auto handle_guard = SCOPE_GUARD { handle_table.Remove(*out_server_handle); }; /* Create a new object name. */ R_TRY(KObjectName::NewFromName(std::addressof(port->GetClientPort()), name)); - /* Perform resource cleanup. */ - port->GetServerPort().Close(); - port->GetClientPort().Close(); - register_guard.Cancel(); + /* We succeeded, so don't leak the handle. */ + handle_guard.Cancel(); } else /* if (max_sessions == 0) */ { /* Ensure that this else case is correct. */ MESOSPHERE_AUDIT(max_sessions == 0); @@ -157,21 +156,18 @@ namespace ams::kern::svc { R_TRY(handle_table.Reserve(out)); auto handle_guard = SCOPE_GUARD { handle_table.Unreserve(*out); }; - /* Create and register session. */ + /* Create the session. */ + KAutoObject *session; if (client_port->IsLight()) { - KLightClientSession *session; - R_TRY(client_port->CreateLightSession(std::addressof(session))); - - handle_table.Register(*out, session); - session->Close(); + R_TRY(client_port->CreateLightSession(reinterpret_cast(std::addressof(session)))); } else { - KClientSession *session; - R_TRY(client_port->CreateSession(std::addressof(session))); - - handle_table.Register(*out, session); - session->Close(); + R_TRY(client_port->CreateSession(reinterpret_cast(std::addressof(session)))); } + /* Register the session. */ + handle_table.Register(*out, session); + session->Close(); + /* We succeeded. */ handle_guard.Cancel(); return ResultSuccess(); diff --git a/libraries/libmesosphere/source/svc/kern_svc_session.cpp b/libraries/libmesosphere/source/svc/kern_svc_session.cpp index dc4e54dc3..f55eea5a1 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_session.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_session.cpp @@ -43,8 +43,8 @@ namespace ams::kern::svc { /* Ensure that we clean up the session (and its only references are handle table) on function end. */ ON_SCOPE_EXIT { - session->GetServerSession().Close(); session->GetClientSession().Close(); + session->GetServerSession().Close(); }; /* Register the session. */ From afb1d68d06ce34bf120f8080af56ed0bae64f727 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 15:16:11 -0700 Subject: [PATCH 151/280] kern: ensure handle table is finalized when deferring termination --- .../include/mesosphere/kern_k_process.hpp | 18 ++++++++++ .../libmesosphere/source/kern_k_process.cpp | 36 +++++++++++-------- 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp index 1706a0721..43d87cbcd 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp @@ -100,6 +100,7 @@ namespace ams::kern { bool m_is_suspended{}; bool m_is_immortal{}; bool m_is_jit_debug{}; + bool m_is_handle_table_initialized{}; ams::svc::DebugEvent m_jit_debug_event_type{}; ams::svc::DebugException m_jit_debug_exception_type{}; uintptr_t m_jit_debug_params[4]{}; @@ -404,6 +405,23 @@ namespace ams::kern { this->NotifyAvailable(); } } + + ALWAYS_INLINE Result InitializeHandleTable(s32 size) { + /* Try to initialize the handle table. */ + R_TRY(m_handle_table.Initialize(size)); + + /* We succeeded, so note that we did. */ + m_is_handle_table_initialized = true; + return ResultSuccess(); + } + + ALWAYS_INLINE void FinalizeHandleTable() { + /* Finalize the table. */ + m_handle_table.Finalize(); + + /* Note that the table is finalized. */ + m_is_handle_table_initialized = false; + } }; } diff --git a/libraries/libmesosphere/source/kern_k_process.cpp b/libraries/libmesosphere/source/kern_k_process.cpp index b72c84bda..97a069a8f 100644 --- a/libraries/libmesosphere/source/kern_k_process.cpp +++ b/libraries/libmesosphere/source/kern_k_process.cpp @@ -211,19 +211,20 @@ namespace ams::kern { KSystemControl::GenerateRandomBytes(m_entropy, sizeof(m_entropy)); /* Clear remaining fields. */ - m_num_running_threads = 0; - m_num_process_switches = 0; - m_num_thread_switches = 0; - m_num_fpu_switches = 0; - m_num_supervisor_calls = 0; - m_num_ipc_messages = 0; + m_num_running_threads = 0; + m_num_process_switches = 0; + m_num_thread_switches = 0; + m_num_fpu_switches = 0; + m_num_supervisor_calls = 0; + m_num_ipc_messages = 0; - m_is_signaled = false; - m_attached_object = nullptr; - m_exception_thread = nullptr; - m_is_suspended = false; - m_memory_release_hint = 0; - m_schedule_count = 0; + m_is_signaled = false; + m_attached_object = nullptr; + m_exception_thread = nullptr; + m_is_suspended = false; + m_memory_release_hint = 0; + m_schedule_count = 0; + m_is_handle_table_initialized = false; /* We're initialized! */ m_is_initialized = true; @@ -400,6 +401,11 @@ namespace ams::kern { /* Terminate child threads. */ TerminateChildren(this, nullptr); + /* Finalize the handle table, if we're not immortal. */ + if (!m_is_immortal && m_is_handle_table_initialized) { + this->FinalizeHandleTable(); + } + /* Call the debug callback. */ KDebug::OnExitProcess(this); @@ -411,7 +417,7 @@ namespace ams::kern { /* Finalize the handle table when we're done, if the process isn't immortal. */ ON_SCOPE_EXIT { if (!m_is_immortal) { - m_handle_table.Finalize(); + this->FinalizeHandleTable(); } }; @@ -905,8 +911,8 @@ namespace ams::kern { R_TRY(m_page_table.SetMaxHeapSize(m_max_process_memory - (m_main_thread_stack_size + m_code_size))); /* Initialize our handle table. */ - R_TRY(m_handle_table.Initialize(m_capabilities.GetHandleTableSize())); - auto ht_guard = SCOPE_GUARD { m_handle_table.Finalize(); }; + R_TRY(this->InitializeHandleTable(m_capabilities.GetHandleTableSize())); + auto ht_guard = SCOPE_GUARD { this->FinalizeHandleTable(); }; /* Create a new thread for the process. */ KThread *main_thread = KThread::Create(); From 6faa3534bff5b35b7e30494f42f4893d3442e790 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 15:30:13 -0700 Subject: [PATCH 152/280] kern: update pinning semantics for terminating threads --- .../include/mesosphere/kern_k_process.hpp | 5 +-- .../arch/arm64/kern_exception_handlers.cpp | 5 +++ .../libmesosphere/source/kern_k_process.cpp | 34 ++++++++++++++++--- .../libmesosphere/source/kern_k_thread.cpp | 19 ++++++----- 4 files changed, 48 insertions(+), 15 deletions(-) diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp index 43d87cbcd..af3e59c7b 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp @@ -127,14 +127,14 @@ namespace ams::kern { Result StartTermination(); void FinishTermination(); - void PinThread(s32 core_id, KThread *thread) { + ALWAYS_INLINE void PinThread(s32 core_id, KThread *thread) { MESOSPHERE_ASSERT(0 <= core_id && core_id < static_cast(cpu::NumCores)); MESOSPHERE_ASSERT(thread != nullptr); MESOSPHERE_ASSERT(m_pinned_threads[core_id] == nullptr); m_pinned_threads[core_id] = thread; } - void UnpinThread(s32 core_id, KThread *thread) { + ALWAYS_INLINE void UnpinThread(s32 core_id, KThread *thread) { MESOSPHERE_UNUSED(thread); MESOSPHERE_ASSERT(0 <= core_id && core_id < static_cast(cpu::NumCores)); MESOSPHERE_ASSERT(thread != nullptr); @@ -340,6 +340,7 @@ namespace ams::kern { void PinCurrentThread(); void UnpinCurrentThread(); + void UnpinThread(KThread *thread); Result SignalToAddress(KProcessAddress address) { return m_cond_var.SignalToAddress(address); diff --git a/libraries/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp b/libraries/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp index 263a304ea..2bb27ae0d 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp +++ b/libraries/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp @@ -521,6 +521,11 @@ namespace ams::kern::arch::arm64 { { KScopedInterruptEnable ei; + /* Terminate the thread, if we should. */ + if (GetCurrentThread().IsTerminationRequested()) { + GetCurrentThread().Exit(); + } + HandleUserException(context, esr, far, afsr0, afsr1, data); } } else { diff --git a/libraries/libmesosphere/source/kern_k_process.cpp b/libraries/libmesosphere/source/kern_k_process.cpp index 97a069a8f..dacd1303e 100644 --- a/libraries/libmesosphere/source/kern_k_process.cpp +++ b/libraries/libmesosphere/source/kern_k_process.cpp @@ -34,6 +34,13 @@ namespace ams::kern { KScopedLightLock proc_lk(process->GetListLock()); KScopedSchedulerLock sl; + if (thread_to_not_terminate != nullptr && process->GetPinnedThread(GetCurrentCoreId()) == thread_to_not_terminate) { + /* NOTE: Here Nintendo unpins the current thread instead of the thread_to_not_terminate. */ + /* This is valid because the only caller which uses non-nullptr as argument uses GetCurrentThreadPointer(), */ + /* but it's still notable because it seems incorrect at first glance. */ + process->UnpinCurrentThread(); + } + auto &thread_list = process->GetThreadList(); for (auto it = thread_list.begin(); it != thread_list.end(); ++it) { if (KThread *thread = std::addressof(*it); thread != thread_to_not_terminate) { @@ -1020,12 +1027,15 @@ namespace ams::kern { const s32 core_id = GetCurrentCoreId(); KThread *cur_thread = GetCurrentThreadPointer(); - /* Pin it. */ - this->PinThread(core_id, cur_thread); - cur_thread->Pin(); + /* If the thread isn't terminated, pin it. */ + if (!cur_thread->IsTerminationRequested()) { + /* Pin it. */ + this->PinThread(core_id, cur_thread); + cur_thread->Pin(); - /* An update is needed. */ - KScheduler::SetSchedulerUpdateNeeded(); + /* An update is needed. */ + KScheduler::SetSchedulerUpdateNeeded(); + } } void KProcess::UnpinCurrentThread() { @@ -1043,6 +1053,20 @@ namespace ams::kern { KScheduler::SetSchedulerUpdateNeeded(); } + void KProcess::UnpinThread(KThread *thread) { + MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread()); + + /* Get the thread's core id. */ + const auto core_id = thread->GetActiveCore(); + + /* Unpin it. */ + this->UnpinThread(core_id, thread); + thread->Unpin(); + + /* An update is needed. */ + KScheduler::SetSchedulerUpdateNeeded(); + } + Result KProcess::GetThreadList(s32 *out_num_threads, ams::kern::svc::KUserPointer out_thread_ids, s32 max_out_count) { /* Lock the list. */ KScopedLightLock lk(m_list_lock); diff --git a/libraries/libmesosphere/source/kern_k_thread.cpp b/libraries/libmesosphere/source/kern_k_thread.cpp index fbc565eaa..de857658b 100644 --- a/libraries/libmesosphere/source/kern_k_thread.cpp +++ b/libraries/libmesosphere/source/kern_k_thread.cpp @@ -483,19 +483,17 @@ namespace ams::kern { } /* Allow performing thread suspension (if termination hasn't been requested). */ - { + if (!this->IsTerminationRequested()) { /* Update our allow flags. */ - if (!this->IsTerminationRequested()) { - m_suspend_allowed_flags |= (1 << (SuspendType_Thread + ThreadState_SuspendShift)); - } + m_suspend_allowed_flags |= (1 << (SuspendType_Thread + ThreadState_SuspendShift)); /* Update our state. */ this->UpdateState(); - } - /* Update our SVC access permissions. */ - MESOSPHERE_ASSERT(m_parent != nullptr); - m_parent->CopyUnpinnedSvcPermissionsTo(this->GetStackParameters()); + /* Update our SVC access permissions. */ + MESOSPHERE_ASSERT(m_parent != nullptr); + m_parent->CopyUnpinnedSvcPermissionsTo(this->GetStackParameters()); + } /* Resume any threads that began waiting on us while we were pinned. */ for (auto it = m_pinned_waiter_list.begin(); it != m_pinned_waiter_list.end(); ++it) { @@ -1218,6 +1216,11 @@ namespace ams::kern { /* Register the terminating dpc. */ this->RegisterDpc(DpcFlag_Terminating); + /* If the thread is pinned, unpin it. */ + if (this->GetStackParameters().is_pinned) { + this->GetOwnerProcess()->UnpinThread(this); + } + /* If the thread is suspended, continue it. */ if (this->IsSuspended()) { m_suspend_allowed_flags = 0; From dc7862882fc8bb20e6fdf20b37c60ea7df562857 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 15:30:36 -0700 Subject: [PATCH 153/280] kern: who needs __purecall? --- .../include/mesosphere/kern_k_synchronization_object.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_synchronization_object.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_synchronization_object.hpp index ea340a155..1465bbe69 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_synchronization_object.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_synchronization_object.hpp @@ -45,7 +45,7 @@ namespace ams::kern { static Result Wait(s32 *out_index, KSynchronizationObject **objects, const s32 num_objects, s64 timeout); public: virtual void Finalize() override; - virtual bool IsSignaled() const = 0; + virtual bool IsSignaled() const { AMS_INFINITE_LOOP(); } virtual void DumpWaiters(); }; From 96937a611dffa9a04e4d41f4d972ecd9bcc8aa70 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 17:07:01 -0700 Subject: [PATCH 154/280] kern: fuck the KPolice^H^H^H^H^H^HPageGroups --- .../arch/arm64/kern_k_process_page_table.hpp | 32 +- .../nintendo/nx/kern_k_device_page_table.hpp | 9 +- .../mesosphere/kern_k_page_table_base.hpp | 27 +- .../nintendo/nx/kern_k_device_page_table.cpp | 280 +++++++++------- .../source/kern_k_device_address_space.cpp | 22 +- .../source/kern_k_page_table_base.cpp | 305 ++++++++++++++---- .../source/svc/kern_svc_cache.cpp | 32 +- .../source/svc/kern_svc_process_memory.cpp | 17 +- 8 files changed, 493 insertions(+), 231 deletions(-) diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp index cf603b14f..2277a5bff 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp @@ -36,6 +36,10 @@ namespace ams::kern::arch::arm64 { void Finalize() { m_page_table.Finalize(); } + ALWAYS_INLINE KScopedLightLock AcquireDeviceMapLock() { + return m_page_table.AcquireDeviceMapLock(); + } + Result SetMemoryPermission(KProcessAddress addr, size_t size, ams::svc::MemoryPermission perm) { return m_page_table.SetMemoryPermission(addr, size, perm); } @@ -148,22 +152,30 @@ namespace ams::kern::arch::arm64 { return m_page_table.WriteDebugIoMemory(address, buffer, size); } - Result LockForDeviceAddressSpace(KPageGroup *out, KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned) { - return m_page_table.LockForDeviceAddressSpace(out, address, size, perm, is_aligned); + Result LockForMapDeviceAddressSpace(KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned) { + return m_page_table.LockForMapDeviceAddressSpace(address, size, perm, is_aligned); + } + + Result LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size) { + return m_page_table.LockForUnmapDeviceAddressSpace(address, size); } Result UnlockForDeviceAddressSpace(KProcessAddress address, size_t size) { return m_page_table.UnlockForDeviceAddressSpace(address, size); } - Result MakePageGroupForUnmapDeviceAddressSpace(KPageGroup *out, KProcessAddress address, size_t size) { - return m_page_table.MakePageGroupForUnmapDeviceAddressSpace(out, address, size); - } - Result UnlockForDeviceAddressSpacePartialMap(KProcessAddress address, size_t size, size_t mapped_size) { return m_page_table.UnlockForDeviceAddressSpacePartialMap(address, size, mapped_size); } + Result OpenMemoryRangeForMapDeviceAddressSpace(KPageTableBase::MemoryRange *out, KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned) { + return m_page_table.OpenMemoryRangeForMapDeviceAddressSpace(out, address, size, perm, is_aligned); + } + + Result OpenMemoryRangeForUnmapDeviceAddressSpace(KPageTableBase::MemoryRange *out, KProcessAddress address, size_t size) { + return m_page_table.OpenMemoryRangeForUnmapDeviceAddressSpace(out, address, size); + } + Result LockForIpcUserBuffer(KPhysicalAddress *out, KProcessAddress address, size_t size) { return m_page_table.LockForIpcUserBuffer(out, address, size); } @@ -188,6 +200,10 @@ namespace ams::kern::arch::arm64 { return m_page_table.UnlockForCodeMemory(address, size, pg); } + Result OpenMemoryRangeForProcessCacheOperation(KPageTableBase::MemoryRange *out, KProcessAddress address, size_t size) { + return m_page_table.OpenMemoryRangeForProcessCacheOperation(out, address, size); + } + Result CopyMemoryFromLinearToUser(KProcessAddress dst_addr, size_t size, KProcessAddress src_addr, u32 src_state_mask, u32 src_state, KMemoryPermission src_test_perm, u32 src_attr_mask, u32 src_attr) { return m_page_table.CopyMemoryFromLinearToUser(dst_addr, size, src_addr, src_state_mask, src_state, src_test_perm, src_attr_mask, src_attr); } @@ -240,6 +256,10 @@ namespace ams::kern::arch::arm64 { return m_page_table.UnmapPhysicalMemoryUnsafe(address, size); } + Result UnmapProcessMemory(KProcessAddress dst_address, size_t size, KProcessPageTable &src_page_table, KProcessAddress src_address) { + return m_page_table.UnmapProcessMemory(dst_address, size, src_page_table.m_page_table, src_address); + } + void DumpMemoryBlocks() const { return m_page_table.DumpMemoryBlocks(); } diff --git a/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_device_page_table.hpp b/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_device_page_table.hpp index 21814b198..6a60af86d 100644 --- a/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_device_page_table.hpp +++ b/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_device_page_table.hpp @@ -69,8 +69,8 @@ namespace ams::kern::board::nintendo::nx { Result Attach(ams::svc::DeviceName device_name, u64 space_address, u64 space_size); Result Detach(ams::svc::DeviceName device_name); - Result Map(size_t *out_mapped_size, const KPageGroup &pg, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool refresh_mappings); - Result Unmap(const KPageGroup &pg, KDeviceVirtualAddress device_address); + Result Map(size_t *out_mapped_size, KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool refresh_mappings); + Result Unmap(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address); void Unmap(KDeviceVirtualAddress device_address, size_t size) { return this->UnmapImpl(device_address, size, false); @@ -78,12 +78,11 @@ namespace ams::kern::board::nintendo::nx { private: Result MapDevicePage(size_t *out_mapped_size, s32 &num_pt, s32 max_pt, KPhysicalAddress phys_addr, u64 size, KDeviceVirtualAddress address, ams::svc::MemoryPermission device_perm); - Result MapImpl(size_t *out_mapped_size, s32 &num_pt, s32 max_pt, const KPageGroup &pg, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm); + Result MapImpl(size_t *out_mapped_size, s32 &num_pt, s32 max_pt, KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool is_aligned); void UnmapImpl(KDeviceVirtualAddress address, u64 size, bool force); bool IsFree(KDeviceVirtualAddress address, u64 size) const; - Result MakePageGroup(KPageGroup *out, KDeviceVirtualAddress address, u64 size) const; - bool Compare(const KPageGroup &pg, KDeviceVirtualAddress device_address) const; + bool Compare(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address) const; public: static void Initialize(); diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp index 1a7afe4ef..6ec7498d3 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp @@ -55,6 +55,13 @@ namespace ams::kern { public: using TraversalEntry = KPageTableImpl::TraversalEntry; using TraversalContext = KPageTableImpl::TraversalContext; + + struct MemoryRange { + KVirtualAddress address; + size_t size; + + void Close(); + }; protected: enum MemoryFillValue { MemoryFillValue_Zero = 0, @@ -155,6 +162,7 @@ namespace ams::kern { size_t m_mapped_ipc_server_memory{}; mutable KLightLock m_general_lock{}; mutable KLightLock m_map_physical_memory_lock{}; + KLightLock m_device_map_lock{}; KPageTableImpl m_impl{}; KMemoryBlockManager m_memory_block_manager{}; u32 m_allocate_option{}; @@ -199,6 +207,10 @@ namespace ams::kern { return this->CanContain(addr, size, KMemoryState_AliasCode); } + ALWAYS_INLINE KScopedLightLock AcquireDeviceMapLock() { + return KScopedLightLock(m_device_map_lock); + } + KProcessAddress GetRegionAddress(KMemoryState state) const; size_t GetRegionSize(KMemoryState state) const; bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const; @@ -290,6 +302,8 @@ namespace ams::kern { Result MakePageGroup(KPageGroup &pg, KProcessAddress addr, size_t num_pages); bool IsValidPageGroup(const KPageGroup &pg, KProcessAddress addr, size_t num_pages); + Result GetContiguousMemoryRangeWithState(MemoryRange *out, KProcessAddress address, size_t size, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr); + NOINLINE Result MapPages(KProcessAddress *out_addr, size_t num_pages, size_t alignment, KPhysicalAddress phys_addr, bool is_pa_valid, KProcessAddress region_start, size_t region_num_pages, KMemoryState state, KMemoryPermission perm); Result MapIoImpl(KProcessAddress *out, PageLinkedList *page_list, KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm); @@ -367,12 +381,15 @@ namespace ams::kern { Result WriteDebugMemory(KProcessAddress address, const void *buffer, size_t size); Result WriteDebugIoMemory(KProcessAddress address, const void *buffer, size_t size); - Result LockForDeviceAddressSpace(KPageGroup *out, KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned); - Result UnlockForDeviceAddressSpace(KProcessAddress address, size_t size); + Result LockForMapDeviceAddressSpace(KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned); + Result LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size); - Result MakePageGroupForUnmapDeviceAddressSpace(KPageGroup *out, KProcessAddress address, size_t size); + Result UnlockForDeviceAddressSpace(KProcessAddress address, size_t size); Result UnlockForDeviceAddressSpacePartialMap(KProcessAddress address, size_t size, size_t mapped_size); + Result OpenMemoryRangeForMapDeviceAddressSpace(KPageTableBase::MemoryRange *out, KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned); + Result OpenMemoryRangeForUnmapDeviceAddressSpace(MemoryRange *out, KProcessAddress address, size_t size); + Result LockForIpcUserBuffer(KPhysicalAddress *out, KProcessAddress address, size_t size); Result UnlockForIpcUserBuffer(KProcessAddress address, size_t size); @@ -381,6 +398,8 @@ namespace ams::kern { Result LockForCodeMemory(KPageGroup *out, KProcessAddress address, size_t size); Result UnlockForCodeMemory(KProcessAddress address, size_t size, const KPageGroup &pg); + Result OpenMemoryRangeForProcessCacheOperation(MemoryRange *out, KProcessAddress address, size_t size); + Result CopyMemoryFromLinearToUser(KProcessAddress dst_addr, size_t size, KProcessAddress src_addr, u32 src_state_mask, u32 src_state, KMemoryPermission src_test_perm, u32 src_attr_mask, u32 src_attr); Result CopyMemoryFromLinearToKernel(KProcessAddress dst_addr, size_t size, KProcessAddress src_addr, u32 src_state_mask, u32 src_state, KMemoryPermission src_test_perm, u32 src_attr_mask, u32 src_attr); Result CopyMemoryFromUserToLinear(KProcessAddress dst_addr, size_t size, u32 dst_state_mask, u32 dst_state, KMemoryPermission dst_test_perm, u32 dst_attr_mask, u32 dst_attr, KProcessAddress src_addr); @@ -398,6 +417,8 @@ namespace ams::kern { Result MapPhysicalMemoryUnsafe(KProcessAddress address, size_t size); Result UnmapPhysicalMemoryUnsafe(KProcessAddress address, size_t size); + Result UnmapProcessMemory(KProcessAddress dst_address, size_t size, KPageTableBase &src_pt, KProcessAddress src_address); + void DumpMemoryBlocksLocked() const { MESOSPHERE_ASSERT(this->IsLockedByCurrentThread()); m_memory_block_manager.DumpBlocks(); diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_device_page_table.cpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_device_page_table.cpp index 582df3f7f..b0fc3bcb0 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_device_page_table.cpp +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_device_page_table.cpp @@ -216,6 +216,12 @@ namespace ams::kern::board::nintendo::nx { return (m_value & (1u << n)); } + template + constexpr ALWAYS_INLINE u32 SelectBits() const { + constexpr u32 Mask = ((1u << Bits) | ...); + return m_value & Mask; + } + constexpr ALWAYS_INLINE bool GetBit(Bit n) const { return this->SelectBit(n) != 0; } @@ -242,12 +248,14 @@ namespace ams::kern::board::nintendo::nx { constexpr ALWAYS_INLINE bool IsNonSecure() const { return this->GetBit(Bit_NonSecure); } constexpr ALWAYS_INLINE bool IsWriteable() const { return this->GetBit(Bit_Writeable); } constexpr ALWAYS_INLINE bool IsReadable() const { return this->GetBit(Bit_Readable); } - constexpr ALWAYS_INLINE bool IsValid() const { return this->IsWriteable() || this->IsReadable(); } + constexpr ALWAYS_INLINE bool IsValid() const { return this->SelectBits(); } - constexpr ALWAYS_INLINE u32 GetAttributes() const { return this->SelectBit(Bit_NonSecure) | this->SelectBit(Bit_Writeable) | this->SelectBit(Bit_Readable); } + constexpr ALWAYS_INLINE u32 GetAttributes() const { return this->SelectBits(); } constexpr ALWAYS_INLINE KPhysicalAddress GetPhysicalAddress() const { return (static_cast(m_value) << DevicePageBits) & PhysicalAddressMask; } + + ALWAYS_INLINE void InvalidateAttributes() { this->SetValue(m_value & ~(0xCu << 28)); } ALWAYS_INLINE void Invalidate() { this->SetValue(0); } }; @@ -847,7 +855,7 @@ namespace ams::kern::board::nintendo::nx { } /* Forcibly unmap all pages. */ - this->UnmapImpl(0, (1ul << DeviceVirtualAddressBits), true); + this->UnmapImpl(0, (1ul << DeviceVirtualAddressBits), false); /* Release all asids. */ for (size_t i = 0; i < TableCount; ++i) { @@ -1117,12 +1125,11 @@ namespace ams::kern::board::nintendo::nx { return ResultSuccess(); } - Result KDevicePageTable::MapImpl(size_t *out_mapped_size, s32 &num_pt, s32 max_pt, const KPageGroup &pg, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm) { + Result KDevicePageTable::MapImpl(size_t *out_mapped_size, s32 &num_pt, s32 max_pt, KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool is_aligned) { /* Clear the output size. */ *out_mapped_size = 0; /* Get the size, and validate the address. */ - const u64 size = pg.GetNumPages() * PageSize; MESOSPHERE_ASSERT((device_address & ~DeviceVirtualAddressMask) == 0); MESOSPHERE_ASSERT(((device_address + size - 1) & ~DeviceVirtualAddressMask) == 0); @@ -1130,28 +1137,33 @@ namespace ams::kern::board::nintendo::nx { R_UNLESS(this->IsFree(device_address, size), svc::ResultInvalidCurrentMemory()); /* Ensure that if we fail, we unmap anything we mapped. */ - auto unmap_guard = SCOPE_GUARD { this->UnmapImpl(device_address, size, true); }; + auto unmap_guard = SCOPE_GUARD { this->UnmapImpl(device_address, size, false); }; /* Iterate, mapping device pages. */ KDeviceVirtualAddress cur_addr = device_address; - for (auto it = pg.begin(); it != pg.end(); ++it) { - /* Require that we be able to map the device page. */ - R_UNLESS(IsHeapVirtualAddress(it->GetAddress()), svc::ResultInvalidCurrentMemory()); + while (true) { + /* Get the current contiguous range. */ + KPageTableBase::MemoryRange contig_range = {}; + R_TRY(page_table->OpenMemoryRangeForMapDeviceAddressSpace(std::addressof(contig_range), process_address + *out_mapped_size, size - *out_mapped_size, ConvertToKMemoryPermission(device_perm), is_aligned)); - /* Get the physical address for the page. */ - const KPhysicalAddress phys_addr = GetHeapPhysicalAddress(it->GetAddress()); + /* Ensure we close the range when we're done. */ + ON_SCOPE_EXIT { contig_range.Close(); }; /* Map the device page. */ - const u64 block_size = it->GetSize(); size_t mapped_size = 0; - R_TRY(this->MapDevicePage(std::addressof(mapped_size), num_pt, max_pt, phys_addr, block_size, cur_addr, device_perm)); + R_TRY(this->MapDevicePage(std::addressof(mapped_size), num_pt, max_pt, GetHeapPhysicalAddress(contig_range.address), contig_range.size, cur_addr, device_perm)); /* Advance. */ - cur_addr += block_size; + cur_addr += contig_range.size; *out_mapped_size += mapped_size; /* If we didn't map as much as we wanted, break. */ - if (mapped_size < block_size) { + if (mapped_size < contig_range.size) { + break; + } + + /* Similarly, if we're done, break. */ + if (*out_mapped_size >= size) { break; } } @@ -1186,8 +1198,6 @@ namespace ams::kern::board::nintendo::nx { /* Check if there's nothing mapped at l1. */ if (l1 == nullptr || !l1[l1_index].IsValid()) { - MESOSPHERE_ASSERT(force); - const size_t remaining_in_entry = (PageTableSize / sizeof(PageTableEntry)) - l2_index; const size_t map_count = std::min(remaining_in_entry, remaining / DevicePageSize); @@ -1201,30 +1211,12 @@ namespace ams::kern::board::nintendo::nx { const size_t remaining_in_entry = (PageTableSize / sizeof(PageTableEntry)) - l2_index; const size_t map_count = std::min(remaining_in_entry, remaining / DevicePageSize); size_t num_closed = 0; - bool invalidated_tlb = false; + /* Invalidate the attributes of all entries. */ for (size_t i = 0; i < map_count; ++i) { if (l2[l2_index + i].IsValid()) { - /* Get the physical address. */ - const KPhysicalAddress phys_addr = l2[l2_index + i].GetPhysicalAddress(); - MESOSPHERE_ASSERT(IsHeapPhysicalAddress(phys_addr)); - - /* Invalidate the entry. */ - l2[l2_index + i].Invalidate(); + l2[l2_index + i].InvalidateAttributes(); ++num_closed; - - /* Try to add the page to the group. */ - if (R_FAILED(pg.AddBlock(GetHeapVirtualAddress(phys_addr), DevicePageSize / PageSize))) { - /* If we can't add it for deferred close, close it now. */ - cpu::StoreDataCache(std::addressof(l2[l2_index + i]), sizeof(PageTableEntry)); - InvalidatePtc(GetPageTablePhysicalAddress(KVirtualAddress(std::addressof(l2[l2_index + i])))); - SmmuSynchronizationBarrier(); - - /* Close the page's reference. */ - mm.Close(GetHeapVirtualAddress(phys_addr), 1); - } - } else { - MESOSPHERE_ASSERT(force); } } cpu::StoreDataCache(std::addressof(l2[l2_index]), map_count * sizeof(PageTableEntry)); @@ -1235,6 +1227,38 @@ namespace ams::kern::board::nintendo::nx { } SmmuSynchronizationBarrier(); + /* Close the memory manager's references to the pages. */ + { + KPhysicalAddress contig_phys_addr = Null; + size_t contig_count = 0; + for (size_t i = 0; i < map_count; ++i) { + /* Get the physical address. */ + const KPhysicalAddress phys_addr = l2[l2_index + i].GetPhysicalAddress(); + MESOSPHERE_ASSERT(IsHeapPhysicalAddress(phys_addr)); + + /* Fully invalidate the entry. */ + l2[l2_index + i].Invalidate(); + + if (contig_count == 0) { + /* Ensure that our address/count is valid. */ + contig_phys_addr = phys_addr; + contig_count = contig_phys_addr != Null ? 1 : 0; + } else if (phys_addr == Null || phys_addr != (contig_phys_addr + (contig_count * DevicePageSize))) { + /* If we're no longer contiguous, close the range we've been building. */ + mm.Close(GetHeapVirtualAddress(contig_phys_addr), (contig_count * DevicePageSize) / PageSize); + + contig_phys_addr = phys_addr; + contig_count = contig_phys_addr != Null ? 1 : 0; + } else { + ++contig_count; + } + } + + if (contig_count > 0) { + mm.Close(GetHeapVirtualAddress(contig_phys_addr), (contig_count * DevicePageSize) / PageSize); + } + } + /* Close the pages. */ if (ptm.Close(KVirtualAddress(l2), num_closed)) { /* Invalidate the l1 entry. */ @@ -1243,22 +1267,12 @@ namespace ams::kern::board::nintendo::nx { /* Synchronize. */ InvalidatePtc(GetPageTablePhysicalAddress(KVirtualAddress(std::addressof(l1[l1_index])))); - InvalidateTlbSection(m_table_asids[l0_index], address); SmmuSynchronizationBarrier(); - /* We invalidated the tlb. */ - invalidated_tlb = true; - /* Free the l2 page. */ ptm.Free(KVirtualAddress(l2)); } - /* Invalidate the tlb if we haven't already. */ - if (!invalidated_tlb) { - InvalidateTlbSection(m_table_asids[l0_index], address); - SmmuSynchronizationBarrier(); - } - /* Advance. */ address += map_count * DevicePageSize; remaining -= map_count * DevicePageSize; @@ -1287,114 +1301,158 @@ namespace ams::kern::board::nintendo::nx { remaining -= DeviceLargePageSize; } } - - /* Close references to the pages in the group. */ - pg.Close(); } - Result KDevicePageTable::MakePageGroup(KPageGroup *out, KDeviceVirtualAddress address, u64 size) const { - MESOSPHERE_ASSERT((address & ~DeviceVirtualAddressMask) == 0); - MESOSPHERE_ASSERT(((address + size - 1) & ~DeviceVirtualAddressMask) == 0); + bool KDevicePageTable::Compare(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address) const { + MESOSPHERE_ASSERT((device_address & ~DeviceVirtualAddressMask) == 0); + MESOSPHERE_ASSERT(((device_address + size - 1) & ~DeviceVirtualAddressMask) == 0); + + /* We need to traverse the ranges that make up our mapping, to make sure they're all good. Start by getting a contiguous range. */ + KPageTableBase::MemoryRange contig_range = {}; + if (R_FAILED(page_table->OpenMemoryRangeForUnmapDeviceAddressSpace(std::addressof(contig_range), process_address, size))) { + return false; + } + + /* Ensure that we close the range when we're done. */ + bool range_open = true; + ON_SCOPE_EXIT { if (range_open) { contig_range.Close(); } }; /* Walk the directory. */ - u64 remaining = size; - bool first = true; - u32 attr = 0; - while (remaining > 0) { - const size_t l0_index = (address / DeviceRegionSize); - const size_t l1_index = (address % DeviceRegionSize) / DeviceLargePageSize; - const size_t l2_index = (address % DeviceLargePageSize) / DevicePageSize; + KProcessAddress cur_process_address = process_address; + size_t remaining_size = size; + KPhysicalAddress cur_phys_address = GetHeapPhysicalAddress(contig_range.address); + size_t remaining_in_range = contig_range.size; + bool first = true; + u32 first_attr = 0; + while (remaining_size > 0) { + /* Convert the device address to a series of indices. */ + const size_t l0_index = (device_address / DeviceRegionSize); + const size_t l1_index = (device_address % DeviceRegionSize) / DeviceLargePageSize; + const size_t l2_index = (device_address % DeviceLargePageSize) / DevicePageSize; /* Get and validate l1. */ const PageDirectoryEntry *l1 = GetPointer(m_tables[l0_index]); - R_UNLESS(l1 != nullptr, svc::ResultInvalidCurrentMemory()); - R_UNLESS(l1[l1_index].IsValid(), svc::ResultInvalidCurrentMemory()); + if (!(l1 != nullptr && l1[l1_index].IsValid())) { + return false; + } if (l1[l1_index].IsTable()) { /* We're acting on an l2 entry. */ const PageTableEntry *l2 = GetPointer(GetPageTableVirtualAddress(l1[l1_index].GetPhysicalAddress())); + /* Determine the number of pages to check. */ const size_t remaining_in_entry = (PageTableSize / sizeof(PageTableEntry)) - l2_index; - const size_t map_count = std::min(remaining_in_entry, remaining / DevicePageSize); + const size_t map_count = std::min(remaining_in_entry, remaining_size / DevicePageSize); + /* Check each page. */ for (size_t i = 0; i < map_count; ++i) { /* Ensure the l2 entry is valid. */ - R_UNLESS(l2[l2_index + i].IsValid(), svc::ResultInvalidCurrentMemory()); - - /* Get the physical address. */ - const KPhysicalAddress phys_addr = l2[l2_index + i].GetPhysicalAddress(); - MESOSPHERE_ASSERT(IsHeapPhysicalAddress(phys_addr)); - - /* Add to the group. */ - R_TRY(out->AddBlock(GetHeapVirtualAddress(phys_addr), DevicePageSize / PageSize)); - - /* If this is our first entry, get the attribute. */ - if (first) { - attr = l2[l2_index + i].GetAttributes(); - first = false; - } else { - /* Validate the attributes match the first entry. */ - R_UNLESS(l2[l2_index + i].GetAttributes() == attr, svc::ResultInvalidCurrentMemory()); + if (!l2[l2_index + i].IsValid()) { + return false; } + + /* Check that the attributes match the first attributes we encountered. */ + const u32 cur_attr = l2[l2_index + i].GetAttributes(); + if (!first && cur_attr != first_attr) { + return false; + } + + /* If there's nothing remaining in the range, refresh the range. */ + if (remaining_in_range == 0) { + contig_range.Close(); + + range_open = false; + if (R_FAILED(page_table->OpenMemoryRangeForUnmapDeviceAddressSpace(std::addressof(contig_range), cur_process_address, remaining_size))) { + return false; + } + range_open = true; + + cur_phys_address = GetHeapPhysicalAddress(contig_range.address); + remaining_in_range = contig_range.size; + } + + /* Check that the physical address is expected. */ + if (l2[l2_index + i].GetPhysicalAddress() != cur_phys_address) { + return false; + } + + /* Advance. */ + cur_phys_address += DevicePageSize; + cur_process_address += DevicePageSize; + remaining_size -= DevicePageSize; + remaining_in_range -= DevicePageSize; + + first = false; + first_attr = cur_attr; } - /* Advance. */ - address += DevicePageSize * map_count; - remaining -= DevicePageSize * map_count; + /* Advance the device address. */ + device_address += map_count * DevicePageSize; } else { /* We're acting on an l1 entry. */ - R_UNLESS(l2_index == 0, svc::ResultInvalidCurrentMemory()); - R_UNLESS(remaining >= DeviceLargePageSize, svc::ResultInvalidCurrentMemory()); + if (!(l2_index == 0 && remaining_size >= DeviceLargePageSize)) { + return false; + } - /* Get the physical address. */ - const KPhysicalAddress phys_addr = l1[l1_index].GetPhysicalAddress(); - MESOSPHERE_ASSERT(IsHeapPhysicalAddress(phys_addr)); + /* Check that the attributes match the first attributes we encountered. */ + const u32 cur_attr = l1[l1_index].GetAttributes(); + if (!first && cur_attr != first_attr) { + return false; + } - /* Add to the group. */ - R_TRY(out->AddBlock(GetHeapVirtualAddress(phys_addr), DeviceLargePageSize / PageSize)); + /* If there's nothing remaining in the range, refresh the range. */ + if (remaining_in_range == 0) { + contig_range.Close(); - /* If this is our first entry, get the attribute. */ - if (first) { - attr = l1[l1_index].GetAttributes(); - first = false; - } else { - /* Validate the attributes match the first entry. */ - R_UNLESS(l1[l1_index].GetAttributes() == attr, svc::ResultInvalidCurrentMemory()); + range_open = false; + if (R_FAILED(page_table->OpenMemoryRangeForUnmapDeviceAddressSpace(std::addressof(contig_range), cur_process_address, remaining_size))) { + return false; + } + range_open = true; + + cur_phys_address = GetHeapPhysicalAddress(contig_range.address); + remaining_in_range = contig_range.size; + } + + /* Check that the physical address is expected, and there's enough in the range. */ + if (remaining_in_range < DeviceLargePageSize || l1[l1_index].GetPhysicalAddress() != cur_phys_address) { + return false; } /* Advance. */ - address += DeviceLargePageSize; - remaining -= DeviceLargePageSize; + cur_phys_address += DeviceLargePageSize; + cur_process_address += DeviceLargePageSize; + remaining_size -= DeviceLargePageSize; + remaining_in_range -= DeviceLargePageSize; + + first = false; + first_attr = cur_attr; + + /* Advance the device address. */ + device_address += DeviceLargePageSize; } } - return ResultSuccess(); + /* The range is valid! */ + return true; } - bool KDevicePageTable::Compare(const KPageGroup &compare_pg, KDeviceVirtualAddress device_address) const { - /* Check whether the page group we expect for the virtual address matches the page group we're validating. */ - KPageGroup calc_pg(std::addressof(Kernel::GetBlockInfoManager())); - return (R_SUCCEEDED(this->MakePageGroup(std::addressof(calc_pg), device_address, compare_pg.GetNumPages() * PageSize))) && - calc_pg.IsEquivalentTo(compare_pg); - } - - Result KDevicePageTable::Map(size_t *out_mapped_size, const KPageGroup &pg, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool refresh_mappings) { + Result KDevicePageTable::Map(size_t *out_mapped_size, KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool refresh_mappings) { /* Clear the output size. */ *out_mapped_size = 0; /* Map the pages. */ s32 num_pt = 0; - return this->MapImpl(out_mapped_size, num_pt, refresh_mappings ? 1 : std::numeric_limits::max(), pg, device_address, device_perm); + return this->MapImpl(out_mapped_size, num_pt, refresh_mappings ? 1 : std::numeric_limits::max(), page_table, process_address, size, device_address, device_perm, refresh_mappings); } - Result KDevicePageTable::Unmap(const KPageGroup &pg, KDeviceVirtualAddress device_address) { + Result KDevicePageTable::Unmap(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address) { /* Validate address/size. */ - const size_t size = pg.GetNumPages() * PageSize; MESOSPHERE_ASSERT((device_address & ~DeviceVirtualAddressMask) == 0); MESOSPHERE_ASSERT(((device_address + size - 1) & ~DeviceVirtualAddressMask) == 0); /* Ensure the page group is correct. */ - R_UNLESS(this->Compare(pg, device_address), svc::ResultInvalidCurrentMemory()); + R_UNLESS(this->Compare(page_table, process_address, size, device_address), svc::ResultInvalidCurrentMemory()); /* Unmap the pages. */ this->UnmapImpl(device_address, size, false); diff --git a/libraries/libmesosphere/source/kern_k_device_address_space.cpp b/libraries/libmesosphere/source/kern_k_device_address_space.cpp index f9d0d23cd..28bfb6934 100644 --- a/libraries/libmesosphere/source/kern_k_device_address_space.cpp +++ b/libraries/libmesosphere/source/kern_k_device_address_space.cpp @@ -71,12 +71,11 @@ namespace ams::kern { /* Lock the address space. */ KScopedLightLock lk(m_lock); - /* Lock the pages. */ - KPageGroup pg(page_table->GetBlockInfoManager()); - R_TRY(page_table->LockForDeviceAddressSpace(std::addressof(pg), process_address, size, ConvertToKMemoryPermission(device_perm), is_aligned)); + /* Lock the page table to prevent concurrent device mapping operations. */ + KScopedLightLock pt_lk = page_table->AcquireDeviceMapLock(); - /* Close the pages we opened when we're done with them. */ - ON_SCOPE_EXIT { pg.Close(); }; + /* Lock the pages. */ + R_TRY(page_table->LockForMapDeviceAddressSpace(process_address, size, ConvertToKMemoryPermission(device_perm), is_aligned)); /* Ensure that if we fail, we don't keep unmapped pages locked. */ auto unlock_guard = SCOPE_GUARD { MESOSPHERE_R_ABORT_UNLESS(page_table->UnlockForDeviceAddressSpace(process_address, size)); }; @@ -87,7 +86,7 @@ namespace ams::kern { auto mapped_size_guard = SCOPE_GUARD { *out_mapped_size = 0; }; /* Perform the mapping. */ - R_TRY(m_table.Map(out_mapped_size, pg, device_address, device_perm, refresh_mappings)); + R_TRY(m_table.Map(out_mapped_size, page_table, process_address, size, device_address, device_perm, refresh_mappings)); /* Ensure that we unmap the pages if we fail to update the protections. */ /* NOTE: Nintendo does not check the result of this unmap call. */ @@ -113,19 +112,18 @@ namespace ams::kern { /* Lock the address space. */ KScopedLightLock lk(m_lock); - /* Make and open a page group for the unmapped region. */ - KPageGroup pg(page_table->GetBlockInfoManager()); - R_TRY(page_table->MakePageGroupForUnmapDeviceAddressSpace(std::addressof(pg), process_address, size)); + /* Lock the page table to prevent concurrent device mapping operations. */ + KScopedLightLock pt_lk = page_table->AcquireDeviceMapLock(); - /* Ensure the page group is closed on scope exit. */ - ON_SCOPE_EXIT { pg.Close(); }; + /* Lock the pages. */ + R_TRY(page_table->LockForUnmapDeviceAddressSpace(process_address, size)); /* If we fail to unmap, we want to do a partial unlock. */ { auto unlock_guard = SCOPE_GUARD { page_table->UnlockForDeviceAddressSpacePartialMap(process_address, size, size); }; /* Unmap. */ - R_TRY(m_table.Unmap(pg, device_address)); + R_TRY(m_table.Unmap(page_table, process_address, size, device_address)); unlock_guard.Cancel(); } diff --git a/libraries/libmesosphere/source/kern_k_page_table_base.cpp b/libraries/libmesosphere/source/kern_k_page_table_base.cpp index 3f2aff676..d47fad006 100644 --- a/libraries/libmesosphere/source/kern_k_page_table_base.cpp +++ b/libraries/libmesosphere/source/kern_k_page_table_base.cpp @@ -74,6 +74,10 @@ namespace ams::kern { } + void KPageTableBase::MemoryRange::Close() { + Kernel::GetMemoryManager().Close(address, size / PageSize); + } + Result KPageTableBase::InitializeForKernel(bool is_64_bit, void *table, KVirtualAddress start, KVirtualAddress end) { /* Initialize our members. */ m_address_space_width = (is_64_bit) ? BITSIZEOF(u64) : BITSIZEOF(u32); @@ -1391,6 +1395,49 @@ namespace ams::kern { return cur_block_address == GetHeapVirtualAddress(cur_addr) && cur_block_pages == (cur_size / PageSize); } + Result KPageTableBase::GetContiguousMemoryRangeWithState(MemoryRange *out, KProcessAddress address, size_t size, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr) { + MESOSPHERE_ASSERT(this->IsLockedByCurrentThread()); + + auto &impl = this->GetImpl(); + + /* Begin a traversal. */ + TraversalContext context; + TraversalEntry cur_entry = {}; + R_UNLESS(impl.BeginTraversal(std::addressof(cur_entry), std::addressof(context), address), svc::ResultInvalidCurrentMemory()); + + /* The region we're traversing has to be heap. */ + const KPhysicalAddress phys_address = cur_entry.phys_addr; + R_UNLESS(this->IsHeapPhysicalAddress(phys_address), svc::ResultInvalidCurrentMemory()); + + /* Traverse until we have enough size or we aren't contiguous any more. */ + size_t contig_size; + for (contig_size = cur_entry.block_size - (GetInteger(phys_address) & (cur_entry.block_size - 1)); contig_size < size; contig_size += cur_entry.block_size) { + if (!impl.ContinueTraversal(std::addressof(cur_entry), std::addressof(context))) { + break; + } + if (cur_entry.phys_addr != phys_address + contig_size) { + break; + } + } + + /* Take the minimum size for our region. */ + size = std::min(size, contig_size); + + /* Check that the memory is contiguous. */ + R_TRY(this->CheckMemoryStateContiguous(address, size, + state_mask | KMemoryState_FlagReferenceCounted, state | KMemoryState_FlagReferenceCounted, + perm_mask, perm, + attr_mask, attr)); + + /* The memory is contiguous, so set the output range. */ + *out = { + .address = GetLinearMappedVirtualAddress(phys_address), + .size = size, + }; + + return ResultSuccess(); + } + Result KPageTableBase::SetMemoryPermission(KProcessAddress addr, size_t size, ams::svc::MemoryPermission svc_perm) { const size_t num_pages = size / PageSize; @@ -2578,7 +2625,7 @@ namespace ams::kern { return ResultSuccess(); } - Result KPageTableBase::LockForDeviceAddressSpace(KPageGroup *out, KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned) { + Result KPageTableBase::LockForMapDeviceAddressSpace(KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned) { /* Lightly validate the range before doing anything else. */ const size_t num_pages = size / PageSize; R_UNLESS(this->Contains(address, size), svc::ResultInvalidCurrentMemory()); @@ -2591,11 +2638,6 @@ namespace ams::kern { size_t num_allocator_blocks; R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, test_state, test_state, perm, perm, KMemoryAttribute_IpcLocked | KMemoryAttribute_Locked, KMemoryAttribute_None, KMemoryAttribute_DeviceShared)); - /* Make the page group, if we should. */ - if (out != nullptr) { - R_TRY(this->MakePageGroup(*out, address, num_pages)); - } - /* Create an update allocator. */ Result allocator_result; KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); @@ -2604,10 +2646,33 @@ namespace ams::kern { /* Update the memory blocks. */ m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages, &KMemoryBlock::ShareToDevice, KMemoryPermission_None); - /* Open the page group. */ - if (out != nullptr) { - out->Open(); - } + return ResultSuccess(); + } + + Result KPageTableBase::LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size) { + /* Lightly validate the range before doing anything else. */ + const size_t num_pages = size / PageSize; + R_UNLESS(this->Contains(address, size), svc::ResultInvalidCurrentMemory()); + + /* Lock the table. */ + KScopedLightLock lk(m_general_lock); + + /* Check the memory state. */ + size_t num_allocator_blocks; + R_TRY(this->CheckMemoryStateContiguous(std::addressof(num_allocator_blocks), + address, size, + KMemoryState_FlagReferenceCounted | KMemoryState_FlagCanDeviceMap, KMemoryState_FlagReferenceCounted | KMemoryState_FlagCanDeviceMap, + KMemoryPermission_None, KMemoryPermission_None, + KMemoryAttribute_DeviceShared | KMemoryAttribute_Locked, KMemoryAttribute_DeviceShared)); + + /* Create an update allocator. */ + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); + + /* Update the memory blocks. */ + const KMemoryBlockManager::MemoryBlockLockFunction lock_func = m_enable_device_address_space_merge ? &KMemoryBlock::UpdateDeviceDisableMergeStateForShare : &KMemoryBlock::UpdateDeviceDisableMergeStateForShareRight; + m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages, lock_func, KMemoryPermission_None); return ResultSuccess(); } @@ -2639,40 +2704,6 @@ namespace ams::kern { return ResultSuccess(); } - Result KPageTableBase::MakePageGroupForUnmapDeviceAddressSpace(KPageGroup *out, KProcessAddress address, size_t size) { - /* Lightly validate the range before doing anything else. */ - const size_t num_pages = size / PageSize; - R_UNLESS(this->Contains(address, size), svc::ResultInvalidCurrentMemory()); - - /* Lock the table. */ - KScopedLightLock lk(m_general_lock); - - /* Check the memory state. */ - size_t num_allocator_blocks; - R_TRY(this->CheckMemoryStateContiguous(std::addressof(num_allocator_blocks), - address, size, - KMemoryState_FlagReferenceCounted | KMemoryState_FlagCanDeviceMap, KMemoryState_FlagReferenceCounted | KMemoryState_FlagCanDeviceMap, - KMemoryPermission_None, KMemoryPermission_None, - KMemoryAttribute_DeviceShared | KMemoryAttribute_Locked, KMemoryAttribute_DeviceShared)); - - /* Create an update allocator. */ - Result allocator_result; - KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); - R_TRY(allocator_result); - - /* Make the page group. */ - R_TRY(this->MakePageGroup(*out, address, num_pages)); - - /* Update the memory blocks. */ - const KMemoryBlockManager::MemoryBlockLockFunction lock_func = m_enable_device_address_space_merge ? &KMemoryBlock::UpdateDeviceDisableMergeStateForShare : &KMemoryBlock::UpdateDeviceDisableMergeStateForShareRight; - m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages, lock_func, KMemoryPermission_None); - - /* Open a reference to the pages in the page group. */ - out->Open(); - - return ResultSuccess(); - } - Result KPageTableBase::UnlockForDeviceAddressSpacePartialMap(KProcessAddress address, size_t size, size_t mapped_size) { /* Lightly validate the range before doing anything else. */ const size_t num_pages = size / PageSize; @@ -2689,23 +2720,23 @@ namespace ams::kern { size_t allocator_num_blocks = 0, unmapped_allocator_num_blocks = 0; if (unmapped_size) { if (m_enable_device_address_space_merge) { - R_TRY(this->CheckMemoryState(std::addressof(allocator_num_blocks), - address, size, - KMemoryState_FlagCanDeviceMap, KMemoryState_FlagCanDeviceMap, - KMemoryPermission_None, KMemoryPermission_None, - KMemoryAttribute_DeviceShared | KMemoryAttribute_Locked, KMemoryAttribute_DeviceShared)); + R_TRY(this->CheckMemoryStateContiguous(std::addressof(allocator_num_blocks), + address, size, + KMemoryState_FlagCanDeviceMap, KMemoryState_FlagCanDeviceMap, + KMemoryPermission_None, KMemoryPermission_None, + KMemoryAttribute_DeviceShared | KMemoryAttribute_Locked, KMemoryAttribute_DeviceShared)); } - R_TRY(this->CheckMemoryState(std::addressof(unmapped_allocator_num_blocks), - mapped_end_address, unmapped_size, - KMemoryState_FlagCanDeviceMap, KMemoryState_FlagCanDeviceMap, - KMemoryPermission_None, KMemoryPermission_None, - KMemoryAttribute_DeviceShared | KMemoryAttribute_Locked, KMemoryAttribute_DeviceShared)); + R_TRY(this->CheckMemoryStateContiguous(std::addressof(unmapped_allocator_num_blocks), + mapped_end_address, unmapped_size, + KMemoryState_FlagCanDeviceMap, KMemoryState_FlagCanDeviceMap, + KMemoryPermission_None, KMemoryPermission_None, + KMemoryAttribute_DeviceShared | KMemoryAttribute_Locked, KMemoryAttribute_DeviceShared)); } else { - R_TRY(this->CheckMemoryState(std::addressof(allocator_num_blocks), - address, size, - KMemoryState_FlagCanDeviceMap, KMemoryState_FlagCanDeviceMap, - KMemoryPermission_None, KMemoryPermission_None, - KMemoryAttribute_DeviceShared | KMemoryAttribute_Locked, KMemoryAttribute_DeviceShared)); + R_TRY(this->CheckMemoryStateContiguous(std::addressof(allocator_num_blocks), + address, size, + KMemoryState_FlagCanDeviceMap, KMemoryState_FlagCanDeviceMap, + KMemoryPermission_None, KMemoryPermission_None, + KMemoryAttribute_DeviceShared | KMemoryAttribute_Locked, KMemoryAttribute_DeviceShared)); } /* Create an update allocator for the region. */ @@ -2750,6 +2781,41 @@ namespace ams::kern { return ResultSuccess(); } + Result KPageTableBase::OpenMemoryRangeForMapDeviceAddressSpace(KPageTableBase::MemoryRange *out, KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned) { + /* Lock the table. */ + KScopedLightLock lk(m_general_lock); + + /* Get the range. */ + const u32 test_state = KMemoryState_FlagReferenceCounted | (is_aligned ? KMemoryState_FlagCanAlignedDeviceMap : KMemoryState_FlagCanDeviceMap); + R_TRY(this->GetContiguousMemoryRangeWithState(out, + address, size, + test_state, test_state, + perm, perm, + KMemoryAttribute_IpcLocked | KMemoryAttribute_Locked, KMemoryAttribute_None)); + + /* We got the range, so open it. */ + Kernel::GetMemoryManager().Open(out->address, out->size / PageSize); + + return ResultSuccess(); + } + + Result KPageTableBase::OpenMemoryRangeForUnmapDeviceAddressSpace(MemoryRange *out, KProcessAddress address, size_t size) { + /* Lock the table. */ + KScopedLightLock lk(m_general_lock); + + /* Get the range. */ + R_TRY(this->GetContiguousMemoryRangeWithState(out, + address, size, + KMemoryState_FlagCanDeviceMap, KMemoryState_FlagCanDeviceMap, + KMemoryPermission_None, KMemoryPermission_None, + KMemoryAttribute_DeviceShared | KMemoryAttribute_Locked, KMemoryAttribute_DeviceShared)); + + /* We got the range, so open it. */ + Kernel::GetMemoryManager().Open(out->address, out->size / PageSize); + + return ResultSuccess(); + } + Result KPageTableBase::LockForIpcUserBuffer(KPhysicalAddress *out, KProcessAddress address, size_t size) { return this->LockMemoryAndOpen(nullptr, out, address, size, KMemoryState_FlagCanIpcUserBuffer, KMemoryState_FlagCanIpcUserBuffer, @@ -2804,6 +2870,23 @@ namespace ams::kern { KMemoryAttribute_Locked, std::addressof(pg)); } + Result KPageTableBase::OpenMemoryRangeForProcessCacheOperation(MemoryRange *out, KProcessAddress address, size_t size) { + /* Lock the table. */ + KScopedLightLock lk(m_general_lock); + + /* Get the range. */ + R_TRY(this->GetContiguousMemoryRangeWithState(out, + address, size, + KMemoryState_FlagReferenceCounted, KMemoryState_FlagReferenceCounted, + KMemoryPermission_UserRead, KMemoryPermission_UserRead, + KMemoryAttribute_Uncached, KMemoryAttribute_None)); + + /* We got the range, so open it. */ + Kernel::GetMemoryManager().Open(out->address, out->size / PageSize); + + return ResultSuccess(); + } + Result KPageTableBase::CopyMemoryFromLinearToUser(KProcessAddress dst_addr, size_t size, KProcessAddress src_addr, u32 src_state_mask, u32 src_state, KMemoryPermission src_test_perm, u32 src_attr_mask, u32 src_attr) { /* Lightly validate the range before doing anything else. */ R_UNLESS(this->Contains(src_addr, size), svc::ResultInvalidCurrentMemory()); @@ -4553,4 +4636,108 @@ namespace ams::kern { return ResultSuccess(); } + Result KPageTableBase::UnmapProcessMemory(KProcessAddress dst_address, size_t size, KPageTableBase &src_page_table, KProcessAddress src_address) { + /* We need to lock both this table, and the current process's table, so set up an alias. */ + KPageTableBase &dst_page_table = *this; + + /* Acquire the table locks. */ + KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock); + + /* Check that the memory is mapped in the destination process. */ + size_t num_allocator_blocks; + R_TRY(dst_page_table.CheckMemoryState(std::addressof(num_allocator_blocks), dst_address, size, KMemoryState_All, KMemoryState_SharedCode, KMemoryPermission_UserReadWrite, KMemoryPermission_UserReadWrite, KMemoryAttribute_All, KMemoryAttribute_None)); + + /* Check that the memory is mapped in the source process. */ + R_TRY(src_page_table.CheckMemoryState(src_address, size, KMemoryState_FlagCanMapProcess, KMemoryState_FlagCanMapProcess, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_All, KMemoryAttribute_None)); + + /* Validate that the memory ranges are compatible. */ + { + /* Define a helper type. */ + struct ContiguousRangeInfo { + public: + KPageTableBase &m_pt; + TraversalContext m_context; + TraversalEntry m_entry; + KPhysicalAddress m_phys_addr; + size_t m_cur_size; + size_t m_remaining_size; + public: + ContiguousRangeInfo(KPageTableBase &pt, KProcessAddress address, size_t size) : m_pt(pt), m_remaining_size(size) { + /* Begin a traversal. */ + MESOSPHERE_ABORT_UNLESS(m_pt.GetImpl().BeginTraversal(std::addressof(m_entry), std::addressof(m_context), address)); + + /* Setup tracking fields. */ + m_phys_addr = m_entry.phys_addr; + m_cur_size = std::min(m_remaining_size, m_entry.block_size - (GetInteger(m_phys_addr) & (m_entry.block_size - 1))); + + /* Consume the whole contiguous block. */ + this->DetermineContiguousBlockExtents(); + } + + void ContinueTraversal() { + /* Update our remaining size. */ + m_remaining_size = m_remaining_size - m_cur_size; + + /* Update our tracking fields. */ + if (m_remaining_size > 0) { + m_phys_addr = m_entry.phys_addr; + m_cur_size = std::min(m_remaining_size, m_entry.block_size); + + /* Consume the whole contiguous block. */ + this->DetermineContiguousBlockExtents(); + } + } + private: + void DetermineContiguousBlockExtents() { + /* Continue traversing until we're not contiguous, or we have enough. */ + while (m_cur_size < m_remaining_size) { + MESOSPHERE_ABORT_UNLESS(m_pt.GetImpl().ContinueTraversal(std::addressof(m_entry), std::addressof(m_context))); + + /* If we're not contiguous, we're done. */ + if (m_entry.phys_addr != m_phys_addr + m_cur_size) { + break; + } + + /* Update our current size. */ + m_cur_size = std::min(m_remaining_size, m_cur_size + m_entry.block_size); + } + } + }; + + /* Create ranges for both tables. */ + ContiguousRangeInfo src_range(src_page_table, src_address, size); + ContiguousRangeInfo dst_range(dst_page_table, dst_address, size); + + /* Validate the ranges. */ + while (src_range.m_remaining_size > 0 && dst_range.m_remaining_size > 0) { + R_UNLESS(src_range.m_phys_addr == dst_range.m_phys_addr, svc::ResultInvalidMemoryRegion()); + R_UNLESS(src_range.m_cur_size == dst_range.m_cur_size, svc::ResultInvalidMemoryRegion()); + + src_range.ContinueTraversal(); + dst_range.ContinueTraversal(); + } + } + + /* We no longer need to hold our lock on the source page table. */ + lk.TryUnlockHalf(src_page_table.m_general_lock); + + /* Create an update allocator. */ + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); + + /* We're going to perform an update, so create a helper. */ + KScopedPageTableUpdater updater(this); + + /* Unmap the memory. */ + const size_t num_pages = size / PageSize; + const KPageProperties unmap_properties = { KMemoryPermission_None, false, false, DisableMergeAttribute_None }; + R_TRY(this->Operate(updater.GetPageList(), dst_address, num_pages, Null, false, unmap_properties, OperationType_Unmap, false)); + + /* Apply the memory block update. */ + m_memory_block_manager.Update(std::addressof(allocator), dst_address, num_pages, KMemoryState_Free, KMemoryPermission_None, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_Normal); + + return ResultSuccess(); + } + } diff --git a/libraries/libmesosphere/source/svc/kern_svc_cache.cpp b/libraries/libmesosphere/source/svc/kern_svc_cache.cpp index 8deb3f595..e5b6e3ae4 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_cache.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_cache.cpp @@ -30,32 +30,24 @@ namespace ams::kern::svc { /* Determine aligned extents. */ const uintptr_t aligned_start = util::AlignDown(address, PageSize); const uintptr_t aligned_end = util::AlignUp(address + size, PageSize); - const size_t num_pages = (aligned_end - aligned_start) / PageSize; - /* Create a page group for the process's memory. */ - KPageGroup pg(page_table.GetBlockInfoManager()); - - /* Make and open the page group. */ - R_TRY(page_table.MakeAndOpenPageGroup(std::addressof(pg), - aligned_start, num_pages, - KMemoryState_FlagReferenceCounted, KMemoryState_FlagReferenceCounted, - KMemoryPermission_UserRead, KMemoryPermission_UserRead, - KMemoryAttribute_Uncached, KMemoryAttribute_None)); - - /* Ensure we don't leak references to the pages we're operating on. */ - ON_SCOPE_EXIT { pg.Close(); }; - - /* Operate on all the blocks. */ + /* Iterate over and operate on contiguous ranges. */ uintptr_t cur_address = aligned_start; size_t remaining = size; - for (const auto &block : pg) { - /* Get the block extents. */ - KVirtualAddress operate_address = block.GetAddress(); - size_t operate_size = block.GetSize(); + while (remaining > 0) { + /* Get a contiguous range to operate on. */ + KPageTableBase::MemoryRange contig_range = {}; + R_TRY(page_table.OpenMemoryRangeForProcessCacheOperation(std::addressof(contig_range), cur_address, aligned_end - cur_address)); + + /* Close the range when we're done operating on it. */ + ON_SCOPE_EXIT { contig_range.Close(); }; /* Adjust to remain within range. */ + KVirtualAddress operate_address = contig_range.address; + size_t operate_size = contig_range.size; if (cur_address < address) { operate_address += (address - cur_address); + operate_size -= (address - cur_address); } if (operate_size > remaining) { operate_size = remaining; @@ -65,7 +57,7 @@ namespace ams::kern::svc { operation.Operate(GetVoidPointer(operate_address), operate_size); /* Advance. */ - cur_address += block.GetSize(); + cur_address += contig_range.size; remaining -= operate_size; } MESOSPHERE_ASSERT(remaining == 0); diff --git a/libraries/libmesosphere/source/svc/kern_svc_process_memory.cpp b/libraries/libmesosphere/source/svc/kern_svc_process_memory.cpp index bd8f8cb0c..7df608e67 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_process_memory.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_process_memory.cpp @@ -122,21 +122,8 @@ namespace ams::kern::svc { R_UNLESS(src_pt.Contains(src_address, size), svc::ResultInvalidCurrentMemory()); R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState_SharedCode), svc::ResultInvalidMemoryRegion()); - /* Create a new page group. */ - KPageGroup pg(dst_pt.GetBlockInfoManager()); - - /* Make the page group. */ - R_TRY(src_pt.MakeAndOpenPageGroup(std::addressof(pg), - src_address, size / PageSize, - KMemoryState_FlagCanMapProcess, KMemoryState_FlagCanMapProcess, - KMemoryPermission_None, KMemoryPermission_None, - KMemoryAttribute_All, KMemoryAttribute_None)); - - /* Close the page group when we're done. */ - ON_SCOPE_EXIT { pg.Close(); }; - - /* Unmap the group. */ - R_TRY(dst_pt.UnmapPageGroup(dst_address, pg, KMemoryState_SharedCode)); + /* Unmap the memory. */ + R_TRY(dst_pt.UnmapProcessMemory(dst_address, size, src_pt, src_address)); return ResultSuccess(); } From 1d2be0a2eb43a345422f89e29f6ecb9ecda40aff Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 17:11:01 -0700 Subject: [PATCH 155/280] kern: mesosphere now implements kernel/sdk 12.3 --- libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp | 2 +- libraries/libvapours/include/vapours/svc/svc_version.hpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp index b8478f5b5..27ffe1a2a 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp @@ -330,7 +330,7 @@ namespace ams::kern { } ALWAYS_INLINE void ClearDpc(DpcFlag flag) { - this->GetStackParameters().dpc_flags.fetch_and(~flag);; + this->GetStackParameters().dpc_flags.fetch_and(~flag); } ALWAYS_INLINE u8 GetDpc() const { diff --git a/libraries/libvapours/include/vapours/svc/svc_version.hpp b/libraries/libvapours/include/vapours/svc/svc_version.hpp index 1a5a5ce1f..52675b5ac 100644 --- a/libraries/libvapours/include/vapours/svc/svc_version.hpp +++ b/libraries/libvapours/include/vapours/svc/svc_version.hpp @@ -57,8 +57,8 @@ namespace ams::svc { /* This is the highest SVC version supported by Atmosphere, to be updated on new kernel releases. */ /* NOTE: Official kernel versions have SVC major = SDK major + 4, SVC minor = SDK minor. */ - constexpr inline u32 SupportedKernelMajorVersion = ConvertToSvcMajorVersion(11); - constexpr inline u32 SupportedKernelMinorVersion = ConvertToSvcMinorVersion( 4); + constexpr inline u32 SupportedKernelMajorVersion = ConvertToSvcMajorVersion(12); + constexpr inline u32 SupportedKernelMinorVersion = ConvertToSvcMinorVersion( 3); constexpr inline u32 SupportedKernelVersion = EncodeKernelVersion(SupportedKernelMajorVersion, SupportedKernelMinorVersion); From dc6a0d756285ff0f1f1ee7caf3e1c8e27a093829 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 17:24:18 -0700 Subject: [PATCH 156/280] ams: add target firmware 12.0.0, fusee recognition --- fusee/fusee-secondary/src/emummc_cfg.h | 3 +++ fusee/fusee-secondary/src/ips.c | 3 +++ fusee/fusee-secondary/src/nxboot.c | 10 +++++++++- .../include/stratosphere/hos/hos_types.hpp | 1 + .../include/vapours/ams/ams_target_firmware.h | 4 +++- 5 files changed, 19 insertions(+), 2 deletions(-) diff --git a/fusee/fusee-secondary/src/emummc_cfg.h b/fusee/fusee-secondary/src/emummc_cfg.h index 0099bd2d8..330305aa3 100644 --- a/fusee/fusee-secondary/src/emummc_cfg.h +++ b/fusee/fusee-secondary/src/emummc_cfg.h @@ -91,6 +91,9 @@ typedef enum { FS_VER_11_0_0, FS_VER_11_0_0_EXFAT, + FS_VER_12_0_0, + FS_VER_12_0_0_EXFAT, + FS_VER_MAX, } emummc_fs_ver_t; diff --git a/fusee/fusee-secondary/src/ips.c b/fusee/fusee-secondary/src/ips.c index 2f17730ed..13957f2f4 100644 --- a/fusee/fusee-secondary/src/ips.c +++ b/fusee/fusee-secondary/src/ips.c @@ -426,6 +426,9 @@ static const uint8_t g_fs_hashes[FS_VER_MAX][0x8] = { "\xE3\x99\x15\x6E\x84\x4E\xB0\xAA", /* FS_VER_11_0_0 */ "\x0B\xA1\x5B\xB3\x04\xB5\x05\x63", /* FS_VER_11_0_0_EXFAT */ + + "\xDC\x2A\x08\x49\x96\xBB\x3C\x01", /* FS_VER_12_0_0 */ + "\xD5\xA5\xBF\x36\x64\x0C\x49\xEA", /* FS_VER_12_0_0_EXFAT */ }; kip1_header_t *apply_kip_ips_patches(kip1_header_t *kip, size_t kip_size, emummc_fs_ver_t *out_fs_ver) { diff --git a/fusee/fusee-secondary/src/nxboot.c b/fusee/fusee-secondary/src/nxboot.c index ee81427e1..67e291ec4 100644 --- a/fusee/fusee-secondary/src/nxboot.c +++ b/fusee/fusee-secondary/src/nxboot.c @@ -285,7 +285,9 @@ static bool is_nca_present(const char *nca_name) { static uint32_t nxboot_get_specific_target_firmware(uint32_t target_firmware){ #define CHECK_NCA(NCA_ID, VERSION) do { if (is_nca_present(NCA_ID)) { return ATMOSPHERE_TARGET_FIRMWARE_##VERSION; } } while(0) - if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_11_0_0) { + if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_12_0_0) { + CHECK_NCA("bd4185843550fbba125b20787005d1d2", 12_0_0); + } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_11_0_0) { CHECK_NCA("56211c7a5ed20a5332f5cdda67121e37", 11_0_1); CHECK_NCA("594c90bcdbcccad6b062eadba0cd0e7e", 11_0_0); } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_10_0_0) { @@ -388,6 +390,8 @@ static uint32_t nxboot_get_target_firmware(const void *package1loader) { return ATMOSPHERE_TARGET_FIRMWARE_10_0_0; } else if (memcmp(package1loader_header->build_timestamp, "20201030", 8) == 0) { return ATMOSPHERE_TARGET_FIRMWARE_11_0_0; + } else if (memcmp(package1loader_header->build_timestamp, "20210129", 8) == 0) { + return ATMOSPHERE_TARGET_FIRMWARE_12_0_0; } else { fatal_error("[NXBOOT] Unable to identify package1!\n"); } @@ -595,6 +599,10 @@ static void nxboot_configure_stratosphere(uint32_t target_firmware) { if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_11_0_0 && !(fuse_get_reserved_odm(7) & ~0x00001FFF)) { kip_patches_set_enable_nogc(); } + + /* NOTE: 12.0.0 added a new lotus firmware, but did not burn a fuse. */ + /* This is literally undetectable using normal fuses.... */ + /* C'est la vie. */ } } diff --git a/libraries/libstratosphere/include/stratosphere/hos/hos_types.hpp b/libraries/libstratosphere/include/stratosphere/hos/hos_types.hpp index 7e9ce33d0..ac7a56c24 100644 --- a/libraries/libstratosphere/include/stratosphere/hos/hos_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/hos/hos_types.hpp @@ -60,6 +60,7 @@ namespace ams::hos { Version_10_2_0 = ::ams::TargetFirmware_10_2_0, Version_11_0_0 = ::ams::TargetFirmware_11_0_0, Version_11_0_1 = ::ams::TargetFirmware_11_0_1, + Version_12_0_0 = ::ams::TargetFirmware_12_0_0, Version_Current = ::ams::TargetFirmware_Current, diff --git a/libraries/libvapours/include/vapours/ams/ams_target_firmware.h b/libraries/libvapours/include/vapours/ams/ams_target_firmware.h index cd6e4dc5f..9a4dde468 100644 --- a/libraries/libvapours/include/vapours/ams/ams_target_firmware.h +++ b/libraries/libvapours/include/vapours/ams/ams_target_firmware.h @@ -58,8 +58,9 @@ #define ATMOSPHERE_TARGET_FIRMWARE_10_2_0 ATMOSPHERE_TARGET_FIRMWARE(10, 2, 0) #define ATMOSPHERE_TARGET_FIRMWARE_11_0_0 ATMOSPHERE_TARGET_FIRMWARE(11, 0, 0) #define ATMOSPHERE_TARGET_FIRMWARE_11_0_1 ATMOSPHERE_TARGET_FIRMWARE(11, 0, 1) +#define ATMOSPHERE_TARGET_FIRMWARE_12_0_0 ATMOSPHERE_TARGET_FIRMWARE(12, 0, 0) -#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_11_0_1 +#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_12_0_0 #define ATMOSPHERE_TARGET_FIRMWARE_MIN ATMOSPHERE_TARGET_FIRMWARE(0, 0, 0) #define ATMOSPHERE_TARGET_FIRMWARE_MAX ATMOSPHERE_TARGET_FIRMWARE_CURRENT @@ -110,6 +111,7 @@ namespace ams { TargetFirmware_10_2_0 = ATMOSPHERE_TARGET_FIRMWARE_10_2_0, TargetFirmware_11_0_0 = ATMOSPHERE_TARGET_FIRMWARE_11_0_0, TargetFirmware_11_0_1 = ATMOSPHERE_TARGET_FIRMWARE_11_0_1, + TargetFirmware_12_0_0 = ATMOSPHERE_TARGET_FIRMWARE_12_0_0, TargetFirmware_Current = ATMOSPHERE_TARGET_FIRMWARE_CURRENT, From e93d71d9329dcc824d0bbb21811c750da0e713a8 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 8 Apr 2021 01:46:16 -0700 Subject: [PATCH 157/280] tipc: tentative core serialization logic (missing imports, won't compile) --- .../libstratosphere/include/stratosphere.hpp | 1 + .../include/stratosphere/tipc.hpp | 19 + .../impl/tipc_impl_command_serialization.hpp | 618 ++++++++++++++++++ .../libvapours/include/vapours/results.hpp | 1 + .../include/vapours/results/tipc_results.hpp | 30 + .../vapours/svc/ipc/svc_message_buffer.hpp | 26 +- 6 files changed, 688 insertions(+), 7 deletions(-) create mode 100644 libraries/libstratosphere/include/stratosphere/tipc.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/tipc/impl/tipc_impl_command_serialization.hpp create mode 100644 libraries/libvapours/include/vapours/results/tipc_results.hpp diff --git a/libraries/libstratosphere/include/stratosphere.hpp b/libraries/libstratosphere/include/stratosphere.hpp index 2e0743841..b2b5cfff4 100644 --- a/libraries/libstratosphere/include/stratosphere.hpp +++ b/libraries/libstratosphere/include/stratosphere.hpp @@ -84,6 +84,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libstratosphere/include/stratosphere/tipc.hpp b/libraries/libstratosphere/include/stratosphere/tipc.hpp new file mode 100644 index 000000000..c8d6589f0 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/tipc.hpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include diff --git a/libraries/libstratosphere/include/stratosphere/tipc/impl/tipc_impl_command_serialization.hpp b/libraries/libstratosphere/include/stratosphere/tipc/impl/tipc_impl_command_serialization.hpp new file mode 100644 index 000000000..619812b85 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/tipc/impl/tipc_impl_command_serialization.hpp @@ -0,0 +1,618 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::tipc { + + struct ClientProcessId { + os::ProcessId value; + }; + static_assert(std::is_trivial::value && sizeof(ClientProcessId) == sizeof(os::ProcessId), "ClientProcessId"); + +} + +namespace ams::tipc::impl { + + /* Machinery for filtering type lists. */ + template + struct TupleCat; + + template + struct TupleCat, std::tuple> { + using type = std::tuple; + }; + + template