├── .clang-format ├── .gitignore ├── AppleHPMLib.h ├── LICENSE ├── Makefile ├── README.md ├── main.cpp └── ssops.h /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | IndentWidth: 4 3 | UseTab: Never 4 | BreakBeforeBraces: Linux 5 | AllowShortIfStatementsOnASingleLine: Never 6 | AllowShortFunctionsOnASingleLine: false 7 | AlignConsecutiveMacros: true 8 | IndentCaseLabels: true 9 | ColumnLimit: 100 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | macvdmtool 2 | -------------------------------------------------------------------------------- /AppleHPMLib.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright © 2019 osy86. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #ifndef AppleHPMLib_h 18 | #define AppleHPMLib_h 19 | 20 | #include 21 | 22 | #define kAppleHPMLibType \ 23 | CFUUIDGetConstantUUIDWithBytes(kCFAllocatorDefault, 0x12, 0xA1, 0xDC, 0xCF, 0xCF, 0x7A, 0x47, \ 24 | 0x75, 0xBE, 0xE5, 0x9C, 0x43, 0x19, 0xF4, 0xCD, 0x2B) 25 | #define kAppleHPMLibInterface \ 26 | CFUUIDGetConstantUUIDWithBytes(kCFAllocatorDefault, 0xC1, 0x3A, 0xCD, 0xD9, 0x20, 0x9E, 0x4B, \ 27 | 0x01, 0xB7, 0xBE, 0xE0, 0x5C, 0xD8, 0x83, 0xC7, 0xB1) 28 | 29 | typedef struct { 30 | IUNKNOWN_C_GUTS; 31 | uint16_t field_20; 32 | uint16_t field_22; 33 | IOReturn (*Read)(void *, uint64_t chipAddr, uint8_t dataAddr, void *buffer, uint64_t maxLen, 34 | uint32_t flags, uint64_t *readLen); 35 | IOReturn (*Write)(void *, uint64_t chipAddr, uint8_t dataAd6dr, const void *buffer, 36 | uint64_t len, uint32_t flags); 37 | IOReturn (*Command)(void *, uint64_t chipAddr, uint32_t cmd, uint32_t flags); 38 | IOReturn (*field_40)(void); 39 | IOReturn (*field_48)(void); 40 | IOReturn (*field_50)(void); 41 | } AppleHPMLib; 42 | 43 | #endif /* AppleHPMLib_h */ 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CXXFLAGS := -std=c++14 2 | LDFLAGS := -framework CoreFoundation -framework IOKit -lc++ 3 | 4 | OBJS := main.o 5 | 6 | all: macvdmtool 7 | 8 | macvdmtool: $(OBJS) 9 | cc -o $@ $(OBJS) $(LDFLAGS) 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Apple Silicon to Apple Silicon VDM tool 2 | 3 | In progress version with basic support for iPhone 15 & support for listing actions. 4 | 5 | This tool lets you get a serial console on an Apple Silicon device and reboot it remotely, using only another Apple Silicon device running macOS and a standard Type C cable. 6 | 7 | ## Disclaimer 8 | 9 | I have no idea what I'm doing with IOKit and CoreFoundation -marcan 10 | 11 | ## Copyright 12 | 13 | This is based on portions of [ThunderboltPatcher](https://github.com/osy/ThunderboltPatcher) and licensed under Apache-2.0. 14 | 15 | * Copyright (C) 2019 osy86. All rights reserved. 16 | * Copyright (C) 2021 The Asahi Linux Contributors 17 | 18 | Thanks to t8012.dev and mrarm for assistance with the VDM and Ace2 host interface commands. 19 | 20 | ## Note about macOS 12 21 | 22 | To have access to the serial console device on macOS Monterey (12), you need to disable the `AppleSerialShim` extension. 23 | 24 | **Note:** This requires downgrading the system security and may cause problems with upgrades. Use it at your own risk! 25 | 26 | Start by generating a new kernel cache, without the `AppleSerialShim` extension: 27 | 28 | ``` 29 | sudo kmutil create -n boot -a arm64e -B /Library/KernelCollections/kc.noshim.macho -V release -k /System/Library/Kernels/kernel.release. -r /System/Library/Extensions -r /System/Library/DriverExtensions -x $(kmutil inspect -V release --no-header | awk '!/AppleSerialShim/ { print " -b "$1; }') 30 | ``` 31 | 32 | Replace `` with `t8101` on M1 Macs and `t6000` on M1 Pro/Max Macs. If you’re unsure, `uname -v` and look at the end of the version string (`RELEASE_ARM64_`). 33 | 34 | Then, enter 1TR: 35 | 36 | 1. Power off your Mac 37 | 2. Press and hold the Power button until the boot menu appears 38 | 3. Select “Options”, then (if necessary) select your macOS volume and enter your administrative password. 39 | 40 | Select Utilities>Startup security and switch the macOS installation to reduced security. Exit Startup security. 41 | 42 | Select Utilities>Terminal and install your custom kernel: 43 | 44 | ``` 45 | kmutil configure-boot -c /Volume//Library/KernelCollections/kc.noshim.macho -C -v /Volume/ 46 | ``` 47 | 48 | Replace `` with the name of your boot volume. 49 | 50 | You can now reboot: macOS should start as normal, and the serial device `/dev/cu.debug-console` should be available. 51 | 52 | To revert back to the default kernel, enter 1TR again, access Utilities>Startup security and switch to full or reduced security. 53 | 54 | ## Building 55 | 56 | Install the XCode commandline tools and type `make`. 57 | 58 | ## Usage 59 | 60 | Connect the two devices via their DFU ports. That's: 61 | - the rear port on MacBook Air and 13" MacBook Pro 62 | - the port next to the MagSafe connector on the 14" and 16" MacBook Pro 63 | - the port nearest to the power plug on Mac Mini 64 | 65 | You need to use a *USB 3.0 compatible* (SuperSpeed) Type C cable. USB 2.0-only cables, including most cables meant for charging, will not work, as they do not have the required pins. Thunderbolt cables work too. 66 | 67 | Run it as root (`sudo ./macvdmtool`). 68 | 69 | ``` 70 | Usage: ./macvdmtool 71 | Commands: 72 | serial - enter serial mode on both ends 73 | reboot - reboot the target 74 | reboot serial - reboot the target and enter serial mode 75 | dfu - put the target into DFU mode 76 | nop - do nothing 77 | ``` 78 | 79 | Use `/dev/cu.debug_console` on the local machine as your serial device. To use it with m1n1, `export M1N1DEVICE=/dev/cu.debug-console`. 80 | 81 | For typical development, the command you want to use is `macvdmtool reboot serial`. This will reboot the target, and immediately put it back into serial mode, with the right timing to make it work. 82 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Apache-2.0 */ 2 | /* Based on https://github.com/osy/ThunderboltPatcher */ 3 | 4 | #include "AppleHPMLib.h" 5 | #include "ssops.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | struct failure : public std::runtime_error { 16 | failure(const char *x) : std::runtime_error(x) 17 | { 18 | } 19 | }; 20 | 21 | struct IOObjectDeleter { 22 | io_object_t arg; 23 | 24 | IOObjectDeleter(io_object_t arg) : arg(arg) 25 | { 26 | } 27 | 28 | ~IOObjectDeleter() 29 | { 30 | IOObjectRelease(arg); 31 | } 32 | }; 33 | 34 | struct HPMPluginInstance { 35 | IOCFPlugInInterface **plugin = nullptr; 36 | AppleHPMLib **device; 37 | 38 | HPMPluginInstance(io_service_t service) 39 | { 40 | SInt32 score; 41 | IOReturn ret = IOCreatePlugInInterfaceForService(service, kAppleHPMLibType, 42 | kIOCFPlugInInterfaceID, &plugin, &score); 43 | if (ret != kIOReturnSuccess) 44 | throw failure("IOCreatePlugInInterfaceForService failed"); 45 | 46 | HRESULT res = (*plugin)->QueryInterface(plugin, CFUUIDGetUUIDBytes(kAppleHPMLibInterface), 47 | (LPVOID *)&device); 48 | if (res != S_OK) 49 | throw failure("QueryInterface failed"); 50 | } 51 | 52 | ~HPMPluginInstance() 53 | { 54 | if (plugin) { 55 | printf("Exiting DBMa mode... "); 56 | if (this->command(0, 'DBMa', "\x00") == 0) 57 | printf("OK\n"); 58 | else 59 | printf("Failed\n"); 60 | IODestroyPlugInInterface(plugin); 61 | } 62 | } 63 | 64 | std::string readRegister(uint64_t chipAddr, uint8_t dataAddr, int flags = 0) 65 | { 66 | std::string ret; 67 | ret.resize(64); 68 | uint64_t rlen = 0; 69 | IOReturn x = (*device)->Read(device, chipAddr, dataAddr, &ret[0], 64, flags, &rlen); 70 | if (x != 0) 71 | throw failure("readRegister failed"); 72 | return ret; 73 | } 74 | 75 | void writeRegister(uint64_t chipAddr, uint8_t dataAddr, std::string value) 76 | { 77 | IOReturn x = (*device)->Write(device, chipAddr, dataAddr, &value[0], value.length(), 0); 78 | if (x != 0) 79 | throw failure("writeRegister failed"); 80 | } 81 | 82 | int command(uint64_t chipAddr, uint32_t cmd, std::string args = "") 83 | { 84 | if (args.length()) 85 | (*device)->Write(device, chipAddr, 9, args.data(), args.length(), 0); 86 | auto ret = (*device)->Command(device, chipAddr, cmd, 0); 87 | if (ret) 88 | return -1; 89 | auto res = this->readRegister(chipAddr, 9); 90 | return res[0] & 0xfu; 91 | } 92 | }; 93 | 94 | uint32_t GetUnlockKey() 95 | { 96 | CFMutableDictionaryRef matching = IOServiceMatching("IOPlatformExpertDevice"); 97 | if (!matching) 98 | throw failure("IOServiceMatching failed (IOPED)"); 99 | 100 | io_iterator_t iter = 0; 101 | IOObjectDeleter iterDel(iter); 102 | io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, matching); 103 | if (!service) 104 | throw failure("IOServiceGetMatchingService failed (IOPED)"); 105 | 106 | IOObjectDeleter deviceDel(service); 107 | 108 | io_name_t deviceName; 109 | if (IORegistryEntryGetName(service, deviceName) != kIOReturnSuccess) { 110 | throw failure("IORegistryEntryGetName failed (IOPED)"); 111 | } 112 | 113 | printf("Mac type: %s\n", deviceName); 114 | 115 | return (deviceName[0] << 24) | (deviceName[1] << 16) | (deviceName[2] << 8) | deviceName[3]; 116 | } 117 | 118 | std::unique_ptr FindDevice() 119 | { 120 | std::unique_ptr ret; 121 | 122 | printf("Looking for HPM devices...\n"); 123 | 124 | CFMutableDictionaryRef matching = IOServiceMatching("AppleHPM"); 125 | if (!matching) 126 | throw failure("IOServiceMatching failed"); 127 | 128 | io_iterator_t iter = 0; 129 | IOObjectDeleter iterDel(iter); 130 | if (IOServiceGetMatchingServices(kIOMasterPortDefault, matching, &iter) != kIOReturnSuccess) 131 | throw failure("IOServiceGetMatchingServices failed"); 132 | 133 | io_service_t device; 134 | while ((device = IOIteratorNext(iter))) { 135 | IOObjectDeleter deviceDel(device); 136 | io_string_t pathName; 137 | 138 | if (IORegistryEntryGetPath(device, kIOServicePlane, pathName) != kIOReturnSuccess) { 139 | fprintf(stderr, "Failed to get device path for object %d.", device); 140 | continue; 141 | } 142 | 143 | CFNumberRef data; 144 | int32_t rid; 145 | data = (CFNumberRef)IORegistryEntryCreateCFProperty(device, CFSTR("RID"), 146 | kCFAllocatorDefault, 0); 147 | if (!data) 148 | throw failure("No RID"); 149 | 150 | CFNumberGetValue(data, kCFNumberSInt32Type, &rid); 151 | CFRelease(data); 152 | 153 | // RID=0 seems to always be the right port 154 | if (rid != 0) 155 | continue; 156 | 157 | printf("Found: %s\n", pathName); 158 | ret = std::make_unique(device); 159 | } 160 | 161 | if (!ret) 162 | throw failure("No matching devices"); 163 | 164 | return ret; 165 | }; 166 | 167 | void UnlockAce(HPMPluginInstance &inst, int no, uint32_t key) 168 | { 169 | printf("Unlocking... "); 170 | std::stringstream args; 171 | put(args, key); 172 | if (inst.command(no, 'LOCK', args.str())) { 173 | printf(" Failed.\n"); 174 | printf("Trying to reset... "); 175 | if (inst.command(no, 'Gaid')) { 176 | printf("Failed.\n"); 177 | throw failure("Failed to unlock device"); 178 | } 179 | printf("OK.\nUnlocking... "); 180 | if (inst.command(no, 'LOCK', args.str())) { 181 | printf(" Failed.\n"); 182 | throw failure("Failed to unlock device"); 183 | } 184 | } 185 | 186 | printf("OK\n"); 187 | } 188 | 189 | auto DoVDM(HPMPluginInstance &inst, int no, std::vector vdm) 190 | { 191 | 192 | auto rs = inst.readRegister(no, 0x4d); 193 | uint8_t rxst = rs[0]; 194 | 195 | std::stringstream args; 196 | put(args, (uint8_t)(((3 << 4) | vdm.size()))); 197 | for (uint32_t i : vdm) 198 | put(args, i); 199 | 200 | auto v = args.str(); 201 | 202 | if (inst.command(no, 'VDMs', args.str())) 203 | throw failure("Failed to send VDM\n"); 204 | 205 | int i; 206 | for (i = 0; i < 16; i++) { 207 | rs = inst.readRegister(no, 0x4d); 208 | if ((uint8_t)rs[0] != rxst) 209 | break; 210 | } 211 | if (i >= 16) 212 | throw failure("Did not get a reply to VDM\n"); 213 | 214 | uint32_t vdmhdr; 215 | std::stringstream reply; 216 | reply.str(rs); 217 | get(reply, rxst); 218 | get(reply, vdmhdr); 219 | 220 | if ((vdmhdr != (vdm[0] | 0x40)) && (vdmhdr != (vdm[0] | 0x840))) { 221 | printf("VDM failed (reply: 0x%08x - expected: %08x)\n", vdmhdr, vdm[0] | 0x40); 222 | throw failure("VDM failed"); 223 | } 224 | 225 | return reply.str().substr(reply.tellg()); 226 | } 227 | 228 | 229 | int DoActions(HPMPluginInstance &inst, int no) { 230 | std::vector actions{0x5AC8010}; 231 | auto r = DoVDM(inst, no, actions); 232 | printf("Supported actions:\n"); 233 | for(int i=0; i < r.size()/2; i++) { 234 | uint16_t r1 = r[i*2] & 0xFF; 235 | uint16_t r2 = r[i*2+1] & 0xFF; 236 | uint16_t action = (r2 << 8) | r1; 237 | if(action == 0x0000) { 238 | break; 239 | } 240 | printf("- %04X\n", action); 241 | } 242 | 243 | printf("OK\n"); 244 | return 0; 245 | } 246 | 247 | int DoSerial(HPMPluginInstance &inst, int no) 248 | { 249 | printf("Putting target into serial mode... "); 250 | 251 | std::vector serial{0x5ac8012, 0x1840306}; 252 | DoVDM(inst, no, serial); 253 | 254 | printf("OK\n"); 255 | 256 | printf("Putting local end into serial mode... "); 257 | 258 | std::stringstream args; 259 | put(args, (uint32_t)0x1840306); 260 | 261 | if (inst.command(no, 'DVEn', args.str())) { 262 | printf("Failed.\n"); 263 | return 1; 264 | } 265 | printf("OK\n"); 266 | 267 | return 0; 268 | } 269 | 270 | int DoReboot(HPMPluginInstance &inst, int no) 271 | { 272 | printf("Rebooting target into normal mode... "); 273 | 274 | std::vector reboot{0x5ac8012, 0x105, 0x80000000}; 275 | DoVDM(inst, no, reboot); 276 | 277 | printf("OK\n"); 278 | return 0; 279 | } 280 | 281 | int DoRebootSerial(HPMPluginInstance &inst, int no) 282 | { 283 | 284 | if (DoReboot(inst, no)) 285 | return 1; 286 | 287 | printf("Waiting for connection..."); 288 | fflush(stdout); 289 | 290 | sleep(1); 291 | int i; 292 | for (i = 0; i < 30; i++) { 293 | printf("."); 294 | fflush(stdout); 295 | auto t = inst.readRegister(no, 0x3f); 296 | if (t[0] & 1) 297 | break; 298 | usleep(100000); 299 | } 300 | if (i >= 30) { 301 | printf(" Timed out\n"); 302 | return 1; 303 | } 304 | printf(" Connected\n"); 305 | sleep(1); 306 | 307 | return DoSerial(inst, no); 308 | } 309 | 310 | int DoDFU(HPMPluginInstance &inst, int no) 311 | { 312 | printf("Rebooting target into DFU mode... "); 313 | 314 | std::vector dfu{0x5ac8012, 0x106, 0x80010000}; 315 | DoVDM(inst, no, dfu); 316 | 317 | printf("OK\n"); 318 | return 0; 319 | } 320 | 321 | int main2(int argc, char **argv) 322 | { 323 | if (argc < 2) { 324 | printf("Usage: %s \n", argv[0]); 325 | printf("Commands:\n"); 326 | printf(" serial - enter serial mode on both ends\n"); 327 | printf(" reboot - reboot the target\n"); 328 | printf(" reboot serial - reboot the target and enter serial mode\n"); 329 | printf(" dfu - put the target into DFU mode\n"); 330 | printf(" nop - do nothing\n"); 331 | printf(" actions - get supported actions\n"); 332 | return 1; 333 | } 334 | 335 | uint32_t key = GetUnlockKey(); 336 | 337 | auto inst = FindDevice(); 338 | int no = 0; 339 | 340 | auto t = inst->readRegister(no, 0x3f); 341 | std::string type = (t[0] & 1) ? ((t[0] & 2) == 0 ? "Source" : "Sink") : "None"; 342 | printf("Connection: %s\n", type.c_str()); 343 | 344 | if (!(t[0] & 1)) 345 | throw failure("No connection detected"); 346 | 347 | auto res = inst->readRegister(no, 0x03); 348 | res.erase(res.find('\0')); 349 | printf("Status: %s\n", res.c_str()); 350 | 351 | if (res != "DBMa") { 352 | UnlockAce(*inst, no, key); 353 | printf("Entering DBMa mode... "); 354 | 355 | if (inst->command(no, 'DBMa', "\x01")) 356 | throw failure("Failed to enter DBMa mode"); 357 | 358 | res = inst->readRegister(no, 0x03); 359 | res.erase(res.find('\0')); 360 | 361 | printf("Status: %s\n", res.c_str()); 362 | if (res != "DBMa") 363 | throw failure("Failed to enter DBMa mode"); 364 | } 365 | 366 | std::string cmd = argv[1]; 367 | std::string arg = ""; 368 | 369 | if (argc >= 3) 370 | arg = argv[2]; 371 | 372 | if (cmd == "serial") 373 | return DoSerial(*inst, no); 374 | else if (cmd == "reboot") { 375 | if (arg == "serial") 376 | return DoRebootSerial(*inst, no); 377 | else 378 | return DoReboot(*inst, no); 379 | } else if (cmd == "dfu") 380 | return DoDFU(*inst, no); 381 | else if (cmd == "nop") 382 | return 0; 383 | else if (cmd == "actions") 384 | return DoActions(*inst, no); 385 | 386 | printf("Unknown command\n"); 387 | return 1; 388 | } 389 | 390 | int main(int argc, char **argv) 391 | { 392 | // This makes sure we call the HPMPluginInstance destructor. 393 | try { 394 | return main2(argc, argv); 395 | } catch (failure e) { 396 | printf("%s\n", e.what()); 397 | return -1; 398 | } 399 | } 400 | -------------------------------------------------------------------------------- /ssops.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Apache-2.0 */ 2 | /* Based on https://github.com/osy/ThunderboltPatcher */ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | template std::stringstream &put(std::stringstream &str, const T &value) 9 | { 10 | union coercion { 11 | T value; 12 | char data[sizeof(T)]; 13 | }; 14 | 15 | coercion c; 16 | 17 | c.value = value; 18 | 19 | str.write(c.data, sizeof(T)); 20 | 21 | return str; 22 | } 23 | 24 | template std::stringstream &get(std::stringstream &str, T &value) 25 | { 26 | union coercion { 27 | T value; 28 | char data[sizeof(T)]; 29 | }; 30 | 31 | coercion c; 32 | 33 | c.value = value; 34 | 35 | str.read(c.data, sizeof(T)); 36 | 37 | value = c.value; 38 | 39 | return str; 40 | } 41 | --------------------------------------------------------------------------------