├── .github └── workflows │ └── main.yml ├── .gitignore ├── Changelog.md ├── LICENSE.txt ├── NVMeFix.xcodeproj ├── project.pbxproj └── xcshareddata │ └── IDETemplateMacros.plist ├── NVMeFix ├── Info.plist ├── Log.hpp ├── NVMeFix.cpp ├── NVMeFixPlugin.hpp ├── linux_types.h ├── nvme.h ├── nvme_apst.cpp ├── nvme_pm.cpp ├── nvme_quirks.cpp └── nvme_quirks.hpp └── README.md /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | release: 8 | types: [published] 9 | 10 | env: 11 | PROJECT_TYPE: KEXT 12 | 13 | jobs: 14 | build: 15 | name: Build 16 | runs-on: macos-latest 17 | env: 18 | JOB_TYPE: BUILD 19 | steps: 20 | - uses: actions/checkout@v3 21 | - uses: actions/checkout@v3 22 | with: 23 | repository: acidanthera/MacKernelSDK 24 | path: MacKernelSDK 25 | - name: CI Bootstrap 26 | run: | 27 | src=$(/usr/bin/curl -Lfs https://raw.githubusercontent.com/acidanthera/ocbuild/master/ci-bootstrap.sh) && eval "$src" || exit 1 28 | - name: Lilu Bootstrap 29 | run: | 30 | src=$(/usr/bin/curl -Lfs https://raw.githubusercontent.com/acidanthera/Lilu/master/Lilu/Scripts/bootstrap.sh) && eval "$src" || exit 1 31 | 32 | - run: xcodebuild -jobs 1 -configuration Debug 33 | - run: xcodebuild -jobs 1 -configuration Release 34 | 35 | - name: Upload to Artifacts 36 | uses: actions/upload-artifact@v3 37 | with: 38 | name: Artifacts 39 | path: build/*/*.zip 40 | - name: Upload to Release 41 | if: github.event_name == 'release' 42 | uses: svenstaro/upload-release-action@e74ff71f7d8a4c4745b560a485cc5fdb9b5b999d 43 | with: 44 | repo_token: ${{ secrets.GITHUB_TOKEN }} 45 | file: build/*/*.zip 46 | tag: ${{ github.ref }} 47 | file_glob: true 48 | 49 | analyze-clang: 50 | name: Analyze Clang 51 | runs-on: macos-latest 52 | env: 53 | JOB_TYPE: ANALYZE 54 | steps: 55 | - uses: actions/checkout@v3 56 | - uses: actions/checkout@v3 57 | with: 58 | repository: acidanthera/MacKernelSDK 59 | path: MacKernelSDK 60 | - name: CI Bootstrap 61 | run: | 62 | src=$(/usr/bin/curl -Lfs https://raw.githubusercontent.com/acidanthera/ocbuild/master/ci-bootstrap.sh) && eval "$src" || exit 1 63 | - name: Lilu Bootstrap 64 | run: | 65 | src=$(/usr/bin/curl -Lfs https://raw.githubusercontent.com/acidanthera/Lilu/master/Lilu/Scripts/bootstrap.sh) && eval "$src" || exit 1 66 | 67 | - run: xcodebuild analyze -quiet -scheme NVMeFix -configuration Debug CLANG_ANALYZER_OUTPUT=plist-html CLANG_ANALYZER_OUTPUT_DIR="$(pwd)/clang-analyze" && [ "$(find clang-analyze -name "*.html")" = "" ] 68 | - run: xcodebuild analyze -quiet -scheme NVMeFix -configuration Release CLANG_ANALYZER_OUTPUT=plist-html CLANG_ANALYZER_OUTPUT_DIR="$(pwd)/clang-analyze" && [ "$(find clang-analyze -name "*.html")" = "" ] 69 | 70 | analyze-coverity: 71 | name: Analyze Coverity 72 | runs-on: macos-latest 73 | env: 74 | JOB_TYPE: COVERITY 75 | if: github.repository_owner == 'acidanthera' && github.event_name != 'pull_request' 76 | steps: 77 | - uses: actions/checkout@v3 78 | - uses: actions/checkout@v3 79 | with: 80 | repository: acidanthera/MacKernelSDK 81 | path: MacKernelSDK 82 | - name: CI Bootstrap 83 | run: | 84 | src=$(/usr/bin/curl -Lfs https://raw.githubusercontent.com/acidanthera/ocbuild/master/ci-bootstrap.sh) && eval "$src" || exit 1 85 | - name: Lilu Bootstrap 86 | run: | 87 | src=$(/usr/bin/curl -Lfs https://raw.githubusercontent.com/acidanthera/Lilu/master/Lilu/Scripts/bootstrap.sh) && eval "$src" || exit 1 88 | 89 | - name: Run Coverity 90 | run: | 91 | src=$(/usr/bin/curl -Lfs https://raw.githubusercontent.com/acidanthera/ocbuild/master/coverity/covstrap.sh) && eval "$src" || exit 1 92 | env: 93 | COVERITY_SCAN_TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} 94 | COVERITY_SCAN_EMAIL: ${{ secrets.COVERITY_SCAN_EMAIL }} 95 | COVERITY_BUILD_COMMAND: xcodebuild -configuration Release 96 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | DerivedData 3 | Lilu.kext 4 | xcuserdata 5 | project.xcworkspace 6 | build 7 | .svace-dir 8 | cov-int 9 | /MacKernelSDK 10 | -------------------------------------------------------------------------------- /Changelog.md: -------------------------------------------------------------------------------- 1 | NVMeFix Changelog 2 | ================= 3 | #### v1.1.2 4 | - Added constants for macOS 15 support 5 | 6 | #### v1.1.1 7 | - Added constants for macOS 14 support 8 | - Fixed macOS 14 compatibility 9 | 10 | #### v1.1.0 11 | - Added constants for macOS 13 support 12 | 13 | #### v1.0.9 14 | - Added constants for macOS 12 support 15 | - Fixed macOS 12 compatibility 16 | 17 | #### v1.0.8 18 | - Fixed applying quirks based on the disk name and serial 19 | - Make Kingston A2000 quirk specific to S5Z42105 20 | 21 | #### v1.0.7 22 | - Fixed symbol solving on macOS 11.3 23 | - Added `-nvmefaspm` boot argument to force ASPM L1 on all NVMe SSDs 24 | 25 | #### v1.0.6 26 | - Added APST workaround for Kingston A2000 27 | 28 | #### v1.0.5 29 | - Fixed quirks enabling per controller 30 | - Fixed initialisation on 10.15+ 31 | 32 | #### v1.0.4 33 | - Added MacKernelSDK with Xcode 12 compatibility 34 | 35 | #### v1.0.3 36 | - Fix re-enabling APST after sleep (1.0.2 regression) 37 | - Added constants for 11.0 support (no full compatibility provided) 38 | 39 | #### v1.0.2 40 | - Prevent timeout panic on certain controllers (VMware, Samsung PM981) 41 | - Only enable active NVMe power management for controllers that do not support APST 42 | 43 | #### v1.0.1 44 | - Add OpenCore support for quirk autodetection 45 | 46 | #### v1.0.0 47 | - Initial release 48 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019, acidanthera 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 8 | 9 | 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. 10 | 11 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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. 14 | 15 | The Linux Kernel is provided under: 16 | 17 | SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note 18 | 19 | Being under the terms of the GNU General Public License version 2 only, 20 | according with: 21 | 22 | LICENSES/preferred/GPL-2.0 23 | 24 | With an explicit syscall exception, as stated at: 25 | 26 | LICENSES/exceptions/Linux-syscall-note 27 | 28 | In addition, other licenses may also apply. Please see: 29 | 30 | Documentation/process/license-rules.rst 31 | 32 | for more details. 33 | 34 | Valid-License-Identifier: GPL-2.0 35 | Valid-License-Identifier: GPL-2.0-only 36 | Valid-License-Identifier: GPL-2.0+ 37 | Valid-License-Identifier: GPL-2.0-or-later 38 | SPDX-URL: https://spdx.org/licenses/GPL-2.0.html 39 | Usage-Guide: 40 | To use this license in source code, put one of the following SPDX 41 | tag/value pairs into a comment according to the placement 42 | guidelines in the licensing rules documentation. 43 | For 'GNU General Public License (GPL) version 2 only' use: 44 | SPDX-License-Identifier: GPL-2.0 45 | or 46 | SPDX-License-Identifier: GPL-2.0-only 47 | For 'GNU General Public License (GPL) version 2 or any later version' use: 48 | SPDX-License-Identifier: GPL-2.0+ 49 | or 50 | SPDX-License-Identifier: GPL-2.0-or-later 51 | License-Text: 52 | 53 | GNU GENERAL PUBLIC LICENSE 54 | Version 2, June 1991 55 | 56 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 57 | 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 58 | Everyone is permitted to copy and distribute verbatim copies 59 | of this license document, but changing it is not allowed. 60 | 61 | Preamble 62 | 63 | The licenses for most software are designed to take away your 64 | freedom to share and change it. By contrast, the GNU General Public 65 | License is intended to guarantee your freedom to share and change free 66 | software--to make sure the software is free for all its users. This 67 | General Public License applies to most of the Free Software 68 | Foundation's software and to any other program whose authors commit to 69 | using it. (Some other Free Software Foundation software is covered by 70 | the GNU Library General Public License instead.) You can apply it to 71 | your programs, too. 72 | 73 | When we speak of free software, we are referring to freedom, not 74 | price. Our General Public Licenses are designed to make sure that you 75 | have the freedom to distribute copies of free software (and charge for 76 | this service if you wish), that you receive source code or can get it 77 | if you want it, that you can change the software or use pieces of it 78 | in new free programs; and that you know you can do these things. 79 | 80 | To protect your rights, we need to make restrictions that forbid 81 | anyone to deny you these rights or to ask you to surrender the rights. 82 | These restrictions translate to certain responsibilities for you if you 83 | distribute copies of the software, or if you modify it. 84 | 85 | For example, if you distribute copies of such a program, whether 86 | gratis or for a fee, you must give the recipients all the rights that 87 | you have. You must make sure that they, too, receive or can get the 88 | source code. And you must show them these terms so they know their 89 | rights. 90 | 91 | We protect your rights with two steps: (1) copyright the software, and 92 | (2) offer you this license which gives you legal permission to copy, 93 | distribute and/or modify the software. 94 | 95 | Also, for each author's protection and ours, we want to make certain 96 | that everyone understands that there is no warranty for this free 97 | software. If the software is modified by someone else and passed on, we 98 | want its recipients to know that what they have is not the original, so 99 | that any problems introduced by others will not reflect on the original 100 | authors' reputations. 101 | 102 | Finally, any free program is threatened constantly by software 103 | patents. We wish to avoid the danger that redistributors of a free 104 | program will individually obtain patent licenses, in effect making the 105 | program proprietary. To prevent this, we have made it clear that any 106 | patent must be licensed for everyone's free use or not licensed at all. 107 | 108 | The precise terms and conditions for copying, distribution and 109 | modification follow. 110 | 111 | GNU GENERAL PUBLIC LICENSE 112 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 113 | 114 | 0. This License applies to any program or other work which contains 115 | a notice placed by the copyright holder saying it may be distributed 116 | under the terms of this General Public License. The "Program", below, 117 | refers to any such program or work, and a "work based on the Program" 118 | means either the Program or any derivative work under copyright law: 119 | that is to say, a work containing the Program or a portion of it, 120 | either verbatim or with modifications and/or translated into another 121 | language. (Hereinafter, translation is included without limitation in 122 | the term "modification".) Each licensee is addressed as "you". 123 | 124 | Activities other than copying, distribution and modification are not 125 | covered by this License; they are outside its scope. The act of 126 | running the Program is not restricted, and the output from the Program 127 | is covered only if its contents constitute a work based on the 128 | Program (independent of having been made by running the Program). 129 | Whether that is true depends on what the Program does. 130 | 131 | 1. You may copy and distribute verbatim copies of the Program's 132 | source code as you receive it, in any medium, provided that you 133 | conspicuously and appropriately publish on each copy an appropriate 134 | copyright notice and disclaimer of warranty; keep intact all the 135 | notices that refer to this License and to the absence of any warranty; 136 | and give any other recipients of the Program a copy of this License 137 | along with the Program. 138 | 139 | You may charge a fee for the physical act of transferring a copy, and 140 | you may at your option offer warranty protection in exchange for a fee. 141 | 142 | 2. You may modify your copy or copies of the Program or any portion 143 | of it, thus forming a work based on the Program, and copy and 144 | distribute such modifications or work under the terms of Section 1 145 | above, provided that you also meet all of these conditions: 146 | 147 | a) You must cause the modified files to carry prominent notices 148 | stating that you changed the files and the date of any change. 149 | 150 | b) You must cause any work that you distribute or publish, that in 151 | whole or in part contains or is derived from the Program or any 152 | part thereof, to be licensed as a whole at no charge to all third 153 | parties under the terms of this License. 154 | 155 | c) If the modified program normally reads commands interactively 156 | when run, you must cause it, when started running for such 157 | interactive use in the most ordinary way, to print or display an 158 | announcement including an appropriate copyright notice and a 159 | notice that there is no warranty (or else, saying that you provide 160 | a warranty) and that users may redistribute the program under 161 | these conditions, and telling the user how to view a copy of this 162 | License. (Exception: if the Program itself is interactive but 163 | does not normally print such an announcement, your work based on 164 | the Program is not required to print an announcement.) 165 | 166 | These requirements apply to the modified work as a whole. If 167 | identifiable sections of that work are not derived from the Program, 168 | and can be reasonably considered independent and separate works in 169 | themselves, then this License, and its terms, do not apply to those 170 | sections when you distribute them as separate works. But when you 171 | distribute the same sections as part of a whole which is a work based 172 | on the Program, the distribution of the whole must be on the terms of 173 | this License, whose permissions for other licensees extend to the 174 | entire whole, and thus to each and every part regardless of who wrote it. 175 | 176 | Thus, it is not the intent of this section to claim rights or contest 177 | your rights to work written entirely by you; rather, the intent is to 178 | exercise the right to control the distribution of derivative or 179 | collective works based on the Program. 180 | 181 | In addition, mere aggregation of another work not based on the Program 182 | with the Program (or with a work based on the Program) on a volume of 183 | a storage or distribution medium does not bring the other work under 184 | the scope of this License. 185 | 186 | 3. You may copy and distribute the Program (or a work based on it, 187 | under Section 2) in object code or executable form under the terms of 188 | Sections 1 and 2 above provided that you also do one of the following: 189 | 190 | a) Accompany it with the complete corresponding machine-readable 191 | source code, which must be distributed under the terms of Sections 192 | 1 and 2 above on a medium customarily used for software interchange; or, 193 | 194 | b) Accompany it with a written offer, valid for at least three 195 | years, to give any third party, for a charge no more than your 196 | cost of physically performing source distribution, a complete 197 | machine-readable copy of the corresponding source code, to be 198 | distributed under the terms of Sections 1 and 2 above on a medium 199 | customarily used for software interchange; or, 200 | 201 | c) Accompany it with the information you received as to the offer 202 | to distribute corresponding source code. (This alternative is 203 | allowed only for noncommercial distribution and only if you 204 | received the program in object code or executable form with such 205 | an offer, in accord with Subsection b above.) 206 | 207 | The source code for a work means the preferred form of the work for 208 | making modifications to it. For an executable work, complete source 209 | code means all the source code for all modules it contains, plus any 210 | associated interface definition files, plus the scripts used to 211 | control compilation and installation of the executable. However, as a 212 | special exception, the source code distributed need not include 213 | anything that is normally distributed (in either source or binary 214 | form) with the major components (compiler, kernel, and so on) of the 215 | operating system on which the executable runs, unless that component 216 | itself accompanies the executable. 217 | 218 | If distribution of executable or object code is made by offering 219 | access to copy from a designated place, then offering equivalent 220 | access to copy the source code from the same place counts as 221 | distribution of the source code, even though third parties are not 222 | compelled to copy the source along with the object code. 223 | 224 | 4. You may not copy, modify, sublicense, or distribute the Program 225 | except as expressly provided under this License. Any attempt 226 | otherwise to copy, modify, sublicense or distribute the Program is 227 | void, and will automatically terminate your rights under this License. 228 | However, parties who have received copies, or rights, from you under 229 | this License will not have their licenses terminated so long as such 230 | parties remain in full compliance. 231 | 232 | 5. You are not required to accept this License, since you have not 233 | signed it. However, nothing else grants you permission to modify or 234 | distribute the Program or its derivative works. These actions are 235 | prohibited by law if you do not accept this License. Therefore, by 236 | modifying or distributing the Program (or any work based on the 237 | Program), you indicate your acceptance of this License to do so, and 238 | all its terms and conditions for copying, distributing or modifying 239 | the Program or works based on it. 240 | 241 | 6. Each time you redistribute the Program (or any work based on the 242 | Program), the recipient automatically receives a license from the 243 | original licensor to copy, distribute or modify the Program subject to 244 | these terms and conditions. You may not impose any further 245 | restrictions on the recipients' exercise of the rights granted herein. 246 | You are not responsible for enforcing compliance by third parties to 247 | this License. 248 | 249 | 7. If, as a consequence of a court judgment or allegation of patent 250 | infringement or for any other reason (not limited to patent issues), 251 | conditions are imposed on you (whether by court order, agreement or 252 | otherwise) that contradict the conditions of this License, they do not 253 | excuse you from the conditions of this License. If you cannot 254 | distribute so as to satisfy simultaneously your obligations under this 255 | License and any other pertinent obligations, then as a consequence you 256 | may not distribute the Program at all. For example, if a patent 257 | license would not permit royalty-free redistribution of the Program by 258 | all those who receive copies directly or indirectly through you, then 259 | the only way you could satisfy both it and this License would be to 260 | refrain entirely from distribution of the Program. 261 | 262 | If any portion of this section is held invalid or unenforceable under 263 | any particular circumstance, the balance of the section is intended to 264 | apply and the section as a whole is intended to apply in other 265 | circumstances. 266 | 267 | It is not the purpose of this section to induce you to infringe any 268 | patents or other property right claims or to contest validity of any 269 | such claims; this section has the sole purpose of protecting the 270 | integrity of the free software distribution system, which is 271 | implemented by public license practices. Many people have made 272 | generous contributions to the wide range of software distributed 273 | through that system in reliance on consistent application of that 274 | system; it is up to the author/donor to decide if he or she is willing 275 | to distribute software through any other system and a licensee cannot 276 | impose that choice. 277 | 278 | This section is intended to make thoroughly clear what is believed to 279 | be a consequence of the rest of this License. 280 | 281 | 8. If the distribution and/or use of the Program is restricted in 282 | certain countries either by patents or by copyrighted interfaces, the 283 | original copyright holder who places the Program under this License 284 | may add an explicit geographical distribution limitation excluding 285 | those countries, so that distribution is permitted only in or among 286 | countries not thus excluded. In such case, this License incorporates 287 | the limitation as if written in the body of this License. 288 | 289 | 9. The Free Software Foundation may publish revised and/or new versions 290 | of the General Public License from time to time. Such new versions will 291 | be similar in spirit to the present version, but may differ in detail to 292 | address new problems or concerns. 293 | 294 | Each version is given a distinguishing version number. If the Program 295 | specifies a version number of this License which applies to it and "any 296 | later version", you have the option of following the terms and conditions 297 | either of that version or of any later version published by the Free 298 | Software Foundation. If the Program does not specify a version number of 299 | this License, you may choose any version ever published by the Free Software 300 | Foundation. 301 | 302 | 10. If you wish to incorporate parts of the Program into other free 303 | programs whose distribution conditions are different, write to the author 304 | to ask for permission. For software which is copyrighted by the Free 305 | Software Foundation, write to the Free Software Foundation; we sometimes 306 | make exceptions for this. Our decision will be guided by the two goals 307 | of preserving the free status of all derivatives of our free software and 308 | of promoting the sharing and reuse of software generally. 309 | 310 | NO WARRANTY 311 | 312 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 313 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 314 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 315 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 316 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 317 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 318 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 319 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 320 | REPAIR OR CORRECTION. 321 | 322 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 323 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 324 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 325 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 326 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 327 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 328 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 329 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 330 | POSSIBILITY OF SUCH DAMAGES. 331 | 332 | END OF TERMS AND CONDITIONS 333 | 334 | How to Apply These Terms to Your New Programs 335 | 336 | If you develop a new program, and you want it to be of the greatest 337 | possible use to the public, the best way to achieve this is to make it 338 | free software which everyone can redistribute and change under these terms. 339 | 340 | To do so, attach the following notices to the program. It is safest 341 | to attach them to the start of each source file to most effectively 342 | convey the exclusion of warranty; and each file should have at least 343 | the "copyright" line and a pointer to where the full notice is found. 344 | 345 | 346 | Copyright (C) 347 | 348 | This program is free software; you can redistribute it and/or modify 349 | it under the terms of the GNU General Public License as published by 350 | the Free Software Foundation; either version 2 of the License, or 351 | (at your option) any later version. 352 | 353 | This program is distributed in the hope that it will be useful, 354 | but WITHOUT ANY WARRANTY; without even the implied warranty of 355 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 356 | GNU General Public License for more details. 357 | 358 | You should have received a copy of the GNU General Public License 359 | along with this program; if not, write to the Free Software 360 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 361 | 362 | 363 | Also add information on how to contact you by electronic and paper mail. 364 | 365 | If the program is interactive, make it output a short notice like this 366 | when it starts in an interactive mode: 367 | 368 | Gnomovision version 69, Copyright (C) year name of author 369 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 370 | This is free software, and you are welcome to redistribute it 371 | under certain conditions; type `show c' for details. 372 | 373 | The hypothetical commands `show w' and `show c' should show the appropriate 374 | parts of the General Public License. Of course, the commands you use may 375 | be called something other than `show w' and `show c'; they could even be 376 | mouse-clicks or menu items--whatever suits your program. 377 | 378 | You should also get your employer (if you work as a programmer) or your 379 | school, if any, to sign a "copyright disclaimer" for the program, if 380 | necessary. Here is a sample; alter the names: 381 | 382 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 383 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 384 | 385 | , 1 April 1989 386 | Ty Coon, President of Vice 387 | 388 | This General Public License does not permit incorporating your program into 389 | proprietary programs. If your program is a subroutine library, you may 390 | consider it more useful to permit linking proprietary applications with the 391 | library. If this is what you want to do, use the GNU Library General 392 | Public License instead of this License. 393 | -------------------------------------------------------------------------------- /NVMeFix.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2F1E835123B6248D0048B956 /* nvme_quirks.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2F1E834F23B6248D0048B956 /* nvme_quirks.cpp */; }; 11 | 2F2BAA3523B7A00500F7DF53 /* nvme_pm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2F2BAA3323B7A00500F7DF53 /* nvme_pm.cpp */; }; 12 | 2F7736C723AE2BF900C87C16 /* NVMeFix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2FAEE25A23AE266E00185076 /* NVMeFix.cpp */; }; 13 | 2F77375D23AE404D00C87C16 /* plugin_start.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2F77373A23AE401900C87C16 /* plugin_start.cpp */; }; 14 | 2FF27FCD23C8B73A00BE79E3 /* nvme_apst.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2FF27FCC23C8B73A00BE79E3 /* nvme_apst.cpp */; }; 15 | CE8DA0E12517E36C008C44E8 /* libkmod.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CE8DA0E02517E36C008C44E8 /* libkmod.a */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXFileReference section */ 19 | 2F1E834F23B6248D0048B956 /* nvme_quirks.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = nvme_quirks.cpp; sourceTree = ""; }; 20 | 2F1E835023B6248D0048B956 /* nvme_quirks.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = nvme_quirks.hpp; sourceTree = ""; }; 21 | 2F1E835223B624C10048B956 /* linux_types.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = linux_types.h; sourceTree = ""; }; 22 | 2F2BAA3323B7A00500F7DF53 /* nvme_pm.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = nvme_pm.cpp; sourceTree = ""; }; 23 | 2F7736C823AE333800C87C16 /* nvme.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = nvme.h; sourceTree = ""; }; 24 | 2F77373123AE401900C87C16 /* entry64.S */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = entry64.S; sourceTree = ""; }; 25 | 2F77373223AE401900C87C16 /* build.tool */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = build.tool; sourceTree = ""; }; 26 | 2F77373323AE401900C87C16 /* entry32.S */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = entry32.S; sourceTree = ""; }; 27 | 2F77373423AE401900C87C16 /* wrappers.inc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.pascal; path = wrappers.inc; sourceTree = ""; }; 28 | 2F77373A23AE401900C87C16 /* plugin_start.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = plugin_start.cpp; sourceTree = ""; }; 29 | 2F77373D23AE401900C87C16 /* kern_config.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = kern_config.hpp; sourceTree = ""; }; 30 | 2F77373F23AE401900C87C16 /* kern_time.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = kern_time.hpp; sourceTree = ""; }; 31 | 2F77374023AE401900C87C16 /* kern_nvram.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = kern_nvram.hpp; sourceTree = ""; }; 32 | 2F77374123AE401900C87C16 /* kern_cpu.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = kern_cpu.hpp; sourceTree = ""; }; 33 | 2F77374223AE401900C87C16 /* kern_devinfo.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = kern_devinfo.hpp; sourceTree = ""; }; 34 | 2F77374323AE401900C87C16 /* kern_efi.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = kern_efi.hpp; sourceTree = ""; }; 35 | 2F77374423AE401900C87C16 /* kern_policy.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = kern_policy.hpp; sourceTree = ""; }; 36 | 2F77374523AE401900C87C16 /* kern_user.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = kern_user.hpp; sourceTree = ""; }; 37 | 2F77374623AE401900C87C16 /* plugin_start.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = plugin_start.hpp; sourceTree = ""; }; 38 | 2F77374723AE401900C87C16 /* kern_iokit.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = kern_iokit.hpp; sourceTree = ""; }; 39 | 2F77374823AE401900C87C16 /* kern_crypto.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = kern_crypto.hpp; sourceTree = ""; }; 40 | 2F77374923AE401900C87C16 /* kern_mach.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = kern_mach.hpp; sourceTree = ""; }; 41 | 2F77374A23AE401900C87C16 /* kern_compression.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = kern_compression.hpp; sourceTree = ""; }; 42 | 2F77374B23AE401900C87C16 /* kern_file.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = kern_file.hpp; sourceTree = ""; }; 43 | 2F77374C23AE401900C87C16 /* kern_rtc.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = kern_rtc.hpp; sourceTree = ""; }; 44 | 2F77374D23AE401900C87C16 /* kern_disasm.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = kern_disasm.hpp; sourceTree = ""; }; 45 | 2F77374F23AE401900C87C16 /* capstone.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = capstone.h; sourceTree = ""; }; 46 | 2F77375023AE401900C87C16 /* mips.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = mips.h; sourceTree = ""; }; 47 | 2F77375123AE401900C87C16 /* sparc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = sparc.h; sourceTree = ""; }; 48 | 2F77375223AE401900C87C16 /* systemz.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = systemz.h; sourceTree = ""; }; 49 | 2F77375323AE401900C87C16 /* arm.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = arm.h; sourceTree = ""; }; 50 | 2F77375423AE401900C87C16 /* x86.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = x86.h; sourceTree = ""; }; 51 | 2F77375523AE401900C87C16 /* ppc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ppc.h; sourceTree = ""; }; 52 | 2F77375623AE401900C87C16 /* arm64.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = arm64.h; sourceTree = ""; }; 53 | 2F77375723AE401900C87C16 /* xcore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = xcore.h; sourceTree = ""; }; 54 | 2F77375823AE401900C87C16 /* platform.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = platform.h; sourceTree = ""; }; 55 | 2F77375923AE401900C87C16 /* kern_patcher.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = kern_patcher.hpp; sourceTree = ""; }; 56 | 2F77375A23AE401900C87C16 /* kern_compat.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = kern_compat.hpp; sourceTree = ""; }; 57 | 2F77375B23AE401900C87C16 /* kern_api.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = kern_api.hpp; sourceTree = ""; }; 58 | 2F77375C23AE401900C87C16 /* kern_util.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = kern_util.hpp; sourceTree = ""; }; 59 | 2F77376223AF98AF00C87C16 /* NVMeFixPlugin.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = NVMeFixPlugin.hpp; sourceTree = ""; }; 60 | 2FAEE25A23AE266E00185076 /* NVMeFix.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = NVMeFix.cpp; sourceTree = ""; }; 61 | 2FF27FCB23C8B53C00BE79E3 /* Log.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Log.hpp; sourceTree = ""; }; 62 | 2FF27FCC23C8B73A00BE79E3 /* nvme_apst.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = nvme_apst.cpp; sourceTree = ""; }; 63 | 2FF3E70F23AE1DA100D8CDEB /* NVMeFix.kext */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NVMeFix.kext; sourceTree = BUILT_PRODUCTS_DIR; }; 64 | 2FF3E71423AE1DA100D8CDEB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 65 | CE7908D6263A27FA00482EE3 /* Changelog.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = Changelog.md; sourceTree = ""; }; 66 | CE8DA0E02517E36C008C44E8 /* libkmod.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libkmod.a; path = ../Lilu/MacKernelSDK/Library/x86_64/libkmod.a; sourceTree = ""; }; 67 | CE9EE6EF263AF4E700D750F5 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 68 | /* End PBXFileReference section */ 69 | 70 | /* Begin PBXFrameworksBuildPhase section */ 71 | 2F77375E23AE405A00C87C16 /* Frameworks */ = { 72 | isa = PBXFrameworksBuildPhase; 73 | buildActionMask = 2147483647; 74 | files = ( 75 | CE8DA0E12517E36C008C44E8 /* libkmod.a in Frameworks */, 76 | ); 77 | runOnlyForDeploymentPostprocessing = 0; 78 | }; 79 | /* End PBXFrameworksBuildPhase section */ 80 | 81 | /* Begin PBXGroup section */ 82 | 2F77372D23AE401900C87C16 /* Lilu */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | 2F77372E23AE401900C87C16 /* Library */, 86 | 2F77373C23AE401900C87C16 /* Headers */, 87 | ); 88 | name = Lilu; 89 | path = Lilu.kext/Contents/Resources; 90 | sourceTree = ""; 91 | }; 92 | 2F77372E23AE401900C87C16 /* Library */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | 2F77373023AE401900C87C16 /* wrappers */, 96 | 2F77373A23AE401900C87C16 /* plugin_start.cpp */, 97 | ); 98 | path = Library; 99 | sourceTree = ""; 100 | }; 101 | 2F77373023AE401900C87C16 /* wrappers */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | 2F77373123AE401900C87C16 /* entry64.S */, 105 | 2F77373223AE401900C87C16 /* build.tool */, 106 | 2F77373323AE401900C87C16 /* entry32.S */, 107 | 2F77373423AE401900C87C16 /* wrappers.inc */, 108 | ); 109 | path = wrappers; 110 | sourceTree = ""; 111 | }; 112 | 2F77373C23AE401900C87C16 /* Headers */ = { 113 | isa = PBXGroup; 114 | children = ( 115 | 2F77373D23AE401900C87C16 /* kern_config.hpp */, 116 | 2F77373F23AE401900C87C16 /* kern_time.hpp */, 117 | 2F77374023AE401900C87C16 /* kern_nvram.hpp */, 118 | 2F77374123AE401900C87C16 /* kern_cpu.hpp */, 119 | 2F77374223AE401900C87C16 /* kern_devinfo.hpp */, 120 | 2F77374323AE401900C87C16 /* kern_efi.hpp */, 121 | 2F77374423AE401900C87C16 /* kern_policy.hpp */, 122 | 2F77374523AE401900C87C16 /* kern_user.hpp */, 123 | 2F77374623AE401900C87C16 /* plugin_start.hpp */, 124 | 2F77374723AE401900C87C16 /* kern_iokit.hpp */, 125 | 2F77374823AE401900C87C16 /* kern_crypto.hpp */, 126 | 2F77374923AE401900C87C16 /* kern_mach.hpp */, 127 | 2F77374A23AE401900C87C16 /* kern_compression.hpp */, 128 | 2F77374B23AE401900C87C16 /* kern_file.hpp */, 129 | 2F77374C23AE401900C87C16 /* kern_rtc.hpp */, 130 | 2F77374D23AE401900C87C16 /* kern_disasm.hpp */, 131 | 2F77374E23AE401900C87C16 /* capstone */, 132 | 2F77375923AE401900C87C16 /* kern_patcher.hpp */, 133 | 2F77375A23AE401900C87C16 /* kern_compat.hpp */, 134 | 2F77375B23AE401900C87C16 /* kern_api.hpp */, 135 | 2F77375C23AE401900C87C16 /* kern_util.hpp */, 136 | ); 137 | path = Headers; 138 | sourceTree = ""; 139 | }; 140 | 2F77374E23AE401900C87C16 /* capstone */ = { 141 | isa = PBXGroup; 142 | children = ( 143 | 2F77374F23AE401900C87C16 /* capstone.h */, 144 | 2F77375023AE401900C87C16 /* mips.h */, 145 | 2F77375123AE401900C87C16 /* sparc.h */, 146 | 2F77375223AE401900C87C16 /* systemz.h */, 147 | 2F77375323AE401900C87C16 /* arm.h */, 148 | 2F77375423AE401900C87C16 /* x86.h */, 149 | 2F77375523AE401900C87C16 /* ppc.h */, 150 | 2F77375623AE401900C87C16 /* arm64.h */, 151 | 2F77375723AE401900C87C16 /* xcore.h */, 152 | 2F77375823AE401900C87C16 /* platform.h */, 153 | ); 154 | path = capstone; 155 | sourceTree = ""; 156 | }; 157 | 2FF3E70523AE1DA100D8CDEB = { 158 | isa = PBXGroup; 159 | children = ( 160 | CE9EE6EF263AF4E700D750F5 /* README.md */, 161 | CE7908D6263A27FA00482EE3 /* Changelog.md */, 162 | 2FF3E71123AE1DA100D8CDEB /* NVMeFix */, 163 | 2FF3E71023AE1DA100D8CDEB /* Products */, 164 | 2F77372D23AE401900C87C16 /* Lilu */, 165 | CE8DA0DF2517E36C008C44E8 /* Frameworks */, 166 | ); 167 | sourceTree = ""; 168 | }; 169 | 2FF3E71023AE1DA100D8CDEB /* Products */ = { 170 | isa = PBXGroup; 171 | children = ( 172 | 2FF3E70F23AE1DA100D8CDEB /* NVMeFix.kext */, 173 | ); 174 | name = Products; 175 | sourceTree = ""; 176 | }; 177 | 2FF3E71123AE1DA100D8CDEB /* NVMeFix */ = { 178 | isa = PBXGroup; 179 | children = ( 180 | 2F77376223AF98AF00C87C16 /* NVMeFixPlugin.hpp */, 181 | 2FF27FCB23C8B53C00BE79E3 /* Log.hpp */, 182 | 2FAEE25A23AE266E00185076 /* NVMeFix.cpp */, 183 | 2FF27FCC23C8B73A00BE79E3 /* nvme_apst.cpp */, 184 | 2F1E834F23B6248D0048B956 /* nvme_quirks.cpp */, 185 | 2F1E835023B6248D0048B956 /* nvme_quirks.hpp */, 186 | 2F2BAA3323B7A00500F7DF53 /* nvme_pm.cpp */, 187 | 2F7736C823AE333800C87C16 /* nvme.h */, 188 | 2F1E835223B624C10048B956 /* linux_types.h */, 189 | 2FF3E71423AE1DA100D8CDEB /* Info.plist */, 190 | ); 191 | path = NVMeFix; 192 | sourceTree = ""; 193 | }; 194 | CE8DA0DF2517E36C008C44E8 /* Frameworks */ = { 195 | isa = PBXGroup; 196 | children = ( 197 | CE8DA0E02517E36C008C44E8 /* libkmod.a */, 198 | ); 199 | name = Frameworks; 200 | sourceTree = ""; 201 | }; 202 | /* End PBXGroup section */ 203 | 204 | /* Begin PBXNativeTarget section */ 205 | 2FF3E70E23AE1DA100D8CDEB /* NVMeFix */ = { 206 | isa = PBXNativeTarget; 207 | buildConfigurationList = 2FF3E71723AE1DA100D8CDEB /* Build configuration list for PBXNativeTarget "NVMeFix" */; 208 | buildPhases = ( 209 | 2F7736C623AE2BF500C87C16 /* Sources */, 210 | 2F77375E23AE405A00C87C16 /* Frameworks */, 211 | CE3C855723C40360006E72F5 /* Archive */, 212 | ); 213 | buildRules = ( 214 | ); 215 | dependencies = ( 216 | ); 217 | name = NVMeFix; 218 | productName = NVMeFix; 219 | productReference = 2FF3E70F23AE1DA100D8CDEB /* NVMeFix.kext */; 220 | productType = "com.apple.product-type.kernel-extension"; 221 | }; 222 | /* End PBXNativeTarget section */ 223 | 224 | /* Begin PBXProject section */ 225 | 2FF3E70623AE1DA100D8CDEB /* Project object */ = { 226 | isa = PBXProject; 227 | attributes = { 228 | LastUpgradeCheck = 1250; 229 | ORGANIZATIONNAME = acidanthera; 230 | TargetAttributes = { 231 | 2FF3E70E23AE1DA100D8CDEB = { 232 | CreatedOnToolsVersion = 11.2; 233 | }; 234 | }; 235 | }; 236 | buildConfigurationList = 2FF3E70923AE1DA100D8CDEB /* Build configuration list for PBXProject "NVMeFix" */; 237 | compatibilityVersion = "Xcode 9.3"; 238 | developmentRegion = en; 239 | hasScannedForEncodings = 0; 240 | knownRegions = ( 241 | en, 242 | Base, 243 | ); 244 | mainGroup = 2FF3E70523AE1DA100D8CDEB; 245 | productRefGroup = 2FF3E71023AE1DA100D8CDEB /* Products */; 246 | projectDirPath = ""; 247 | projectRoot = ""; 248 | targets = ( 249 | 2FF3E70E23AE1DA100D8CDEB /* NVMeFix */, 250 | ); 251 | }; 252 | /* End PBXProject section */ 253 | 254 | /* Begin PBXShellScriptBuildPhase section */ 255 | CE3C855723C40360006E72F5 /* Archive */ = { 256 | isa = PBXShellScriptBuildPhase; 257 | buildActionMask = 2147483647; 258 | files = ( 259 | ); 260 | inputFileListPaths = ( 261 | ); 262 | inputPaths = ( 263 | ); 264 | name = Archive; 265 | outputFileListPaths = ( 266 | ); 267 | outputPaths = ( 268 | ); 269 | runOnlyForDeploymentPostprocessing = 0; 270 | shellPath = /bin/bash; 271 | shellScript = "cd \"${TARGET_BUILD_DIR}\"\n\ndist=(\"$FULL_PRODUCT_NAME\")\nif [ -d \"$DWARF_DSYM_FILE_NAME\" ]; then dist+=(\"$DWARF_DSYM_FILE_NAME\"); fi\n\narchive=\"${PRODUCT_NAME}-${MODULE_VERSION}-$(echo $CONFIGURATION | tr /a-z/ /A-Z/).zip\"\nrm -rf *.zip\nif [ \"$CONFIGURATION\" == \"Release\" ]; then\n strip -x -T \"${EXECUTABLE_PATH}\" &>/dev/null || strip -x \"${EXECUTABLE_PATH}\"\nfi\nzip -qry -FS \"${archive}\" \"${dist[@]}\"\n"; 272 | }; 273 | /* End PBXShellScriptBuildPhase section */ 274 | 275 | /* Begin PBXSourcesBuildPhase section */ 276 | 2F7736C623AE2BF500C87C16 /* Sources */ = { 277 | isa = PBXSourcesBuildPhase; 278 | buildActionMask = 2147483647; 279 | files = ( 280 | 2F77375D23AE404D00C87C16 /* plugin_start.cpp in Sources */, 281 | 2F1E835123B6248D0048B956 /* nvme_quirks.cpp in Sources */, 282 | 2F2BAA3523B7A00500F7DF53 /* nvme_pm.cpp in Sources */, 283 | 2FF27FCD23C8B73A00BE79E3 /* nvme_apst.cpp in Sources */, 284 | 2F7736C723AE2BF900C87C16 /* NVMeFix.cpp in Sources */, 285 | ); 286 | runOnlyForDeploymentPostprocessing = 0; 287 | }; 288 | /* End PBXSourcesBuildPhase section */ 289 | 290 | /* Begin XCBuildConfiguration section */ 291 | 2FF3E71523AE1DA100D8CDEB /* Debug */ = { 292 | isa = XCBuildConfiguration; 293 | buildSettings = { 294 | ALWAYS_SEARCH_USER_PATHS = NO; 295 | ARCHS = x86_64; 296 | CLANG_ANALYZER_NONNULL = YES; 297 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 298 | CLANG_CXX_LANGUAGE_STANDARD = "c++1y"; 299 | CLANG_CXX_LIBRARY = "libc++"; 300 | CLANG_ENABLE_OBJC_ARC = YES; 301 | CLANG_ENABLE_OBJC_WEAK = YES; 302 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 303 | CLANG_WARN_BOOL_CONVERSION = YES; 304 | CLANG_WARN_COMMA = YES; 305 | CLANG_WARN_CONSTANT_CONVERSION = YES; 306 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 307 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 308 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 309 | CLANG_WARN_EMPTY_BODY = YES; 310 | CLANG_WARN_ENUM_CONVERSION = YES; 311 | CLANG_WARN_INFINITE_RECURSION = YES; 312 | CLANG_WARN_INT_CONVERSION = YES; 313 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 314 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 315 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 316 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 317 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 318 | CLANG_WARN_STRICT_PROTOTYPES = YES; 319 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 320 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 321 | CLANG_WARN_UNREACHABLE_CODE = YES; 322 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 323 | DEBUG_INFORMATION_FORMAT = dwarf; 324 | ENABLE_STRICT_OBJC_MSGSEND = YES; 325 | ENABLE_TESTABILITY = YES; 326 | GCC_C_LANGUAGE_STANDARD = c11; 327 | GCC_NO_COMMON_BLOCKS = YES; 328 | GCC_OPTIMIZATION_LEVEL = 0; 329 | GCC_PREPROCESSOR_DEFINITIONS = ( 330 | "DEBUG=1", 331 | "$(inherited)", 332 | ); 333 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 334 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 335 | GCC_WARN_UNDECLARED_SELECTOR = YES; 336 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 337 | GCC_WARN_UNUSED_FUNCTION = YES; 338 | GCC_WARN_UNUSED_VARIABLE = YES; 339 | KERNEL_EXTENSION_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/MacKernelSDK/Headers"; 340 | KERNEL_FRAMEWORK_HEADERS = "$(PROJECT_DIR)/MacKernelSDK/Headers"; 341 | MACOSX_DEPLOYMENT_TARGET = 10.14; 342 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 343 | MTL_FAST_MATH = YES; 344 | ONLY_ACTIVE_ARCH = YES; 345 | SDKROOT = macosx; 346 | }; 347 | name = Debug; 348 | }; 349 | 2FF3E71623AE1DA100D8CDEB /* Release */ = { 350 | isa = XCBuildConfiguration; 351 | buildSettings = { 352 | ALWAYS_SEARCH_USER_PATHS = NO; 353 | ARCHS = x86_64; 354 | CLANG_ANALYZER_NONNULL = YES; 355 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 356 | CLANG_CXX_LANGUAGE_STANDARD = "c++1y"; 357 | CLANG_CXX_LIBRARY = "libc++"; 358 | CLANG_ENABLE_OBJC_ARC = YES; 359 | CLANG_ENABLE_OBJC_WEAK = YES; 360 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 361 | CLANG_WARN_BOOL_CONVERSION = YES; 362 | CLANG_WARN_COMMA = YES; 363 | CLANG_WARN_CONSTANT_CONVERSION = YES; 364 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 365 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 366 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 367 | CLANG_WARN_EMPTY_BODY = YES; 368 | CLANG_WARN_ENUM_CONVERSION = YES; 369 | CLANG_WARN_INFINITE_RECURSION = YES; 370 | CLANG_WARN_INT_CONVERSION = YES; 371 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 372 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 373 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 374 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 375 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 376 | CLANG_WARN_STRICT_PROTOTYPES = YES; 377 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 378 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 379 | CLANG_WARN_UNREACHABLE_CODE = YES; 380 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 381 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 382 | ENABLE_NS_ASSERTIONS = NO; 383 | ENABLE_STRICT_OBJC_MSGSEND = YES; 384 | GCC_C_LANGUAGE_STANDARD = c11; 385 | GCC_NO_COMMON_BLOCKS = YES; 386 | GCC_OPTIMIZATION_LEVEL = 3; 387 | GCC_SYMBOLS_PRIVATE_EXTERN = YES; 388 | GCC_UNROLL_LOOPS = YES; 389 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 390 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 391 | GCC_WARN_UNDECLARED_SELECTOR = YES; 392 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 393 | GCC_WARN_UNUSED_FUNCTION = YES; 394 | GCC_WARN_UNUSED_VARIABLE = YES; 395 | KERNEL_EXTENSION_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/MacKernelSDK/Headers"; 396 | KERNEL_FRAMEWORK_HEADERS = "$(PROJECT_DIR)/MacKernelSDK/Headers"; 397 | MACOSX_DEPLOYMENT_TARGET = 10.14; 398 | MTL_ENABLE_DEBUG_INFO = NO; 399 | MTL_FAST_MATH = YES; 400 | SDKROOT = macosx; 401 | }; 402 | name = Release; 403 | }; 404 | 2FF3E71823AE1DA100D8CDEB /* Debug */ = { 405 | isa = XCBuildConfiguration; 406 | buildSettings = { 407 | CLANG_CXX_LANGUAGE_STANDARD = "c++1y"; 408 | CLANG_WARN_DOCUMENTATION_COMMENTS = NO; 409 | CODE_SIGN_IDENTITY = ""; 410 | COMPILER_INDEX_STORE_ENABLE = NO; 411 | CURRENT_PROJECT_VERSION = "$(MODULE_VERSION)"; 412 | DEPLOYMENT_POSTPROCESSING = YES; 413 | GCC_ENABLE_FLOATING_POINT_LIBRARY_CALLS = NO; 414 | GCC_ENABLE_KERNEL_DEVELOPMENT = NO; 415 | GCC_GENERATE_DEBUGGING_SYMBOLS = NO; 416 | GCC_PREPROCESSOR_DEFINITIONS = ( 417 | "MODULE_VERSION=$(MODULE_VERSION)", 418 | "PRODUCT_NAME=$(PRODUCT_NAME)", 419 | "MACH_ASSERT=1", 420 | "$(inherited)", 421 | ); 422 | GCC_WARN_PEDANTIC = NO; 423 | HEADER_SEARCH_PATHS = ( 424 | "${PROJECT_DIR}/Lilu.kext/Contents/Resources/", 425 | "${PROJECT_DIR}", 426 | ); 427 | INFOPLIST_FILE = NVMeFix/Info.plist; 428 | LIBRARY_SEARCH_PATHS = ( 429 | "$(inherited)", 430 | "$(PROJECT_DIR)/MacKernelSDK/Library/x86_64", 431 | ); 432 | MODULE_NAME = org.acidanthera.NVMeFix; 433 | MODULE_START = NVMeFix_kern_start; 434 | MODULE_STOP = NVMeFix_kern_stop; 435 | MODULE_VERSION = 1.1.3; 436 | OTHER_CFLAGS = ( 437 | "-mmmx", 438 | "-msse", 439 | "-msse2", 440 | "-msse3", 441 | "-mfpmath=sse", 442 | "-mssse3", 443 | "-ftree-vectorize", 444 | "-fno-non-call-exceptions", 445 | "-fno-builtin", 446 | "-fno-asynchronous-unwind-tables", 447 | "-Wno-unknown-warning-option", 448 | "-Wno-ossharedptr-misuse", 449 | "-Wno-vla", 450 | ); 451 | OTHER_LDFLAGS = "-static"; 452 | PRODUCT_BUNDLE_IDENTIFIER = org.acidanthera.NVMeFix; 453 | PRODUCT_NAME = "$(TARGET_NAME)"; 454 | WRAPPER_EXTENSION = kext; 455 | }; 456 | name = Debug; 457 | }; 458 | 2FF3E71923AE1DA100D8CDEB /* Release */ = { 459 | isa = XCBuildConfiguration; 460 | buildSettings = { 461 | CLANG_CXX_LANGUAGE_STANDARD = "c++1y"; 462 | CLANG_WARN_DOCUMENTATION_COMMENTS = NO; 463 | CODE_SIGN_IDENTITY = ""; 464 | COMPILER_INDEX_STORE_ENABLE = NO; 465 | COPY_PHASE_STRIP = YES; 466 | CURRENT_PROJECT_VERSION = "$(MODULE_VERSION)"; 467 | DEAD_CODE_STRIPPING = YES; 468 | DEPLOYMENT_POSTPROCESSING = YES; 469 | GCC_ENABLE_FLOATING_POINT_LIBRARY_CALLS = NO; 470 | GCC_ENABLE_KERNEL_DEVELOPMENT = NO; 471 | GCC_PREPROCESSOR_DEFINITIONS = ( 472 | "PRODUCT_NAME=$(PRODUCT_NAME)", 473 | "MODULE_VERSION=$(MODULE_VERSION)", 474 | ); 475 | GCC_WARN_PEDANTIC = NO; 476 | HEADER_SEARCH_PATHS = ( 477 | "${PROJECT_DIR}/Lilu.kext/Contents/Resources/", 478 | "${PROJECT_DIR}", 479 | ); 480 | INFOPLIST_FILE = NVMeFix/Info.plist; 481 | LIBRARY_SEARCH_PATHS = ( 482 | "$(inherited)", 483 | "$(PROJECT_DIR)/MacKernelSDK/Library/x86_64", 484 | ); 485 | LLVM_LTO = YES; 486 | MODULE_NAME = org.acidanthera.NVMeFix; 487 | MODULE_START = NVMeFix_kern_start; 488 | MODULE_STOP = NVMeFix_kern_stop; 489 | MODULE_VERSION = 1.1.3; 490 | OTHER_CFLAGS = ( 491 | "-mmmx", 492 | "-msse", 493 | "-msse2", 494 | "-msse3", 495 | "-mfpmath=sse", 496 | "-mssse3", 497 | "-ftree-vectorize", 498 | "-fno-non-call-exceptions", 499 | "-fno-builtin", 500 | "-fno-asynchronous-unwind-tables", 501 | "-Wno-unknown-warning-option", 502 | "-Wno-ossharedptr-misuse", 503 | "-Wno-vla", 504 | ); 505 | OTHER_LDFLAGS = "-static"; 506 | PRODUCT_BUNDLE_IDENTIFIER = org.acidanthera.NVMeFix; 507 | PRODUCT_NAME = "$(TARGET_NAME)"; 508 | STRIP_STYLE = "non-global"; 509 | WRAPPER_EXTENSION = kext; 510 | }; 511 | name = Release; 512 | }; 513 | /* End XCBuildConfiguration section */ 514 | 515 | /* Begin XCConfigurationList section */ 516 | 2FF3E70923AE1DA100D8CDEB /* Build configuration list for PBXProject "NVMeFix" */ = { 517 | isa = XCConfigurationList; 518 | buildConfigurations = ( 519 | 2FF3E71523AE1DA100D8CDEB /* Debug */, 520 | 2FF3E71623AE1DA100D8CDEB /* Release */, 521 | ); 522 | defaultConfigurationIsVisible = 0; 523 | defaultConfigurationName = Release; 524 | }; 525 | 2FF3E71723AE1DA100D8CDEB /* Build configuration list for PBXNativeTarget "NVMeFix" */ = { 526 | isa = XCConfigurationList; 527 | buildConfigurations = ( 528 | 2FF3E71823AE1DA100D8CDEB /* Debug */, 529 | 2FF3E71923AE1DA100D8CDEB /* Release */, 530 | ); 531 | defaultConfigurationIsVisible = 0; 532 | defaultConfigurationName = Release; 533 | }; 534 | /* End XCConfigurationList section */ 535 | }; 536 | rootObject = 2FF3E70623AE1DA100D8CDEB /* Project object */; 537 | } 538 | -------------------------------------------------------------------------------- /NVMeFix.xcodeproj/xcshareddata/IDETemplateMacros.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | FILEHEADER 6 | 7 | // @file ___FILENAME___ 8 | // 9 | // ___PROJECTNAME___ 10 | // 11 | // ___COPYRIGHT___ 12 | // 13 | // This program and the accompanying materials 14 | // are licensed and made available under the terms and conditions of the BSD License 15 | // which accompanies this distribution. The full text of the license may be found at 16 | // http://opensource.org/licenses/bsd-license.php 17 | // THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 18 | // WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /NVMeFix/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundleShortVersionString 16 | $(MODULE_VERSION) 17 | CFBundlePackageType 18 | KEXT 19 | CFBundleVersion 20 | $(MODULE_VERSION) 21 | OSBundleCompatibleVersion 22 | 1.0 23 | NSHumanReadableCopyright 24 | Copyright © 2019 acidanthera. All rights reserved. 25 | OSBundleLibraries 26 | 27 | com.apple.kpi.bsd 28 | 12.0.0 29 | com.apple.kpi.dsep 30 | 12.0.0 31 | com.apple.kpi.iokit 32 | 12.0.0 33 | com.apple.kpi.libkern 34 | 12.0.0 35 | com.apple.kpi.mach 36 | 12.0.0 37 | com.apple.kpi.unsupported 38 | 12.0.0 39 | as.vit9696.Lilu 40 | 1.4.1 41 | 42 | OSBundleRequired 43 | Root 44 | IOKitPersonalities 45 | 46 | org.acidanthera.NVMeFix 47 | 48 | CFBundleIdentifier 49 | $(PRODUCT_BUNDLE_IDENTIFIER) 50 | IOClass 51 | $(PRODUCT_NAME:rfc1034identifier) 52 | IOProviderClass 53 | IOResources 54 | IOResourceMatch 55 | IOKit 56 | IOMatchCategory 57 | $(PRODUCT_NAME:rfc1034identifier) 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /NVMeFix/Log.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // @file Log.hpp 3 | // 4 | // NVMeFix 5 | // 6 | // Copyright © 2020 acidanthera. All rights reserved. 7 | // 8 | // This program and the accompanying materials 9 | // are licensed and made available under the terms and conditions of the BSD License 10 | // which accompanies this distribution. The full text of the license may be found at 11 | // http://opensource.org/licenses/bsd-license.php 12 | // THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 14 | 15 | 16 | #ifndef Log_h 17 | #define Log_h 18 | 19 | namespace Log { 20 | static constexpr auto Plugin {"nvmef"}, 21 | APST {"apst"}, 22 | PM {"pm"}, 23 | Quirks {"quirks"}, 24 | Feature {"feature"}, 25 | Disasm {"disasm"}; 26 | }; 27 | 28 | #endif /* Log_h */ 29 | -------------------------------------------------------------------------------- /NVMeFix/NVMeFix.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // @file NVMeFix.cpp 3 | // 4 | // NVMeFix 5 | // 6 | // Copyright © 2019 acidanthera. All rights reserved. 7 | // 8 | // This program and the accompanying materials 9 | // are licensed and made available under the terms and conditions of the BSD License 10 | // which accompanies this distribution. The full text of the license may be found at 11 | // http://opensource.org/licenses/bsd-license.php 12 | // THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "Log.hpp" 31 | #include "NVMeFixPlugin.hpp" 32 | 33 | static NVMeFixPlugin plugin; 34 | 35 | NVMeFixPlugin& NVMeFixPlugin::globalPlugin() { 36 | return plugin; 37 | } 38 | 39 | /** 40 | * This may be invoked before or after we get IOBSD mount notification, so in the both functions we 41 | * attempt to solve symbols and handle the controllers. 42 | */ 43 | void NVMeFixPlugin::processKext(void* that, KernelPatcher& patcher, size_t index, mach_vm_address_t, 44 | size_t) { 45 | auto plugin = static_cast(that); 46 | assert(plugin); 47 | 48 | if (index != plugin->kextInfo.loadIndex) 49 | return; 50 | 51 | DBGLOG(Log::Plugin, "processKext %s", plugin->kextInfo.id); 52 | 53 | if (plugin->solveSymbols(patcher)) { 54 | atomic_store_explicit(&plugin->solvedSymbols, true, memory_order_release); 55 | plugin->handleControllers(); 56 | } 57 | } 58 | 59 | bool NVMeFixPlugin::solveSymbols(KernelPatcher& kp) { 60 | auto idx = plugin.kextInfo.loadIndex; 61 | bool res = true; 62 | res &= (kextFuncs.IONVMeController.IssueIdentifyCommandNew.solve(kp, idx) || 63 | kextFuncs.IONVMeController.IssueIdentifyCommand.solve(kp, idx)) && 64 | kextFuncs.IONVMeController.ProcessSyncNVMeRequest.solve(kp, idx) && 65 | (kextFuncs.IONVMeController.GetRequestNew.solve(kp, idx) || 66 | kextFuncs.IONVMeController.GetRequest.solve(kp, idx)) && 67 | kextFuncs.AppleNVMeRequest.BuildCommandGetFeatures.solve(kp, idx) && 68 | kextFuncs.AppleNVMeRequest.BuildCommandSetFeaturesCommon.solve(kp, idx) && 69 | kextFuncs.IONVMeController.ReturnRequest.solve(kp, idx) && 70 | kextFuncs.AppleNVMeRequest.GetStatus.solve(kp, idx) && 71 | kextFuncs.AppleNVMeRequest.GetOpcode.solve(kp, idx) && 72 | kextFuncs.AppleNVMeRequest.GenerateIOVMSegments.solve(kp, idx) && 73 | kextFuncs.IONVMeController.FilterInterruptRequest.solve(kp, idx); 74 | 75 | /* mov eax, [rdi+0xA8] */ 76 | res &= kextMembers.AppleNVMeRequest.result.fromFunc(kextFuncs.AppleNVMeRequest.GetStatus.fptr, 77 | 0x8b, 0, 7, 4) && 78 | /* movzx eax, byte ptr [rdi+0x10A] */ 79 | kextMembers.AppleNVMeRequest.command.fromFunc(kextFuncs.AppleNVMeRequest.GetOpcode.fptr, 80 | 0xf, 0, 7) && 81 | /* mov [r14+0xC0], r15 (14.0+) or mov [rbx+0xC0], r15 (11.3-13.x) or mov [rbx+0xC0], r12 (<=11.2) */ 82 | (kextFuncs.IONVMeController.IssueIdentifyCommandNew.fptr ? 83 | kextMembers.AppleNVMeRequest.prpDescriptor.fromFunc(kextFuncs.IONVMeController.IssueIdentifyCommandNew.fptr, 0x89, 7, getKernelVersion() >= KernelVersion::Sonoma ? 14 : 3) : 84 | kextMembers.AppleNVMeRequest.prpDescriptor.fromFunc(kextFuncs.IONVMeController.IssueIdentifyCommand.fptr, 0x89, 4, 3)); 85 | 86 | /* cmp byte ptr [rdi+269h], 0 */ 87 | kextMembers.IONVMeController.ANS2MSIWorkaround.fromFunc(kextFuncs.IONVMeController.FilterInterruptRequest.fptr, 88 | 0x80, 7, 7, 0, 32); 89 | 90 | if (res) 91 | kextMembers.AppleNVMeRequest.controller.offs = kextMembers.AppleNVMeRequest.result.offs - 12; 92 | 93 | res &= PM.solveSymbols(kp); 94 | if (!res) 95 | DBGLOG(Log::Plugin, "Failed to solve symbols"); 96 | return res; 97 | } 98 | 99 | /** 100 | * This handler will be invoked when a media (whole disk or a partition) BSD node becomes registered. 101 | * We need to do two things now: 102 | * 1. Discover any undetected NVMe controllers. 103 | * 2. Try and solve symbols. If the relevant partition for symbol solving is not available, the call 104 | * will fail and we may succeed at next mount. 105 | * If we have all the symbols ready, we can proceed working with controllers. 106 | */ 107 | bool NVMeFixPlugin::matchingNotificationHandler(void* that, void* , IOService* service, 108 | IONotifier* notifier) { 109 | auto plugin = static_cast(that); 110 | assert(plugin); 111 | assert(service); 112 | 113 | IOLockLock(plugin->lck); 114 | 115 | DBGLOG("nvmef", "matchingNotificationHandler for %s", service->getName()); 116 | 117 | auto parent = service->getProvider(); 118 | 119 | /* Typical depth is 9 on real setups */ 120 | for (int i = 0; parent && i < controllerSearchDepth; i++) { 121 | // DBGLOG("nvmef", "Parent %s", parent->getName()); 122 | 123 | if (parent->metaCast("IONVMeController")) { 124 | bool has = false; 125 | 126 | for (size_t i = 0; i < plugin->controllers.size(); i++) 127 | if (plugin->controllers[i]->controller == parent) { 128 | has = true; 129 | break; 130 | } 131 | 132 | if (!has) { 133 | auto entry = new ControllerEntry(parent); 134 | if (!entry) { 135 | SYSLOG(Log::Plugin, "Failed to allocate ControllerEntry memory"); 136 | break; 137 | } 138 | if (!plugin->controllers.push_back(entry)) { 139 | SYSLOG(Log::Plugin, "Failed to insert ControllerEntry memory"); 140 | ControllerEntry::deleter(entry); 141 | break; 142 | } 143 | break; 144 | } 145 | } 146 | 147 | parent = parent->getProvider(); 148 | } 149 | 150 | // DBGLOG("nvmef", "Discovered %u controllers", plugin->controllers.size()); 151 | 152 | IOLockUnlock(plugin->lck); 153 | 154 | if (atomic_load_explicit(&plugin->solvedSymbols, memory_order_acquire)) 155 | plugin->handleControllers(); 156 | 157 | return true; 158 | } 159 | 160 | void NVMeFixPlugin::handleControllers() { 161 | DBGLOG("nvmef", "handleControllers for %u controllers", controllers.size()); 162 | for (size_t i = 0; i < controllers.size(); i++) { 163 | IOLockLock(controllers[i]->lck); 164 | controllers[i]->controller->retain(); 165 | handleController(*controllers[i]); 166 | controllers[i]->controller->release(); 167 | IOLockUnlock(controllers[i]->lck); 168 | } 169 | } 170 | 171 | void NVMeFixPlugin::forceEnableASPM(IOService *device) { 172 | IOPCIDevice *pci = static_cast(device->metaCast("IOPCIDevice")); 173 | if (!pci) return; 174 | 175 | uint32_t aspm = 0; 176 | auto prop = device->getProperty("pci-aspm-default"); 177 | if (prop) { 178 | auto num = OSDynamicCast(OSNumber, prop); 179 | if (num != nullptr) { 180 | aspm = num->unsigned32BitValue(); 181 | } else { 182 | auto data = OSDynamicCast(OSData, prop); 183 | if (data != nullptr && data->getLength() == sizeof(aspm)) 184 | lilu_os_memcpy(&aspm, data->getBytesNoCopy(), sizeof(aspm)); 185 | } 186 | } 187 | 188 | DBGLOG(Log::Plugin, "Activating ASPM on %s, currently %X", safeString(device->getName()), aspm); 189 | 190 | // Do not repeat what is already done. 191 | if ((aspm & ASPM_Mask) == ASPM_L1EntryEnabled) return; 192 | 193 | UInt8 offset = 0; 194 | if (!pci->findPCICapability(kIOPCICapabilityIDPCIExpress, &offset)) { 195 | SYSLOG(Log::Plugin, "NO PCIe capability support on %s", safeString(device->getName())); 196 | return; 197 | } 198 | 199 | offset += 0x10; // link control offset. 200 | 201 | IOPCIAddressSpace space = pci->space; 202 | space.es.registerNumExtended = 0; 203 | 204 | auto linkControl = pci->configRead16(space, offset); 205 | pci->configWrite16(space, offset, (linkControl & ~ASPM_Mask) | ASPM_L1EntryEnabled); 206 | auto newLinkControl = pci->configRead16(space, offset); 207 | DBGLOG(Log::Plugin, "ASPM transition on %s from %X to %X", safeString(device->getName()), linkControl, newLinkControl); 208 | pci->setProperty("pci-aspm-custom", newLinkControl, 32); 209 | } 210 | 211 | void NVMeFixPlugin::handleController(ControllerEntry& entry) { 212 | assert(entry.controller); 213 | 214 | if (entry.processed) 215 | return; 216 | 217 | /* No error signaling -- just ACK the discovery to notification handler */ 218 | entry.processed = true; 219 | 220 | uint32_t vendor {}; 221 | propertyFromParent(entry.controller, "vendor-id", vendor); 222 | if (vendor == 0x106b || entry.controller->metaCast("AppleNVMeController")) { 223 | SYSLOG(Log::Plugin, "Ignoring Apple controller"); 224 | return; 225 | } 226 | 227 | /** 228 | * Force enable ASPM when the user cannot provide device properties. 229 | */ 230 | if (checkKernelArgument("-nvmefaspm")) { 231 | auto ssd = OSDynamicCast(IOService, entry.controller->getParentEntry(gIOServicePlane)); 232 | if (ssd) { 233 | forceEnableASPM(ssd); 234 | auto bridge = OSDynamicCast(IOService, ssd->getParentEntry(gIODTPlane)); 235 | if (bridge) 236 | forceEnableASPM(bridge); 237 | } 238 | } 239 | 240 | /** 241 | * Force enable ANS2MSIWorkaround. 242 | * 243 | * We would often get a panic with I/O Read command timeout on VMware and Samsung PM981. 244 | * Some investigation showed that CQ head entry phase and CQ phase would mismatch. 245 | * This implies there is a race such that CQ head gets updated to point to an entry with inverted 246 | * phase. 247 | * Since FilterIRQ detects phase mismatch, it does not schedule HandleIRQ at the workloop of NVMe 248 | * controller, so a request is never handled and we get a timeout. If Filter and Handle IRQ calls can 249 | * possibly race, this may happen because HandleIRQ does not manage to update CQ phase in time before 250 | * FilterIRQ phase check is scheduled, observing the old phase. This is the case in situation where 251 | * two FilterIRQ calls happen with no HandleIRQ in between where the controller was too slow to 252 | * notice INTMS being set. 253 | * Normally, IRQ is masked just before HandleIRQ is scheduled in FilterIRQ, and unmasked when 254 | * HandleIRQ is done. IONVMeController::ANS2MSIWorkaround forces IRQ to be masked at the very 255 | * start of FilterIRQ instead so that FilterIRQ does not race with itself. This seems to eliminate 256 | * the timeouts. 257 | **/ 258 | if (kextMembers.IONVMeController.ANS2MSIWorkaround.has()) { 259 | kextMembers.IONVMeController.ANS2MSIWorkaround.get(entry.controller) = 1; 260 | } else { 261 | DBGLOG(Log::Plugin, "Ignoring ANS2 workaround patch on newer system"); 262 | } 263 | 264 | /* First get quirks based on PCI device */ 265 | entry.quirks = NVMe::quirksForController(entry.controller); 266 | propertyFromParent(entry.controller, "ps-max-latency-us", entry.ps_max_latency_us); 267 | 268 | IOBufferMemoryDescriptor* identifyDesc {nullptr}; 269 | 270 | if (identify(entry, identifyDesc) != kIOReturnSuccess || !identifyDesc) { 271 | SYSLOG(Log::Plugin, "Failed to identify controller"); 272 | return; 273 | } 274 | 275 | auto ctrl = reinterpret_cast(identifyDesc->getBytesNoCopy()); 276 | if (!ctrl) { 277 | DBGLOG(Log::Plugin, "Failed to get identify buffer bytes"); 278 | if (identifyDesc) 279 | identifyDesc->release(); 280 | return; 281 | } 282 | 283 | entry.identify = identifyDesc; 284 | 285 | /* Get additional quirks based on identify data */ 286 | entry.quirks |= NVMe::quirksForController(ctrl->vid, ctrl->mn, ctrl->fr); 287 | 288 | entry.controller->setProperty("quirks", OSNumber::withNumber(entry.quirks, 8 * sizeof(entry.quirks))); 289 | 290 | #ifdef DEBUG 291 | char mn[40]; 292 | lilu_os_memcpy(mn, ctrl->mn, sizeof(mn)); 293 | mn[sizeof(mn) - 1] = '\0'; 294 | 295 | DBGLOG(Log::Plugin, "Identified model %s (vid 0x%x)", mn, ctrl->vid); 296 | #endif 297 | 298 | if (!enableAPST(entry, ctrl)) 299 | SYSLOG(Log::APST, "Failed to enable APST"); 300 | 301 | if (!PM.init(entry, ctrl, entry.apste)) 302 | SYSLOG(Log::PM, "Failed to initialise power management"); 303 | } 304 | 305 | IOReturn NVMeFixPlugin::identify(ControllerEntry& entry, IOBufferMemoryDescriptor*& desc) { 306 | IOReturn ret = kIOReturnSuccess; 307 | 308 | uint8_t* data {nullptr}; 309 | bool prepared {false}; 310 | 311 | desc = IOBufferMemoryDescriptor::withCapacity(sizeof(NVMe::nvme_id_ctrl), kIODirectionIn); 312 | 313 | if (!desc) { 314 | SYSLOG(Log::Plugin, "Failed to init descriptor"); 315 | ret = kIOReturnNoResources; 316 | goto fail; 317 | } 318 | data = static_cast(desc->getBytesNoCopy()); 319 | memset(data, '\0', desc->getLength()); 320 | 321 | ret = desc->prepare(); 322 | if (ret != kIOReturnSuccess) { 323 | SYSLOG(Log::Plugin, "Failed to prepare descriptor"); 324 | goto fail; 325 | } 326 | prepared = true; 327 | 328 | if (kextFuncs.IONVMeController.IssueIdentifyCommandNew.fptr) 329 | ret = kextFuncs.IONVMeController.IssueIdentifyCommandNew(entry.controller, desc, 0, false); 330 | else 331 | ret = kextFuncs.IONVMeController.IssueIdentifyCommand(entry.controller, desc, nullptr, 0); 332 | if (ret != kIOReturnSuccess) { 333 | SYSLOG(Log::Plugin, "issueIdentifyCommand failed"); 334 | goto fail; 335 | } 336 | 337 | fail: 338 | if (prepared) 339 | desc->complete(); 340 | if (ret != kIOReturnSuccess && desc) 341 | desc->release(); 342 | return ret; 343 | } 344 | 345 | IOReturn NVMeFixPlugin::NVMeFeatures(ControllerEntry& entry, unsigned fid, unsigned* dword11, 346 | IOBufferMemoryDescriptor* desc, uint32_t* res, bool set) { 347 | auto ret = kIOReturnSuccess; 348 | 349 | bool prepared {false}; 350 | 351 | if (desc) { 352 | ret = desc->prepare(); 353 | prepared = ret == kIOReturnSuccess; 354 | } 355 | 356 | if (!desc || prepared) { 357 | void *req; 358 | if (kextFuncs.IONVMeController.GetRequestNew.fptr) 359 | req = kextFuncs.IONVMeController.GetRequestNew(entry.controller, 1, 0); /* Set 0b10 to tickle */ 360 | else 361 | req = kextFuncs.IONVMeController.GetRequest(entry.controller, 1); /* Set 0b10 to tickle */ 362 | 363 | if (!req) { 364 | DBGLOG(Log::Feature, "IONVMeController::GetRequest failed"); 365 | ret = kIOReturnNoResources; 366 | } else if (desc) 367 | ret = reinterpret_cast(req)->setMemoryDescriptor(desc); 368 | 369 | if (ret == kIOReturnSuccess) { 370 | if (set) 371 | kextFuncs.AppleNVMeRequest.BuildCommandSetFeaturesCommon(req, fid); 372 | else 373 | kextFuncs.AppleNVMeRequest.BuildCommandGetFeatures(req, fid); 374 | 375 | if (dword11) 376 | kextMembers.AppleNVMeRequest.command.get(req).features.dword11 = *dword11; 377 | if (desc) { 378 | kextMembers.AppleNVMeRequest.prpDescriptor.get(req) = desc; 379 | ret = reinterpret_cast(req)->prepare(0, desc->getLength()); 380 | } 381 | 382 | if (ret != kIOReturnSuccess) 383 | DBGLOG(Log::Feature, "Failed to prepare DMA command"); 384 | else { 385 | if (desc) 386 | ret = kextFuncs.AppleNVMeRequest.GenerateIOVMSegments(req, 0, 387 | desc->getLength()); 388 | 389 | if (ret != kIOReturnSuccess) 390 | DBGLOG(Log::Feature, "Failed to generate IO VM segments"); 391 | else { 392 | kextMembers.AppleNVMeRequest.controller.get(req) = entry.controller; 393 | 394 | ret = kextFuncs.IONVMeController.ProcessSyncNVMeRequest(entry.controller, 395 | req); 396 | if (ret != kIOReturnSuccess) 397 | DBGLOG(Log::Feature, "ProcessSyncNVMeRequest failed"); 398 | else if (res) 399 | *res = kextMembers.AppleNVMeRequest.result.get(req); 400 | } 401 | } 402 | if (desc) 403 | reinterpret_cast(req)->complete(); 404 | kextFuncs.IONVMeController.ReturnRequest(entry.controller, req); 405 | } 406 | } else 407 | SYSLOG(Log::Feature, "Failed to prepare buffer"); 408 | 409 | if (desc && prepared) 410 | desc->complete(); 411 | 412 | return ret; 413 | } 414 | 415 | /* Notifications are serialized for a single controller, so we don't have to sync with removal */ 416 | bool NVMeFixPlugin::terminatedNotificationHandler(void* that, void* , IOService* service, 417 | IONotifier* notifier) { 418 | auto plugin = static_cast(that); 419 | assert(plugin); 420 | assert(service && service->metaCast("IONVMeController")); 421 | 422 | /* Controller retain count should equal 0, so we don't need to hold its lock now */ 423 | IOLockLock(plugin->lck); 424 | for (size_t i = 0; i < plugin->controllers.size(); i++) 425 | if (plugin->controllers[i]->controller == service) { 426 | plugin->controllers.erase(i); 427 | break; 428 | } 429 | IOLockUnlock(plugin->lck); 430 | 431 | return false; 432 | } 433 | 434 | /** 435 | * NOTE: We are in kmod context, not IOService. This works fine as long as we publish our personality 436 | * in Info.plist to match something in ioreg, but specify a non-existing IOClass so that IOKit attempts 437 | * to load us anyway. It is otherwise unsafe to use matching notifications from kmod when we have a 438 | * living IOService. 439 | */ 440 | void NVMeFixPlugin::init() { 441 | LiluAPI::Error err; 442 | 443 | if (!(lck = IOLockAlloc())) { 444 | SYSLOG(Log::Plugin, "Failed to alloc lock"); 445 | goto fail; 446 | } 447 | 448 | atomic_store_explicit(&solvedSymbols, false, memory_order_relaxed); 449 | 450 | matchingNotifier = IOService::addMatchingNotification(gIOPublishNotification, 451 | IOService::serviceMatching("IOMediaBSDClient"), 452 | matchingNotificationHandler, 453 | this); 454 | if (!matchingNotifier) { 455 | SYSLOG(Log::Plugin, "Failed to register for matching notification"); 456 | goto fail; 457 | } 458 | 459 | terminationNotifier = IOService::addMatchingNotification(gIOTerminatedNotification, 460 | IOService::serviceMatching("IONVMeController"), 461 | terminatedNotificationHandler, 462 | this); 463 | if (!terminationNotifier) { 464 | SYSLOG(Log::Plugin, "Failed to register for termination notification"); 465 | goto fail; 466 | } 467 | 468 | DBGLOG(Log::Plugin, "Registered for matching notifications"); 469 | 470 | err = lilu.onKextLoad(&kextInfo, 1, NVMeFixPlugin::processKext, this); 471 | if (err != LiluAPI::Error::NoError) { 472 | SYSLOG(Log::Plugin, "Failed to register kext load cb"); 473 | goto fail; 474 | } 475 | 476 | return; 477 | fail: 478 | if (lck) 479 | IOLockFree(lck); 480 | if (matchingNotifier) 481 | matchingNotifier->remove(); 482 | if (terminationNotifier) 483 | terminationNotifier->remove(); 484 | } 485 | 486 | void NVMeFixPlugin::deinit() { 487 | /* This kext is not unloadable */ 488 | panic("nvmef: deinit called"); 489 | } 490 | 491 | NVMeFixPlugin::ControllerEntry* NVMeFixPlugin::entryForController(IOService* controller) const { 492 | for (size_t i = 0; i < controllers.size(); i++) 493 | if (controllers[i]->controller == controller) 494 | return controllers[i]; 495 | return nullptr; 496 | } 497 | 498 | static const char *bootargOff[] { 499 | "-nvmefoff" 500 | }; 501 | 502 | static const char *bootargDebug[] { 503 | "-nvmefdbg" 504 | }; 505 | 506 | PluginConfiguration ADDPR(config) { 507 | xStringify(PRODUCT_NAME), 508 | parseModuleVersion(xStringify(MODULE_VERSION)), 509 | LiluAPI::AllowNormal | LiluAPI::AllowInstallerRecovery, 510 | bootargOff, 511 | arrsize(bootargOff), 512 | bootargDebug, 513 | arrsize(bootargDebug), 514 | nullptr, 515 | 0, 516 | KernelVersion::Mojave, 517 | KernelVersion::Sequoia, 518 | []() { 519 | NVMeFixPlugin::globalPlugin().init(); 520 | } 521 | }; 522 | -------------------------------------------------------------------------------- /NVMeFix/NVMeFixPlugin.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // @file NVMeFixPlugin.hpp 3 | // 4 | // NVMeFix 5 | // 6 | // Copyright © 2019 acidanthera. All rights reserved. 7 | // 8 | // This program and the accompanying materials 9 | // are licensed and made available under the terms and conditions of the BSD License 10 | // which accompanies this distribution. The full text of the license may be found at 11 | // http://opensource.org/licenses/bsd-license.php 12 | // THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 14 | 15 | 16 | #ifndef NVMeFixPlugin_h 17 | #define NVMeFixPlugin_h 18 | 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "Log.hpp" 30 | #include "nvme.h" 31 | #include "nvme_quirks.hpp" 32 | 33 | class NVMeFixPlugin { 34 | public: 35 | friend class NVMePMProxy; 36 | 37 | void init(); 38 | void deinit(); 39 | static NVMeFixPlugin& globalPlugin(); 40 | 41 | explicit NVMeFixPlugin() : PM(*this) {} 42 | private: 43 | static void processKext(void*, KernelPatcher&, size_t, mach_vm_address_t, size_t); 44 | static bool matchingNotificationHandler(void*, void*, IOService*, IONotifier*); 45 | static bool terminatedNotificationHandler(void*, void*, IOService*, IONotifier*); 46 | bool solveSymbols(KernelPatcher& kp); 47 | 48 | atomic_bool solvedSymbols = false; 49 | 50 | IONotifier* matchingNotifier {nullptr}, * terminationNotifier {nullptr}; 51 | 52 | /* Used for synchronising concurrent access to this class from notification handlers */ 53 | IOLock* lck {nullptr}; 54 | 55 | const char* kextPath { 56 | "/System/Library/Extensions/IONVMeFamily.kext/Contents/MacOS/IONVMeFamily" 57 | }; 58 | 59 | KernelPatcher::KextInfo kextInfo { 60 | "com.apple.iokit.IONVMeFamily", 61 | &kextPath, 62 | 1, 63 | {true}, 64 | {}, 65 | KernelPatcher::KextInfo::Unloaded 66 | }; 67 | 68 | enum { 69 | ASPM_L0sAndL1EntryDisabled = 0, 70 | ASPM_L0sEntryEnabled = 1, 71 | ASPM_L1EntryEnabled = 2, 72 | ASPM_L0sAndL1EntryEnabled = 3, 73 | ASPM_Mask = ASPM_L0sAndL1EntryEnabled 74 | }; 75 | 76 | struct { 77 | template 78 | struct Func { 79 | const char* const name; 80 | mach_vm_address_t fptr {}; 81 | 82 | bool solve(KernelPatcher& kp, size_t idx) { 83 | /* Cache the result */ 84 | if (!fptr) { 85 | fptr = kp.solveSymbol(idx, name); 86 | DBGLOG_COND(fptr == 0, Log::Plugin, "Failed to solve %s", name); 87 | } 88 | return fptr; 89 | } 90 | 91 | bool route(KernelPatcher& kp, size_t idx, T(*repl)(Args...)) { 92 | if (!solve(kp, idx)) 93 | return false; 94 | fptr = kp.routeFunction(fptr, reinterpret_cast(repl), true); 95 | return fptr; 96 | } 97 | 98 | bool routeVirtual(KernelPatcher& kp, size_t idx, const char* vtFor, size_t offs, T(*repl)(Args...)) { 99 | using PtrTy = T(**)(Args...); 100 | assert(vtFor); 101 | auto vt = kp.solveSymbol(idx, vtFor); 102 | return vt && KernelPatcher::routeVirtual(&vt, offs, repl, reinterpret_cast(&fptr)); 103 | } 104 | 105 | T operator()(Args... args) const { 106 | assertf(fptr, "%s not solved", name ? name : "(unknown)"); 107 | return (*reinterpret_cast(fptr))(static_cast(args)...); 108 | } 109 | }; 110 | 111 | struct { 112 | Func IssueIdentifyCommand { 113 | "__ZN16IONVMeController20IssueIdentifyCommandEP18IOMemoryDescriptorP16AppleNVMeRequestj" 114 | }; 115 | 116 | Func IssueIdentifyCommandNew { 117 | "__ZN16IONVMeController20IssueIdentifyCommandEP18IOMemoryDescriptorjb" 118 | }; 119 | 120 | Func ProcessSyncNVMeRequest { 121 | "__ZN16IONVMeController22ProcessSyncNVMeRequestEP16AppleNVMeRequest" 122 | }; 123 | 124 | Func GetRequest { 125 | "__ZN16IONVMeController10GetRequestEj" 126 | }; 127 | Func GetRequestNew { 128 | "__ZN16IONVMeController10GetRequestEjh" 129 | }; 130 | Func ReturnRequest { 131 | "__ZN16IONVMeController13ReturnRequestEP16AppleNVMeRequest" 132 | }; 133 | Func activityTickle {}; 134 | Func FilterInterruptRequest { 135 | "__ZN16IONVMeController22FilterInterruptRequestEP28IOFilterInterruptEventSource" 136 | }; 137 | } IONVMeController; 138 | 139 | struct { 140 | Func BuildCommandGetFeatures { 141 | "__ZN16AppleNVMeRequest23BuildCommandGetFeaturesEh" 142 | }; 143 | 144 | Func BuildCommandSetFeaturesCommon { 145 | "__ZN16AppleNVMeRequest29BuildCommandSetFeaturesCommonEh" 146 | }; 147 | 148 | Func GetStatus { 149 | "__ZN16AppleNVMeRequest9GetStatusEv" 150 | }; 151 | 152 | Func GetOpcode { 153 | "__ZN16AppleNVMeRequest9GetOpcodeEv" 154 | }; 155 | 156 | Func GenerateIOVMSegments { 157 | "__ZN16AppleNVMeRequest20GenerateIOVMSegmentsEyy" 158 | }; 159 | } AppleNVMeRequest; 160 | } kextFuncs; 161 | 162 | struct { 163 | template 164 | struct Member { 165 | #ifdef DEBUG 166 | #define NAMED_MEMBER(n) n {.name = #n} 167 | #else 168 | #define NAMED_MEMBER(n) n 169 | #endif 170 | 171 | #ifdef DEBUG 172 | const char* const name {}; 173 | #endif 174 | mach_vm_address_t offs {}; 175 | T& get(void* obj) { 176 | assert(offs); 177 | assert(obj); 178 | 179 | return getMember(obj, offs); 180 | } 181 | 182 | bool has() { 183 | return offs != 0; 184 | } 185 | 186 | bool fromFunc(mach_vm_address_t start, uint32_t opcode, uint32_t reg, uint32_t rm, 187 | uint32_t add=0, size_t ninsts_max=128) { 188 | if (offs) 189 | return true; 190 | 191 | if (!start) { 192 | DBGLOG(Log::Disasm, "No start specified for %s", name); 193 | return false; 194 | } 195 | hde64s dis; 196 | 197 | for (size_t i = 0; i < ninsts_max; i++) { 198 | auto sz = Disassembler::hdeDisasm(start, &dis); 199 | 200 | if (dis.flags & F_ERROR) { 201 | DBGLOG(Log::Disasm, "Error disassembling %s", name); 202 | break; 203 | } 204 | 205 | /* mov reg, [reg+disp] */ 206 | if (dis.opcode == opcode && dis.modrm_reg == reg && dis.modrm_rm == (rm & 7) && dis.rex_b == ((rm & 8) >> 3)) { 207 | offs = dis.disp.disp32 + add; 208 | DBGLOG(Log::Disasm, "Offset 0x%x for %s", offs, name); 209 | return true; 210 | } 211 | 212 | start += sz; 213 | } 214 | 215 | DBGLOG(Log::Disasm, "Failed to find %s", name); 216 | return false; 217 | }; 218 | }; 219 | 220 | struct { 221 | Member NAMED_MEMBER(ANS2MSIWorkaround); 222 | } IONVMeController; 223 | 224 | struct { 225 | Member NAMED_MEMBER(result); 226 | Member NAMED_MEMBER(controller); 227 | Member NAMED_MEMBER(command); 228 | Member NAMED_MEMBER(prpDescriptor); 229 | } AppleNVMeRequest; 230 | #undef NAMED_MEMBER 231 | } kextMembers; 232 | 233 | /* 234 | * How far should we traverse ioreg searching for parent NVMe controller 235 | * Typical depth is 9 on real setups 236 | */ 237 | static constexpr int controllerSearchDepth {20}; 238 | 239 | struct ControllerEntry { 240 | IOService* controller {nullptr}; 241 | bool processed {false}; 242 | NVMe::nvme_quirks quirks {NVMe::NVME_QUIRK_NONE}; 243 | uint64_t ps_max_latency_us {100000}; 244 | IOPMPowerState* powerStates {nullptr}; 245 | size_t nstates {0}; 246 | IOLock* lck {nullptr}; 247 | IOService* pm {nullptr}; 248 | IOBufferMemoryDescriptor* identify {nullptr}; 249 | bool apste {false}; 250 | 251 | bool apstAllowed() { 252 | return !(quirks & NVMe::nvme_quirks::NVME_QUIRK_NO_APST) && ps_max_latency_us > 0; 253 | } 254 | 255 | static void deleter(ControllerEntry* entry) { 256 | assert(entry); 257 | 258 | /* PM functions don't check for validity of entry or its members, so let's stop it early */ 259 | if (entry->pm) { 260 | if (entry->controller) 261 | entry->controller->deRegisterInterestedDriver(entry->pm); 262 | entry->pm->PMstop(); 263 | entry->pm->release(); 264 | } 265 | if (entry->powerStates) 266 | delete[] entry->powerStates; 267 | if (entry->identify) 268 | entry->identify->release(); 269 | if (entry->lck) 270 | IOLockFree(entry->lck); 271 | 272 | delete entry; 273 | } 274 | 275 | explicit ControllerEntry(IOService* c) : controller(c) { 276 | lck = IOLockAlloc(); 277 | assert(lck); 278 | } 279 | }; 280 | 281 | evector controllers; 282 | void handleControllers(); 283 | void forceEnableASPM(IOService*); 284 | void handleController(ControllerEntry&); 285 | IOReturn identify(ControllerEntry&,IOBufferMemoryDescriptor*&); 286 | bool enableAPST(ControllerEntry&, const NVMe::nvme_id_ctrl*); 287 | IOReturn configureAPST(ControllerEntry&,const NVMe::nvme_id_ctrl*); 288 | IOReturn APSTenabled(ControllerEntry&, bool&); 289 | IOReturn dumpAPST(ControllerEntry&, int npss); 290 | IOReturn NVMeFeatures(ControllerEntry&, unsigned fid, unsigned* dword11, IOBufferMemoryDescriptor* desc, 291 | uint32_t* res, bool set); 292 | ControllerEntry* entryForController(IOService*) const; 293 | struct PM { 294 | /** 295 | * If `apst`, initialises and enables NVMePMProxy to handle controller power state change 296 | * events in order to re-enable APST after reset. 297 | * If `!apst`, active NVMe PM is configured. 298 | */ 299 | bool init(ControllerEntry&,const NVMe::nvme_id_ctrl*,bool apst); 300 | bool solveSymbols(KernelPatcher&); 301 | 302 | static constexpr unsigned idlePeriod {2}; /* seconds */ 303 | 304 | static bool activityTickle(void*,unsigned long,unsigned long); 305 | 306 | explicit PM(NVMeFixPlugin& plugin) : plugin(plugin) {} 307 | private: 308 | NVMeFixPlugin& plugin; 309 | 310 | bool initActivePM(ControllerEntry&, const NVMe::nvme_id_ctrl*); 311 | } PM; 312 | }; 313 | 314 | class NVMePMProxy : public IOService { 315 | OSDeclareDefaultStructors(NVMePMProxy) 316 | public: 317 | // NVMe power management 318 | virtual IOReturn setPowerState( 319 | unsigned long powerStateOrdinal, 320 | IOService * whatDevice ) override; 321 | 322 | // Monitoring IONVMeController power state to re-enable APST 323 | virtual IOReturn powerStateDidChangeTo( 324 | IOPMPowerFlags capabilities, 325 | unsigned long stateNumber, 326 | IOService * whatDevice ) override; 327 | 328 | NVMeFixPlugin::ControllerEntry* entry {nullptr}; 329 | }; 330 | 331 | #endif /* NVMeFixPlugin_h */ 332 | -------------------------------------------------------------------------------- /NVMeFix/linux_types.h: -------------------------------------------------------------------------------- 1 | // 2 | // @file linux_types.h 3 | // 4 | // NVMeFix 5 | // 6 | // Copyright © 2019 acidanthera. All rights reserved. 7 | // 8 | // This program and the accompanying materials 9 | // are licensed and made available under the terms and conditions of the BSD License 10 | // which accompanies this distribution. The full text of the license may be found at 11 | // http://opensource.org/licenses/bsd-license.php 12 | // THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 14 | 15 | 16 | #ifndef linux_types_h 17 | #define linux_types_h 18 | 19 | #include 20 | 21 | namespace linux_types { 22 | #define USING_TY(p, sz) using p ## sz = uint ## sz ## _t; 23 | 24 | USING_TY(__le, 16) 25 | USING_TY(__le, 32) 26 | USING_TY(__le, 64) 27 | USING_TY(__u, 8) 28 | USING_TY(__u, 16) 29 | USING_TY(__u, 32) 30 | USING_TY(__u, 64) 31 | 32 | using kernel_ulong_t = unsigned long; 33 | #undef USING_TY 34 | }; 35 | 36 | #endif /* linux_types_h */ 37 | -------------------------------------------------------------------------------- /NVMeFix/nvme.h: -------------------------------------------------------------------------------- 1 | // 2 | // @file nvme.h 3 | // 4 | // NVMeFix 5 | // 6 | // Copyright © 2019 acidanthera. All rights reserved. 7 | // 8 | // This program and the accompanying materials 9 | // are licensed and made available under the terms and conditions of the BSD License 10 | // which accompanies this distribution. The full text of the license may be found at 11 | // http://opensource.org/licenses/bsd-license.php 12 | // THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 14 | 15 | /* SPDX-License-Identifier: GPL-2.0 */ 16 | /* 17 | * Definitions for the NVM Express interface 18 | * Portions Copyright (c) 2011-2014, Intel Corporation. 19 | */ 20 | 21 | /* linux/include/linux/nvme.h */ 22 | 23 | #ifndef nvme_h 24 | #define nvme_h 25 | 26 | #include "linux_types.h" 27 | 28 | #pragma pack(push,1) 29 | namespace NVMe { 30 | 31 | using namespace linux_types; 32 | 33 | #define unlikely 34 | 35 | /* NQN names in commands fields specified one size */ 36 | #define NVMF_NQN_FIELD_LEN 256 37 | 38 | /* However the max length of a qualified name is another size */ 39 | #define NVMF_NQN_SIZE 223 40 | 41 | #define NVMF_TRSVCID_SIZE 32 42 | #define NVMF_TRADDR_SIZE 256 43 | #define NVMF_TSAS_SIZE 256 44 | 45 | #define NVME_DISC_SUBSYS_NAME "nqn.2014-08.org.nvmexpress.discovery" 46 | 47 | #define NVME_RDMA_IP_PORT 4420 48 | 49 | #define NVME_NSID_ALL 0xffffffff 50 | 51 | enum nvme_subsys_type { 52 | NVME_NQN_DISC = 1, /* Discovery type target subsystem */ 53 | NVME_NQN_NVME = 2, /* NVME type target subsystem */ 54 | }; 55 | 56 | /* Address Family codes for Discovery Log Page entry ADRFAM field */ 57 | enum { 58 | NVMF_ADDR_FAMILY_PCI = 0, /* PCIe */ 59 | NVMF_ADDR_FAMILY_IP4 = 1, /* IP4 */ 60 | NVMF_ADDR_FAMILY_IP6 = 2, /* IP6 */ 61 | NVMF_ADDR_FAMILY_IB = 3, /* InfiniBand */ 62 | NVMF_ADDR_FAMILY_FC = 4, /* Fibre Channel */ 63 | }; 64 | 65 | /* Transport Type codes for Discovery Log Page entry TRTYPE field */ 66 | enum { 67 | NVMF_TRTYPE_RDMA = 1, /* RDMA */ 68 | NVMF_TRTYPE_FC = 2, /* Fibre Channel */ 69 | NVMF_TRTYPE_TCP = 3, /* TCP/IP */ 70 | NVMF_TRTYPE_LOOP = 254, /* Reserved for host usage */ 71 | NVMF_TRTYPE_MAX, 72 | }; 73 | 74 | /* Transport Requirements codes for Discovery Log Page entry TREQ field */ 75 | enum { 76 | NVMF_TREQ_NOT_SPECIFIED = 0, /* Not specified */ 77 | NVMF_TREQ_REQUIRED = 1, /* Required */ 78 | NVMF_TREQ_NOT_REQUIRED = 2, /* Not Required */ 79 | #define NVME_TREQ_SECURE_CHANNEL_MASK \ 80 | (NVMF_TREQ_REQUIRED | NVMF_TREQ_NOT_REQUIRED) 81 | 82 | NVMF_TREQ_DISABLE_SQFLOW = (1 << 2), /* Supports SQ flow control disable */ 83 | }; 84 | 85 | /* RDMA QP Service Type codes for Discovery Log Page entry TSAS 86 | * RDMA_QPTYPE field 87 | */ 88 | enum { 89 | NVMF_RDMA_QPTYPE_CONNECTED = 1, /* Reliable Connected */ 90 | NVMF_RDMA_QPTYPE_DATAGRAM = 2, /* Reliable Datagram */ 91 | }; 92 | 93 | /* RDMA QP Service Type codes for Discovery Log Page entry TSAS 94 | * RDMA_QPTYPE field 95 | */ 96 | enum { 97 | NVMF_RDMA_PRTYPE_NOT_SPECIFIED = 1, /* No Provider Specified */ 98 | NVMF_RDMA_PRTYPE_IB = 2, /* InfiniBand */ 99 | NVMF_RDMA_PRTYPE_ROCE = 3, /* InfiniBand RoCE */ 100 | NVMF_RDMA_PRTYPE_ROCEV2 = 4, /* InfiniBand RoCEV2 */ 101 | NVMF_RDMA_PRTYPE_IWARP = 5, /* IWARP */ 102 | }; 103 | 104 | /* RDMA Connection Management Service Type codes for Discovery Log Page 105 | * entry TSAS RDMA_CMS field 106 | */ 107 | enum { 108 | NVMF_RDMA_CMS_RDMA_CM = 1, /* Sockets based endpoint addressing */ 109 | }; 110 | 111 | #define NVME_AQ_DEPTH 32 112 | #define NVME_NR_AEN_COMMANDS 1 113 | #define NVME_AQ_BLK_MQ_DEPTH (NVME_AQ_DEPTH - NVME_NR_AEN_COMMANDS) 114 | 115 | /* 116 | * Subtract one to leave an empty queue entry for 'Full Queue' condition. See 117 | * NVM-Express 1.2 specification, section 4.1.2. 118 | */ 119 | #define NVME_AQ_MQ_TAG_DEPTH (NVME_AQ_BLK_MQ_DEPTH - 1) 120 | 121 | enum { 122 | NVME_REG_CAP = 0x0000, /* Controller Capabilities */ 123 | NVME_REG_VS = 0x0008, /* Version */ 124 | NVME_REG_INTMS = 0x000c, /* Interrupt Mask Set */ 125 | NVME_REG_INTMC = 0x0010, /* Interrupt Mask Clear */ 126 | NVME_REG_CC = 0x0014, /* Controller Configuration */ 127 | NVME_REG_CSTS = 0x001c, /* Controller Status */ 128 | NVME_REG_NSSR = 0x0020, /* NVM Subsystem Reset */ 129 | NVME_REG_AQA = 0x0024, /* Admin Queue Attributes */ 130 | NVME_REG_ASQ = 0x0028, /* Admin SQ Base Address */ 131 | NVME_REG_ACQ = 0x0030, /* Admin CQ Base Address */ 132 | NVME_REG_CMBLOC = 0x0038, /* Controller Memory Buffer Location */ 133 | NVME_REG_CMBSZ = 0x003c, /* Controller Memory Buffer Size */ 134 | NVME_REG_DBS = 0x1000, /* SQ 0 Tail Doorbell */ 135 | }; 136 | 137 | #define NVME_CAP_MQES(cap) ((cap) & 0xffff) 138 | #define NVME_CAP_TIMEOUT(cap) (((cap) >> 24) & 0xff) 139 | #define NVME_CAP_STRIDE(cap) (((cap) >> 32) & 0xf) 140 | #define NVME_CAP_NSSRC(cap) (((cap) >> 36) & 0x1) 141 | #define NVME_CAP_MPSMIN(cap) (((cap) >> 48) & 0xf) 142 | #define NVME_CAP_MPSMAX(cap) (((cap) >> 52) & 0xf) 143 | 144 | #define NVME_CMB_BIR(cmbloc) ((cmbloc) & 0x7) 145 | #define NVME_CMB_OFST(cmbloc) (((cmbloc) >> 12) & 0xfffff) 146 | 147 | enum { 148 | NVME_CMBSZ_SQS = 1 << 0, 149 | NVME_CMBSZ_CQS = 1 << 1, 150 | NVME_CMBSZ_LISTS = 1 << 2, 151 | NVME_CMBSZ_RDS = 1 << 3, 152 | NVME_CMBSZ_WDS = 1 << 4, 153 | 154 | NVME_CMBSZ_SZ_SHIFT = 12, 155 | NVME_CMBSZ_SZ_MASK = 0xfffff, 156 | 157 | NVME_CMBSZ_SZU_SHIFT = 8, 158 | NVME_CMBSZ_SZU_MASK = 0xf, 159 | }; 160 | 161 | /* 162 | * Submission and Completion Queue Entry Sizes for the NVM command set. 163 | * (In bytes and specified as a power of two (2^n)). 164 | */ 165 | #define NVME_ADM_SQES 6 166 | #define NVME_NVM_IOSQES 6 167 | #define NVME_NVM_IOCQES 4 168 | 169 | enum { 170 | NVME_CC_ENABLE = 1 << 0, 171 | NVME_CC_CSS_NVM = 0 << 4, 172 | NVME_CC_EN_SHIFT = 0, 173 | NVME_CC_CSS_SHIFT = 4, 174 | NVME_CC_MPS_SHIFT = 7, 175 | NVME_CC_AMS_SHIFT = 11, 176 | NVME_CC_SHN_SHIFT = 14, 177 | NVME_CC_IOSQES_SHIFT = 16, 178 | NVME_CC_IOCQES_SHIFT = 20, 179 | NVME_CC_AMS_RR = 0 << NVME_CC_AMS_SHIFT, 180 | NVME_CC_AMS_WRRU = 1 << NVME_CC_AMS_SHIFT, 181 | NVME_CC_AMS_VS = 7 << NVME_CC_AMS_SHIFT, 182 | NVME_CC_SHN_NONE = 0 << NVME_CC_SHN_SHIFT, 183 | NVME_CC_SHN_NORMAL = 1 << NVME_CC_SHN_SHIFT, 184 | NVME_CC_SHN_ABRUPT = 2 << NVME_CC_SHN_SHIFT, 185 | NVME_CC_SHN_MASK = 3 << NVME_CC_SHN_SHIFT, 186 | NVME_CC_IOSQES = NVME_NVM_IOSQES << NVME_CC_IOSQES_SHIFT, 187 | NVME_CC_IOCQES = NVME_NVM_IOCQES << NVME_CC_IOCQES_SHIFT, 188 | NVME_CSTS_RDY = 1 << 0, 189 | NVME_CSTS_CFS = 1 << 1, 190 | NVME_CSTS_NSSRO = 1 << 4, 191 | NVME_CSTS_PP = 1 << 5, 192 | NVME_CSTS_SHST_NORMAL = 0 << 2, 193 | NVME_CSTS_SHST_OCCUR = 1 << 2, 194 | NVME_CSTS_SHST_CMPLT = 2 << 2, 195 | NVME_CSTS_SHST_MASK = 3 << 2, 196 | }; 197 | 198 | struct nvme_id_power_state { 199 | __le16 max_power; /* centiwatts */ 200 | __u8 rsvd2; 201 | __u8 flags; 202 | __le32 entry_lat; /* microseconds */ 203 | __le32 exit_lat; /* microseconds */ 204 | __u8 read_tput; 205 | __u8 read_lat; 206 | __u8 write_tput; 207 | __u8 write_lat; 208 | __le16 idle_power; 209 | __u8 idle_scale; 210 | __u8 rsvd19; 211 | __le16 active_power; 212 | __u8 active_work_scale; 213 | __u8 rsvd23[9]; 214 | }; 215 | 216 | enum { 217 | NVME_PS_FLAGS_MAX_POWER_SCALE = 1 << 0, 218 | NVME_PS_FLAGS_NON_OP_STATE = 1 << 1, 219 | }; 220 | 221 | enum nvme_ctrl_attr { 222 | NVME_CTRL_ATTR_HID_128_BIT = (1 << 0), 223 | NVME_CTRL_ATTR_TBKAS = (1 << 6), 224 | }; 225 | 226 | struct nvme_id_ctrl { 227 | __le16 vid; 228 | __le16 ssvid; 229 | char sn[20]; 230 | char mn[40]; 231 | char fr[8]; 232 | __u8 rab; 233 | __u8 ieee[3]; 234 | __u8 cmic; 235 | __u8 mdts; 236 | __le16 cntlid; 237 | __le32 ver; 238 | __le32 rtd3r; 239 | __le32 rtd3e; 240 | __le32 oaes; 241 | __le32 ctratt; 242 | __u8 rsvd100[28]; 243 | __le16 crdt1; 244 | __le16 crdt2; 245 | __le16 crdt3; 246 | __u8 rsvd134[122]; 247 | __le16 oacs; 248 | __u8 acl; 249 | __u8 aerl; 250 | __u8 frmw; 251 | __u8 lpa; 252 | __u8 elpe; 253 | __u8 npss; 254 | __u8 avscc; 255 | __u8 apsta; 256 | __le16 wctemp; 257 | __le16 cctemp; 258 | __le16 mtfa; 259 | __le32 hmpre; 260 | __le32 hmmin; 261 | __u8 tnvmcap[16]; 262 | __u8 unvmcap[16]; 263 | __le32 rpmbs; 264 | __le16 edstt; 265 | __u8 dsto; 266 | __u8 fwug; 267 | __le16 kas; 268 | __le16 hctma; 269 | __le16 mntmt; 270 | __le16 mxtmt; 271 | __le32 sanicap; 272 | __le32 hmminds; 273 | __le16 hmmaxd; 274 | __u8 rsvd338[4]; 275 | __u8 anatt; 276 | __u8 anacap; 277 | __le32 anagrpmax; 278 | __le32 nanagrpid; 279 | __u8 rsvd352[160]; 280 | __u8 sqes; 281 | __u8 cqes; 282 | __le16 maxcmd; 283 | __le32 nn; 284 | __le16 oncs; 285 | __le16 fuses; 286 | __u8 fna; 287 | __u8 vwc; 288 | __le16 awun; 289 | __le16 awupf; 290 | __u8 nvscc; 291 | __u8 nwpc; 292 | __le16 acwu; 293 | __u8 rsvd534[2]; 294 | __le32 sgls; 295 | __le32 mnan; 296 | __u8 rsvd544[224]; 297 | char subnqn[256]; 298 | __u8 rsvd1024[768]; 299 | __le32 ioccsz; 300 | __le32 iorcsz; 301 | __le16 icdoff; 302 | __u8 ctrattr; 303 | __u8 msdbd; 304 | __u8 rsvd1804[244]; 305 | struct nvme_id_power_state psd[32]; 306 | __u8 vs[1024]; 307 | }; 308 | 309 | enum { 310 | NVME_CTRL_ONCS_COMPARE = 1 << 0, 311 | NVME_CTRL_ONCS_WRITE_UNCORRECTABLE = 1 << 1, 312 | NVME_CTRL_ONCS_DSM = 1 << 2, 313 | NVME_CTRL_ONCS_WRITE_ZEROES = 1 << 3, 314 | NVME_CTRL_ONCS_TIMESTAMP = 1 << 6, 315 | NVME_CTRL_VWC_PRESENT = 1 << 0, 316 | NVME_CTRL_OACS_SEC_SUPP = 1 << 0, 317 | NVME_CTRL_OACS_DIRECTIVES = 1 << 5, 318 | NVME_CTRL_OACS_DBBUF_SUPP = 1 << 8, 319 | NVME_CTRL_LPA_CMD_EFFECTS_LOG = 1 << 1, 320 | }; 321 | 322 | struct nvme_lbaf { 323 | __le16 ms; 324 | __u8 ds; 325 | __u8 rp; 326 | }; 327 | 328 | struct nvme_id_ns { 329 | __le64 nsze; 330 | __le64 ncap; 331 | __le64 nuse; 332 | __u8 nsfeat; 333 | __u8 nlbaf; 334 | __u8 flbas; 335 | __u8 mc; 336 | __u8 dpc; 337 | __u8 dps; 338 | __u8 nmic; 339 | __u8 rescap; 340 | __u8 fpi; 341 | __u8 dlfeat; 342 | __le16 nawun; 343 | __le16 nawupf; 344 | __le16 nacwu; 345 | __le16 nabsn; 346 | __le16 nabo; 347 | __le16 nabspf; 348 | __le16 noiob; 349 | __u8 nvmcap[16]; 350 | __le16 npwg; 351 | __le16 npwa; 352 | __le16 npdg; 353 | __le16 npda; 354 | __le16 nows; 355 | __u8 rsvd74[18]; 356 | __le32 anagrpid; 357 | __u8 rsvd96[3]; 358 | __u8 nsattr; 359 | __le16 nvmsetid; 360 | __le16 endgid; 361 | __u8 nguid[16]; 362 | __u8 eui64[8]; 363 | struct nvme_lbaf lbaf[16]; 364 | __u8 rsvd192[192]; 365 | __u8 vs[3712]; 366 | }; 367 | 368 | enum { 369 | NVME_ID_CNS_NS = 0x00, 370 | NVME_ID_CNS_CTRL = 0x01, 371 | NVME_ID_CNS_NS_ACTIVE_LIST = 0x02, 372 | NVME_ID_CNS_NS_DESC_LIST = 0x03, 373 | NVME_ID_CNS_NS_PRESENT_LIST = 0x10, 374 | NVME_ID_CNS_NS_PRESENT = 0x11, 375 | NVME_ID_CNS_CTRL_NS_LIST = 0x12, 376 | NVME_ID_CNS_CTRL_LIST = 0x13, 377 | }; 378 | 379 | enum { 380 | NVME_DIR_IDENTIFY = 0x00, 381 | NVME_DIR_STREAMS = 0x01, 382 | NVME_DIR_SND_ID_OP_ENABLE = 0x01, 383 | NVME_DIR_SND_ST_OP_REL_ID = 0x01, 384 | NVME_DIR_SND_ST_OP_REL_RSC = 0x02, 385 | NVME_DIR_RCV_ID_OP_PARAM = 0x01, 386 | NVME_DIR_RCV_ST_OP_PARAM = 0x01, 387 | NVME_DIR_RCV_ST_OP_STATUS = 0x02, 388 | NVME_DIR_RCV_ST_OP_RESOURCE = 0x03, 389 | NVME_DIR_ENDIR = 0x01, 390 | }; 391 | 392 | enum { 393 | NVME_NS_FEAT_THIN = 1 << 0, 394 | NVME_NS_FLBAS_LBA_MASK = 0xf, 395 | NVME_NS_FLBAS_META_EXT = 0x10, 396 | NVME_LBAF_RP_BEST = 0, 397 | NVME_LBAF_RP_BETTER = 1, 398 | NVME_LBAF_RP_GOOD = 2, 399 | NVME_LBAF_RP_DEGRADED = 3, 400 | NVME_NS_DPC_PI_LAST = 1 << 4, 401 | NVME_NS_DPC_PI_FIRST = 1 << 3, 402 | NVME_NS_DPC_PI_TYPE3 = 1 << 2, 403 | NVME_NS_DPC_PI_TYPE2 = 1 << 1, 404 | NVME_NS_DPC_PI_TYPE1 = 1 << 0, 405 | NVME_NS_DPS_PI_FIRST = 1 << 3, 406 | NVME_NS_DPS_PI_MASK = 0x7, 407 | NVME_NS_DPS_PI_TYPE1 = 1, 408 | NVME_NS_DPS_PI_TYPE2 = 2, 409 | NVME_NS_DPS_PI_TYPE3 = 3, 410 | }; 411 | 412 | struct nvme_ns_id_desc { 413 | __u8 nidt; 414 | __u8 nidl; 415 | __le16 reserved; 416 | }; 417 | 418 | #define NVME_NIDT_EUI64_LEN 8 419 | #define NVME_NIDT_NGUID_LEN 16 420 | #define NVME_NIDT_UUID_LEN 16 421 | 422 | enum { 423 | NVME_NIDT_EUI64 = 0x01, 424 | NVME_NIDT_NGUID = 0x02, 425 | NVME_NIDT_UUID = 0x03, 426 | }; 427 | 428 | struct nvme_smart_log { 429 | __u8 critical_warning; 430 | __u8 temperature[2]; 431 | __u8 avail_spare; 432 | __u8 spare_thresh; 433 | __u8 percent_used; 434 | __u8 rsvd6[26]; 435 | __u8 data_units_read[16]; 436 | __u8 data_units_written[16]; 437 | __u8 host_reads[16]; 438 | __u8 host_writes[16]; 439 | __u8 ctrl_busy_time[16]; 440 | __u8 power_cycles[16]; 441 | __u8 power_on_hours[16]; 442 | __u8 unsafe_shutdowns[16]; 443 | __u8 media_errors[16]; 444 | __u8 num_err_log_entries[16]; 445 | __le32 warning_temp_time; 446 | __le32 critical_comp_time; 447 | __le16 temp_sensor[8]; 448 | __u8 rsvd216[296]; 449 | }; 450 | 451 | struct nvme_fw_slot_info_log { 452 | __u8 afi; 453 | __u8 rsvd1[7]; 454 | __le64 frs[7]; 455 | __u8 rsvd64[448]; 456 | }; 457 | 458 | enum { 459 | NVME_CMD_EFFECTS_CSUPP = 1 << 0, 460 | NVME_CMD_EFFECTS_LBCC = 1 << 1, 461 | NVME_CMD_EFFECTS_NCC = 1 << 2, 462 | NVME_CMD_EFFECTS_NIC = 1 << 3, 463 | NVME_CMD_EFFECTS_CCC = 1 << 4, 464 | NVME_CMD_EFFECTS_CSE_MASK = 3 << 16, 465 | }; 466 | 467 | struct nvme_effects_log { 468 | __le32 acs[256]; 469 | __le32 iocs[256]; 470 | __u8 resv[2048]; 471 | }; 472 | 473 | enum nvme_ana_state { 474 | NVME_ANA_OPTIMIZED = 0x01, 475 | NVME_ANA_NONOPTIMIZED = 0x02, 476 | NVME_ANA_INACCESSIBLE = 0x03, 477 | NVME_ANA_PERSISTENT_LOSS = 0x04, 478 | NVME_ANA_CHANGE = 0x0f, 479 | }; 480 | 481 | struct nvme_ana_group_desc { 482 | __le32 grpid; 483 | __le32 nnsids; 484 | __le64 chgcnt; 485 | __u8 state; 486 | __u8 rsvd17[15]; 487 | __le32 nsids[]; 488 | }; 489 | 490 | /* flag for the log specific field of the ANA log */ 491 | #define NVME_ANA_LOG_RGO (1 << 0) 492 | 493 | struct nvme_ana_rsp_hdr { 494 | __le64 chgcnt; 495 | __le16 ngrps; 496 | __le16 rsvd10[3]; 497 | }; 498 | 499 | enum { 500 | NVME_SMART_CRIT_SPARE = 1 << 0, 501 | NVME_SMART_CRIT_TEMPERATURE = 1 << 1, 502 | NVME_SMART_CRIT_RELIABILITY = 1 << 2, 503 | NVME_SMART_CRIT_MEDIA = 1 << 3, 504 | NVME_SMART_CRIT_VOLATILE_MEMORY = 1 << 4, 505 | }; 506 | 507 | enum { 508 | NVME_AER_ERROR = 0, 509 | NVME_AER_SMART = 1, 510 | NVME_AER_NOTICE = 2, 511 | NVME_AER_CSS = 6, 512 | NVME_AER_VS = 7, 513 | }; 514 | 515 | enum { 516 | NVME_AER_NOTICE_NS_CHANGED = 0x00, 517 | NVME_AER_NOTICE_FW_ACT_STARTING = 0x01, 518 | NVME_AER_NOTICE_ANA = 0x03, 519 | NVME_AER_NOTICE_DISC_CHANGED = 0xf0, 520 | }; 521 | 522 | enum { 523 | NVME_AEN_BIT_NS_ATTR = 8, 524 | NVME_AEN_BIT_FW_ACT = 9, 525 | NVME_AEN_BIT_ANA_CHANGE = 11, 526 | NVME_AEN_BIT_DISC_CHANGE = 31, 527 | }; 528 | 529 | enum { 530 | NVME_AEN_CFG_NS_ATTR = 1 << NVME_AEN_BIT_NS_ATTR, 531 | NVME_AEN_CFG_FW_ACT = 1 << NVME_AEN_BIT_FW_ACT, 532 | NVME_AEN_CFG_ANA_CHANGE = 1 << NVME_AEN_BIT_ANA_CHANGE, 533 | NVME_AEN_CFG_DISC_CHANGE = 1 << NVME_AEN_BIT_DISC_CHANGE, 534 | }; 535 | 536 | struct nvme_lba_range_type { 537 | __u8 type; 538 | __u8 attributes; 539 | __u8 rsvd2[14]; 540 | __u64 slba; 541 | __u64 nlb; 542 | __u8 guid[16]; 543 | __u8 rsvd48[16]; 544 | }; 545 | 546 | enum { 547 | NVME_LBART_TYPE_FS = 0x01, 548 | NVME_LBART_TYPE_RAID = 0x02, 549 | NVME_LBART_TYPE_CACHE = 0x03, 550 | NVME_LBART_TYPE_SWAP = 0x04, 551 | 552 | NVME_LBART_ATTRIB_TEMP = 1 << 0, 553 | NVME_LBART_ATTRIB_HIDE = 1 << 1, 554 | }; 555 | 556 | struct nvme_reservation_status { 557 | __le32 gen; 558 | __u8 rtype; 559 | __u8 regctl[2]; 560 | __u8 resv5[2]; 561 | __u8 ptpls; 562 | __u8 resv10[13]; 563 | struct { 564 | __le16 cntlid; 565 | __u8 rcsts; 566 | __u8 resv3[5]; 567 | __le64 hostid; 568 | __le64 rkey; 569 | } regctl_ds[]; 570 | }; 571 | 572 | enum nvme_async_event_type { 573 | NVME_AER_TYPE_ERROR = 0, 574 | NVME_AER_TYPE_SMART = 1, 575 | NVME_AER_TYPE_NOTICE = 2, 576 | }; 577 | 578 | /* I/O commands */ 579 | 580 | enum nvme_opcode { 581 | nvme_cmd_flush = 0x00, 582 | nvme_cmd_write = 0x01, 583 | nvme_cmd_read = 0x02, 584 | nvme_cmd_write_uncor = 0x04, 585 | nvme_cmd_compare = 0x05, 586 | nvme_cmd_write_zeroes = 0x08, 587 | nvme_cmd_dsm = 0x09, 588 | nvme_cmd_resv_register = 0x0d, 589 | nvme_cmd_resv_report = 0x0e, 590 | nvme_cmd_resv_acquire = 0x11, 591 | nvme_cmd_resv_release = 0x15, 592 | }; 593 | 594 | #define nvme_opcode_name(opcode) { opcode, #opcode } 595 | #define show_nvm_opcode_name(val) \ 596 | __print_symbolic(val, \ 597 | nvme_opcode_name(nvme_cmd_flush), \ 598 | nvme_opcode_name(nvme_cmd_write), \ 599 | nvme_opcode_name(nvme_cmd_read), \ 600 | nvme_opcode_name(nvme_cmd_write_uncor), \ 601 | nvme_opcode_name(nvme_cmd_compare), \ 602 | nvme_opcode_name(nvme_cmd_write_zeroes), \ 603 | nvme_opcode_name(nvme_cmd_dsm), \ 604 | nvme_opcode_name(nvme_cmd_resv_register), \ 605 | nvme_opcode_name(nvme_cmd_resv_report), \ 606 | nvme_opcode_name(nvme_cmd_resv_acquire), \ 607 | nvme_opcode_name(nvme_cmd_resv_release)) 608 | 609 | 610 | /* 611 | * Descriptor subtype - lower 4 bits of nvme_(keyed_)sgl_desc identifier 612 | * 613 | * @NVME_SGL_FMT_ADDRESS: absolute address of the data block 614 | * @NVME_SGL_FMT_OFFSET: relative offset of the in-capsule data block 615 | * @NVME_SGL_FMT_TRANSPORT_A: transport defined format, value 0xA 616 | * @NVME_SGL_FMT_INVALIDATE: RDMA transport specific remote invalidation 617 | * request subtype 618 | */ 619 | enum { 620 | NVME_SGL_FMT_ADDRESS = 0x00, 621 | NVME_SGL_FMT_OFFSET = 0x01, 622 | NVME_SGL_FMT_TRANSPORT_A = 0x0A, 623 | NVME_SGL_FMT_INVALIDATE = 0x0f, 624 | }; 625 | 626 | /* 627 | * Descriptor type - upper 4 bits of nvme_(keyed_)sgl_desc identifier 628 | * 629 | * For struct nvme_sgl_desc: 630 | * @NVME_SGL_FMT_DATA_DESC: data block descriptor 631 | * @NVME_SGL_FMT_SEG_DESC: sgl segment descriptor 632 | * @NVME_SGL_FMT_LAST_SEG_DESC: last sgl segment descriptor 633 | * 634 | * For struct nvme_keyed_sgl_desc: 635 | * @NVME_KEY_SGL_FMT_DATA_DESC: keyed data block descriptor 636 | * 637 | * Transport-specific SGL types: 638 | * @NVME_TRANSPORT_SGL_DATA_DESC: Transport SGL data dlock descriptor 639 | */ 640 | enum { 641 | NVME_SGL_FMT_DATA_DESC = 0x00, 642 | NVME_SGL_FMT_SEG_DESC = 0x02, 643 | NVME_SGL_FMT_LAST_SEG_DESC = 0x03, 644 | NVME_KEY_SGL_FMT_DATA_DESC = 0x04, 645 | NVME_TRANSPORT_SGL_DATA_DESC = 0x05, 646 | }; 647 | 648 | struct nvme_sgl_desc { 649 | __le64 addr; 650 | __le32 length; 651 | __u8 rsvd[3]; 652 | __u8 type; 653 | }; 654 | 655 | struct nvme_keyed_sgl_desc { 656 | __le64 addr; 657 | __u8 length[3]; 658 | __u8 key[4]; 659 | __u8 type; 660 | }; 661 | 662 | union nvme_data_ptr { 663 | struct { 664 | __le64 prp1; 665 | __le64 prp2; 666 | }; 667 | struct nvme_sgl_desc sgl; 668 | struct nvme_keyed_sgl_desc ksgl; 669 | }; 670 | 671 | /* 672 | * Lowest two bits of our flags field (FUSE field in the spec): 673 | * 674 | * @NVME_CMD_FUSE_FIRST: Fused Operation, first command 675 | * @NVME_CMD_FUSE_SECOND: Fused Operation, second command 676 | * 677 | * Highest two bits in our flags field (PSDT field in the spec): 678 | * 679 | * @NVME_CMD_PSDT_SGL_METABUF: Use SGLS for this transfer, 680 | * If used, MPTR contains addr of single physical buffer (byte aligned). 681 | * @NVME_CMD_PSDT_SGL_METASEG: Use SGLS for this transfer, 682 | * If used, MPTR contains an address of an SGL segment containing 683 | * exactly 1 SGL descriptor (qword aligned). 684 | */ 685 | enum { 686 | NVME_CMD_FUSE_FIRST = (1 << 0), 687 | NVME_CMD_FUSE_SECOND = (1 << 1), 688 | 689 | NVME_CMD_SGL_METABUF = (1 << 6), 690 | NVME_CMD_SGL_METASEG = (1 << 7), 691 | NVME_CMD_SGL_ALL = NVME_CMD_SGL_METABUF | NVME_CMD_SGL_METASEG, 692 | }; 693 | 694 | struct nvme_common_command { 695 | __u8 opcode; 696 | __u8 flags; 697 | __u16 command_id; 698 | __le32 nsid; 699 | __le32 cdw2[2]; 700 | __le64 metadata; 701 | union nvme_data_ptr dptr; 702 | __le32 cdw10; 703 | __le32 cdw11; 704 | __le32 cdw12; 705 | __le32 cdw13; 706 | __le32 cdw14; 707 | __le32 cdw15; 708 | }; 709 | 710 | struct nvme_rw_command { 711 | __u8 opcode; 712 | __u8 flags; 713 | __u16 command_id; 714 | __le32 nsid; 715 | __u64 rsvd2; 716 | __le64 metadata; 717 | union nvme_data_ptr dptr; 718 | __le64 slba; 719 | __le16 length; 720 | __le16 control; 721 | __le32 dsmgmt; 722 | __le32 reftag; 723 | __le16 apptag; 724 | __le16 appmask; 725 | }; 726 | 727 | enum { 728 | NVME_RW_LR = 1 << 15, 729 | NVME_RW_FUA = 1 << 14, 730 | NVME_RW_DSM_FREQ_UNSPEC = 0, 731 | NVME_RW_DSM_FREQ_TYPICAL = 1, 732 | NVME_RW_DSM_FREQ_RARE = 2, 733 | NVME_RW_DSM_FREQ_READS = 3, 734 | NVME_RW_DSM_FREQ_WRITES = 4, 735 | NVME_RW_DSM_FREQ_RW = 5, 736 | NVME_RW_DSM_FREQ_ONCE = 6, 737 | NVME_RW_DSM_FREQ_PREFETCH = 7, 738 | NVME_RW_DSM_FREQ_TEMP = 8, 739 | NVME_RW_DSM_LATENCY_NONE = 0 << 4, 740 | NVME_RW_DSM_LATENCY_IDLE = 1 << 4, 741 | NVME_RW_DSM_LATENCY_NORM = 2 << 4, 742 | NVME_RW_DSM_LATENCY_LOW = 3 << 4, 743 | NVME_RW_DSM_SEQ_REQ = 1 << 6, 744 | NVME_RW_DSM_COMPRESSED = 1 << 7, 745 | NVME_RW_PRINFO_PRCHK_REF = 1 << 10, 746 | NVME_RW_PRINFO_PRCHK_APP = 1 << 11, 747 | NVME_RW_PRINFO_PRCHK_GUARD = 1 << 12, 748 | NVME_RW_PRINFO_PRACT = 1 << 13, 749 | NVME_RW_DTYPE_STREAMS = 1 << 4, 750 | }; 751 | 752 | struct nvme_dsm_cmd { 753 | __u8 opcode; 754 | __u8 flags; 755 | __u16 command_id; 756 | __le32 nsid; 757 | __u64 rsvd2[2]; 758 | union nvme_data_ptr dptr; 759 | __le32 nr; 760 | __le32 attributes; 761 | __u32 rsvd12[4]; 762 | }; 763 | 764 | enum { 765 | NVME_DSMGMT_IDR = 1 << 0, 766 | NVME_DSMGMT_IDW = 1 << 1, 767 | NVME_DSMGMT_AD = 1 << 2, 768 | }; 769 | 770 | #define NVME_DSM_MAX_RANGES 256 771 | 772 | struct nvme_dsm_range { 773 | __le32 cattr; 774 | __le32 nlb; 775 | __le64 slba; 776 | }; 777 | 778 | struct nvme_write_zeroes_cmd { 779 | __u8 opcode; 780 | __u8 flags; 781 | __u16 command_id; 782 | __le32 nsid; 783 | __u64 rsvd2; 784 | __le64 metadata; 785 | union nvme_data_ptr dptr; 786 | __le64 slba; 787 | __le16 length; 788 | __le16 control; 789 | __le32 dsmgmt; 790 | __le32 reftag; 791 | __le16 apptag; 792 | __le16 appmask; 793 | }; 794 | 795 | /* Features */ 796 | 797 | struct nvme_feat_auto_pst { 798 | __le64 entries[32]; 799 | }; 800 | 801 | enum { 802 | NVME_HOST_MEM_ENABLE = (1 << 0), 803 | NVME_HOST_MEM_RETURN = (1 << 1), 804 | }; 805 | 806 | struct nvme_feat_host_behavior { 807 | __u8 acre; 808 | __u8 resv1[511]; 809 | }; 810 | 811 | enum { 812 | NVME_ENABLE_ACRE = 1, 813 | }; 814 | 815 | /* Admin commands */ 816 | 817 | enum nvme_admin_opcode { 818 | nvme_admin_delete_sq = 0x00, 819 | nvme_admin_create_sq = 0x01, 820 | nvme_admin_get_log_page = 0x02, 821 | nvme_admin_delete_cq = 0x04, 822 | nvme_admin_create_cq = 0x05, 823 | nvme_admin_identify = 0x06, 824 | nvme_admin_abort_cmd = 0x08, 825 | nvme_admin_set_features = 0x09, 826 | nvme_admin_get_features = 0x0a, 827 | nvme_admin_async_event = 0x0c, 828 | nvme_admin_ns_mgmt = 0x0d, 829 | nvme_admin_activate_fw = 0x10, 830 | nvme_admin_download_fw = 0x11, 831 | nvme_admin_ns_attach = 0x15, 832 | nvme_admin_keep_alive = 0x18, 833 | nvme_admin_directive_send = 0x19, 834 | nvme_admin_directive_recv = 0x1a, 835 | nvme_admin_dbbuf = 0x7C, 836 | nvme_admin_format_nvm = 0x80, 837 | nvme_admin_security_send = 0x81, 838 | nvme_admin_security_recv = 0x82, 839 | nvme_admin_sanitize_nvm = 0x84, 840 | nvme_admin_get_lba_status = 0x86, 841 | }; 842 | 843 | #define nvme_admin_opcode_name(opcode) { opcode, #opcode } 844 | #define show_admin_opcode_name(val) \ 845 | __print_symbolic(val, \ 846 | nvme_admin_opcode_name(nvme_admin_delete_sq), \ 847 | nvme_admin_opcode_name(nvme_admin_create_sq), \ 848 | nvme_admin_opcode_name(nvme_admin_get_log_page), \ 849 | nvme_admin_opcode_name(nvme_admin_delete_cq), \ 850 | nvme_admin_opcode_name(nvme_admin_create_cq), \ 851 | nvme_admin_opcode_name(nvme_admin_identify), \ 852 | nvme_admin_opcode_name(nvme_admin_abort_cmd), \ 853 | nvme_admin_opcode_name(nvme_admin_set_features), \ 854 | nvme_admin_opcode_name(nvme_admin_get_features), \ 855 | nvme_admin_opcode_name(nvme_admin_async_event), \ 856 | nvme_admin_opcode_name(nvme_admin_ns_mgmt), \ 857 | nvme_admin_opcode_name(nvme_admin_activate_fw), \ 858 | nvme_admin_opcode_name(nvme_admin_download_fw), \ 859 | nvme_admin_opcode_name(nvme_admin_ns_attach), \ 860 | nvme_admin_opcode_name(nvme_admin_keep_alive), \ 861 | nvme_admin_opcode_name(nvme_admin_directive_send), \ 862 | nvme_admin_opcode_name(nvme_admin_directive_recv), \ 863 | nvme_admin_opcode_name(nvme_admin_dbbuf), \ 864 | nvme_admin_opcode_name(nvme_admin_format_nvm), \ 865 | nvme_admin_opcode_name(nvme_admin_security_send), \ 866 | nvme_admin_opcode_name(nvme_admin_security_recv), \ 867 | nvme_admin_opcode_name(nvme_admin_sanitize_nvm), \ 868 | nvme_admin_opcode_name(nvme_admin_get_lba_status)) 869 | 870 | enum { 871 | NVME_QUEUE_PHYS_CONTIG = (1 << 0), 872 | NVME_CQ_IRQ_ENABLED = (1 << 1), 873 | NVME_SQ_PRIO_URGENT = (0 << 1), 874 | NVME_SQ_PRIO_HIGH = (1 << 1), 875 | NVME_SQ_PRIO_MEDIUM = (2 << 1), 876 | NVME_SQ_PRIO_LOW = (3 << 1), 877 | NVME_FEAT_ARBITRATION = 0x01, 878 | NVME_FEAT_POWER_MGMT = 0x02, 879 | NVME_FEAT_LBA_RANGE = 0x03, 880 | NVME_FEAT_TEMP_THRESH = 0x04, 881 | NVME_FEAT_ERR_RECOVERY = 0x05, 882 | NVME_FEAT_VOLATILE_WC = 0x06, 883 | NVME_FEAT_NUM_QUEUES = 0x07, 884 | NVME_FEAT_IRQ_COALESCE = 0x08, 885 | NVME_FEAT_IRQ_CONFIG = 0x09, 886 | NVME_FEAT_WRITE_ATOMIC = 0x0a, 887 | NVME_FEAT_ASYNC_EVENT = 0x0b, 888 | NVME_FEAT_AUTO_PST = 0x0c, 889 | NVME_FEAT_HOST_MEM_BUF = 0x0d, 890 | NVME_FEAT_TIMESTAMP = 0x0e, 891 | NVME_FEAT_KATO = 0x0f, 892 | NVME_FEAT_HCTM = 0x10, 893 | NVME_FEAT_NOPSC = 0x11, 894 | NVME_FEAT_RRL = 0x12, 895 | NVME_FEAT_PLM_CONFIG = 0x13, 896 | NVME_FEAT_PLM_WINDOW = 0x14, 897 | NVME_FEAT_HOST_BEHAVIOR = 0x16, 898 | NVME_FEAT_SW_PROGRESS = 0x80, 899 | NVME_FEAT_HOST_ID = 0x81, 900 | NVME_FEAT_RESV_MASK = 0x82, 901 | NVME_FEAT_RESV_PERSIST = 0x83, 902 | NVME_FEAT_WRITE_PROTECT = 0x84, 903 | NVME_LOG_ERROR = 0x01, 904 | NVME_LOG_SMART = 0x02, 905 | NVME_LOG_FW_SLOT = 0x03, 906 | NVME_LOG_CHANGED_NS = 0x04, 907 | NVME_LOG_CMD_EFFECTS = 0x05, 908 | NVME_LOG_ANA = 0x0c, 909 | NVME_LOG_DISC = 0x70, 910 | NVME_LOG_RESERVATION = 0x80, 911 | NVME_FWACT_REPL = (0 << 3), 912 | NVME_FWACT_REPL_ACTV = (1 << 3), 913 | NVME_FWACT_ACTV = (2 << 3), 914 | }; 915 | 916 | /* NVMe Namespace Write Protect State */ 917 | enum { 918 | NVME_NS_NO_WRITE_PROTECT = 0, 919 | NVME_NS_WRITE_PROTECT, 920 | NVME_NS_WRITE_PROTECT_POWER_CYCLE, 921 | NVME_NS_WRITE_PROTECT_PERMANENT, 922 | }; 923 | 924 | #define NVME_MAX_CHANGED_NAMESPACES 1024 925 | 926 | struct nvme_identify { 927 | __u8 opcode; 928 | __u8 flags; 929 | __u16 command_id; 930 | __le32 nsid; 931 | __u64 rsvd2[2]; 932 | union nvme_data_ptr dptr; 933 | __u8 cns; 934 | __u8 rsvd3; 935 | __le16 ctrlid; 936 | __u32 rsvd11[5]; 937 | }; 938 | 939 | #define NVME_IDENTIFY_DATA_SIZE 4096 940 | 941 | struct nvme_features { 942 | __u8 opcode; 943 | __u8 flags; 944 | __u16 command_id; 945 | __le32 nsid; 946 | __u64 rsvd2[2]; 947 | union nvme_data_ptr dptr; 948 | __le32 fid; 949 | __le32 dword11; 950 | __le32 dword12; 951 | __le32 dword13; 952 | __le32 dword14; 953 | __le32 dword15; 954 | }; 955 | 956 | struct nvme_host_mem_buf_desc { 957 | __le64 addr; 958 | __le32 size; 959 | __u32 rsvd; 960 | }; 961 | 962 | struct nvme_create_cq { 963 | __u8 opcode; 964 | __u8 flags; 965 | __u16 command_id; 966 | __u32 rsvd1[5]; 967 | __le64 prp1; 968 | __u64 rsvd8; 969 | __le16 cqid; 970 | __le16 qsize; 971 | __le16 cq_flags; 972 | __le16 irq_vector; 973 | __u32 rsvd12[4]; 974 | }; 975 | 976 | struct nvme_create_sq { 977 | __u8 opcode; 978 | __u8 flags; 979 | __u16 command_id; 980 | __u32 rsvd1[5]; 981 | __le64 prp1; 982 | __u64 rsvd8; 983 | __le16 sqid; 984 | __le16 qsize; 985 | __le16 sq_flags; 986 | __le16 cqid; 987 | __u32 rsvd12[4]; 988 | }; 989 | 990 | struct nvme_delete_queue { 991 | __u8 opcode; 992 | __u8 flags; 993 | __u16 command_id; 994 | __u32 rsvd1[9]; 995 | __le16 qid; 996 | __u16 rsvd10; 997 | __u32 rsvd11[5]; 998 | }; 999 | 1000 | struct nvme_abort_cmd { 1001 | __u8 opcode; 1002 | __u8 flags; 1003 | __u16 command_id; 1004 | __u32 rsvd1[9]; 1005 | __le16 sqid; 1006 | __u16 cid; 1007 | __u32 rsvd11[5]; 1008 | }; 1009 | 1010 | struct nvme_download_firmware { 1011 | __u8 opcode; 1012 | __u8 flags; 1013 | __u16 command_id; 1014 | __u32 rsvd1[5]; 1015 | union nvme_data_ptr dptr; 1016 | __le32 numd; 1017 | __le32 offset; 1018 | __u32 rsvd12[4]; 1019 | }; 1020 | 1021 | struct nvme_format_cmd { 1022 | __u8 opcode; 1023 | __u8 flags; 1024 | __u16 command_id; 1025 | __le32 nsid; 1026 | __u64 rsvd2[4]; 1027 | __le32 cdw10; 1028 | __u32 rsvd11[5]; 1029 | }; 1030 | 1031 | struct nvme_get_log_page_command { 1032 | __u8 opcode; 1033 | __u8 flags; 1034 | __u16 command_id; 1035 | __le32 nsid; 1036 | __u64 rsvd2[2]; 1037 | union nvme_data_ptr dptr; 1038 | __u8 lid; 1039 | __u8 lsp; /* upper 4 bits reserved */ 1040 | __le16 numdl; 1041 | __le16 numdu; 1042 | __u16 rsvd11; 1043 | union { 1044 | struct { 1045 | __le32 lpol; 1046 | __le32 lpou; 1047 | }; 1048 | __le64 lpo; 1049 | }; 1050 | __u32 rsvd14[2]; 1051 | }; 1052 | 1053 | struct nvme_directive_cmd { 1054 | __u8 opcode; 1055 | __u8 flags; 1056 | __u16 command_id; 1057 | __le32 nsid; 1058 | __u64 rsvd2[2]; 1059 | union nvme_data_ptr dptr; 1060 | __le32 numd; 1061 | __u8 doper; 1062 | __u8 dtype; 1063 | __le16 dspec; 1064 | __u8 endir; 1065 | __u8 tdtype; 1066 | __u16 rsvd15; 1067 | 1068 | __u32 rsvd16[3]; 1069 | }; 1070 | 1071 | /* 1072 | * Fabrics subcommands. 1073 | */ 1074 | enum nvmf_fabrics_opcode { 1075 | nvme_fabrics_command = 0x7f, 1076 | }; 1077 | 1078 | enum nvmf_capsule_command { 1079 | nvme_fabrics_type_property_set = 0x00, 1080 | nvme_fabrics_type_connect = 0x01, 1081 | nvme_fabrics_type_property_get = 0x04, 1082 | }; 1083 | 1084 | #define nvme_fabrics_type_name(type) { type, #type } 1085 | #define show_fabrics_type_name(type) \ 1086 | __print_symbolic(type, \ 1087 | nvme_fabrics_type_name(nvme_fabrics_type_property_set), \ 1088 | nvme_fabrics_type_name(nvme_fabrics_type_connect), \ 1089 | nvme_fabrics_type_name(nvme_fabrics_type_property_get)) 1090 | 1091 | /* 1092 | * If not fabrics command, fctype will be ignored. 1093 | */ 1094 | #define show_opcode_name(qid, opcode, fctype) \ 1095 | ((opcode) == nvme_fabrics_command ? \ 1096 | show_fabrics_type_name(fctype) : \ 1097 | ((qid) ? \ 1098 | show_nvm_opcode_name(opcode) : \ 1099 | show_admin_opcode_name(opcode))) 1100 | 1101 | struct nvmf_common_command { 1102 | __u8 opcode; 1103 | __u8 resv1; 1104 | __u16 command_id; 1105 | __u8 fctype; 1106 | __u8 resv2[35]; 1107 | __u8 ts[24]; 1108 | }; 1109 | 1110 | /* 1111 | * The legal cntlid range a NVMe Target will provide. 1112 | * Note that cntlid of value 0 is considered illegal in the fabrics world. 1113 | * Devices based on earlier specs did not have the subsystem concept; 1114 | * therefore, those devices had their cntlid value set to 0 as a result. 1115 | */ 1116 | #define NVME_CNTLID_MIN 1 1117 | #define NVME_CNTLID_MAX 0xffef 1118 | #define NVME_CNTLID_DYNAMIC 0xffff 1119 | 1120 | #define MAX_DISC_LOGS 255 1121 | 1122 | /* Discovery log page entry */ 1123 | struct nvmf_disc_rsp_page_entry { 1124 | __u8 trtype; 1125 | __u8 adrfam; 1126 | __u8 subtype; 1127 | __u8 treq; 1128 | __le16 portid; 1129 | __le16 cntlid; 1130 | __le16 asqsz; 1131 | __u8 resv8[22]; 1132 | char trsvcid[NVMF_TRSVCID_SIZE]; 1133 | __u8 resv64[192]; 1134 | char subnqn[NVMF_NQN_FIELD_LEN]; 1135 | char traddr[NVMF_TRADDR_SIZE]; 1136 | union tsas { 1137 | char common[NVMF_TSAS_SIZE]; 1138 | struct rdma { 1139 | __u8 qptype; 1140 | __u8 prtype; 1141 | __u8 cms; 1142 | __u8 resv3[5]; 1143 | __u16 pkey; 1144 | __u8 resv10[246]; 1145 | } rdma; 1146 | } tsas; 1147 | }; 1148 | 1149 | /* Discovery log page header */ 1150 | struct nvmf_disc_rsp_page_hdr { 1151 | __le64 genctr; 1152 | __le64 numrec; 1153 | __le16 recfmt; 1154 | __u8 resv14[1006]; 1155 | struct nvmf_disc_rsp_page_entry entries[0]; 1156 | }; 1157 | 1158 | enum { 1159 | NVME_CONNECT_DISABLE_SQFLOW = (1 << 2), 1160 | }; 1161 | 1162 | struct nvmf_connect_command { 1163 | __u8 opcode; 1164 | __u8 resv1; 1165 | __u16 command_id; 1166 | __u8 fctype; 1167 | __u8 resv2[19]; 1168 | union nvme_data_ptr dptr; 1169 | __le16 recfmt; 1170 | __le16 qid; 1171 | __le16 sqsize; 1172 | __u8 cattr; 1173 | __u8 resv3; 1174 | __le32 kato; 1175 | __u8 resv4[12]; 1176 | }; 1177 | 1178 | struct nvmf_connect_data { 1179 | uuid_t hostid; 1180 | __le16 cntlid; 1181 | char resv4[238]; 1182 | char subsysnqn[NVMF_NQN_FIELD_LEN]; 1183 | char hostnqn[NVMF_NQN_FIELD_LEN]; 1184 | char resv5[256]; 1185 | }; 1186 | 1187 | struct nvmf_property_set_command { 1188 | __u8 opcode; 1189 | __u8 resv1; 1190 | __u16 command_id; 1191 | __u8 fctype; 1192 | __u8 resv2[35]; 1193 | __u8 attrib; 1194 | __u8 resv3[3]; 1195 | __le32 offset; 1196 | __le64 value; 1197 | __u8 resv4[8]; 1198 | }; 1199 | 1200 | struct nvmf_property_get_command { 1201 | __u8 opcode; 1202 | __u8 resv1; 1203 | __u16 command_id; 1204 | __u8 fctype; 1205 | __u8 resv2[35]; 1206 | __u8 attrib; 1207 | __u8 resv3[3]; 1208 | __le32 offset; 1209 | __u8 resv4[16]; 1210 | }; 1211 | 1212 | struct nvme_dbbuf { 1213 | __u8 opcode; 1214 | __u8 flags; 1215 | __u16 command_id; 1216 | __u32 rsvd1[5]; 1217 | __le64 prp1; 1218 | __le64 prp2; 1219 | __u32 rsvd12[6]; 1220 | }; 1221 | 1222 | struct streams_directive_params { 1223 | __le16 msl; 1224 | __le16 nssa; 1225 | __le16 nsso; 1226 | __u8 rsvd[10]; 1227 | __le32 sws; 1228 | __le16 sgs; 1229 | __le16 nsa; 1230 | __le16 nso; 1231 | __u8 rsvd2[6]; 1232 | }; 1233 | 1234 | struct nvme_command { 1235 | union { 1236 | struct nvme_common_command common; 1237 | struct nvme_rw_command rw; 1238 | struct nvme_identify identify; 1239 | struct nvme_features features; 1240 | struct nvme_create_cq create_cq; 1241 | struct nvme_create_sq create_sq; 1242 | struct nvme_delete_queue delete_queue; 1243 | struct nvme_download_firmware dlfw; 1244 | struct nvme_format_cmd format; 1245 | struct nvme_dsm_cmd dsm; 1246 | struct nvme_write_zeroes_cmd write_zeroes; 1247 | struct nvme_abort_cmd abort; 1248 | struct nvme_get_log_page_command get_log_page; 1249 | struct nvmf_common_command fabrics; 1250 | struct nvmf_connect_command connect; 1251 | struct nvmf_property_set_command prop_set; 1252 | struct nvmf_property_get_command prop_get; 1253 | struct nvme_dbbuf dbbuf; 1254 | struct nvme_directive_cmd directive; 1255 | }; 1256 | }; 1257 | 1258 | static inline bool nvme_is_fabrics(struct nvme_command *cmd) 1259 | { 1260 | return cmd->common.opcode == nvme_fabrics_command; 1261 | } 1262 | 1263 | struct nvme_error_slot { 1264 | __le64 error_count; 1265 | __le16 sqid; 1266 | __le16 cmdid; 1267 | __le16 status_field; 1268 | __le16 param_error_location; 1269 | __le64 lba; 1270 | __le32 nsid; 1271 | __u8 vs; 1272 | __u8 resv[3]; 1273 | __le64 cs; 1274 | __u8 resv2[24]; 1275 | }; 1276 | 1277 | static inline bool nvme_is_write(struct nvme_command *cmd) 1278 | { 1279 | /* 1280 | * What a mess... 1281 | * 1282 | * Why can't we simply have a Fabrics In and Fabrics out command? 1283 | */ 1284 | if (unlikely(nvme_is_fabrics(cmd))) 1285 | return cmd->fabrics.fctype & 1; 1286 | return cmd->common.opcode & 1; 1287 | } 1288 | 1289 | enum { 1290 | /* 1291 | * Generic Command Status: 1292 | */ 1293 | NVME_SC_SUCCESS = 0x0, 1294 | NVME_SC_INVALID_OPCODE = 0x1, 1295 | NVME_SC_INVALID_FIELD = 0x2, 1296 | NVME_SC_CMDID_CONFLICT = 0x3, 1297 | NVME_SC_DATA_XFER_ERROR = 0x4, 1298 | NVME_SC_POWER_LOSS = 0x5, 1299 | NVME_SC_INTERNAL = 0x6, 1300 | NVME_SC_ABORT_REQ = 0x7, 1301 | NVME_SC_ABORT_QUEUE = 0x8, 1302 | NVME_SC_FUSED_FAIL = 0x9, 1303 | NVME_SC_FUSED_MISSING = 0xa, 1304 | NVME_SC_INVALID_NS = 0xb, 1305 | NVME_SC_CMD_SEQ_ERROR = 0xc, 1306 | NVME_SC_SGL_INVALID_LAST = 0xd, 1307 | NVME_SC_SGL_INVALID_COUNT = 0xe, 1308 | NVME_SC_SGL_INVALID_DATA = 0xf, 1309 | NVME_SC_SGL_INVALID_METADATA = 0x10, 1310 | NVME_SC_SGL_INVALID_TYPE = 0x11, 1311 | 1312 | NVME_SC_SGL_INVALID_OFFSET = 0x16, 1313 | NVME_SC_SGL_INVALID_SUBTYPE = 0x17, 1314 | 1315 | NVME_SC_NS_WRITE_PROTECTED = 0x20, 1316 | 1317 | NVME_SC_LBA_RANGE = 0x80, 1318 | NVME_SC_CAP_EXCEEDED = 0x81, 1319 | NVME_SC_NS_NOT_READY = 0x82, 1320 | NVME_SC_RESERVATION_CONFLICT = 0x83, 1321 | 1322 | /* 1323 | * Command Specific Status: 1324 | */ 1325 | NVME_SC_CQ_INVALID = 0x100, 1326 | NVME_SC_QID_INVALID = 0x101, 1327 | NVME_SC_QUEUE_SIZE = 0x102, 1328 | NVME_SC_ABORT_LIMIT = 0x103, 1329 | NVME_SC_ABORT_MISSING = 0x104, 1330 | NVME_SC_ASYNC_LIMIT = 0x105, 1331 | NVME_SC_FIRMWARE_SLOT = 0x106, 1332 | NVME_SC_FIRMWARE_IMAGE = 0x107, 1333 | NVME_SC_INVALID_VECTOR = 0x108, 1334 | NVME_SC_INVALID_LOG_PAGE = 0x109, 1335 | NVME_SC_INVALID_FORMAT = 0x10a, 1336 | NVME_SC_FW_NEEDS_CONV_RESET = 0x10b, 1337 | NVME_SC_INVALID_QUEUE = 0x10c, 1338 | NVME_SC_FEATURE_NOT_SAVEABLE = 0x10d, 1339 | NVME_SC_FEATURE_NOT_CHANGEABLE = 0x10e, 1340 | NVME_SC_FEATURE_NOT_PER_NS = 0x10f, 1341 | NVME_SC_FW_NEEDS_SUBSYS_RESET = 0x110, 1342 | NVME_SC_FW_NEEDS_RESET = 0x111, 1343 | NVME_SC_FW_NEEDS_MAX_TIME = 0x112, 1344 | NVME_SC_FW_ACTIVATE_PROHIBITED = 0x113, 1345 | NVME_SC_OVERLAPPING_RANGE = 0x114, 1346 | NVME_SC_NS_INSUFFICIENT_CAP = 0x115, 1347 | NVME_SC_NS_ID_UNAVAILABLE = 0x116, 1348 | NVME_SC_NS_ALREADY_ATTACHED = 0x118, 1349 | NVME_SC_NS_IS_PRIVATE = 0x119, 1350 | NVME_SC_NS_NOT_ATTACHED = 0x11a, 1351 | NVME_SC_THIN_PROV_NOT_SUPP = 0x11b, 1352 | NVME_SC_CTRL_LIST_INVALID = 0x11c, 1353 | 1354 | /* 1355 | * I/O Command Set Specific - NVM commands: 1356 | */ 1357 | NVME_SC_BAD_ATTRIBUTES = 0x180, 1358 | NVME_SC_INVALID_PI = 0x181, 1359 | NVME_SC_READ_ONLY = 0x182, 1360 | NVME_SC_ONCS_NOT_SUPPORTED = 0x183, 1361 | 1362 | /* 1363 | * I/O Command Set Specific - Fabrics commands: 1364 | */ 1365 | NVME_SC_CONNECT_FORMAT = 0x180, 1366 | NVME_SC_CONNECT_CTRL_BUSY = 0x181, 1367 | NVME_SC_CONNECT_INVALID_PARAM = 0x182, 1368 | NVME_SC_CONNECT_RESTART_DISC = 0x183, 1369 | NVME_SC_CONNECT_INVALID_HOST = 0x184, 1370 | 1371 | NVME_SC_DISCOVERY_RESTART = 0x190, 1372 | NVME_SC_AUTH_REQUIRED = 0x191, 1373 | 1374 | /* 1375 | * Media and Data Integrity Errors: 1376 | */ 1377 | NVME_SC_WRITE_FAULT = 0x280, 1378 | NVME_SC_READ_ERROR = 0x281, 1379 | NVME_SC_GUARD_CHECK = 0x282, 1380 | NVME_SC_APPTAG_CHECK = 0x283, 1381 | NVME_SC_REFTAG_CHECK = 0x284, 1382 | NVME_SC_COMPARE_FAILED = 0x285, 1383 | NVME_SC_ACCESS_DENIED = 0x286, 1384 | NVME_SC_UNWRITTEN_BLOCK = 0x287, 1385 | 1386 | /* 1387 | * Path-related Errors: 1388 | */ 1389 | NVME_SC_ANA_PERSISTENT_LOSS = 0x301, 1390 | NVME_SC_ANA_INACCESSIBLE = 0x302, 1391 | NVME_SC_ANA_TRANSITION = 0x303, 1392 | NVME_SC_HOST_PATH_ERROR = 0x370, 1393 | 1394 | NVME_SC_CRD = 0x1800, 1395 | NVME_SC_DNR = 0x4000, 1396 | }; 1397 | 1398 | struct nvme_completion { 1399 | /* 1400 | * Used by Admin and Fabrics commands to return data: 1401 | */ 1402 | union nvme_result { 1403 | __le16 u16; 1404 | __le32 u32; 1405 | __le64 u64; 1406 | } result; 1407 | __le16 sq_head; /* how much of this queue may be reclaimed */ 1408 | __le16 sq_id; /* submission queue that generated this entry */ 1409 | __u16 command_id; /* of the command which completed */ 1410 | __le16 status; /* did the command fail, and if so, why? */ 1411 | }; 1412 | 1413 | #define NVME_VS(major, minor, tertiary) \ 1414 | (((major) << 16) | ((minor) << 8) | (tertiary)) 1415 | 1416 | #define NVME_MAJOR(ver) ((ver) >> 16) 1417 | #define NVME_MINOR(ver) (((ver) >> 8) & 0xff) 1418 | #define NVME_TERTIARY(ver) ((ver) & 0xff) 1419 | 1420 | #undef unlikely 1421 | 1422 | #pragma pack(pop) 1423 | } 1424 | #endif /* nvme_h */ 1425 | -------------------------------------------------------------------------------- /NVMeFix/nvme_apst.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // @file nvme_apst.cpp 3 | // 4 | // NVMeFix 5 | // 6 | // Copyright © 2020 acidanthera. All rights reserved. 7 | // 8 | // This program and the accompanying materials 9 | // are licensed and made available under the terms and conditions of the BSD License 10 | // which accompanies this distribution. The full text of the license may be found at 11 | // http://opensource.org/licenses/bsd-license.php 12 | // THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 14 | 15 | // SPDX-License-Identifier: GPL-2.0 16 | /* 17 | * NVM Express device driver 18 | * Portions Copyright (c) 2011-2014, Intel Corporation. 19 | */ 20 | 21 | #include "Log.hpp" 22 | #include "NVMeFixPlugin.hpp" 23 | 24 | bool NVMeFixPlugin::enableAPST(ControllerEntry& entry, const NVMe::nvme_id_ctrl* ctrl) { 25 | #ifdef DEBUG 26 | if (APSTenabled(entry, entry.apste) == kIOReturnSuccess) 27 | DBGLOG(Log::APST, "APST status %d", entry.apste); 28 | #endif 29 | 30 | if (entry.apstAllowed()) { 31 | DBGLOG("apst", "Configuring APST"); 32 | auto res = configureAPST(entry, ctrl); 33 | if (res != kIOReturnSuccess) { 34 | DBGLOG("nvmef", "Failed to configure APST with 0x%x", res); 35 | entry.apste = false; 36 | } else /* Assume we turn APST on without double checking in RELEASE builds */ 37 | entry.apste = true; 38 | } else 39 | DBGLOG(Log::APST, "Not configuring APST (it is already enabled or quirks prohibit it)"); 40 | 41 | #ifdef DEBUG 42 | if (APSTenabled(entry, entry.apste) == kIOReturnSuccess) { 43 | DBGLOG(Log::APST, "APST status %d", entry.apste); 44 | } 45 | if (entry.apste && dumpAPST(entry, ctrl->npss)) 46 | DBGLOG(Log::APST, "Failed to dump APST table"); 47 | #endif 48 | 49 | entry.controller->setProperty("apst", entry.apste); 50 | return entry.apste; 51 | } 52 | 53 | /* linux/drivers/nvme/host/core.c:nvme_configure_apst */ 54 | IOReturn NVMeFixPlugin::configureAPST(ControllerEntry& entry, const NVMe::nvme_id_ctrl* ctrl) { 55 | assert(ctrl); 56 | assert(entry.controller); 57 | 58 | if (!ctrl->apsta) { 59 | SYSLOG(Log::APST, "APST unsupported by this controller"); 60 | return kIOReturnUnsupported; 61 | } 62 | if (ctrl->npss > 31) { 63 | SYSLOG(Log::APST, "Invalid NPSS"); 64 | return kIOReturnUnsupported; 65 | } 66 | 67 | auto ret = kIOReturnSuccess; 68 | auto apstDesc = IOBufferMemoryDescriptor::withCapacity(sizeof(NVMe::nvme_feat_auto_pst), 69 | kIODirectionOut); 70 | 71 | if (!apstDesc) { 72 | SYSLOG(Log::APST, "Failed to create APST table descriptor"); 73 | return kIOReturnNoResources; 74 | } 75 | 76 | auto apstTable = reinterpret_cast(apstDesc->getBytesNoCopy()); 77 | int max_ps {-1}; 78 | 79 | if (apstTable) { 80 | memset(apstTable, '\0', sizeof(*apstTable)); 81 | 82 | uint64_t target {0}; 83 | uint64_t max_lat_us {0}; 84 | 85 | /* 86 | * Walk through all states from lowest- to highest-power. 87 | * According to the spec, lower-numbered states use more 88 | * power. NPSS, despite the name, is the index of the 89 | * lowest-power state, not the number of states. 90 | */ 91 | for (int state = ctrl->npss; state >= 0; state--) { 92 | if (target) { 93 | apstTable->entries[state] = target; 94 | DBGLOG(Log::APST, "Set entry %d to 0x%llx", state, target); 95 | } 96 | 97 | /* 98 | * Don't allow transitions to the deepest state 99 | * if it's quirked off. 100 | */ 101 | if (state == ctrl->npss && (entry.quirks & NVMe::nvme_quirks::NVME_QUIRK_NO_DEEPEST_PS)) 102 | continue; 103 | 104 | /* 105 | * Is this state a useful non-operational state for 106 | * higher-power states to autonomously transition to? 107 | */ 108 | if (!(ctrl->psd[state].flags & NVMe::NVME_PS_FLAGS_NON_OP_STATE)) 109 | continue; 110 | 111 | uint64_t exit_latency_us = ctrl->psd[state].exit_lat; 112 | if (exit_latency_us > entry.ps_max_latency_us) 113 | continue; 114 | 115 | uint64_t total_latency_us = exit_latency_us + ctrl->psd[state].entry_lat; 116 | /* 117 | * This state is good. Use it as the APST idle 118 | * target for higher power states. 119 | */ 120 | uint64_t transition_ms = total_latency_us + 19; 121 | transition_ms /= 20; 122 | if (transition_ms > (1ull << 24) - 1) 123 | transition_ms = (1ull << 24) - 1; 124 | 125 | target = (state << 3ull) | (transition_ms << 8ull); 126 | 127 | if (max_ps == -1) 128 | max_ps = state; 129 | 130 | if (total_latency_us > max_lat_us) 131 | max_lat_us = total_latency_us; 132 | } 133 | 134 | if (max_ps == -1) 135 | DBGLOG(Log::APST, "No non-operational states are available"); 136 | else 137 | DBGLOG(Log::APST, "APST enabled: max PS = %d, max round-trip latency = %lluus\n", 138 | max_ps, max_lat_us); 139 | } else 140 | SYSLOG(Log::APST, "Failed to get table buffer"); 141 | 142 | if (max_ps != -1) { 143 | uint32_t dword11 {1}; 144 | ret = NVMeFeatures(entry, NVMe::NVME_FEAT_AUTO_PST, &dword11, apstDesc, nullptr, true); 145 | } 146 | 147 | if (apstDesc) 148 | apstDesc->release(); 149 | return ret; 150 | } 151 | 152 | IOReturn NVMeFixPlugin::APSTenabled(ControllerEntry& entry, bool& enabled) { 153 | uint32_t res {}; 154 | auto ret = NVMeFeatures(entry, NVMe::NVME_FEAT_AUTO_PST, nullptr, nullptr, &res, false); 155 | 156 | if (ret != kIOReturnSuccess) 157 | DBGLOG(Log::APST, "Failed to get features"); 158 | else 159 | enabled = res; 160 | return ret; 161 | } 162 | 163 | IOReturn NVMeFixPlugin::dumpAPST(ControllerEntry& entry, int npss) { 164 | assert(entry.controller); 165 | 166 | auto ret = kIOReturnSuccess; 167 | auto apstDesc = IOBufferMemoryDescriptor::withCapacity(sizeof(NVMe::nvme_feat_auto_pst), 168 | kIODirectionIn); 169 | if (!apstDesc) { 170 | SYSLOG(Log::APST, "Failed to create APST table descriptor"); 171 | return kIOReturnNoResources; 172 | } 173 | 174 | auto apstTable = static_cast(apstDesc->getBytesNoCopy()); 175 | memset(apstTable, '\0', apstDesc->getLength()); 176 | 177 | if (NVMeFeatures(entry, NVMe::NVME_FEAT_AUTO_PST, nullptr, apstDesc, nullptr, false) != 178 | kIOReturnSuccess) { 179 | DBGLOG(Log::APST, "Failed to get features"); 180 | goto fail; 181 | } 182 | 183 | for (int state = npss; state >= 0; state--) 184 | DBGLOG(Log::APST, "entry %d : 0x%llx", state, apstTable->entries[state]); 185 | 186 | fail: 187 | if (apstDesc) 188 | apstDesc->release(); 189 | return ret; 190 | } 191 | -------------------------------------------------------------------------------- /NVMeFix/nvme_pm.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // @file nvme_pm.cpp 3 | // 4 | // NVMeFix 5 | // 6 | // Copyright © 2019 acidanthera. All rights reserved. 7 | // 8 | // This program and the accompanying materials 9 | // are licensed and made available under the terms and conditions of the BSD License 10 | // which accompanies this distribution. The full text of the license may be found at 11 | // http://opensource.org/licenses/bsd-license.php 12 | // THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "Log.hpp" 21 | #include "NVMeFixPlugin.hpp" 22 | 23 | /** 24 | * For Apple Controllers, AppleNVMeController toggles self-refresh for low-power states, and 25 | * completely ignores PCI PM. For generic controllers, IONVMeController uses PCI PM and ignores 26 | * NVMe Power Management features. 27 | * We implement active power management by attaching our own IOService to PM root and registering 28 | * the operational power states of the controller. We intercept activityTickle method of the relevant 29 | * IONVMeController to tickle our service, and use NVMe power management feature to set the 30 | * corresponding state. 31 | * Our PM should operate transparently w.r.t PCI link PM and APST. APST is still useful, because typical 32 | * idle intervals for APST transitions are in order of hundreds of milliseconds, while IOPM has seconds 33 | * resolution; PCI link power management is still used by IONVMeController. 34 | * As we never transition to idle states, we need not freeze the command queue, so we don't have to 35 | * touch the internal state of IONVMe. 36 | */ 37 | bool NVMeFixPlugin::PM::init(ControllerEntry& entry, const NVMe::nvme_id_ctrl* ctrl, bool apst) { 38 | entry.pm = new NVMePMProxy(); 39 | if (entry.pm && !entry.pm->init()) { 40 | DBGLOG(Log::PM, "Failed to init IOService"); 41 | return false; 42 | } 43 | static_cast(entry.pm)->entry = &entry; 44 | 45 | // If we did not manage to enable APST, assume we can't reenable it next 46 | if (apst) { 47 | DBGLOG(Log::PM, "Registering power change interest"); 48 | entry.controller->registerInterestedDriver(entry.pm); 49 | entry.nstates = 0; 50 | entry.powerStates = nullptr; 51 | } 52 | 53 | entry.pm->PMinit(); 54 | auto root = IOService::getPMRootDomain(); 55 | if (root) 56 | reinterpret_cast(root)->joinPMtree(entry.pm); 57 | assert(root); 58 | 59 | if (!apst) 60 | return initActivePM(entry, ctrl); 61 | 62 | return true; 63 | } 64 | 65 | bool NVMeFixPlugin::PM::initActivePM(ControllerEntry& entry, const NVMe::nvme_id_ctrl* ctrl) { 66 | unsigned op {}; 67 | 68 | for (int state = ctrl->npss; state >= 0; state--) 69 | if (!(ctrl->psd[state].flags & NVMe::NVME_PS_FLAGS_NON_OP_STATE)) 70 | op++; 71 | 72 | entry.powerStates = new IOPMPowerState[entry.nstates]; 73 | if (!entry.powerStates) { 74 | SYSLOG(Log::PM, "Failed to allocate power state buffer"); 75 | return false; 76 | } 77 | 78 | if (op <= 1) 79 | SYSLOG(Log::PM, "Controller declares too few operational power states"); 80 | DBGLOG(Log::PM, "npss 0x%x", ctrl->npss); 81 | 82 | entry.powerStates[0] = {kIOPMPowerStateVersion1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 83 | 84 | /** 85 | * Linux has a different model: they save state upon suspend and switch to 86 | * npss state; upon resume, they restore last state. They also reset ps 87 | * when they fail to set or get it, but it's unclear how that would ever 88 | * occur given that NVMe 1.4 spec 5.21.1.2 only mentions error when trying 89 | * to set unsupported state. 90 | */ 91 | size_t idx {1}; 92 | for (int state = ctrl->npss; state >= 0; state--) { 93 | if (ctrl->psd[state].flags & NVMe::NVME_PS_FLAGS_NON_OP_STATE) 94 | continue; 95 | 96 | auto& ps = entry.powerStates[idx]; 97 | ps = {kIOPMPowerStateVersion1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 98 | /** 99 | * We shouldn't have any IOPM clients that require power, so don't 100 | * set outputPowerCharacter. 101 | */ 102 | // ps.outputPowerCharacter = kIOPMPowerOn; 103 | ps.inputPowerRequirement = kIOPMPowerOn; 104 | 105 | /* In non-operational state device is not usable and we may sleep */ 106 | ps.capabilityFlags |= kIOPMPreventIdleSleep; 107 | ps.capabilityFlags |= kIOPMDeviceUsable; 108 | DBGLOG(Log::PM, "Setting ps %u capabilityFlags 0x%x", idx, ps.capabilityFlags); 109 | idx++; 110 | } 111 | 112 | DBGLOG(Log::PM, "Publishing %u states", entry.nstates); 113 | 114 | auto status = entry.pm->registerPowerDriver(entry.pm, entry.powerStates, entry.nstates); 115 | if (status != kIOReturnSuccess) { 116 | SYSLOG(Log::PM, "registerPowerDriver failed with 0x%x", status); 117 | goto fail; 118 | } 119 | 120 | status = entry.pm->makeUsable(); 121 | if (status != kIOReturnSuccess) { 122 | SYSLOG(Log::PM, "makeUsable failed with 0x%x", status); 123 | goto fail; 124 | } 125 | 126 | entry.pm->changePowerStateTo(1); /* Clamp lowest PS at 1 */ 127 | entry.pm->setIdleTimerPeriod(idlePeriod); 128 | 129 | return true; 130 | fail: 131 | if (entry.powerStates) { 132 | delete[] entry.powerStates; 133 | entry.powerStates = nullptr; 134 | } 135 | /* Do not release PM IOService -- we need it for tracking controller power state change */ 136 | return false; 137 | } 138 | 139 | OSDefineMetaClassAndStructors(NVMePMProxy, IOService); 140 | 141 | IOReturn NVMePMProxy::setPowerState(unsigned long powerStateOrdinal, IOService *whatDevice) { 142 | DBGLOG(Log::PM, "setPowerState %lu", powerStateOrdinal); 143 | 144 | if (powerStateOrdinal == 0) 145 | return kIOPMAckImplied; 146 | 147 | auto& plugin = NVMeFixPlugin::globalPlugin(); 148 | 149 | unsigned dword11 = ((static_cast(entry->nstates) - 1) - 150 | static_cast(powerStateOrdinal)) & 0b1111; 151 | /* It's ok to skip active PM */ 152 | if (IOLockTryLock(entry->lck)) { 153 | uint32_t res {}; 154 | auto ret = plugin.NVMeFeatures(*entry, NVMe::NVME_FEAT_POWER_MGMT, nullptr, nullptr, &res, 155 | false); 156 | res &= 0b1111; 157 | 158 | DBGLOG(Log::PM, "Current ps 0x%x, proposed 0x%x", res, dword11); 159 | 160 | if (ret != kIOReturnSuccess) { 161 | SYSLOG(Log::PM, "Failed to get power state"); 162 | } else if (res < entry->nstates - 1) { /* Only transition to op state if we're not in nop state due to APST */ 163 | DBGLOG(Log::PM, "Setting power state 0x%x", dword11); 164 | 165 | ret = plugin.NVMeFeatures(*entry, NVMe::NVME_FEAT_POWER_MGMT, &dword11, nullptr, nullptr, 166 | true); 167 | if (ret != kIOReturnSuccess) 168 | SYSLOG(Log::PM, "Failed to set power state"); 169 | } 170 | 171 | IOLockUnlock(entry->lck); 172 | } else 173 | DBGLOG(Log::PM, "Failed to obtain entry lock"); 174 | 175 | /** 176 | * FIXME: We should return entry + exit + switching overhead latency here, but at least in my tests 177 | * it is always 0. 178 | */ 179 | return kIOPMAckImplied; /* No real way to signal error (not that we expect any) */ 180 | } 181 | 182 | IOReturn NVMePMProxy::powerStateDidChangeTo(IOPMPowerFlags capabilities, unsigned long stateNumber, 183 | IOService *whatDevice) { 184 | DBGLOG(Log::PM, "powerStateDidChangeTo 0x%x", stateNumber); 185 | 186 | /* FIXME: Should we ignore PAUSE->ACTIVE transition? */ 187 | if (!(capabilities & kIOPMDeviceUsable)) { 188 | DBGLOG(Log::PM, "Ignoring transition to non-usable state 0x%x", stateNumber); 189 | return kIOPMAckImplied; 190 | } 191 | 192 | auto& plugin = NVMeFixPlugin::globalPlugin(); 193 | 194 | /* We only get once chance after wake, so we insist on getting to critical section */ 195 | IOLockLock(entry->lck); 196 | NVMe::nvme_id_ctrl* identify {}; 197 | 198 | if (entry->controller != whatDevice) { 199 | DBGLOG(Log::PM, "Power state change for irrelevant device %s", 200 | whatDevice->getMetaClass()->getClassName()); 201 | goto done; 202 | } 203 | 204 | if (!entry->apstAllowed()) { 205 | DBGLOG(Log::PM, "APST not allowed"); 206 | goto done; 207 | } 208 | 209 | if (!entry->apste) { 210 | DBGLOG(Log::PM, "APST not enabled yet; not re-enabling"); 211 | goto done; 212 | } 213 | 214 | assert(entry->identify); 215 | identify = static_cast(entry->identify->getBytesNoCopy()); 216 | if (!identify) { 217 | DBGLOG(Log::PM, "Failed to get identify bytes"); 218 | goto done; 219 | } else if (!plugin.enableAPST(*entry, identify)) 220 | DBGLOG(Log::PM, "Failed to re-enable APST"); 221 | 222 | done: 223 | IOLockUnlock(entry->lck); 224 | 225 | return IOPMAckImplied; 226 | } 227 | 228 | bool NVMeFixPlugin::PM::solveSymbols(KernelPatcher& kp) { 229 | auto idx = plugin.kextInfo.loadIndex; 230 | bool ret = 231 | plugin.kextFuncs.IONVMeController.activityTickle.routeVirtual(kp, idx, 232 | "__ZTV16IONVMeController", 249, activityTickle); 233 | 234 | return ret; 235 | } 236 | 237 | bool NVMeFixPlugin::PM::activityTickle(void* controller, unsigned long type, unsigned long stateNumber) { 238 | auto& plugin = NVMeFixPlugin::globalPlugin(); 239 | 240 | ControllerEntry* entry {nullptr}; 241 | IOLockLock(plugin.lck); 242 | entry = plugin.entryForController(static_cast(controller)); 243 | IOLockUnlock(plugin.lck); 244 | 245 | /* If APST is enabled, we do not manage NVMe PM ourselves. */ 246 | /* We cannot avoid hooking activityTickle, however, as don't know if we have APST in advance */ 247 | if (entry && IOLockTryLock(entry->lck)) { 248 | if (!entry->apste && entry->powerStates && entry->pm) { 249 | // DBGLOG("pm", "activityTickle"); 250 | entry->pm->activityTickle(kIOPMSuperclassPolicy1, entry->nstates - 1); 251 | } 252 | IOLockUnlock(entry->lck); 253 | } 254 | 255 | return plugin.kextFuncs.IONVMeController.activityTickle(controller, type, stateNumber); 256 | } 257 | -------------------------------------------------------------------------------- /NVMeFix/nvme_quirks.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // @file nvme_quirks.cpp 3 | // 4 | // NVMeFix 5 | // 6 | // Copyright © 2019 acidanthera. All rights reserved. 7 | // 8 | // This program and the accompanying materials 9 | // are licensed and made available under the terms and conditions of the BSD License 10 | // which accompanies this distribution. The full text of the license may be found at 11 | // http://opensource.org/licenses/bsd-license.php 12 | // THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 14 | 15 | #include "Log.hpp" 16 | #include "nvme_quirks.hpp" 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | /* SPDX-License-Identifier: GPL-2.0 */ 27 | /* 28 | * Device tables which are exported to userspace via 29 | * scripts/mod/file2alias.c. You must keep that file in sync with this 30 | * header. 31 | */ 32 | 33 | /* linux/include/mod_devicetable.h */ 34 | 35 | /** 36 | * struct pci_device_id - PCI device ID structure 37 | * @vendor: Vendor ID to match (or PCI_ANY_ID) 38 | * @device: Device ID to match (or PCI_ANY_ID) 39 | * @subvendor: Subsystem vendor ID to match (or PCI_ANY_ID) 40 | * @subdevice: Subsystem device ID to match (or PCI_ANY_ID) 41 | * @class: Device class, subclass, and "interface" to match. 42 | * See Appendix D of the PCI Local Bus Spec or 43 | * include/linux/pci_ids.h for a full list of classes. 44 | * Most drivers do not need to specify class/class_mask 45 | * as vendor/device is normally sufficient. 46 | * @class_mask: Limit which sub-fields of the class field are compared. 47 | * See drivers/scsi/sym53c8xx_2/ for example of usage. 48 | * @driver_data: Data private to the driver. 49 | * Most drivers don't need to use driver_data field. 50 | * Best practice is to use driver_data as an index 51 | * into a static list of equivalent device types, 52 | * instead of using it as a pointer. 53 | */ 54 | struct pci_device_id { 55 | linux_types::__u32 vendor, device; /* Vendor and device ID or PCI_ANY_ID*/ 56 | // linux_types::__u32 subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */ 57 | // linux_types::__u32 /* class */ cls, class_mask; /* (class,subclass,prog-if) triplet */ 58 | /* kernel_ulong_t */ unsigned long driver_data; /* Data private to the driver */ 59 | }; 60 | 61 | // SPDX-License-Identifier: GPL-2.0 62 | /* 63 | * NVM Express device driver 64 | * Copyright (c) 2011-2014, Intel Corporation. 65 | */ 66 | 67 | /* linux/drivers/nvme/host/pci.c */ 68 | 69 | namespace NVMe { 70 | 71 | static constexpr struct pci_device_id nvme_id_table[] = { 72 | { 0x8086, 0x0953, 73 | NVME_QUIRK_STRIPE_SIZE | 74 | NVME_QUIRK_DEALLOCATE_ZEROES, }, 75 | { 0x8086, 0x0a53, 76 | NVME_QUIRK_STRIPE_SIZE | 77 | NVME_QUIRK_DEALLOCATE_ZEROES, }, 78 | { 0x8086, 0x0a54, 79 | NVME_QUIRK_STRIPE_SIZE | 80 | NVME_QUIRK_DEALLOCATE_ZEROES, }, 81 | { 0x8086, 0x0a55, 82 | NVME_QUIRK_STRIPE_SIZE | 83 | NVME_QUIRK_DEALLOCATE_ZEROES, }, 84 | { 0x8086, 0xf1a5, /* Intel 600P/P3100 */ 85 | NVME_QUIRK_NO_DEEPEST_PS | 86 | NVME_QUIRK_MEDIUM_PRIO_SQ }, 87 | { 0x8086, 0xf1a6, /* Intel 760p/Pro 7600p */ 88 | NVME_QUIRK_IGNORE_DEV_SUBNQN, }, 89 | { 0x8086, 0x5845, /* Qemu emulated controller */ 90 | NVME_QUIRK_IDENTIFY_CNS | 91 | NVME_QUIRK_DISABLE_WRITE_ZEROES, }, 92 | { 0x1bb1, 0x0100, /* Seagate Nytro Flash Storage */ 93 | NVME_QUIRK_DELAY_BEFORE_CHK_RDY, }, 94 | { 0x1c58, 0x0003, /* HGST adapter */ 95 | NVME_QUIRK_DELAY_BEFORE_CHK_RDY, }, 96 | { 0x1c58, 0x0023, /* WDC SN200 adapter */ 97 | NVME_QUIRK_DELAY_BEFORE_CHK_RDY, }, 98 | { 0x1c5f, 0x0540, /* Memblaze Pblaze4 adapter */ 99 | NVME_QUIRK_DELAY_BEFORE_CHK_RDY, }, 100 | { 0x144d, 0xa821, /* Samsung PM1725 */ 101 | NVME_QUIRK_DELAY_BEFORE_CHK_RDY, }, 102 | { 0x144d, 0xa822, /* Samsung PM1725a */ 103 | NVME_QUIRK_DELAY_BEFORE_CHK_RDY, }, 104 | { 0x1d1d, 0x1f1f, /* LighNVM qemu device */ 105 | NVME_QUIRK_LIGHTNVM, }, 106 | { 0x1d1d, 0x2807, /* CNEX WL */ 107 | NVME_QUIRK_LIGHTNVM, }, 108 | { 0x1d1d, 0x2601, /* CNEX Granby */ 109 | NVME_QUIRK_LIGHTNVM, }, 110 | { 0x10ec, 0x5762, /* ADATA SX6000LNP */ 111 | NVME_QUIRK_IGNORE_DEV_SUBNQN, }, 112 | { 0x1cc1, 0x8201, /* ADATA SX8200PNP 512GB */ 113 | NVME_QUIRK_NO_DEEPEST_PS | 114 | NVME_QUIRK_IGNORE_DEV_SUBNQN, }, 115 | 116 | /* Should be taken care of by IONVMeFamily */ 117 | #if 0 118 | { PCI_DEVICE(PCI_VENDOR_ID_APPLE, 0x2001) }, 119 | { PCI_DEVICE(PCI_VENDOR_ID_APPLE, 0x2003) }, 120 | { PCI_DEVICE(PCI_VENDOR_ID_APPLE, 0x2005), 121 | .driver_data = NVME_QUIRK_SINGLE_VECTOR | 122 | NVME_QUIRK_128_BYTES_SQES | 123 | NVME_QUIRK_SHARED_TAGS }, 124 | #endif 125 | { 0, } 126 | }; 127 | 128 | /** 129 | * FIXME: This will only work with Clover 130 | */ 131 | static nvme_quirks check_vendor_combination_bug(uint32_t vendor, uint32_t device) { 132 | auto platform = IORegistryEntry::fromPath("/efi/platform", gIODTPlane); 133 | unsigned ret = NVME_QUIRK_NONE; 134 | 135 | char vendorName[64]; 136 | char productName[64]; 137 | char boardName[64]; 138 | 139 | bool foundVendor {false}, foundProduct {false}, foundBoard {false}; 140 | 141 | auto getStrProp = [](auto& platform, auto name, auto& res) { 142 | auto ret = OSDynamicCast(OSData, platform->getProperty(name)); 143 | 144 | if (ret && ret->getLength() > 0 && ret->getBytesNoCopy()) { 145 | auto sz = min(ret->getLength(), sizeof(res)); 146 | lilu_os_memcpy(res, ret->getBytesNoCopy(), sz); 147 | res[sz - 1] = '\0'; 148 | DBGLOG(Log::Quirks, "Found %s = %s", name, res); 149 | 150 | return true; 151 | } else { 152 | DBGLOG(Log::Quirks, "Failed to find IODeviceTree:/efi/platform %s", name); 153 | return false; 154 | } 155 | }; 156 | 157 | /* Strings in NVRAM are not NUL-terminated */ 158 | auto getEFIProp = [](auto& ser, auto name, auto& res) { 159 | uint32_t attr; 160 | uint64_t szRead {sizeof(res)}; 161 | auto status = ser->getVariable(name, &EfiRuntimeServices::LiluVendorGuid, &attr, &szRead, res); 162 | if (status != EFI_SUCCESS) 163 | DBGLOG(Log::Quirks, "Failed to find LiluVendorGuid:%s", name); 164 | else { 165 | res[min(sizeof(res) - 1, szRead)] = '\0'; 166 | DBGLOG(Log::Quirks, "Found LiluVendorGuid:%s = %s", name, res); 167 | } 168 | 169 | return status == EFI_SUCCESS; 170 | }; 171 | 172 | auto getNVRAMProp = [](auto& storage, auto name, auto& res) { 173 | uint32_t szRead {sizeof(res)}; 174 | auto data = storage.read(name, szRead); 175 | if (!data) { 176 | DBGLOG(Log::Quirks, "Failed to find LiluVendorGuid:%s", name); 177 | return false; 178 | } else { 179 | auto sz = min(szRead, sizeof(res)); 180 | lilu_os_memcpy(res, data, sz); 181 | res[min(szRead, sizeof(res) - 1)] = '\0'; 182 | Buffer::deleter(data); 183 | DBGLOG(Log::Quirks, "Found LiluVendorGuid:%s = %s", name, res); 184 | return true; 185 | } 186 | }; 187 | 188 | if (platform) { 189 | DBGLOG(Log::Quirks, "Reading OEM info from IODT"); 190 | 191 | foundProduct = getStrProp(platform, "OEMProduct", productName); 192 | foundVendor = getStrProp(platform, "OEMVendor", vendorName); 193 | foundBoard = getStrProp(platform, "OEMBoard", boardName); 194 | } if (!foundProduct || !foundVendor || !foundBoard) { 195 | DBGLOG(Log::Quirks, "Reading OEM info from NVRAM"); 196 | 197 | NVStorage storage; 198 | if (storage.init()) { 199 | foundProduct = getNVRAMProp(storage, "oem-product", productName); 200 | foundVendor = getNVRAMProp(storage, "oem-vendor", vendorName); 201 | foundBoard = getNVRAMProp(storage, "oem-board", boardName); 202 | 203 | storage.deinit(); 204 | } else { 205 | auto ser = EfiRuntimeServices::get(); 206 | if (!ser) 207 | DBGLOG(Log::Quirks, "Failed to get EFI services"); 208 | else { 209 | foundProduct = getEFIProp(ser, u"oem-product", productName); 210 | foundVendor = getEFIProp(ser, u"oem-vendor", vendorName); 211 | foundBoard = getEFIProp(ser, u"oem-board", boardName); 212 | } 213 | } 214 | } 215 | 216 | if (vendor == 0x144d && device == 0xa802 && foundProduct && foundVendor) { 217 | /* 218 | * Several Samsung devices seem to drop off the PCIe bus 219 | * randomly when APST is on and uses the deepest sleep state. 220 | * This has been observed on a Samsung "SM951 NVMe SAMSUNG 221 | * 256GB", a "PM951 NVMe SAMSUNG 512GB", and a "Samsung SSD 222 | * 950 PRO 256GB", but it seems to be restricted to two Dell 223 | * laptops. 224 | */ 225 | if (!strcmp("Dell Inc.", vendorName) && (!strcmp(productName, "XPS 15 9550") || 226 | !strcmp(productName, "Precision 5510"))) 227 | ret |= NVME_QUIRK_NO_DEEPEST_PS; 228 | } else if (vendor == 0x144d && device == 0xa804 && foundVendor && foundBoard) { 229 | /* 230 | * Samsung SSD 960 EVO drops off the PCIe bus after system 231 | * suspend on a Ryzen board, ASUS PRIME B350M-A, as well as 232 | * within few minutes after bootup on a Coffee Lake board - 233 | * ASUS PRIME Z370-A 234 | */ 235 | if (!strcmp(vendorName, "ASUSTeK COMPUTER INC.") && (!strcmp(boardName, "PRIME B350M-A") || 236 | !strcmp(boardName, "PRIME Z370-A"))) 237 | ret |= NVME_QUIRK_NO_APST; 238 | } 239 | 240 | if (platform) 241 | platform->release(); 242 | 243 | return static_cast(ret); 244 | } 245 | 246 | nvme_quirks quirksForController(IOService* controller) { 247 | assert(controller); 248 | 249 | uint32_t vendor {0}, device {0}; 250 | propertyFromParent(controller, "vendor-id", vendor); 251 | propertyFromParent(controller, "device-id", device); 252 | 253 | auto parent = controller->getParentEntry(gIOServicePlane); 254 | if (!parent || !parent->metaCast("IOPCIDevice")) { 255 | DBGLOG(Log::Quirks, "Controller parent is not an IOPCIDevice"); 256 | return NVME_QUIRK_NONE; 257 | } 258 | 259 | if (!vendor || !device) { 260 | DBGLOG(Log::Quirks, "Failed to get vendor or device id"); 261 | return NVME_QUIRK_NONE; 262 | } 263 | 264 | unsigned ret = NVME_QUIRK_NONE; 265 | for (const auto& entry : nvme_id_table) 266 | if (vendor == entry.vendor && device == entry.device) 267 | ret |= entry.driver_data; 268 | 269 | ret |= check_vendor_combination_bug(vendor, device); 270 | 271 | return static_cast(ret); 272 | } 273 | 274 | struct nvme_core_quirk_entry { 275 | /* 276 | * NVMe model and firmware strings are padded with spaces. For 277 | * simplicity, strings in the quirk table are padded with NULLs 278 | * instead. 279 | */ 280 | linux_types::__u16 vid; 281 | const char *mn; 282 | const char *fr; 283 | unsigned long quirks; 284 | }; 285 | 286 | static const struct nvme_core_quirk_entry core_quirks[] = { 287 | { 288 | /* 289 | * This Toshiba device seems to die using any APST states. See: 290 | * https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1678184/comments/11 291 | */ 292 | 0x1179, 293 | "THNSF5256GPUK TOSHIBA", 294 | nullptr, 295 | NVME_QUIRK_NO_APST, 296 | }, 297 | { 298 | /* 299 | * This LiteON CL1-3D*-Q11 firmware version has a race 300 | * condition associated with actions related to suspend to idle 301 | * LiteON has resolved the problem in future firmware 302 | */ 303 | 0x14a4, 304 | nullptr, 305 | "22301111", 306 | NVME_QUIRK_SIMPLE_SUSPEND, 307 | }, 308 | { 309 | /* 310 | * This Kingston E8FK11.T firmware version has no interrupt 311 | * after resume with actions related to suspend to idle 312 | * https://bugzilla.kernel.org/show_bug.cgi?id=204887 313 | */ 314 | 0x2646, 315 | nullptr, 316 | "E8FK11", 317 | NVME_QUIRK_SIMPLE_SUSPEND, 318 | }, 319 | { 320 | /* 321 | * Kingston A2000 devices with 5Z42105 firmware can become 322 | * unresponsive after entering the deepest power state 323 | * https://lore.kernel.org/linux-nvme/20210129052442.310780-1-linux@leemhuis.info/ 324 | */ 325 | 0x2646, 326 | nullptr, 327 | "S5Z42105", 328 | NVME_QUIRK_NO_DEEPEST_PS, 329 | }, 330 | }; 331 | 332 | template 333 | static bool id_ctrl_match(const char* str, const char (&id_str)[S]) { 334 | if (str == nullptr) 335 | return true; 336 | 337 | /* Strings in the core quirk table end with NULL */ 338 | auto i = strlen(str); 339 | 340 | if (i > S || memcmp(str, id_str, i)) 341 | return false; 342 | 343 | /* Controller identity strings in struct nvme_id_ctrl are padded with spaces */ 344 | while (i < S) { 345 | if (id_str[i++] != 0x20) 346 | return false; 347 | } 348 | 349 | return true; 350 | } 351 | 352 | nvme_quirks quirksForController(uint16_t vid, mn_ref_t mn, fr_ref_t fr) { 353 | unsigned ret {NVME_QUIRK_NONE}; 354 | 355 | for (const auto& entry : core_quirks) { 356 | auto match {true}; 357 | match &= !entry.vid || entry.vid == vid; 358 | match &= id_ctrl_match(entry.mn, mn); 359 | match &= id_ctrl_match(entry.fr, fr); 360 | 361 | if (match) 362 | ret |= entry.quirks; 363 | } 364 | 365 | return static_cast(ret); 366 | } 367 | } 368 | -------------------------------------------------------------------------------- /NVMeFix/nvme_quirks.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // @file nvme_quirks.hpp 3 | // 4 | // NVMeFix 5 | // 6 | // Copyright © 2019 acidanthera. All rights reserved. 7 | // 8 | // This program and the accompanying materials 9 | // are licensed and made available under the terms and conditions of the BSD License 10 | // which accompanies this distribution. The full text of the license may be found at 11 | // http://opensource.org/licenses/bsd-license.php 12 | // THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 14 | 15 | 16 | #ifndef nvme_quirks_hpp 17 | #define nvme_quirks_hpp 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "linux_types.h" 24 | 25 | /* SPDX-License-Identifier: GPL-2.0 */ 26 | /* 27 | * Copyright (c) 2011-2014, Intel Corporation. 28 | */ 29 | 30 | /* linux/drivers/host/nvme.h */ 31 | namespace NVMe { 32 | 33 | /* 34 | * List of workarounds for devices that required behavior not specified in 35 | * the standard. 36 | */ 37 | enum nvme_quirks : unsigned long long { 38 | NVME_QUIRK_NONE = 0, 39 | /* 40 | * Prefers I/O aligned to a stripe size specified in a vendor 41 | * specific Identify field. 42 | */ 43 | NVME_QUIRK_STRIPE_SIZE = (1 << 0), 44 | 45 | /* 46 | * The controller doesn't handle Identify value others than 0 or 1 47 | * correctly. 48 | */ 49 | NVME_QUIRK_IDENTIFY_CNS = (1 << 1), 50 | 51 | /* 52 | * The controller deterministically returns O's on reads to 53 | * logical blocks that deallocate was called on. 54 | */ 55 | NVME_QUIRK_DEALLOCATE_ZEROES = (1 << 2), 56 | 57 | /* 58 | * The controller needs a delay before starts checking the device 59 | * readiness, which is done by reading the NVME_CSTS_RDY bit. 60 | */ 61 | NVME_QUIRK_DELAY_BEFORE_CHK_RDY = (1 << 3), 62 | 63 | /* 64 | * APST should not be used. 65 | */ 66 | NVME_QUIRK_NO_APST = (1 << 4), 67 | 68 | /* 69 | * The deepest sleep state should not be used. 70 | */ 71 | NVME_QUIRK_NO_DEEPEST_PS = (1 << 5), 72 | 73 | /* 74 | * Supports the LighNVM command set if indicated in vs[1]. 75 | */ 76 | NVME_QUIRK_LIGHTNVM = (1 << 6), 77 | 78 | /* 79 | * Set MEDIUM priority on SQ creation 80 | */ 81 | NVME_QUIRK_MEDIUM_PRIO_SQ = (1 << 7), 82 | 83 | /* 84 | * Ignore device provided subnqn. 85 | */ 86 | NVME_QUIRK_IGNORE_DEV_SUBNQN = (1 << 8), 87 | 88 | /* 89 | * Broken Write Zeroes. 90 | */ 91 | NVME_QUIRK_DISABLE_WRITE_ZEROES = (1 << 9), 92 | 93 | /* 94 | * Force simple suspend/resume path. 95 | */ 96 | NVME_QUIRK_SIMPLE_SUSPEND = (1 << 10), 97 | 98 | /* 99 | * Use only one interrupt vector for all queues 100 | */ 101 | NVME_QUIRK_SINGLE_VECTOR = (1 << 11), 102 | 103 | /* 104 | * Use non-standard 128 bytes SQEs. 105 | */ 106 | NVME_QUIRK_128_BYTES_SQES = (1 << 12), 107 | 108 | /* 109 | * Prevent tag overlap between queues 110 | */ 111 | NVME_QUIRK_SHARED_TAGS = (1 << 13), 112 | }; 113 | 114 | template 115 | constexpr nvme_quirks operator|(T a, T b) { 116 | return static_cast(static_cast(a) | 117 | static_cast(b)); 118 | } 119 | 120 | template 121 | constexpr nvme_quirks operator|=(T& a, T b) { 122 | a = a | b; 123 | return a; 124 | } 125 | 126 | template 127 | constexpr nvme_quirks operator&(T a, T b) { 128 | return static_cast(static_cast(a) & 129 | static_cast(b)); 130 | } 131 | 132 | template 133 | constexpr nvme_quirks operator&=(T& a, T b) { 134 | a = a & b; 135 | return a; 136 | } 137 | 138 | using mn_ref_t = const char(&)[40]; 139 | using fr_ref_t = const char(&)[8]; 140 | nvme_quirks quirksForController(IOService*); 141 | nvme_quirks quirksForController(uint16_t,mn_ref_t,fr_ref_t); 142 | } 143 | 144 | template 145 | static bool propertyFromParent(IOService* controller, const char* name, T& prop) { 146 | assertf(controller->metaCast("IONVMeController"), "Controller has wrong type"); 147 | 148 | auto parent = controller->getParentEntry(gIOServicePlane); 149 | if (!parent || !parent->metaCast("IOPCIDevice")) { 150 | DBGLOG("quirks", "Controller parent is not an IOPCIDevice"); 151 | return false; 152 | } 153 | 154 | auto data = parent->getProperty(name); 155 | if (data) 156 | return WIOKit::getOSDataValue(data, name, prop); 157 | else 158 | DBGLOG("nvmef", "Property %s not found for parent service", name); 159 | 160 | return true; 161 | } 162 | 163 | #endif /* nvme_quirks_hpp */ 164 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | NVMeFix 2 | ======= 3 | 4 | [![Build Status](https://github.com/acidanthera/NVMeFix/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/acidanthera/NVMeFix/actions) [![Scan Status](https://scan.coverity.com/projects/22192/badge.svg?flat=1)](https://scan.coverity.com/projects/22192) 5 | 6 | ***NOTE***: NVMeFix supports up to macOS Sequoia (15), some features might not be available on newer versions as of now, like the timeout panic fix. 7 | 8 | NVMeFix is a set of patches for the Apple NVMe storage driver, IONVMeFamily. 9 | Its goal is to improve compatibility with non-Apple SSDs. It may be used both on Apple and non-Apple 10 | computers. 11 | 12 | The following features are implemented: 13 | 14 | - Autonomous Power State Transition to reduce idle power consumption of the controller. 15 | - Host-driver active power state management. 16 | - Workaround for timeout panics on certain controllers (VMware, Samsung PM981). 17 | 18 | Other incompatibilities with third-party SSDs may be addressed provided enough information is 19 | submitted to our [bugtracker](https://github.com/acidanthera/bugtracker). 20 | 21 | Unfortunately, some issues cannot be fixed purely by a kernel-side driver. For example, MacBookPro 22 | 11,1 EFI includes an old version of NVMHCI DXE driver that causes a hang when resuming from 23 | hibernation with full disk encryption on. 24 | 25 | Installation 26 | ------------ 27 | 28 | NVMeFix requires at least Lilu 1.4.1 and at least 10.14 system version. It may be compatible with 29 | older systems, but has not been tested. 30 | 31 | It may be installed to `/Library/Extensions` on 10.15 and earlier, or injected by the bootloader on all versions. 32 | 33 | Configuration 34 | ------------- 35 | 36 | `-nvmefdbg` enables detailed logging for `DEBUG` build. 37 | 38 | `-nvmefoff` disables the kext. 39 | 40 | `-nvmefaspm` forces ASPM L1 on all the devices. This argument is recommended exclusively for testing purposes, 41 | as for daily usage one could inject `pci-aspm-default` device property with `<02 00 00 00>` value into the SSD devices and bridge devices they are connected to onboard. 42 | Updated values will be visible as `pci-aspm-custom` in the affected devices. 43 | 44 | Some SSDs misbehave when APST is on. NVMeFix attempts to detect broken motherboard and SSD 45 | combinations and work around them. Motherboard is detected via IORegistry keys injected by Clover, 46 | or NVRAM variables provided by OpenCore. 47 | 48 | APST table entries specify minimum idle latency for the transition to occur. Maximum acceptable 49 | latency is 100000 microseconds, and may be overriden via little-endian 8-byte property 50 | `ps-max-latency-us` of parent PCI device (e.g. 51 | `IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/RP06@1C,5/IOPP/SSD0@0`). If set to 0, APST 52 | will be disabled completely. 53 | 54 | Diagnostics 55 | ----------- 56 | 57 | `RELEASE` build will only log high-level information about failures. 58 | 59 | `DEBUG` build will additionally log used power states, detailed error messages, and attempt to 60 | fetch APST status and table from the controller. 61 | 62 | APST enable status is posted to the IONVMeController IORegistry entry `apst` key. 63 | 64 | If active power management initialisation is successful, an `NVMePMProxy` entry will be created 65 | in the IOPower IORegistry plane with IOPowerManagement dictionary. 66 | 67 | Information about power states supported by the controller may be obtained e.g. using `smartmontools`. 68 | For example, in the following output the controller reports 5 states, where the former three 69 | high-power states will be used by NVMeFix for active power management, and the latter two may be 70 | used for APST depending on `ps-max-latency-us`. 71 | 72 | Supported Power States 73 | St Op Max Active Idle RL RT WL WT Ent_Lat Ex_Lat 74 | 0 + 9.00W - - 0 0 0 0 0 0 75 | 1 + 4.60W - - 1 1 1 1 0 0 76 | 2 + 3.80W - - 2 2 2 2 0 0 77 | 3 - 0.0450W - - 3 3 3 3 2000 2000 78 | 4 - 0.0040W - - 4 4 4 4 6000 8000 79 | 80 | IONVMeFamily supports the following debug flag bitfield, which are passed either via `nvme` bootarg 81 | or `debug.NVMe` sysctl: 82 | 83 | 1: Log some events via kprintf 84 | 2: Detailed event trace via kernel_debug with 0x61500xx debugid 85 | 4: PRP-related event trace via kernel_debug with 0x61540xx debugid 86 | 8: Force disable LPSR for Apple controllers 87 | 16: Perform only PCI initialisation of NVMe controller 88 | 32: Ignore initialisation errors 89 | 128: Disable LPSR for Apple controllers 90 | 512: Disable Unmap feature for IONVMeBlockStorageDevice 91 | 92 | IONVMeFamily supports the following additional bootargs: 93 | 94 | nand-io-timeoutms: Timeout for NVMe requests in ms, 35 s by default 95 | enable-IO-log: Issue CORE_DEBUG_ENABLE_IOLOG ASP command (for Apple controllers) 96 | --------------------------------------------------------------------------------